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;
61 smallint need_rescan; /* = 1; */
64 #define G (*(struct globals*)&bb_common_bufsiz1)
66 #define svdir (G.svdir )
67 #define svnum (G.svnum )
68 #define rplog (G.rplog )
69 #define rploglen (G.rploglen )
70 #define logpipe (G.logpipe )
72 #define stamplog (G.stamplog )
73 #define need_rescan (G.need_rescan )
74 #define set_pgrp (G.set_pgrp )
75 #define INIT_G() do { \
79 static void fatal2_cannot(const char *m1, const char *m2)
81 bb_perror_msg_and_die("%s: fatal: cannot %s%s", svdir, m1, m2);
84 static void warn3x(const char *m1, const char *m2, const char *m3)
86 bb_error_msg("%s: warning: %s%s%s", svdir, m1, m2, m3);
88 static void warn2_cannot(const char *m1, const char *m2)
90 warn3x("cannot ", m1, m2);
92 #if ENABLE_FEATURE_RUNSVDIR_LOG
93 static void warnx(const char *m1)
99 static void runsv(int no, const char *name)
104 warn2_cannot("vfork", "");
112 * Signals set to be caught by the calling process image
113 * shall be set to the default action in the new process image.
114 * Therefore, we do not need this: */
121 execlp("runsv", "runsv", name, NULL);
122 fatal2_cannot("start runsv ", name);
127 static void do_rescan(void)
136 warn2_cannot("open directory ", svdir);
139 for (i = 0; i < svnum; i++)
147 if (d->d_name[0] == '.')
149 if (stat(d->d_name, &s) == -1) {
150 warn2_cannot("stat ", d->d_name);
153 if (!S_ISDIR(s.st_mode))
155 for (i = 0; i < svnum; i++) {
156 if ((sv[i].ino == s.st_ino)
158 && (sv[i].dev == s.st_dev)
169 struct service *svnew = realloc(sv, (i+1) * sizeof(*sv));
171 warn2_cannot("start runsv ", d->d_name);
176 memset(&sv[i], 0, sizeof(sv[i]));
177 sv[i].ino = s.st_ino;
179 sv[i].dev = s.st_dev;
182 /*sv[i].isgone = 0;*/
190 warn2_cannot("read directory ", svdir);
195 /* Send SIGTERM to runsv whose directories were not found (removed) */
196 for (i = 0; i < svnum; i++) {
200 kill(sv[i].pid, SIGTERM);
203 i--; /* so that we don't skip new sv[i] (bug was here!) */
208 int runsvdir_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
209 int runsvdir_main(int argc UNUSED_PARAM, char **argv)
212 dev_t last_dev = last_dev; /* for gcc */
213 ino_t last_ino = last_ino; /* for gcc */
214 time_t last_mtime = 0;
225 opt_complementary = "-1";
226 set_pgrp = getopt32(argv, "P");
232 /* For busybox's init, SIGTERM == reboot,
234 * SIGUSR2 == poweroff
235 * so we need to intercept SIGUSRn too.
236 * Note that we do not implement actual reboot
237 * (killall(TERM) + umount, etc), we just pause
238 * respawing and avoid exiting (-> making kernel oops).
239 * The user is responsible for the rest. */
240 | (getpid() == 1 ? ((1 << SIGUSR1) | (1 << SIGUSR2)) : 0)
244 #if ENABLE_FEATURE_RUNSVDIR_LOG
248 rploglen = strlen(rplog);
250 warnx("log must have at least seven characters");
251 } else if (piped_pair(logpipe)) {
252 warnx("cannot create pipe for log");
254 close_on_exec_on(logpipe.rd);
255 close_on_exec_on(logpipe.wr);
256 ndelay_on(logpipe.rd);
257 ndelay_on(logpipe.wr);
258 if (dup2(logpipe.wr, 2) == -1) {
259 warnx("cannot set filedescriptor for log");
261 pfd[0].fd = logpipe.rd;
262 pfd[0].events = POLLIN;
263 stamplog = monotonic_sec();
268 warnx("log service disabled");
272 curdir = open_read(".");
274 fatal2_cannot("open current directory", "");
275 close_on_exec_on(curdir);
277 stampcheck = monotonic_sec();
280 /* collect children */
282 pid = wait_any_nohang(&wstat);
285 for (i = 0; i < svnum; i++) {
286 if (pid == sv[i].pid) {
295 now = monotonic_sec();
296 if ((int)(now - stampcheck) >= 0) {
297 /* wait at least a second */
298 stampcheck = now + 1;
300 if (stat(svdir, &s) != -1) {
301 if (need_rescan || s.st_mtime != last_mtime
302 || s.st_ino != last_ino || s.st_dev != last_dev
305 if (chdir(svdir) != -1) {
306 last_mtime = s.st_mtime;
313 while (fchdir(curdir) == -1) {
314 warn2_cannot("change directory, pausing", "");
318 warn2_cannot("change directory to ", svdir);
321 warn2_cannot("stat ", svdir);
324 #if ENABLE_FEATURE_RUNSVDIR_LOG
326 if ((int)(now - stamplog) >= 0) {
327 write(logpipe.wr, ".", 1);
328 stamplog = now + 900;
333 deadline = (need_rescan ? 1 : 5);
336 #if ENABLE_FEATURE_RUNSVDIR_LOG
338 poll(pfd, 1, deadline*1000);
342 sig_unblock(SIGCHLD);
344 #if ENABLE_FEATURE_RUNSVDIR_LOG
345 if (pfd[0].revents & POLLIN) {
347 while (read(logpipe.rd, &ch, 1) > 0) {
350 for (i = 6; i < rploglen; i++)
351 rplog[i-1] = rplog[i];
352 rplog[rploglen-1] = ch;
356 switch (bb_got_signal) {
358 for (i = 0; i < svnum; i++)
360 kill(sv[i].pid, SIGTERM);
363 /* exit, unless we are init */
367 /* so we are init. do not exit,
368 * and pause respawning - we may be rebooting... */
375 return (SIGHUP == bb_got_signal) ? 111 : EXIT_SUCCESS;