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: cannot %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("cannot ", 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) /* -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, 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;
229 opt_complementary = "-1";
236 /* For busybox's init, SIGTERM == reboot,
238 * SIGUSR2 == poweroff
239 * so we need to intercept SIGUSRn too.
240 * Note that we do not implement actual reboot
241 * (killall(TERM) + umount, etc), we just pause
242 * respawing and avoid exiting (-> making kernel oops).
243 * The user is responsible for the rest. */
244 | (getpid() == 1 ? ((1 << SIGUSR1) | (1 << SIGUSR2)) : 0)
248 #if ENABLE_FEATURE_RUNSVDIR_LOG
252 rploglen = strlen(rplog);
254 warnx("log must have at least seven characters");
255 } else if (piped_pair(logpipe)) {
256 warnx("cannot create pipe for log");
258 close_on_exec_on(logpipe.rd);
259 close_on_exec_on(logpipe.wr);
260 ndelay_on(logpipe.rd);
261 ndelay_on(logpipe.wr);
262 if (dup2(logpipe.wr, 2) == -1) {
263 warnx("cannot set filedescriptor for log");
265 pfd[0].fd = logpipe.rd;
266 pfd[0].events = POLLIN;
267 stamplog = monotonic_sec();
272 warnx("log service disabled");
276 curdir = open_read(".");
278 fatal2_cannot("open current directory", "");
279 close_on_exec_on(curdir);
281 stampcheck = monotonic_sec();
284 /* collect children */
286 pid = wait_any_nohang(&wstat);
289 for (i = 0; i < svnum; i++) {
290 if (pid == sv[i].pid) {
298 now = monotonic_sec();
299 if ((int)(now - stampcheck) >= 0) {
300 /* wait at least a second */
301 stampcheck = now + 1;
303 if (stat(svdir, &s) != -1) {
304 if (need_rescan || s.st_mtime != last_mtime
305 || s.st_ino != last_ino || s.st_dev != last_dev
308 if (chdir(svdir) != -1) {
309 last_mtime = s.st_mtime;
314 need_rescan = do_rescan();
315 while (fchdir(curdir) == -1) {
316 warn2_cannot("change directory, pausing", "");
320 warn2_cannot("change directory to ", svdir);
324 warn2_cannot("stat ", svdir);
328 #if ENABLE_FEATURE_RUNSVDIR_LOG
330 if ((int)(now - stamplog) >= 0) {
331 write(logpipe.wr, ".", 1);
332 stamplog = now + 900;
337 deadline = (need_rescan ? 1 : 5);
340 #if ENABLE_FEATURE_RUNSVDIR_LOG
342 poll(pfd, 1, deadline*1000);
346 sig_unblock(SIGCHLD);
348 #if ENABLE_FEATURE_RUNSVDIR_LOG
349 if (pfd[0].revents & POLLIN) {
351 while (read(logpipe.rd, &ch, 1) > 0) {
354 for (i = 6; i < rploglen; i++)
355 rplog[i-1] = rplog[i];
356 rplog[rploglen-1] = ch;
360 switch (bb_got_signal) {
361 case 0: /* we are not signaled, business as usual */
364 for (i = 0; i < svnum; i++)
366 kill(sv[i].pid, SIGTERM);
369 /* exit, unless we are init */
373 /* so we are init. do not exit,
374 * and pause respawning - we may be rebooting
375 * (but SIGHUP is not a reboot, make short pause) */
376 deadline = (SIGHUP == bb_got_signal) ? 5 : 60;
382 return (SIGHUP == bb_got_signal) ? 111 : EXIT_SUCCESS;