From 4aca87515a5083ae0e31ce3177189fd43b6d05ac Mon Sep 17 00:00:00 2001 From: Andreas Baumann Date: Sat, 3 Jan 2015 13:58:15 +0100 Subject: patch to Vanilla Tomato 1.28 --- release/src/router/busybox/runit/runsvdir.c | 393 ++++++++++++++++++++++++++++ 1 file changed, 393 insertions(+) create mode 100644 release/src/router/busybox/runit/runsvdir.c (limited to 'release/src/router/busybox/runit/runsvdir.c') diff --git a/release/src/router/busybox/runit/runsvdir.c b/release/src/router/busybox/runit/runsvdir.c new file mode 100644 index 00000000..a77bc3fd --- /dev/null +++ b/release/src/router/busybox/runit/runsvdir.c @@ -0,0 +1,393 @@ +/* +Copyright (c) 2001-2006, Gerrit Pape +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* Busyboxed by Denys Vlasenko */ +/* TODO: depends on runit_lib.c - review and reduce/eliminate */ + +#include +#include +#include "libbb.h" +#include "runit_lib.h" + +#define MAXSERVICES 1000 + +/* Should be not needed - all dirs are on same FS, right? */ +#define CHECK_DEVNO_TOO 0 + +struct service { +#if CHECK_DEVNO_TOO + dev_t dev; +#endif + ino_t ino; + pid_t pid; + smallint isgone; +}; + +struct globals { + struct service *sv; + char *svdir; + int svnum; +#if ENABLE_FEATURE_RUNSVDIR_LOG + char *rplog; + int rploglen; + struct fd_pair logpipe; + struct pollfd pfd[1]; + unsigned stamplog; +#endif +}; +#define G (*(struct globals*)&bb_common_bufsiz1) +#define sv (G.sv ) +#define svdir (G.svdir ) +#define svnum (G.svnum ) +#define rplog (G.rplog ) +#define rploglen (G.rploglen ) +#define logpipe (G.logpipe ) +#define pfd (G.pfd ) +#define stamplog (G.stamplog ) +#define INIT_G() do { \ +} while (0) + +static void fatal2_cannot(const char *m1, const char *m2) +{ + bb_perror_msg_and_die("%s: fatal: cannot %s%s", svdir, m1, m2); + /* was exiting 100 */ +} +static void warn3x(const char *m1, const char *m2, const char *m3) +{ + bb_error_msg("%s: warning: %s%s%s", svdir, m1, m2, m3); +} +static void warn2_cannot(const char *m1, const char *m2) +{ + warn3x("cannot ", m1, m2); +} +#if ENABLE_FEATURE_RUNSVDIR_LOG +static void warnx(const char *m1) +{ + warn3x(m1, "", ""); +} +#endif + +/* 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; +} + +/* gcc 4.3.0 does better with NOINLINE */ +static NOINLINE int do_rescan(void) +{ + DIR *dir; + direntry *d; + int i; + struct stat s; + int need_rescan = 0; + + dir = opendir("."); + if (!dir) { + warn2_cannot("open directory ", svdir); + return 1; /* need to rescan again soon */ + } + for (i = 0; i < svnum; i++) + sv[i].isgone = 1; + + while (1) { + errno = 0; + d = readdir(dir); + if (!d) + break; + if (d->d_name[0] == '.') + continue; + if (stat(d->d_name, &s) == -1) { + warn2_cannot("stat ", d->d_name); + continue; + } + if (!S_ISDIR(s.st_mode)) + continue; + /* Do we have this service listed already? */ + for (i = 0; i < svnum; i++) { + if ((sv[i].ino == s.st_ino) +#if CHECK_DEVNO_TOO + && (sv[i].dev == s.st_dev) +#endif + ) { + if (sv[i].pid == 0) /* restart if it has died */ + goto run_ith_sv; + sv[i].isgone = 0; /* "we still see you" */ + goto next_dentry; + } + } + { /* Not found, make new service */ + struct service *svnew = realloc(sv, (i+1) * sizeof(*sv)); + if (!svnew) { + warn2_cannot("start runsv ", d->d_name); + need_rescan = 1; + continue; + } + sv = svnew; + svnum++; +#if CHECK_DEVNO_TOO + sv[i].dev = s.st_dev; +#endif + sv[i].ino = s.st_ino; + run_ith_sv: + sv[i].pid = runsv(d->d_name); + sv[i].isgone = 0; + } + next_dentry: ; + } + i = errno; + closedir(dir); + if (i) { /* readdir failed */ + warn2_cannot("read directory ", svdir); + return 1; /* need to rescan again soon */ + } + + /* Send SIGTERM to runsv whose directories + * were no longer found (-> must have been removed) */ + for (i = 0; i < svnum; i++) { + if (!sv[i].isgone) + continue; + if (sv[i].pid) + kill(sv[i].pid, SIGTERM); + svnum--; + sv[i] = sv[svnum]; + i--; /* so that we don't skip new sv[i] (bug was here!) */ + } + return need_rescan; +} + +int runsvdir_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +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("cannot 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("cannot 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_read("."); + 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 (now <= mtime) + // sleep(1); + 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 (;;) */ +} -- cgit v1.2.3-54-g00ecf