source: MondoRescue/branches/3.0/mindi-busybox/runit/runsvdir.c@ 3085

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