/* Generate children*/ static void fork_children(void) { while (shm->running_childs < max_children) { int childno; int pid = 0; if (shm->spawn_no_more == TRUE) return; /* a new child means a new seed, or the new child * will do the same syscalls as the one in the child it's replacing. * (special case startup, or we reseed unnecessarily) */ if (shm->ready == TRUE) reseed(); /* Find a space for it in the pid map */ childno = find_childno(EMPTY_PIDSLOT); if (childno == CHILD_NOT_FOUND) { outputerr("## Pid map was full!\n"); dump_childnos(); exit_main_fail(); } fflush(stdout); pid = fork(); if (pid == 0) { /* Child process. */ init_child(childno); child_process(); debugf("child %d %d exiting.\n", childno, getpid()); close_logfile(&this_child->logfile); _exit(EXIT_SUCCESS); } else { if (pid == -1) { /* We failed, wait for a child to exit before retrying. */ if (shm->running_childs > 0) return; output(0, "couldn't create child! (%s)\n", strerror(errno)); panic(EXIT_FORK_FAILURE); exit_main_fail(); } } shm->children[childno]->pid = pid; shm->running_childs++; debugf("Created child %d (pid:%d) [total:%d/%d]\n", childno, pid, shm->running_childs, max_children); if (shm->exit_reason != STILL_RUNNING) return; } shm->ready = TRUE; debugf("created enough children\n"); }
/* * reap_child: Remove all references to a running child. * * This can get called from three possible places. * 1. A child calls this itself just before it exits to clear out * its child struct in the shm. * 2. From the watchdog if it finds reference to a pid that no longer exists. * 3. From the main pid if it gets a SIGBUS or SIGSTOP from the child. * * The reaper lock protects against these happening at the same time. */ void reap_child(pid_t childpid) { struct childdata *child; int i; lock(&shm->reaper_lock); if (childpid == shm->last_reaped) { debugf("already reaped %d!\n", childpid); goto out; } i = find_childno(childpid); if (i == CHILD_NOT_FOUND) goto out; debugf("Removing pid %d from pidmap.\n", childpid); child = shm->children[i]; child->pid = EMPTY_PIDSLOT; child->syscall.tv.tv_sec = 0; shm->running_childs--; shm->last_reaped = childpid; out: unlock(&shm->reaper_lock); }
/* * level defines whether it gets displayed to the screen with printf. * (it always logs). * 0 = everything, even all the registers * 1 = prints syscall count * 2 = Just the reseed values * */ void output(unsigned char level, const char *fmt, ...) { va_list args; int n; FILE *handle; pid_t pid; char outputbuf[BUFSIZE]; char *prefix = NULL; char main_prefix[]="[main]"; char child_prefix[32]; if (logging == LOGGING_DISABLED && level >= quiet_level) return; /* prefix preparation */ pid = getpid(); if (pid == mainpid) prefix = main_prefix; else if (prefix == NULL) { unsigned int childno; childno = find_childno(pid); snprintf(child_prefix, sizeof(child_prefix), "[child%u:%u]", childno, pid); prefix = child_prefix; shm->children[childno]->logdirty = TRUE; } /* formatting output */ va_start(args, fmt); n = vsnprintf(outputbuf, sizeof(outputbuf), fmt, args); va_end(args); if (n < 0) { outputerr("## Something went wrong in output() [%d]\n", n); exit(EXIT_FAILURE); } /* stdout output if needed */ if (quiet_level >= level) { printf("%s %s", prefix, outputbuf); (void)fflush(stdout); } /* go on with file logs only if enabled */ if (logging == LOGGING_FILES) return; handle = find_logfile_handle(); if (!handle) return; strip_ansi(outputbuf); fprintf(handle, "%s %s", prefix, outputbuf); (void)fflush(handle); }
static FILE * find_child_logfile_handle(pid_t pid) { int i; unsigned int j; FILE *log = NULL; i = find_childno(pid); if (i != CHILD_NOT_FOUND) { log = shm->children[i]->logfile; } else { /* This is pretty ugly, and should never happen, * but try again a second later, in case we're racing setup/teardown. * FIXME: We may not even need this now that we have proper locking; test it. */ sleep(1); i = find_childno(pid); if (i == CHILD_NOT_FOUND) { outputerr("Couldn't find child for pid %d\n", pid); return mainlogfile; } log = shm->children[i]->logfile; } if (log != NULL) return log; /* if the logfile hadn't been set, log to main. */ shm->children[i]->logfile = mainlogfile; outputerr("## child %d logfile handle was null logging to main!\n", i); outputerr("## Couldn't find logfile for pid %d\n", pid); dump_childnos(); outputerr("## Logfiles for pids: "); for_each_child(j) outputerr("%p ", shm->children[j]->logfile); outputerr("\n"); (void)fflush(stdout); sleep(5); return mainlogfile; }
void kill_pid(pid_t pid) { int ret; int childno; childno = find_childno(pid); if (childno != CHILD_NOT_FOUND) { if (shm->children[childno]->dontkillme == TRUE) return; } ret = kill(pid, SIGKILL); if (ret != 0) debugf("couldn't kill pid %d [%s]\n", pid, strerror(errno)); }
static FILE * find_logfile_handle(void) { pid_t pid; int i; pid = getpid(); if (pid == initpid) return mainlogfile; if (pid == shm->mainpid) return mainlogfile; if (pid == watchdog_pid) return mainlogfile; i = find_childno(pid); if (i != CHILD_NOT_FOUND) return shm->children[i]->logfile; else { /* try one more time. FIXME: This is awful. */ unsigned int j; sleep(1); i = find_childno(pid); if (i != CHILD_NOT_FOUND) return shm->children[i]->logfile; outputerr("## Couldn't find logfile for pid %d\n", pid); dump_childnos(); outputerr("## Logfiles for pids: "); for_each_child(j) outputerr("%p ", shm->children[j]->logfile); outputerr("\n"); } return NULL; }
static void sighandler(int sig) { int childno; sigwas = sig; switch (sig) { case SIGALRM: childno = find_childno(getpid()); if (childno == CHILD_NOT_FOUND) _exit(EXIT_SUCCESS); /* Hell knows what happened, just bail. */ /* Re-arm the alarm. */ alarm(1); /* Jump back, maybe we'll make progress. */ (void)signal(sig, sighandler); siglongjmp(ret_jump, 1); break; default: _exit(EXIT_SUCCESS); } }
static void handle_child(pid_t childpid, int childstatus) { switch (childpid) { case 0: //debugf("Nothing changed. children:%d\n", shm->running_childs); break; case -1: if (shm->exit_reason != STILL_RUNNING) return; if (errno == ECHILD) { unsigned int i; bool seen = FALSE; debugf("All children exited!\n"); for_each_child(i) { struct childdata *child; child = shm->children[i]; if (child->pid != EMPTY_PIDSLOT) { if (pid_alive(child->pid) == -1) { debugf("Removing %d from pidmap\n", child->pid); child->pid = EMPTY_PIDSLOT; shm->running_childs--; } else { debugf("%d looks still alive! ignoring.\n", child->pid); } seen = TRUE; } } if (seen == FALSE) shm->running_childs = 0; break; } output(0, "error! (%s)\n", strerror(errno)); break; default: debugf("Something happened to pid %d\n", childpid); if (WIFEXITED(childstatus)) { int childno; childno = find_childno(childpid); if (childno != CHILD_NOT_FOUND) { debugf("Child %d exited after %ld operations.\n", childpid, shm->children[childno]->syscall.op_nr); reap_child(childpid); } break; } else if (WIFSIGNALED(childstatus)) { handle_childsig(childpid, childstatus, FALSE); } else if (WIFSTOPPED(childstatus)) { handle_childsig(childpid, childstatus, TRUE); } else if (WIFCONTINUED(childstatus)) { break; } else { output(0, "erk, wtf\n"); } }