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

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