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

Last change on this file since 3085 was 2725, checked in by bruno, 8 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.