/* inlining + vfork -> bigger code */ static NOINLINE pid_t runsv(const char *name) { pid_t pid; /* If we got signaled, stop spawning children at once! */ if (bb_got_signal) return 0; pid = vfork(); if (pid == -1) { warn2_cannot("vfork", ""); return 0; } if (pid == 0) { /* child */ if (option_mask32 & 1) /* -P option? */ setsid(); /* man execv: * "Signals set to be caught by the calling process image * shall be set to the default action in the new process image." * Therefore, we do not need this: */ #if 0 bb_signals(0 | (1 << SIGHUP) | (1 << SIGTERM) , SIG_DFL); #endif execlp("runsv", "runsv", name, (char *) NULL); fatal2_cannot("start runsv ", name); } return pid; }
static void runsv(int no, const char *name) { pid_t pid; char *prog[3]; prog[0] = (char*)"runsv"; prog[1] = (char*)name; prog[2] = NULL; pid = vfork(); if (pid == -1) { warn2_cannot("vfork", ""); return; } if (pid == 0) { /* child */ if (set_pgrp) setsid(); signal(SIGHUP, SIG_DFL); signal(SIGTERM, SIG_DFL); execvp(prog[0], prog); fatal2_cannot("start runsv ", name); } sv[no].pid = pid; }
static void runsv(int no, const char *name) { int pid = fork(); if (pid == -1) { warn2_cannot("fork for ", name); return; } if (pid == 0) { /* child */ char *prog[3]; prog[0] = (char*)"runsv"; prog[1] = (char*)name; prog[2] = NULL; if (pgrp) setsid(); signal(SIGHUP, SIG_DFL); signal(SIGTERM, SIG_DFL); BB_EXECVP(prog[0], prog); //pathexec_run(*prog, prog, (char* const*)environ); fatal2_cannot("start runsv ", name); } sv[no].pid = pid; }
static void startservice(struct svdir *s) { int p; char *run[2]; if (s->state == S_FINISH) run[0] = (char*)"./finish"; else { run[0] = (char*)"./run"; custom(s, 'u'); } run[1] = NULL; if (s->pid != 0) stopservice(s); /* should never happen */ while ((p = vfork()) == -1) { warn_cannot("vfork, sleeping"); sleep(5); } if (p == 0) { /* child */ if (haslog) { /* NB: bug alert! right order is close, then dup2 */ if (s->islog) { xchdir("./log"); close(logpipe.wr); xdup2(logpipe.rd, 0); } else { close(logpipe.rd); xdup2(logpipe.wr, 1); } } bb_signals(0 + (1 << SIGCHLD) + (1 << SIGTERM) , SIG_DFL); sig_unblock(SIGCHLD); sig_unblock(SIGTERM); execvp(*run, run); fatal2_cannot(s->islog ? "start log/" : "start ", *run); } /* parent */ if (s->state != S_FINISH) { gettimeofday_ns(&s->start); s->state = S_RUN; } s->pid = p; pidchanged = 1; s->ctrl = C_NOOP; update_status(s); }
int runsvdir_main(int argc, char **argv) { struct stat s; dev_t last_dev = last_dev; /* for gcc */ ino_t last_ino = last_ino; /* for gcc */ time_t last_mtime = 0; int wstat; int curdir; int pid; unsigned deadline; unsigned now; unsigned stampcheck; char ch; int i; argv++; if (!*argv) bb_show_usage(); if (argv[0][0] == '-') { switch (argv[0][1]) { case 'P': set_pgrp = 1; case '-': ++argv; } if (!*argv) bb_show_usage(); } sig_catch(SIGTERM, s_term); sig_catch(SIGHUP, s_hangup); svdir = *argv++; if (argv && *argv) { rplog = *argv; if (setup_log() != 1) { rplog = 0; warnx("log service disabled"); } } curdir = open_read("."); if (curdir == -1) fatal2_cannot("open current directory", ""); coe(curdir); stampcheck = monotonic_sec(); for (;;) { /* collect children */ for (;;) { pid = wait_nohang(&wstat); if (pid <= 0) break; for (i = 0; i < svnum; i++) { if (pid == sv[i].pid) { /* runsv has gone */ sv[i].pid = 0; check = 1; break; } } } now = monotonic_sec(); if ((int)(now - stampcheck) >= 0) { /* wait at least a second */ stampcheck = now + 1; if (stat(svdir, &s) != -1) { if (check || s.st_mtime != last_mtime || s.st_ino != last_ino || s.st_dev != last_dev ) { /* svdir modified */ if (chdir(svdir) != -1) { last_mtime = s.st_mtime; last_dev = s.st_dev; last_ino = s.st_ino; check = 0; //if (now <= mtime) // sleep(1); runsvdir(); while (fchdir(curdir) == -1) { warn2_cannot("change directory, pausing", ""); sleep(5); } } else warn2_cannot("change directory to ", svdir); } } else warn2_cannot("stat ", svdir); } if (rplog) { if ((int)(now - stamplog) >= 0) { write(logpipe[1], ".", 1); stamplog = now + 900; } } pfd[0].revents = 0; sig_block(SIGCHLD); deadline = (check ? 1 : 5); if (rplog) poll(pfd, 1, deadline*1000); else sleep(deadline); sig_unblock(SIGCHLD); if (pfd[0].revents & POLLIN) { while (read(logpipe[0], &ch, 1) > 0) { if (ch) { for (i = 6; i < rploglen; i++) rplog[i-1] = rplog[i]; rplog[rploglen-1] = ch; } } } switch (exitsoon) { case 1: _exit(0); case 2: for (i = 0; i < svnum; i++) if (sv[i].pid) kill(sv[i].pid, SIGTERM); _exit(111); } } /* not reached */ return 0; }
int runsvdir_main(int argc UNUSED_PARAM, char **argv) { struct stat s; dev_t last_dev = last_dev; /* for gcc */ ino_t last_ino = last_ino; /* for gcc */ time_t last_mtime = 0; int wstat; int curdir; pid_t pid; unsigned deadline; unsigned now; unsigned stampcheck; int i; int need_rescan = 1; char *opt_s_argv[3]; INIT_G(); opt_complementary = "-1"; opt_s_argv[0] = NULL; opt_s_argv[2] = NULL; getopt32(argv, "Ps:", &opt_s_argv[0]); argv += optind; bb_signals(0 | (1 << SIGTERM) | (1 << SIGHUP) /* For busybox's init, SIGTERM == reboot, * SIGUSR1 == halt * SIGUSR2 == poweroff * so we need to intercept SIGUSRn too. * Note that we do not implement actual reboot * (killall(TERM) + umount, etc), we just pause * respawing and avoid exiting (-> making kernel oops). * The user is responsible for the rest. */ | (getpid() == 1 ? ((1 << SIGUSR1) | (1 << SIGUSR2)) : 0) , record_signo); svdir = *argv++; #if ENABLE_FEATURE_RUNSVDIR_LOG /* setup log */ if (*argv) { rplog = *argv; rploglen = strlen(rplog); if (rploglen < 7) { warnx("log must have at least seven characters"); } else if (piped_pair(logpipe)) { warnx("can't create pipe for log"); } else { close_on_exec_on(logpipe.rd); close_on_exec_on(logpipe.wr); ndelay_on(logpipe.rd); ndelay_on(logpipe.wr); if (dup2(logpipe.wr, 2) == -1) { warnx("can't set filedescriptor for log"); } else { pfd[0].fd = logpipe.rd; pfd[0].events = POLLIN; stamplog = monotonic_sec(); goto run; } } rplog = NULL; warnx("log service disabled"); } run: #endif curdir = open(".", O_RDONLY|O_NDELAY); if (curdir == -1) fatal2_cannot("open current directory", ""); close_on_exec_on(curdir); stampcheck = monotonic_sec(); for (;;) { /* collect children */ for (;;) { pid = wait_any_nohang(&wstat); if (pid <= 0) break; for (i = 0; i < svnum; i++) { if (pid == sv[i].pid) { /* runsv has died */ sv[i].pid = 0; need_rescan = 1; } } } now = monotonic_sec(); if ((int)(now - stampcheck) >= 0) { /* wait at least a second */ stampcheck = now + 1; if (stat(svdir, &s) != -1) { if (need_rescan || s.st_mtime != last_mtime || s.st_ino != last_ino || s.st_dev != last_dev ) { /* svdir modified */ if (chdir(svdir) != -1) { last_mtime = s.st_mtime; last_dev = s.st_dev; last_ino = s.st_ino; /* if the svdir changed this very second, wait until the * next second, because we won't be able to detect more * changes within this second */ while (time(NULL) == last_mtime) usleep(100000); need_rescan = do_rescan(); while (fchdir(curdir) == -1) { warn2_cannot("change directory, pausing", ""); sleep(5); } } else { warn2_cannot("change directory to ", svdir); } } } else { warn2_cannot("stat ", svdir); } } #if ENABLE_FEATURE_RUNSVDIR_LOG if (rplog) { if ((int)(now - stamplog) >= 0) { write(logpipe.wr, ".", 1); stamplog = now + 900; } } pfd[0].revents = 0; #endif deadline = (need_rescan ? 1 : 5); sig_block(SIGCHLD); #if ENABLE_FEATURE_RUNSVDIR_LOG if (rplog) poll(pfd, 1, deadline*1000); else #endif sleep(deadline); sig_unblock(SIGCHLD); #if ENABLE_FEATURE_RUNSVDIR_LOG if (pfd[0].revents & POLLIN) { char ch; while (read(logpipe.rd, &ch, 1) > 0) { if (ch < ' ') ch = ' '; for (i = 6; i < rploglen; i++) rplog[i-1] = rplog[i]; rplog[rploglen-1] = ch; } } #endif if (!bb_got_signal) continue; /* -s SCRIPT: useful if we are init. * In this case typically script never returns, * it halts/powers off/reboots the system. */ if (opt_s_argv[0]) { /* Single parameter: signal# */ opt_s_argv[1] = utoa(bb_got_signal); pid = spawn(opt_s_argv); if (pid > 0) { /* Remembering to wait for _any_ children, * not just pid */ while (wait(NULL) != pid) continue; } } if (bb_got_signal == SIGHUP) { for (i = 0; i < svnum; i++) if (sv[i].pid) kill(sv[i].pid, SIGTERM); } /* SIGHUP or SIGTERM (or SIGUSRn if we are init) */ /* Exit unless we are init */ if (getpid() != 1) return (SIGHUP == bb_got_signal) ? 111 : EXIT_SUCCESS; /* init continues to monitor services forever */ bb_got_signal = 0; } /* for (;;) */ }
static void startservice(struct svdir *s) { int p; const char *arg[4]; char exitcode[sizeof(int)*3 + 2]; if (s->state == S_FINISH) { /* Two arguments are given to ./finish. The first one is ./run exit code, * or -1 if ./run didnt exit normally. The second one is * the least significant byte of the exit status as determined by waitpid; * for instance it is 0 if ./run exited normally, and the signal number * if ./run was terminated by a signal. If runsv cannot start ./run * for some reason, the exit code is 111 and the status is 0. */ arg[0] = "./finish"; arg[1] = "-1"; if (WIFEXITED(s->wstat)) { *utoa_to_buf(WEXITSTATUS(s->wstat), exitcode, sizeof(exitcode)) = '\0'; arg[1] = exitcode; } //arg[2] = "0"; //if (WIFSIGNALED(s->wstat)) { arg[2] = utoa(WTERMSIG(s->wstat)); //} arg[3] = NULL; } else { arg[0] = "./run"; arg[1] = NULL; custom(s, 'u'); } if (s->pid != 0) stopservice(s); /* should never happen */ while ((p = vfork()) == -1) { warn_cannot("vfork, sleeping"); sleep(5); } if (p == 0) { /* child */ if (haslog) { /* NB: bug alert! right order is close, then dup2 */ if (s->islog) { xchdir("./log"); close(logpipe.wr); xdup2(logpipe.rd, 0); } else { close(logpipe.rd); xdup2(logpipe.wr, 1); } } /* Non-ignored signals revert to SIG_DFL on exec anyway */ /*bb_signals(0 + (1 << SIGCHLD) + (1 << SIGTERM) , SIG_DFL);*/ sig_unblock(SIGCHLD); sig_unblock(SIGTERM); execv(arg[0], (char**) arg); fatal2_cannot(s->islog ? "start log/" : "start ", arg[0]); } /* parent */ if (s->state != S_FINISH) { gettimeofday_ns(&s->start); s->state = S_RUN; } s->pid = p; pidchanged = 1; s->ctrl = C_NOOP; update_status(s); }
static void fatal_cannot(const char *m) { fatal2_cannot(m, ""); /* was exiting 111 */ }
int runsvdir_main(int argc, char **argv) { struct stat s; time_t mtime = 0; int wstat; int curdir; int pid; struct taia deadline; struct taia now; struct taia stampcheck; char ch; int i; argv++; if (!argv || !*argv) usage(); if (**argv == '-') { switch (*(*argv + 1)) { case 'P': pgrp = 1; case '-': ++argv; } if (!argv || !*argv) usage(); } sig_catch(SIGTERM, s_term); sig_catch(SIGHUP, s_hangup); svdir = *argv++; if (argv && *argv) { rplog = *argv; if (setup_log() != 1) { rplog = 0; warnx("log service disabled"); } } curdir = open_read("."); if (curdir == -1) fatal2_cannot("open current directory", ""); coe(curdir); taia_now(&stampcheck); for (;;) { /* collect children */ for (;;) { pid = wait_nohang(&wstat); if (pid <= 0) break; for (i = 0; i < svnum; i++) { if (pid == sv[i].pid) { /* runsv has gone */ sv[i].pid = 0; check = 1; break; } } } taia_now(&now); if (now.sec.x < (stampcheck.sec.x - 3)) { /* time warp */ warnx("time warp: resetting time stamp"); taia_now(&stampcheck); taia_now(&now); if (rplog) taia_now(&stamplog); } if (taia_less(&now, &stampcheck) == 0) { /* wait at least a second */ taia_uint(&deadline, 1); taia_add(&stampcheck, &now, &deadline); if (stat(svdir, &s) != -1) { if (check || s.st_mtime != mtime || s.st_ino != ino || s.st_dev != dev ) { /* svdir modified */ if (chdir(svdir) != -1) { mtime = s.st_mtime; dev = s.st_dev; ino = s.st_ino; check = 0; if (now.sec.x <= (4611686018427387914ULL + (uint64_t)mtime)) sleep(1); runsvdir(); while (fchdir(curdir) == -1) { warn2_cannot("change directory, pausing", ""); sleep(5); } } else warn2_cannot("change directory to ", svdir); } } else warn2_cannot("stat ", svdir); } if (rplog) { if (taia_less(&now, &stamplog) == 0) { write(logpipe[1], ".", 1); taia_uint(&deadline, 900); taia_add(&stamplog, &now, &deadline); } } taia_uint(&deadline, check ? 1 : 5); taia_add(&deadline, &now, &deadline); sig_block(SIGCHLD); if (rplog) iopause(io, 1, &deadline, &now); else iopause(0, 0, &deadline, &now); sig_unblock(SIGCHLD); if (rplog && (io[0].revents | IOPAUSE_READ)) while (read(logpipe[0], &ch, 1) > 0) if (ch) { for (i = 6; i < rploglen; i++) rplog[i-1] = rplog[i]; rplog[rploglen-1] = ch; } switch (exitsoon) { case 1: _exit(0); case 2: for (i = 0; i < svnum; i++) if (sv[i].pid) kill(sv[i].pid, SIGTERM); _exit(111); } } /* not reached */ return 0; }