source: MondoRescue/branches/3.3/mindi-busybox/runit/runsvdir.c@ 3621

Last change on this file since 3621 was 3621, checked in by Bruno Cornec, 7 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

  • Property svn:eol-style set to native
File size: 10.7 KB
Line 
1/*
2Copyright (c) 2001-2006, Gerrit Pape
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are met:
7
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.
15
16THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27
28/* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
29
30//config:config RUNSVDIR
31//config: bool "runsvdir"
32//config: default y
33//config: help
34//config: runsvdir starts a runsv process for each subdirectory, or symlink to
35//config: a directory, in the services directory dir, up to a limit of 1000
36//config: subdirectories, and restarts a runsv process if it terminates.
37//config:
38//config:config FEATURE_RUNSVDIR_LOG
39//config: bool "Enable scrolling argument log"
40//config: depends on RUNSVDIR
41//config: default n
42//config: help
43//config: Enable feature where second parameter of runsvdir holds last error
44//config: message (viewable via top/ps). Otherwise (feature is off
45//config: or no parameter), error messages go to stderr only.
46
47//applet:IF_RUNSVDIR(APPLET(runsvdir, BB_DIR_USR_BIN, BB_SUID_DROP))
48
49//kbuild:lib-$(CONFIG_RUNSVDIR) += runsvdir.o
50
51//usage:#define runsvdir_trivial_usage
52//usage: "[-P] [-s SCRIPT] DIR"
53//usage:#define runsvdir_full_usage "\n\n"
54//usage: "Start a runsv process for each subdirectory. If it exits, restart it.\n"
55//usage: "\n -P Put each runsv in a new session"
56//usage: "\n -s SCRIPT Run SCRIPT <signo> after signal is processed"
57
58#include <sys/file.h>
59#include "libbb.h"
60#include "common_bufsiz.h"
61#include "runit_lib.h"
62
63#define MAXSERVICES 1000
64
65/* Should be not needed - all dirs are on same FS, right? */
66#define CHECK_DEVNO_TOO 0
67
68struct service {
69#if CHECK_DEVNO_TOO
70 dev_t dev;
71#endif
72 ino_t ino;
73 pid_t pid;
74 smallint isgone;
75};
76
77struct globals {
78 struct service *sv;
79 char *svdir;
80 int svnum;
81#if ENABLE_FEATURE_RUNSVDIR_LOG
82 char *rplog;
83 struct fd_pair logpipe;
84 struct pollfd pfd[1];
85 unsigned stamplog;
86#endif
87} FIX_ALIASING;
88#define G (*(struct globals*)bb_common_bufsiz1)
89#define sv (G.sv )
90#define svdir (G.svdir )
91#define svnum (G.svnum )
92#define rplog (G.rplog )
93#define logpipe (G.logpipe )
94#define pfd (G.pfd )
95#define stamplog (G.stamplog )
96#define INIT_G() do { setup_common_bufsiz(); } while (0)
97
98static void fatal2_cannot(const char *m1, const char *m2)
99{
100 bb_perror_msg_and_die("%s: fatal: can't %s%s", svdir, m1, m2);
101 /* was exiting 100 */
102}
103static void warn3x(const char *m1, const char *m2, const char *m3)
104{
105 bb_error_msg("%s: warning: %s%s%s", svdir, m1, m2, m3);
106}
107static void warn2_cannot(const char *m1, const char *m2)
108{
109 warn3x("can't ", m1, m2);
110}
111#if ENABLE_FEATURE_RUNSVDIR_LOG
112static void warnx(const char *m1)
113{
114 warn3x(m1, "", "");
115}
116#endif
117
118/* inlining + vfork -> bigger code */
119static NOINLINE pid_t runsv(const char *name)
120{
121 pid_t pid;
122
123 /* If we got signaled, stop spawning children at once! */
124 if (bb_got_signal)
125 return 0;
126
127 pid = vfork();
128 if (pid == -1) {
129 warn2_cannot("vfork", "");
130 return 0;
131 }
132 if (pid == 0) {
133 /* child */
134 if (option_mask32 & 1) /* -P option? */
135 setsid();
136/* man execv:
137 * "Signals set to be caught by the calling process image
138 * shall be set to the default action in the new process image."
139 * Therefore, we do not need this: */
140#if 0
141 bb_signals(0
142 | (1 << SIGHUP)
143 | (1 << SIGTERM)
144 , SIG_DFL);
145#endif
146 execlp("runsv", "runsv", name, (char *) NULL);
147 fatal2_cannot("start runsv ", name);
148 }
149 return pid;
150}
151
152/* gcc 4.3.0 does better with NOINLINE */
153static NOINLINE int do_rescan(void)
154{
155 DIR *dir;
156 struct dirent *d;
157 int i;
158 struct stat s;
159 int need_rescan = 0;
160
161 dir = opendir(".");
162 if (!dir) {
163 warn2_cannot("open directory ", svdir);
164 return 1; /* need to rescan again soon */
165 }
166 for (i = 0; i < svnum; i++)
167 sv[i].isgone = 1;
168
169 while (1) {
170 errno = 0;
171 d = readdir(dir);
172 if (!d)
173 break;
174 if (d->d_name[0] == '.')
175 continue;
176 if (stat(d->d_name, &s) == -1) {
177 warn2_cannot("stat ", d->d_name);
178 continue;
179 }
180 if (!S_ISDIR(s.st_mode))
181 continue;
182 /* Do we have this service listed already? */
183 for (i = 0; i < svnum; i++) {
184 if ((sv[i].ino == s.st_ino)
185#if CHECK_DEVNO_TOO
186 && (sv[i].dev == s.st_dev)
187#endif
188 ) {
189 if (sv[i].pid == 0) /* restart if it has died */
190 goto run_ith_sv;
191 sv[i].isgone = 0; /* "we still see you" */
192 goto next_dentry;
193 }
194 }
195 { /* Not found, make new service */
196 struct service *svnew = realloc(sv, (i+1) * sizeof(*sv));
197 if (!svnew) {
198 warn2_cannot("start runsv ", d->d_name);
199 need_rescan = 1;
200 continue;
201 }
202 sv = svnew;
203 svnum++;
204#if CHECK_DEVNO_TOO
205 sv[i].dev = s.st_dev;
206#endif
207 sv[i].ino = s.st_ino;
208 run_ith_sv:
209 sv[i].pid = runsv(d->d_name);
210 sv[i].isgone = 0;
211 }
212 next_dentry: ;
213 }
214 i = errno;
215 closedir(dir);
216 if (i) { /* readdir failed */
217 warn2_cannot("read directory ", svdir);
218 return 1; /* need to rescan again soon */
219 }
220
221 /* Send SIGTERM to runsv whose directories
222 * were no longer found (-> must have been removed) */
223 for (i = 0; i < svnum; i++) {
224 if (!sv[i].isgone)
225 continue;
226 if (sv[i].pid)
227 kill(sv[i].pid, SIGTERM);
228 svnum--;
229 sv[i] = sv[svnum];
230 i--; /* so that we don't skip new sv[i] (bug was here!) */
231 }
232 return need_rescan;
233}
234
235int runsvdir_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
236int runsvdir_main(int argc UNUSED_PARAM, char **argv)
237{
238 struct stat s;
239 dev_t last_dev = last_dev; /* for gcc */
240 ino_t last_ino = last_ino; /* for gcc */
241 time_t last_mtime;
242 int curdir;
243 unsigned stampcheck;
244 int i;
245 int need_rescan;
246 bool i_am_init;
247 char *opt_s_argv[3];
248
249 INIT_G();
250
251 opt_complementary = "-1";
252 opt_s_argv[0] = NULL;
253 opt_s_argv[2] = NULL;
254 getopt32(argv, "Ps:", &opt_s_argv[0]);
255 argv += optind;
256
257 i_am_init = (getpid() == 1);
258 bb_signals(0
259 | (1 << SIGTERM)
260 | (1 << SIGHUP)
261 /* For busybox's init, SIGTERM == reboot,
262 * SIGUSR1 == halt,
263 * SIGUSR2 == poweroff,
264 * Ctlr-ALt-Del sends SIGINT to init,
265 * so we need to intercept SIGUSRn and SIGINT too.
266 * Note that we do not implement actual reboot
267 * (killall(TERM) + umount, etc), we just pause
268 * respawing and avoid exiting (-> making kernel oops).
269 * The user is responsible for the rest.
270 */
271 | (i_am_init ? ((1 << SIGUSR1) | (1 << SIGUSR2) | (1 << SIGINT)) : 0)
272 , record_signo);
273 svdir = *argv++;
274
275#if ENABLE_FEATURE_RUNSVDIR_LOG
276 /* setup log */
277 if (*argv) {
278 rplog = *argv;
279 if (strlen(rplog) < 7) {
280 warnx("log must have at least seven characters");
281 } else if (piped_pair(logpipe)) {
282 warnx("can't create pipe for log");
283 } else {
284 close_on_exec_on(logpipe.rd);
285 close_on_exec_on(logpipe.wr);
286 ndelay_on(logpipe.rd);
287 ndelay_on(logpipe.wr);
288 if (dup2(logpipe.wr, 2) == -1) {
289 warnx("can't set filedescriptor for log");
290 } else {
291 pfd[0].fd = logpipe.rd;
292 pfd[0].events = POLLIN;
293 stamplog = monotonic_sec();
294 goto run;
295 }
296 }
297 rplog = NULL;
298 warnx("log service disabled");
299 }
300 run:
301#endif
302 curdir = open(".", O_RDONLY|O_NDELAY);
303 if (curdir == -1)
304 fatal2_cannot("open current directory", "");
305 close_on_exec_on(curdir);
306
307 stampcheck = monotonic_sec();
308 need_rescan = 1;
309 last_mtime = 0;
310
311 for (;;) {
312 unsigned now;
313 unsigned sig;
314
315 /* collect children */
316 for (;;) {
317 pid_t pid = wait_any_nohang(NULL);
318 if (pid <= 0)
319 break;
320 for (i = 0; i < svnum; i++) {
321 if (pid == sv[i].pid) {
322 /* runsv has died */
323 sv[i].pid = 0;
324 need_rescan = 1;
325 }
326 }
327 }
328
329 now = monotonic_sec();
330 if ((int)(now - stampcheck) >= 0) {
331 /* wait at least a second */
332 stampcheck = now + 1;
333
334 if (stat(svdir, &s) != -1) {
335 if (need_rescan || s.st_mtime != last_mtime
336 || s.st_ino != last_ino || s.st_dev != last_dev
337 ) {
338 /* svdir modified */
339 if (chdir(svdir) != -1) {
340 last_mtime = s.st_mtime;
341 last_dev = s.st_dev;
342 last_ino = s.st_ino;
343 /* if the svdir changed this very second, wait until the
344 * next second, because we won't be able to detect more
345 * changes within this second */
346 while (time(NULL) == last_mtime)
347 usleep(100000);
348 need_rescan = do_rescan();
349 while (fchdir(curdir) == -1) {
350 warn2_cannot("change directory, pausing", "");
351 sleep(5);
352 }
353 } else {
354 warn2_cannot("change directory to ", svdir);
355 }
356 }
357 } else {
358 warn2_cannot("stat ", svdir);
359 }
360 }
361
362#if ENABLE_FEATURE_RUNSVDIR_LOG
363 if (rplog) {
364 if ((int)(now - stamplog) >= 0) {
365 write(logpipe.wr, ".", 1);
366 stamplog = now + 900;
367 }
368 }
369 pfd[0].revents = 0;
370#endif
371 {
372 unsigned deadline = (need_rescan ? 1 : 5);
373#if ENABLE_FEATURE_RUNSVDIR_LOG
374 if (rplog)
375 poll(pfd, 1, deadline*1000);
376 else
377#endif
378 sleep(deadline);
379 }
380
381#if ENABLE_FEATURE_RUNSVDIR_LOG
382 if (pfd[0].revents & POLLIN) {
383 char ch;
384 while (read(logpipe.rd, &ch, 1) > 0) {
385 if (ch < ' ')
386 ch = ' ';
387 for (i = 6; rplog[i] != '\0'; i++)
388 rplog[i-1] = rplog[i];
389 rplog[i-1] = ch;
390 }
391 }
392#endif
393 sig = bb_got_signal;
394 if (!sig)
395 continue;
396 bb_got_signal = 0;
397
398 /* -s SCRIPT: useful if we are init.
399 * In this case typically script never returns,
400 * it halts/powers off/reboots the system. */
401 if (opt_s_argv[0]) {
402 pid_t pid;
403
404 /* Single parameter: signal# */
405 opt_s_argv[1] = utoa(sig);
406 pid = spawn(opt_s_argv);
407 if (pid > 0) {
408 /* Remembering to wait for _any_ children,
409 * not just pid */
410 while (wait(NULL) != pid)
411 continue;
412 }
413 }
414
415 if (sig == SIGHUP) {
416 for (i = 0; i < svnum; i++)
417 if (sv[i].pid)
418 kill(sv[i].pid, SIGTERM);
419 }
420 /* SIGHUP or SIGTERM (or SIGUSRn if we are init) */
421 /* Exit unless we are init */
422 if (!i_am_init)
423 return (SIGHUP == sig) ? 111 : EXIT_SUCCESS;
424
425 /* init continues to monitor services forever */
426 } /* for (;;) */
427}
Note: See TracBrowser for help on using the repository browser.