source: MondoRescue/branches/3.2/mindi-busybox/runit/runsvdir.c@ 3232

Last change on this file since 3232 was 3232, checked in by Bruno Cornec, 10 years ago
  • Update mindi-busybox to 1.21.1
  • Property svn:eol-style set to native
File size: 10.0 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/* TODO: depends on runit_lib.c - review and reduce/eliminate */
30
31//usage:#define runsvdir_trivial_usage
32//usage: "[-P] [-s SCRIPT] DIR"
33//usage:#define runsvdir_full_usage "\n\n"
34//usage: "Start a runsv process for each subdirectory. If it exits, restart it.\n"
35//usage: "\n -P Put each runsv in a new session"
36//usage: "\n -s SCRIPT Run SCRIPT <signo> after signal is processed"
37
38#include <sys/poll.h>
39#include <sys/file.h>
40#include "libbb.h"
41#include "runit_lib.h"
42
43#define MAXSERVICES 1000
44
45/* Should be not needed - all dirs are on same FS, right? */
46#define CHECK_DEVNO_TOO 0
47
48struct service {
49#if CHECK_DEVNO_TOO
50 dev_t dev;
51#endif
52 ino_t ino;
53 pid_t pid;
54 smallint isgone;
55};
56
57struct globals {
58 struct service *sv;
59 char *svdir;
60 int svnum;
61#if ENABLE_FEATURE_RUNSVDIR_LOG
62 char *rplog;
63 int rploglen;
64 struct fd_pair logpipe;
65 struct pollfd pfd[1];
66 unsigned stamplog;
67#endif
68} FIX_ALIASING;
69#define G (*(struct globals*)&bb_common_bufsiz1)
70#define sv (G.sv )
71#define svdir (G.svdir )
72#define svnum (G.svnum )
73#define rplog (G.rplog )
74#define rploglen (G.rploglen )
75#define logpipe (G.logpipe )
76#define pfd (G.pfd )
77#define stamplog (G.stamplog )
78#define INIT_G() do { } while (0)
79
80static void fatal2_cannot(const char *m1, const char *m2)
81{
82 bb_perror_msg_and_die("%s: fatal: can't %s%s", svdir, m1, m2);
83 /* was exiting 100 */
84}
85static void warn3x(const char *m1, const char *m2, const char *m3)
86{
87 bb_error_msg("%s: warning: %s%s%s", svdir, m1, m2, m3);
88}
89static void warn2_cannot(const char *m1, const char *m2)
90{
91 warn3x("can't ", m1, m2);
92}
93#if ENABLE_FEATURE_RUNSVDIR_LOG
94static void warnx(const char *m1)
95{
96 warn3x(m1, "", "");
97}
98#endif
99
100/* inlining + vfork -> bigger code */
101static NOINLINE pid_t runsv(const char *name)
102{
103 pid_t pid;
104
105 /* If we got signaled, stop spawning children at once! */
106 if (bb_got_signal)
107 return 0;
108
109 pid = vfork();
110 if (pid == -1) {
111 warn2_cannot("vfork", "");
112 return 0;
113 }
114 if (pid == 0) {
115 /* child */
116 if (option_mask32 & 1) /* -P option? */
117 setsid();
118/* man execv:
119 * "Signals set to be caught by the calling process image
120 * shall be set to the default action in the new process image."
121 * Therefore, we do not need this: */
122#if 0
123 bb_signals(0
124 | (1 << SIGHUP)
125 | (1 << SIGTERM)
126 , SIG_DFL);
127#endif
128 execlp("runsv", "runsv", name, (char *) NULL);
129 fatal2_cannot("start runsv ", name);
130 }
131 return pid;
132}
133
134/* gcc 4.3.0 does better with NOINLINE */
135static NOINLINE int do_rescan(void)
136{
137 DIR *dir;
138 struct dirent *d;
139 int i;
140 struct stat s;
141 int need_rescan = 0;
142
143 dir = opendir(".");
144 if (!dir) {
145 warn2_cannot("open directory ", svdir);
146 return 1; /* need to rescan again soon */
147 }
148 for (i = 0; i < svnum; i++)
149 sv[i].isgone = 1;
150
151 while (1) {
152 errno = 0;
153 d = readdir(dir);
154 if (!d)
155 break;
156 if (d->d_name[0] == '.')
157 continue;
158 if (stat(d->d_name, &s) == -1) {
159 warn2_cannot("stat ", d->d_name);
160 continue;
161 }
162 if (!S_ISDIR(s.st_mode))
163 continue;
164 /* Do we have this service listed already? */
165 for (i = 0; i < svnum; i++) {
166 if ((sv[i].ino == s.st_ino)
167#if CHECK_DEVNO_TOO
168 && (sv[i].dev == s.st_dev)
169#endif
170 ) {
171 if (sv[i].pid == 0) /* restart if it has died */
172 goto run_ith_sv;
173 sv[i].isgone = 0; /* "we still see you" */
174 goto next_dentry;
175 }
176 }
177 { /* Not found, make new service */
178 struct service *svnew = realloc(sv, (i+1) * sizeof(*sv));
179 if (!svnew) {
180 warn2_cannot("start runsv ", d->d_name);
181 need_rescan = 1;
182 continue;
183 }
184 sv = svnew;
185 svnum++;
186#if CHECK_DEVNO_TOO
187 sv[i].dev = s.st_dev;
188#endif
189 sv[i].ino = s.st_ino;
190 run_ith_sv:
191 sv[i].pid = runsv(d->d_name);
192 sv[i].isgone = 0;
193 }
194 next_dentry: ;
195 }
196 i = errno;
197 closedir(dir);
198 if (i) { /* readdir failed */
199 warn2_cannot("read directory ", svdir);
200 return 1; /* need to rescan again soon */
201 }
202
203 /* Send SIGTERM to runsv whose directories
204 * were no longer found (-> must have been removed) */
205 for (i = 0; i < svnum; i++) {
206 if (!sv[i].isgone)
207 continue;
208 if (sv[i].pid)
209 kill(sv[i].pid, SIGTERM);
210 svnum--;
211 sv[i] = sv[svnum];
212 i--; /* so that we don't skip new sv[i] (bug was here!) */
213 }
214 return need_rescan;
215}
216
217int runsvdir_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
218int runsvdir_main(int argc UNUSED_PARAM, char **argv)
219{
220 struct stat s;
221 dev_t last_dev = last_dev; /* for gcc */
222 ino_t last_ino = last_ino; /* for gcc */
223 time_t last_mtime = 0;
224 int wstat;
225 int curdir;
226 pid_t pid;
227 unsigned deadline;
228 unsigned now;
229 unsigned stampcheck;
230 int i;
231 int need_rescan = 1;
232 char *opt_s_argv[3];
233
234 INIT_G();
235
236 opt_complementary = "-1";
237 opt_s_argv[0] = NULL;
238 opt_s_argv[2] = NULL;
239 getopt32(argv, "Ps:", &opt_s_argv[0]);
240 argv += optind;
241
242 bb_signals(0
243 | (1 << SIGTERM)
244 | (1 << SIGHUP)
245 /* For busybox's init, SIGTERM == reboot,
246 * SIGUSR1 == halt
247 * SIGUSR2 == poweroff
248 * so we need to intercept SIGUSRn too.
249 * Note that we do not implement actual reboot
250 * (killall(TERM) + umount, etc), we just pause
251 * respawing and avoid exiting (-> making kernel oops).
252 * The user is responsible for the rest. */
253 | (getpid() == 1 ? ((1 << SIGUSR1) | (1 << SIGUSR2)) : 0)
254 , record_signo);
255 svdir = *argv++;
256
257#if ENABLE_FEATURE_RUNSVDIR_LOG
258 /* setup log */
259 if (*argv) {
260 rplog = *argv;
261 rploglen = strlen(rplog);
262 if (rploglen < 7) {
263 warnx("log must have at least seven characters");
264 } else if (piped_pair(logpipe)) {
265 warnx("can't create pipe for log");
266 } else {
267 close_on_exec_on(logpipe.rd);
268 close_on_exec_on(logpipe.wr);
269 ndelay_on(logpipe.rd);
270 ndelay_on(logpipe.wr);
271 if (dup2(logpipe.wr, 2) == -1) {
272 warnx("can't set filedescriptor for log");
273 } else {
274 pfd[0].fd = logpipe.rd;
275 pfd[0].events = POLLIN;
276 stamplog = monotonic_sec();
277 goto run;
278 }
279 }
280 rplog = NULL;
281 warnx("log service disabled");
282 }
283 run:
284#endif
285 curdir = open(".", O_RDONLY|O_NDELAY);
286 if (curdir == -1)
287 fatal2_cannot("open current directory", "");
288 close_on_exec_on(curdir);
289
290 stampcheck = monotonic_sec();
291
292 for (;;) {
293 /* collect children */
294 for (;;) {
295 pid = wait_any_nohang(&wstat);
296 if (pid <= 0)
297 break;
298 for (i = 0; i < svnum; i++) {
299 if (pid == sv[i].pid) {
300 /* runsv has died */
301 sv[i].pid = 0;
302 need_rescan = 1;
303 }
304 }
305 }
306
307 now = monotonic_sec();
308 if ((int)(now - stampcheck) >= 0) {
309 /* wait at least a second */
310 stampcheck = now + 1;
311
312 if (stat(svdir, &s) != -1) {
313 if (need_rescan || s.st_mtime != last_mtime
314 || s.st_ino != last_ino || s.st_dev != last_dev
315 ) {
316 /* svdir modified */
317 if (chdir(svdir) != -1) {
318 last_mtime = s.st_mtime;
319 last_dev = s.st_dev;
320 last_ino = s.st_ino;
321 /* if the svdir changed this very second, wait until the
322 * next second, because we won't be able to detect more
323 * changes within this second */
324 while (time(NULL) == last_mtime)
325 usleep(100000);
326 need_rescan = do_rescan();
327 while (fchdir(curdir) == -1) {
328 warn2_cannot("change directory, pausing", "");
329 sleep(5);
330 }
331 } else {
332 warn2_cannot("change directory to ", svdir);
333 }
334 }
335 } else {
336 warn2_cannot("stat ", svdir);
337 }
338 }
339
340#if ENABLE_FEATURE_RUNSVDIR_LOG
341 if (rplog) {
342 if ((int)(now - stamplog) >= 0) {
343 write(logpipe.wr, ".", 1);
344 stamplog = now + 900;
345 }
346 }
347 pfd[0].revents = 0;
348#endif
349 deadline = (need_rescan ? 1 : 5);
350 sig_block(SIGCHLD);
351#if ENABLE_FEATURE_RUNSVDIR_LOG
352 if (rplog)
353 poll(pfd, 1, deadline*1000);
354 else
355#endif
356 sleep(deadline);
357 sig_unblock(SIGCHLD);
358
359#if ENABLE_FEATURE_RUNSVDIR_LOG
360 if (pfd[0].revents & POLLIN) {
361 char ch;
362 while (read(logpipe.rd, &ch, 1) > 0) {
363 if (ch < ' ')
364 ch = ' ';
365 for (i = 6; i < rploglen; i++)
366 rplog[i-1] = rplog[i];
367 rplog[rploglen-1] = ch;
368 }
369 }
370#endif
371 if (!bb_got_signal)
372 continue;
373
374 /* -s SCRIPT: useful if we are init.
375 * In this case typically script never returns,
376 * it halts/powers off/reboots the system. */
377 if (opt_s_argv[0]) {
378 /* Single parameter: signal# */
379 opt_s_argv[1] = utoa(bb_got_signal);
380 pid = spawn(opt_s_argv);
381 if (pid > 0) {
382 /* Remembering to wait for _any_ children,
383 * not just pid */
384 while (wait(NULL) != pid)
385 continue;
386 }
387 }
388
389 if (bb_got_signal == SIGHUP) {
390 for (i = 0; i < svnum; i++)
391 if (sv[i].pid)
392 kill(sv[i].pid, SIGTERM);
393 }
394 /* SIGHUP or SIGTERM (or SIGUSRn if we are init) */
395 /* Exit unless we are init */
396 if (getpid() != 1)
397 return (SIGHUP == bb_got_signal) ? 111 : EXIT_SUCCESS;
398
399 /* init continues to monitor services forever */
400 bb_got_signal = 0;
401 } /* for (;;) */
402}
Note: See TracBrowser for help on using the repository browser.