2 Copyright (c) 2001-2006, Gerrit Pape
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
8 1. Redistributions of source code must retain the above copyright notice,
9 this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13 3. The name of the author may not be used to endorse or promote products
14 derived from this software without specific prior written permission.
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 /* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
29 /* TODO: depends on runit_lib.c - review and reduce/eliminate */
34 #include "runit_lib.h"
36 #define MAXSERVICES 1000
38 /* Should be not needed - all dirs are on same FS, right? */
39 #define CHECK_DEVNO_TOO 0
54 #if ENABLE_FEATURE_RUNSVDIR_LOG
57 struct fd_pair logpipe;
63 #define G (*(struct globals*)&bb_common_bufsiz1)
65 #define svdir (G.svdir )
66 #define svnum (G.svnum )
67 #define rplog (G.rplog )
68 #define rploglen (G.rploglen )
69 #define logpipe (G.logpipe )
71 #define stamplog (G.stamplog )
72 #define set_pgrp (G.set_pgrp )
73 #define INIT_G() do { \
76 static void fatal2_cannot(const char *m1, const char *m2)
78 bb_perror_msg_and_die("%s: fatal: cannot %s%s", svdir, m1, m2);
81 static void warn3x(const char *m1, const char *m2, const char *m3)
83 bb_error_msg("%s: warning: %s%s%s", svdir, m1, m2, m3);
85 static void warn2_cannot(const char *m1, const char *m2)
87 warn3x("cannot ", m1, m2);
89 #if ENABLE_FEATURE_RUNSVDIR_LOG
90 static void warnx(const char *m1)
96 /* inlining + vfork -> bigger code */
97 static NOINLINE pid_t runsv(const char *name)
101 /* If we got signaled, stop spawning children at once! */
107 warn2_cannot("vfork", "");
115 * "Signals set to be caught by the calling process image
116 * shall be set to the default action in the new process image."
117 * Therefore, we do not need this: */
124 execlp("runsv", "runsv", name, NULL);
125 fatal2_cannot("start runsv ", name);
130 /* gcc 4.3.0 does better with NOINLINE */
131 static NOINLINE int do_rescan(void)
141 warn2_cannot("open directory ", svdir);
142 return 1; /* need to rescan again soon */
144 for (i = 0; i < svnum; i++)
152 if (d->d_name[0] == '.')
154 if (stat(d->d_name, &s) == -1) {
155 warn2_cannot("stat ", d->d_name);
158 if (!S_ISDIR(s.st_mode))
160 /* Do we have this service listed already? */
161 for (i = 0; i < svnum; i++) {
162 if ((sv[i].ino == s.st_ino)
164 && (sv[i].dev == s.st_dev)
167 if (sv[i].pid == 0) /* restart if it has died */
169 sv[i].isgone = 0; /* "we still see you" */
173 { /* Not found, make new service */
174 struct service *svnew = realloc(sv, (i+1) * sizeof(*sv));
176 warn2_cannot("start runsv ", d->d_name);
183 sv[i].dev = s.st_dev;
185 sv[i].ino = s.st_ino;
187 sv[i].pid = runsv(d->d_name);
194 if (i) { /* readdir failed */
195 warn2_cannot("read directory ", svdir);
196 return 1; /* need to rescan again soon */
199 /* Send SIGTERM to runsv whose directories
200 * were no longer found (-> must have been removed) */
201 for (i = 0; i < svnum; i++) {
205 kill(sv[i].pid, SIGTERM);
208 i--; /* so that we don't skip new sv[i] (bug was here!) */
213 int runsvdir_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
214 int runsvdir_main(int argc UNUSED_PARAM, char **argv)
217 dev_t last_dev = last_dev; /* for gcc */
218 ino_t last_ino = last_ino; /* for gcc */
219 time_t last_mtime = 0;
231 opt_complementary = "-1";
232 set_pgrp = getopt32(argv, "P");
238 /* For busybox's init, SIGTERM == reboot,
240 * SIGUSR2 == poweroff
241 * so we need to intercept SIGUSRn too.
242 * Note that we do not implement actual reboot
243 * (killall(TERM) + umount, etc), we just pause
244 * respawing and avoid exiting (-> making kernel oops).
245 * The user is responsible for the rest. */
246 | (getpid() == 1 ? ((1 << SIGUSR1) | (1 << SIGUSR2)) : 0)
250 #if ENABLE_FEATURE_RUNSVDIR_LOG
254 rploglen = strlen(rplog);
256 warnx("log must have at least seven characters");
257 } else if (piped_pair(logpipe)) {
258 warnx("cannot create pipe for log");
260 close_on_exec_on(logpipe.rd);
261 close_on_exec_on(logpipe.wr);
262 ndelay_on(logpipe.rd);
263 ndelay_on(logpipe.wr);
264 if (dup2(logpipe.wr, 2) == -1) {
265 warnx("cannot set filedescriptor for log");
267 pfd[0].fd = logpipe.rd;
268 pfd[0].events = POLLIN;
269 stamplog = monotonic_sec();
274 warnx("log service disabled");
278 curdir = open_read(".");
280 fatal2_cannot("open current directory", "");
281 close_on_exec_on(curdir);
283 stampcheck = monotonic_sec();
286 /* collect children */
288 pid = wait_any_nohang(&wstat);
291 for (i = 0; i < svnum; i++) {
292 if (pid == sv[i].pid) {
300 now = monotonic_sec();
301 if ((int)(now - stampcheck) >= 0) {
302 /* wait at least a second */
303 stampcheck = now + 1;
305 if (stat(svdir, &s) != -1) {
306 if (need_rescan || s.st_mtime != last_mtime
307 || s.st_ino != last_ino || s.st_dev != last_dev
310 if (chdir(svdir) != -1) {
311 last_mtime = s.st_mtime;
316 need_rescan = do_rescan();
317 while (fchdir(curdir) == -1) {
318 warn2_cannot("change directory, pausing", "");
322 warn2_cannot("change directory to ", svdir);
326 warn2_cannot("stat ", svdir);
330 #if ENABLE_FEATURE_RUNSVDIR_LOG
332 if ((int)(now - stamplog) >= 0) {
333 write(logpipe.wr, ".", 1);
334 stamplog = now + 900;
339 deadline = (need_rescan ? 1 : 5);
342 #if ENABLE_FEATURE_RUNSVDIR_LOG
344 poll(pfd, 1, deadline*1000);
348 sig_unblock(SIGCHLD);
350 #if ENABLE_FEATURE_RUNSVDIR_LOG
351 if (pfd[0].revents & POLLIN) {
353 while (read(logpipe.rd, &ch, 1) > 0) {
356 for (i = 6; i < rploglen; i++)
357 rplog[i-1] = rplog[i];
358 rplog[rploglen-1] = ch;
362 switch (bb_got_signal) {
363 case 0: /* we are not signaled, business as usual */
366 for (i = 0; i < svnum; i++)
368 kill(sv[i].pid, SIGTERM);
371 /* exit, unless we are init */
375 /* so we are init. do not exit,
376 * and pause respawning - we may be rebooting... */
383 return (SIGHUP == bb_got_signal) ? 111 : EXIT_SUCCESS;