/* * 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; static 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; } } static 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; } static 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; } } static int have_children(void) { struct child_info *child; child = children; while (child) { if (!child->gone) return 1; child = child->next; } return 0; } static 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; /* fall-through */ 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; } static void signal_handler(int signum) { (void)signum; kill_children(); } int result = 0; int alarm_gone_off = 0; static void alarm_handler(int signum) { (void)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; }