blob: 298ee268314d64b80b565dde12d5dc7ec3ce61ea [file] [log] [blame]
/*
* Copyright (C) 2007 Nokia Corporation.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Author: Adrian Hunter
*/
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
struct child_info {
struct child_info *next;
pid_t pid;
int terminated;
int killed;
int gone;
};
struct child_info *children = 0;
void kill_children(void)
{
struct child_info *child;
child = children;
while (child) {
if (!child->gone) {
if (!child->terminated) {
child->terminated = 1;
kill(child->pid, SIGTERM);
} /*else if (!child->killed) {
child->killed = 1;
kill(child->pid, SIGKILL);
}*/
}
child = child->next;
}
}
void add_child(pid_t child_pid)
{
struct child_info *child;
size_t sz;
sz = sizeof(struct child_info);
child = (struct child_info *) malloc(sz);
memset(child, 0, sz);
child->pid = child_pid;
child->next = children;
children = child;
}
void mark_child_gone(pid_t child_pid)
{
struct child_info *child;
child = children;
while (child) {
if (child->pid == child_pid) {
child->gone = 1;
break;
}
child = child->next;
}
}
int have_children(void)
{
struct child_info *child;
child = children;
while (child) {
if (!child->gone)
return 1;
child = child->next;
}
return 0;
}
int parse_command_line(char *cmdline, int *pargc, char ***pargv)
{
char **tmp;
char *p, *v, *q;
size_t sz;
int argc = 0;
int state = 0;
char *argv[1024];
if (!cmdline)
return 1;
q = v = (char *) malloc(strlen(cmdline) + 1024);
if (!v)
return 1;
p = cmdline;
for (;;) {
char c = *p++;
if (!c) {
*v++ = 0;
break;
}
switch (state) {
case 0: /* Between args */
if (isspace(c))
break;
argv[argc++] = v;
if (c == '"') {
state = 2;
break;
} else if (c == '\'') {
state = 3;
break;
}
state = 1;
case 1: /* Not quoted */
if (c == '\\') {
if (*p)
*v++ = *p;
} else if (isspace(c)) {
*v++ = 0;
state = 0;
} else
*v++ = c;
break;
case 2: /* Double quoted */
if (c == '\\' && *p == '"') {
*v++ = '"';
++p;
} else if (c == '"') {
*v++ = 0;
state = 0;
} else
*v++ = c;
break;
case 3: /* Single quoted */
if (c == '\'') {
*v++ = 0;
state = 0;
} else
*v++ = c;
break;
}
}
argv[argc] = 0;
sz = sizeof(char *) * (argc + 1);
tmp = (char **) malloc(sz);
if (!tmp) {
free(q);
return 1;
}
if (argc == 0)
free(q);
memcpy(tmp, argv, sz);
*pargc = argc;
*pargv = tmp;
return 0;
}
void signal_handler(int signum)
{
kill_children();
}
int result = 0;
int alarm_gone_off = 0;
void alarm_handler(int signum)
{
if (!result)
alarm_gone_off = 1;
kill_children();
}
int main(int argc, char *argv[], char **env)
{
int p;
pid_t child_pid;
int status;
int duration = 0;
p = 1;
if (argc > 1) {
if (strncmp(argv[p], "--help", 6) == 0 ||
strncmp(argv[p], "-h", 2) == 0) {
printf( "Usage is: "
"fstest_monitor options programs...\n"
" Options are:\n"
" -h, --help "
"This help message\n"
" -d, --duration arg "
"Stop after arg seconds\n"
"\n"
"Run programs and wait for them."
" If duration is specified,\n"
"kill all programs"
" after that number of seconds have elapsed.\n"
"Example: "
"fstest_monitor \"/bin/ls -l\" /bin/date\n"
);
return 1;
}
if (strncmp(argv[p], "--duration", 10) == 0 ||
strncmp(argv[p], "-d", 2) == 0) {
char *s;
if (p+1 < argc && !isdigit(argv[p][strlen(argv[p])-1]))
++p;
s = argv[p];
while (*s && !isdigit(*s))
++s;
duration = atoi(s);
++p;
}
}
signal(SIGTERM, signal_handler);
signal(SIGINT, signal_handler);
for (; p < argc; ++p) {
child_pid = fork();
if (child_pid) {
/* Parent */
if (child_pid == (pid_t) -1) {
kill_children();
result = 1;
break;
}
add_child(child_pid);
} else {
/* Child */
int cargc;
char **cargv;
if (parse_command_line(argv[p], &cargc, &cargv))
return 1;
execve(cargv[0], cargv, env);
return 1;
}
}
if (!result && duration > 0) {
signal(SIGALRM, alarm_handler);
alarm(duration);
}
while (have_children()) {
status = 0;
child_pid = wait(&status);
if (child_pid == (pid_t) -1) {
if (errno == EINTR)
continue;
kill_children();
return 1;
}
mark_child_gone(child_pid);
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
result = 1;
kill_children();
}
}
if (alarm_gone_off)
return 0;
return result;
}