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;
62 #define G (*(struct globals*)&bb_common_bufsiz1)
64 #define svdir (G.svdir )
65 #define svnum (G.svnum )
66 #define rplog (G.rplog )
67 #define rploglen (G.rploglen )
68 #define logpipe (G.logpipe )
70 #define stamplog (G.stamplog )
71 #define INIT_G() do { \
74 static void fatal2_cannot(const char *m1, const char *m2)
76 bb_perror_msg_and_die("%s: fatal: can't %s%s", svdir, m1, m2);
79 static void warn3x(const char *m1, const char *m2, const char *m3)
81 bb_error_msg("%s: warning: %s%s%s", svdir, m1, m2, m3);
83 static void warn2_cannot(const char *m1, const char *m2)
85 warn3x("can't ", m1, m2);
87 #if ENABLE_FEATURE_RUNSVDIR_LOG
88 static void warnx(const char *m1)
94 /* inlining + vfork -> bigger code */
95 static NOINLINE pid_t runsv(const char *name)
99 /* If we got signaled, stop spawning children at once! */
105 warn2_cannot("vfork", "");
110 if (option_mask32 & 1) /* -P option? */
113 * "Signals set to be caught by the calling process image
114 * shall be set to the default action in the new process image."
115 * Therefore, we do not need this: */
122 execlp("runsv", "runsv", name, (char *) NULL);
123 fatal2_cannot("start runsv ", name);
128 /* gcc 4.3.0 does better with NOINLINE */
129 static NOINLINE int do_rescan(void)
139 warn2_cannot("open directory ", svdir);
140 return 1; /* need to rescan again soon */
142 for (i = 0; i < svnum; i++)
150 if (d->d_name[0] == '.')
152 if (stat(d->d_name, &s) == -1) {
153 warn2_cannot("stat ", d->d_name);
156 if (!S_ISDIR(s.st_mode))
158 /* Do we have this service listed already? */
159 for (i = 0; i < svnum; i++) {
160 if ((sv[i].ino == s.st_ino)
162 && (sv[i].dev == s.st_dev)
165 if (sv[i].pid == 0) /* restart if it has died */
167 sv[i].isgone = 0; /* "we still see you" */
171 { /* Not found, make new service */
172 struct service *svnew = realloc(sv, (i+1) * sizeof(*sv));
174 warn2_cannot("start runsv ", d->d_name);
181 sv[i].dev = s.st_dev;
183 sv[i].ino = s.st_ino;
185 sv[i].pid = runsv(d->d_name);
192 if (i) { /* readdir failed */
193 warn2_cannot("read directory ", svdir);
194 return 1; /* need to rescan again soon */
197 /* Send SIGTERM to runsv whose directories
198 * were no longer found (-> must have been removed) */
199 for (i = 0; i < svnum; i++) {
203 kill(sv[i].pid, SIGTERM);
206 i--; /* so that we don't skip new sv[i] (bug was here!) */
211 int runsvdir_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
212 int runsvdir_main(int argc UNUSED_PARAM, char **argv)
215 dev_t last_dev = last_dev; /* for gcc */
216 ino_t last_ino = last_ino; /* for gcc */
217 time_t last_mtime = 0;
230 opt_complementary = "-1";
231 opt_s_argv[0] = NULL;
232 opt_s_argv[2] = NULL;
233 getopt32(argv, "Ps:", &opt_s_argv[0]);
239 /* For busybox's init, SIGTERM == reboot,
241 * SIGUSR2 == poweroff
242 * so we need to intercept SIGUSRn too.
243 * Note that we do not implement actual reboot
244 * (killall(TERM) + umount, etc), we just pause
245 * respawing and avoid exiting (-> making kernel oops).
246 * The user is responsible for the rest. */
247 | (getpid() == 1 ? ((1 << SIGUSR1) | (1 << SIGUSR2)) : 0)
251 #if ENABLE_FEATURE_RUNSVDIR_LOG
255 rploglen = strlen(rplog);
257 warnx("log must have at least seven characters");
258 } else if (piped_pair(logpipe)) {
259 warnx("can't create pipe for log");
261 close_on_exec_on(logpipe.rd);
262 close_on_exec_on(logpipe.wr);
263 ndelay_on(logpipe.rd);
264 ndelay_on(logpipe.wr);
265 if (dup2(logpipe.wr, 2) == -1) {
266 warnx("can't set filedescriptor for log");
268 pfd[0].fd = logpipe.rd;
269 pfd[0].events = POLLIN;
270 stamplog = monotonic_sec();
275 warnx("log service disabled");
279 curdir = open_read(".");
281 fatal2_cannot("open current directory", "");
282 close_on_exec_on(curdir);
284 stampcheck = monotonic_sec();
287 /* collect children */
289 pid = wait_any_nohang(&wstat);
292 for (i = 0; i < svnum; i++) {
293 if (pid == sv[i].pid) {
301 now = monotonic_sec();
302 if ((int)(now - stampcheck) >= 0) {
303 /* wait at least a second */
304 stampcheck = now + 1;
306 if (stat(svdir, &s) != -1) {
307 if (need_rescan || s.st_mtime != last_mtime
308 || s.st_ino != last_ino || s.st_dev != last_dev
311 if (chdir(svdir) != -1) {
312 last_mtime = s.st_mtime;
317 need_rescan = do_rescan();
318 while (fchdir(curdir) == -1) {
319 warn2_cannot("change directory, pausing", "");
323 warn2_cannot("change directory to ", svdir);
327 warn2_cannot("stat ", svdir);
331 #if ENABLE_FEATURE_RUNSVDIR_LOG
333 if ((int)(now - stamplog) >= 0) {
334 write(logpipe.wr, ".", 1);
335 stamplog = now + 900;
340 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;
365 /* -s SCRIPT: useful if we are init.
366 * In this case typically script never returns,
367 * it halts/powers off/reboots the system. */
369 /* Single parameter: signal# */
370 opt_s_argv[1] = utoa(bb_got_signal);
371 pid = spawn(opt_s_argv);
373 /* Remembering to wait for _any_ children,
375 while (wait(NULL) != pid)
380 if (bb_got_signal == SIGHUP) {
381 for (i = 0; i < svnum; i++)
383 kill(sv[i].pid, SIGTERM);
385 /* SIGHUP or SIGTERM (or SIGUSRn if we are init) */
386 /* Exit unless we are init */
388 return (SIGHUP == bb_got_signal) ? 111 : EXIT_SUCCESS;
390 /* init continues to monitor services forever */