source: branches/3.0/mindi-busybox/runit/runsv.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: 16.1 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#if ENABLE_MONOTONIC_SYSCALL
37#include <sys/syscall.h>
38
39/* libc has incredibly messy way of doing this,
40 * typically requiring -lrt. We just skip all this mess */
41static void gettimeofday_ns(struct timespec *ts)
42{
43    syscall(__NR_clock_gettime, CLOCK_REALTIME, ts);
44}
45#else
46static void gettimeofday_ns(struct timespec *ts)
47{
48    if (sizeof(struct timeval) == sizeof(struct timespec)
49     && sizeof(((struct timeval*)ts)->tv_usec) == sizeof(ts->tv_nsec)
50    ) {
51        /* Cheat */
52        gettimeofday((void*)ts, NULL);
53        ts->tv_nsec *= 1000;
54    } else {
55        extern void BUG_need_to_implement_gettimeofday_ns(void);
56        BUG_need_to_implement_gettimeofday_ns();
57    }
58}
59#endif
60
61/* Compare possibly overflowing unsigned counters */
62#define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
63
64/* state */
65#define S_DOWN 0
66#define S_RUN 1
67#define S_FINISH 2
68/* ctrl */
69#define C_NOOP 0
70#define C_TERM 1
71#define C_PAUSE 2
72/* want */
73#define W_UP 0
74#define W_DOWN 1
75#define W_EXIT 2
76
77struct svdir {
78    int pid;
79    smallint state;
80    smallint ctrl;
81    smallint sd_want;
82    smallint islog;
83    struct timespec start;
84    int fdlock;
85    int fdcontrol;
86    int fdcontrolwrite;
87    int wstat;
88};
89
90struct globals {
91    smallint haslog;
92    smallint sigterm;
93    smallint pidchanged;
94    struct fd_pair selfpipe;
95    struct fd_pair logpipe;
96    char *dir;
97    struct svdir svd[2];
98} FIX_ALIASING;
99#define G (*(struct globals*)&bb_common_bufsiz1)
100#define haslog       (G.haslog      )
101#define sigterm      (G.sigterm     )
102#define pidchanged   (G.pidchanged  )
103#define selfpipe     (G.selfpipe    )
104#define logpipe      (G.logpipe     )
105#define dir          (G.dir         )
106#define svd          (G.svd         )
107#define INIT_G() do { \
108    pidchanged = 1; \
109} while (0)
110
111static void fatal2_cannot(const char *m1, const char *m2)
112{
113    bb_perror_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
114    /* was exiting 111 */
115}
116static void fatal_cannot(const char *m)
117{
118    fatal2_cannot(m, "");
119    /* was exiting 111 */
120}
121static void fatal2x_cannot(const char *m1, const char *m2)
122{
123    bb_error_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
124    /* was exiting 111 */
125}
126static void warn_cannot(const char *m)
127{
128    bb_perror_msg("%s: warning: cannot %s", dir, m);
129}
130
131static void s_child(int sig_no UNUSED_PARAM)
132{
133    write(selfpipe.wr, "", 1);
134}
135
136static void s_term(int sig_no UNUSED_PARAM)
137{
138    sigterm = 1;
139    write(selfpipe.wr, "", 1); /* XXX */
140}
141
142/* libbb candidate */
143static char *bb_stpcpy(char *p, const char *to_add)
144{
145    while ((*p = *to_add) != '\0') {
146        p++;
147        to_add++;
148    }
149    return p;
150}
151
152static int open_trunc_or_warn(const char *name)
153{
154    /* Why O_NDELAY? */
155    int fd = open(name, O_WRONLY | O_NDELAY | O_TRUNC | O_CREAT, 0644);
156    if (fd < 0)
157        bb_perror_msg("%s: warning: cannot open %s",
158                dir, name);
159    return fd;
160}
161
162static void update_status(struct svdir *s)
163{
164    ssize_t sz;
165    int fd;
166    svstatus_t status;
167
168    /* pid */
169    if (pidchanged) {
170        fd = open_trunc_or_warn("supervise/pid.new");
171        if (fd < 0)
172            return;
173        if (s->pid) {
174            char spid[sizeof(int)*3 + 2];
175            int size = sprintf(spid, "%u\n", (unsigned)s->pid);
176            write(fd, spid, size);
177        }
178        close(fd);
179        if (rename_or_warn("supervise/pid.new",
180            s->islog ? "log/supervise/pid" : "log/supervise/pid"+4))
181            return;
182        pidchanged = 0;
183    }
184
185    /* stat */
186    fd = open_trunc_or_warn("supervise/stat.new");
187    if (fd < -1)
188        return;
189
190    {
191        char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
192        char *p = stat_buf;
193        switch (s->state) {
194        case S_DOWN:
195            p = bb_stpcpy(p, "down");
196            break;
197        case S_RUN:
198            p = bb_stpcpy(p, "run");
199            break;
200        case S_FINISH:
201            p = bb_stpcpy(p, "finish");
202            break;
203        }
204        if (s->ctrl & C_PAUSE)
205            p = bb_stpcpy(p, ", paused");
206        if (s->ctrl & C_TERM)
207            p = bb_stpcpy(p, ", got TERM");
208        if (s->state != S_DOWN)
209            switch (s->sd_want) {
210            case W_DOWN:
211                p = bb_stpcpy(p, ", want down");
212                break;
213            case W_EXIT:
214                p = bb_stpcpy(p, ", want exit");
215                break;
216            }
217        *p++ = '\n';
218        write(fd, stat_buf, p - stat_buf);
219        close(fd);
220    }
221
222    rename_or_warn("supervise/stat.new",
223        s->islog ? "log/supervise/stat" : "log/supervise/stat"+4);
224
225    /* supervise compatibility */
226    memset(&status, 0, sizeof(status));
227    status.time_be64 = SWAP_BE64(s->start.tv_sec + 0x400000000000000aULL);
228    status.time_nsec_be32 = SWAP_BE32(s->start.tv_nsec);
229    status.pid_le32 = SWAP_LE32(s->pid);
230    if (s->ctrl & C_PAUSE)
231        status.paused = 1;
232    if (s->sd_want == W_UP)
233        status.want = 'u';
234    else
235        status.want = 'd';
236    if (s->ctrl & C_TERM)
237        status.got_term = 1;
238    status.run_or_finish = s->state;
239    fd = open_trunc_or_warn("supervise/status.new");
240    if (fd < 0)
241        return;
242    sz = write(fd, &status, sizeof(status));
243    close(fd);
244    if (sz != sizeof(status)) {
245        warn_cannot("write supervise/status.new");
246        unlink("supervise/status.new");
247        return;
248    }
249    rename_or_warn("supervise/status.new",
250        s->islog ? "log/supervise/status" : "log/supervise/status"+4);
251}
252
253static unsigned custom(struct svdir *s, char c)
254{
255    pid_t pid;
256    int w;
257    char a[10];
258    struct stat st;
259
260    if (s->islog)
261        return 0;
262    strcpy(a, "control/?");
263    a[8] = c; /* replace '?' */
264    if (stat(a, &st) == 0) {
265        if (st.st_mode & S_IXUSR) {
266            pid = vfork();
267            if (pid == -1) {
268                warn_cannot("vfork for control/?");
269                return 0;
270            }
271            if (pid == 0) {
272                /* child */
273                if (haslog && dup2(logpipe.wr, 1) == -1)
274                    warn_cannot("setup stdout for control/?");
275                execl(a, a, (char *) NULL);
276                fatal_cannot("run control/?");
277            }
278            /* parent */
279            if (safe_waitpid(pid, &w, 0) == -1) {
280                warn_cannot("wait for child control/?");
281                return 0;
282            }
283            return WEXITSTATUS(w) == 0;
284        }
285    } else {
286        if (errno != ENOENT)
287            warn_cannot("stat control/?");
288    }
289    return 0;
290}
291
292static void stopservice(struct svdir *s)
293{
294    if (s->pid && !custom(s, 't')) {
295        kill(s->pid, SIGTERM);
296        s->ctrl |= C_TERM;
297        update_status(s);
298    }
299    if (s->sd_want == W_DOWN) {
300        kill(s->pid, SIGCONT);
301        custom(s, 'd');
302        return;
303    }
304    if (s->sd_want == W_EXIT) {
305        kill(s->pid, SIGCONT);
306        custom(s, 'x');
307    }
308}
309
310static void startservice(struct svdir *s)
311{
312    int p;
313    const char *arg[4];
314    char exitcode[sizeof(int)*3 + 2];
315
316    if (s->state == S_FINISH) {
317/* Two arguments are given to ./finish. The first one is ./run exit code,
318 * or -1 if ./run didnt exit normally. The second one is
319 * the least significant byte of the exit status as determined by waitpid;
320 * for instance it is 0 if ./run exited normally, and the signal number
321 * if ./run was terminated by a signal. If runsv cannot start ./run
322 * for some reason, the exit code is 111 and the status is 0.
323 */
324        arg[0] = "./finish";
325        arg[1] = "-1";
326        if (WIFEXITED(s->wstat)) {
327            *utoa_to_buf(WEXITSTATUS(s->wstat), exitcode, sizeof(exitcode)) = '\0';
328            arg[1] = exitcode;
329        }
330        //arg[2] = "0";
331        //if (WIFSIGNALED(s->wstat)) {
332            arg[2] = utoa(WTERMSIG(s->wstat));
333        //}
334        arg[3] = NULL;
335    } else {
336        arg[0] = "./run";
337        arg[1] = NULL;
338        custom(s, 'u');
339    }
340
341    if (s->pid != 0)
342        stopservice(s); /* should never happen */
343    while ((p = vfork()) == -1) {
344        warn_cannot("vfork, sleeping");
345        sleep(5);
346    }
347    if (p == 0) {
348        /* child */
349        if (haslog) {
350            /* NB: bug alert! right order is close, then dup2 */
351            if (s->islog) {
352                xchdir("./log");
353                close(logpipe.wr);
354                xdup2(logpipe.rd, 0);
355            } else {
356                close(logpipe.rd);
357                xdup2(logpipe.wr, 1);
358            }
359        }
360        /* Non-ignored signals revert to SIG_DFL on exec anyway */
361        /*bb_signals(0
362            + (1 << SIGCHLD)
363            + (1 << SIGTERM)
364            , SIG_DFL);*/
365        sig_unblock(SIGCHLD);
366        sig_unblock(SIGTERM);
367        execv(arg[0], (char**) arg);
368        fatal2_cannot(s->islog ? "start log/" : "start ", arg[0]);
369    }
370    /* parent */
371    if (s->state != S_FINISH) {
372        gettimeofday_ns(&s->start);
373        s->state = S_RUN;
374    }
375    s->pid = p;
376    pidchanged = 1;
377    s->ctrl = C_NOOP;
378    update_status(s);
379}
380
381static int ctrl(struct svdir *s, char c)
382{
383    int sig;
384
385    switch (c) {
386    case 'd': /* down */
387        s->sd_want = W_DOWN;
388        update_status(s);
389        if (s->pid && s->state != S_FINISH)
390            stopservice(s);
391        break;
392    case 'u': /* up */
393        s->sd_want = W_UP;
394        update_status(s);
395        if (s->pid == 0)
396            startservice(s);
397        break;
398    case 'x': /* exit */
399        if (s->islog)
400            break;
401        s->sd_want = W_EXIT;
402        update_status(s);
403        /* FALLTHROUGH */
404    case 't': /* sig term */
405        if (s->pid && s->state != S_FINISH)
406            stopservice(s);
407        break;
408    case 'k': /* sig kill */
409        if (s->pid && !custom(s, c))
410            kill(s->pid, SIGKILL);
411        s->state = S_DOWN;
412        break;
413    case 'p': /* sig pause */
414        if (s->pid && !custom(s, c))
415            kill(s->pid, SIGSTOP);
416        s->ctrl |= C_PAUSE;
417        update_status(s);
418        break;
419    case 'c': /* sig cont */
420        if (s->pid && !custom(s, c))
421            kill(s->pid, SIGCONT);
422        s->ctrl &= ~C_PAUSE;
423        update_status(s);
424        break;
425    case 'o': /* once */
426        s->sd_want = W_DOWN;
427        update_status(s);
428        if (!s->pid)
429            startservice(s);
430        break;
431    case 'a': /* sig alarm */
432        sig = SIGALRM;
433        goto sendsig;
434    case 'h': /* sig hup */
435        sig = SIGHUP;
436        goto sendsig;
437    case 'i': /* sig int */
438        sig = SIGINT;
439        goto sendsig;
440    case 'q': /* sig quit */
441        sig = SIGQUIT;
442        goto sendsig;
443    case '1': /* sig usr1 */
444        sig = SIGUSR1;
445        goto sendsig;
446    case '2': /* sig usr2 */
447        sig = SIGUSR2;
448        goto sendsig;
449    }
450    return 1;
451 sendsig:
452    if (s->pid && !custom(s, c))
453        kill(s->pid, sig);
454    return 1;
455}
456
457int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
458int runsv_main(int argc UNUSED_PARAM, char **argv)
459{
460    struct stat s;
461    int fd;
462    int r;
463    char buf[256];
464
465    INIT_G();
466
467    dir = single_argv(argv);
468
469    xpiped_pair(selfpipe);
470    close_on_exec_on(selfpipe.rd);
471    close_on_exec_on(selfpipe.wr);
472    ndelay_on(selfpipe.rd);
473    ndelay_on(selfpipe.wr);
474
475    sig_block(SIGCHLD);
476    bb_signals_recursive_norestart(1 << SIGCHLD, s_child);
477    sig_block(SIGTERM);
478    bb_signals_recursive_norestart(1 << SIGTERM, s_term);
479
480    xchdir(dir);
481    /* bss: svd[0].pid = 0; */
482    if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
483    if (C_NOOP) svd[0].ctrl = C_NOOP;
484    if (W_UP) svd[0].sd_want = W_UP;
485    /* bss: svd[0].islog = 0; */
486    /* bss: svd[1].pid = 0; */
487    gettimeofday_ns(&svd[0].start);
488    if (stat("down", &s) != -1)
489        svd[0].sd_want = W_DOWN;
490
491    if (stat("log", &s) == -1) {
492        if (errno != ENOENT)
493            warn_cannot("stat ./log");
494    } else {
495        if (!S_ISDIR(s.st_mode)) {
496            errno = 0;
497            warn_cannot("stat log/down: log is not a directory");
498        } else {
499            haslog = 1;
500            svd[1].state = S_DOWN;
501            svd[1].ctrl = C_NOOP;
502            svd[1].sd_want = W_UP;
503            svd[1].islog = 1;
504            gettimeofday_ns(&svd[1].start);
505            if (stat("log/down", &s) != -1)
506                svd[1].sd_want = W_DOWN;
507            xpiped_pair(logpipe);
508            close_on_exec_on(logpipe.rd);
509            close_on_exec_on(logpipe.wr);
510        }
511    }
512
513    if (mkdir("supervise", 0700) == -1) {
514        r = readlink("supervise", buf, sizeof(buf));
515        if (r != -1) {
516            if (r == sizeof(buf))
517                fatal2x_cannot("readlink ./supervise", ": name too long");
518            buf[r] = 0;
519            mkdir(buf, 0700);
520        } else {
521            if ((errno != ENOENT) && (errno != EINVAL))
522                fatal_cannot("readlink ./supervise");
523        }
524    }
525    svd[0].fdlock = xopen3("log/supervise/lock"+4,
526            O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
527    if (flock(svd[0].fdlock, LOCK_EX | LOCK_NB) == -1)
528        fatal_cannot("lock supervise/lock");
529    close_on_exec_on(svd[0].fdlock);
530    if (haslog) {
531        if (mkdir("log/supervise", 0700) == -1) {
532            r = readlink("log/supervise", buf, 256);
533            if (r != -1) {
534                if (r == 256)
535                    fatal2x_cannot("readlink ./log/supervise", ": name too long");
536                buf[r] = 0;
537                fd = xopen(".", O_RDONLY|O_NDELAY);
538                xchdir("./log");
539                mkdir(buf, 0700);
540                if (fchdir(fd) == -1)
541                    fatal_cannot("change back to service directory");
542                close(fd);
543            }
544            else {
545                if ((errno != ENOENT) && (errno != EINVAL))
546                    fatal_cannot("readlink ./log/supervise");
547            }
548        }
549        svd[1].fdlock = xopen3("log/supervise/lock",
550                O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
551        if (flock(svd[1].fdlock, LOCK_EX) == -1)
552            fatal_cannot("lock log/supervise/lock");
553        close_on_exec_on(svd[1].fdlock);
554    }
555
556    mkfifo("log/supervise/control"+4, 0600);
557    svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
558    close_on_exec_on(svd[0].fdcontrol);
559    svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
560    close_on_exec_on(svd[0].fdcontrolwrite);
561    update_status(&svd[0]);
562    if (haslog) {
563        mkfifo("log/supervise/control", 0600);
564        svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
565        close_on_exec_on(svd[1].fdcontrol);
566        svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
567        close_on_exec_on(svd[1].fdcontrolwrite);
568        update_status(&svd[1]);
569    }
570    mkfifo("log/supervise/ok"+4, 0600);
571    fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
572    close_on_exec_on(fd);
573    if (haslog) {
574        mkfifo("log/supervise/ok", 0600);
575        fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
576        close_on_exec_on(fd);
577    }
578    for (;;) {
579        struct pollfd x[3];
580        unsigned deadline;
581        char ch;
582
583        if (haslog)
584            if (!svd[1].pid && svd[1].sd_want == W_UP)
585                startservice(&svd[1]);
586        if (!svd[0].pid)
587            if (svd[0].sd_want == W_UP || svd[0].state == S_FINISH)
588                startservice(&svd[0]);
589
590        x[0].fd = selfpipe.rd;
591        x[0].events = POLLIN;
592        x[1].fd = svd[0].fdcontrol;
593        x[1].events = POLLIN;
594        /* x[2] is used only if haslog == 1 */
595        x[2].fd = svd[1].fdcontrol;
596        x[2].events = POLLIN;
597        sig_unblock(SIGTERM);
598        sig_unblock(SIGCHLD);
599        poll(x, 2 + haslog, 3600*1000);
600        sig_block(SIGTERM);
601        sig_block(SIGCHLD);
602
603        while (read(selfpipe.rd, &ch, 1) == 1)
604            continue;
605
606        for (;;) {
607            pid_t child;
608            int wstat;
609
610            child = wait_any_nohang(&wstat);
611            if (!child)
612                break;
613            if ((child == -1) && (errno != EINTR))
614                break;
615            if (child == svd[0].pid) {
616                svd[0].wstat = wstat;
617                svd[0].pid = 0;
618                pidchanged = 1;
619                svd[0].ctrl &= ~C_TERM;
620                if (svd[0].state != S_FINISH) {
621                    fd = open("finish", O_RDONLY|O_NDELAY);
622                    if (fd != -1) {
623                        close(fd);
624                        svd[0].state = S_FINISH;
625                        update_status(&svd[0]);
626                        continue;
627                    }
628                }
629                svd[0].state = S_DOWN;
630                deadline = svd[0].start.tv_sec + 1;
631                gettimeofday_ns(&svd[0].start);
632                update_status(&svd[0]);
633                if (LESS(svd[0].start.tv_sec, deadline))
634                    sleep(1);
635            }
636            if (haslog) {
637                if (child == svd[1].pid) {
638                    svd[0].wstat = wstat;
639                    svd[1].pid = 0;
640                    pidchanged = 1;
641                    svd[1].state = S_DOWN;
642                    svd[1].ctrl &= ~C_TERM;
643                    deadline = svd[1].start.tv_sec + 1;
644                    gettimeofday_ns(&svd[1].start);
645                    update_status(&svd[1]);
646                    if (LESS(svd[1].start.tv_sec, deadline))
647                        sleep(1);
648                }
649            }
650        } /* for (;;) */
651        if (read(svd[0].fdcontrol, &ch, 1) == 1)
652            ctrl(&svd[0], ch);
653        if (haslog)
654            if (read(svd[1].fdcontrol, &ch, 1) == 1)
655                ctrl(&svd[1], ch);
656
657        if (sigterm) {
658            ctrl(&svd[0], 'x');
659            sigterm = 0;
660        }
661
662        if (svd[0].sd_want == W_EXIT && svd[0].state == S_DOWN) {
663            if (svd[1].pid == 0)
664                _exit(EXIT_SUCCESS);
665            if (svd[1].sd_want != W_EXIT) {
666                svd[1].sd_want = W_EXIT;
667                /* stopservice(&svd[1]); */
668                update_status(&svd[1]);
669                close(logpipe.wr);
670                close(logpipe.rd);
671            }
672        }
673    } /* for (;;) */
674    /* not reached */
675    return 0;
676}
Note: See TracBrowser for help on using the repository browser.