source: branches/2.2.5/mindi-busybox/runit/runsv.c @ 1765

Last change on this file since 1765 was 1765, checked in by Bruno Cornec, 13 years ago

Update to busybox 1.7.2

  • Property svn:eol-style set to native
File size: 14.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 Denis 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
64static int selfpipe[2];
65
66/* state */
67#define S_DOWN 0
68#define S_RUN 1
69#define S_FINISH 2
70/* ctrl */
71#define C_NOOP 0
72#define C_TERM 1
73#define C_PAUSE 2
74/* want */
75#define W_UP 0
76#define W_DOWN 1
77#define W_EXIT 2
78
79struct svdir {
80    int pid;
81    smallint state;
82    smallint ctrl;
83    smallint want;
84    smallint islog;
85    struct timespec start;
86    int fdlock;
87    int fdcontrol;
88    int fdcontrolwrite;
89};
90
91static struct svdir svd[2];
92static smallint sigterm;
93static smallint haslog;
94static smallint pidchanged = 1;
95static int logpipe[2];
96static char *dir;
97
98static void fatal2_cannot(const char *m1, const char *m2)
99{
100    bb_perror_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
101    /* was exiting 111 */
102}
103static void fatal_cannot(const char *m)
104{
105    fatal2_cannot(m, "");
106    /* was exiting 111 */
107}
108static void fatal2x_cannot(const char *m1, const char *m2)
109{
110    bb_error_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
111    /* was exiting 111 */
112}
113static void warn_cannot(const char *m)
114{
115    bb_perror_msg("%s: warning: cannot %s", dir, m);
116}
117
118static void s_child(int sig_no)
119{
120    write(selfpipe[1], "", 1);
121}
122
123static void s_term(int sig_no)
124{
125    sigterm = 1;
126    write(selfpipe[1], "", 1); /* XXX */
127}
128
129static char *add_str(char *p, const char *to_add)
130{
131    while ((*p = *to_add) != '\0') {
132        p++;
133        to_add++;
134    }
135    return p;
136}
137
138static int open_trunc_or_warn(const char *name)
139{
140    int fd = open_trunc(name);
141    if (fd < 0)
142        bb_perror_msg("%s: warning: cannot open %s",
143                dir, name);
144    return fd;
145}
146
147static int rename_or_warn(const char *old, const char *new)
148{
149    if (rename(old, new) == -1) {
150        bb_perror_msg("%s: warning: cannot rename %s to %s",
151                dir, old, new);
152        return -1;
153    }
154    return 0;
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 = add_str(p, "down");
191            break;
192        case S_RUN:
193            p = add_str(p, "run");
194            break;
195        case S_FINISH:
196            p = add_str(p, "finish");
197            break;
198        }
199        if (s->ctrl & C_PAUSE) p = add_str(p, ", paused");
200        if (s->ctrl & C_TERM) p = add_str(p, ", got TERM");
201        if (s->state != S_DOWN)
202            switch (s->want) {
203            case W_DOWN:
204                p = add_str(p, ", want down");
205                break;
206            case W_EXIT:
207                p = add_str(p, ", want exit");
208                break;
209            }
210        *p++ = '\n';
211        write(fd, stat_buf, p - stat_buf);
212        close(fd);
213    }
214
215    rename_or_warn("supervise/stat.new",
216        s->islog ? "log/supervise/stat" : "log/supervise/stat"+4);
217
218    /* supervise compatibility */
219    memset(&status, 0, sizeof(status));
220    status.time_be64 = SWAP_BE64(s->start.tv_sec + 0x400000000000000aULL);
221    status.time_nsec_be32 = SWAP_BE32(s->start.tv_nsec);
222    status.pid_le32 = SWAP_LE32(s->pid);
223    if (s->ctrl & C_PAUSE)
224        status.paused = 1;
225    if (s->want == W_UP)
226        status.want = 'u';
227    else
228        status.want = 'd';
229    if (s->ctrl & C_TERM)
230        status.got_term = 1;
231    status.run_or_finish = s->state;
232    fd = open_trunc_or_warn("supervise/status.new");
233    if (fd < 0)
234        return;
235    sz = write(fd, &status, sizeof(status));
236    close(fd);
237    if (sz != sizeof(status)) {
238        warn_cannot("write supervise/status.new");
239        unlink("supervise/status.new");
240        return;
241    }
242    rename_or_warn("supervise/status.new",
243        s->islog ? "log/supervise/status" : "log/supervise/status"+4);
244}
245
246static unsigned custom(struct svdir *s, char c)
247{
248    int pid;
249    int w;
250    char a[10];
251    struct stat st;
252    char *prog[2];
253
254    if (s->islog) return 0;
255    strcpy(a, "control/?");
256    a[8] = c;
257    if (stat(a, &st) == 0) {
258        if (st.st_mode & S_IXUSR) {
259            pid = fork();
260            if (pid == -1) {
261                warn_cannot("fork for control/?");
262                return 0;
263            }
264            if (!pid) {
265                if (haslog && dup2(logpipe[1], 1) == -1)
266                    warn_cannot("setup stdout for control/?");
267                prog[0] = a;
268                prog[1] = NULL;
269                execve(a, prog, environ);
270                fatal_cannot("run control/?");
271            }
272            while (wait_pid(&w, pid) == -1) {
273                if (errno == EINTR) continue;
274                warn_cannot("wait for child control/?");
275                return 0;
276            }
277            return !wait_exitcode(w);
278        }
279    } else {
280        if (errno != ENOENT)
281            warn_cannot("stat control/?");
282    }
283    return 0;
284}
285
286static void stopservice(struct svdir *s)
287{
288    if (s->pid && !custom(s, 't')) {
289        kill(s->pid, SIGTERM);
290        s->ctrl |= C_TERM;
291        update_status(s);
292    }
293    if (s->want == W_DOWN) {
294        kill(s->pid, SIGCONT);
295        custom(s, 'd');
296        return;
297    }
298    if (s->want == W_EXIT) {
299        kill(s->pid, SIGCONT);
300        custom(s, 'x');
301    }
302}
303
304static void startservice(struct svdir *s)
305{
306    int p;
307    char *run[2];
308
309    if (s->state == S_FINISH)
310        run[0] = (char*)"./finish";
311    else {
312        run[0] = (char*)"./run";
313        custom(s, 'u');
314    }
315    run[1] = NULL;
316
317    if (s->pid != 0)
318        stopservice(s); /* should never happen */
319    while ((p = fork()) == -1) {
320        warn_cannot("fork, sleeping");
321        sleep(5);
322    }
323    if (p == 0) {
324        /* child */
325        if (haslog) {
326            if (s->islog) {
327                xdup2(logpipe[0], 0);
328                close(logpipe[1]);
329                xchdir("./log");
330            } else {
331                xdup2(logpipe[1], 1);
332                close(logpipe[0]);
333            }
334        }
335        signal(SIGCHLD, SIG_DFL);
336        signal(SIGTERM, SIG_DFL);
337        sig_unblock(SIGCHLD);
338        sig_unblock(SIGTERM);
339        execvp(*run, run);
340        fatal2_cannot(s->islog ? "start log/" : "start ", *run);
341    }
342    if (s->state != S_FINISH) {
343        gettimeofday_ns(&s->start);
344        s->state = S_RUN;
345    }
346    s->pid = p;
347    pidchanged = 1;
348    s->ctrl = C_NOOP;
349    update_status(s);
350}
351
352static int ctrl(struct svdir *s, char c)
353{
354    int sig;
355
356    switch (c) {
357    case 'd': /* down */
358        s->want = W_DOWN;
359        update_status(s);
360        if (s->pid && s->state != S_FINISH)
361            stopservice(s);
362        break;
363    case 'u': /* up */
364        s->want = W_UP;
365        update_status(s);
366        if (s->pid == 0)
367            startservice(s);
368        break;
369    case 'x': /* exit */
370        if (s->islog)
371            break;
372        s->want = W_EXIT;
373        update_status(s);
374        /* FALLTHROUGH */
375    case 't': /* sig term */
376        if (s->pid && s->state != S_FINISH)
377            stopservice(s);
378        break;
379    case 'k': /* sig kill */
380        if (s->pid && !custom(s, c))
381            kill(s->pid, SIGKILL);
382        s->state = S_DOWN;
383        break;
384    case 'p': /* sig pause */
385        if (s->pid && !custom(s, c))
386            kill(s->pid, SIGSTOP);
387        s->ctrl |= C_PAUSE;
388        update_status(s);
389        break;
390    case 'c': /* sig cont */
391        if (s->pid && !custom(s, c))
392            kill(s->pid, SIGCONT);
393        if (s->ctrl & C_PAUSE)
394            s->ctrl &= ~C_PAUSE;
395        update_status(s);
396        break;
397    case 'o': /* once */
398        s->want = W_DOWN;
399        update_status(s);
400        if (!s->pid)
401            startservice(s);
402        break;
403    case 'a': /* sig alarm */
404        sig = SIGALRM;
405        goto sendsig;
406    case 'h': /* sig hup */
407        sig = SIGHUP;
408        goto sendsig;
409    case 'i': /* sig int */
410        sig = SIGINT;
411        goto sendsig;
412    case 'q': /* sig quit */
413        sig = SIGQUIT;
414        goto sendsig;
415    case '1': /* sig usr1 */
416        sig = SIGUSR1;
417        goto sendsig;
418    case '2': /* sig usr2 */
419        sig = SIGUSR2;
420        goto sendsig;
421    }
422    return 1;
423 sendsig:
424    if (s->pid && !custom(s, c))
425        kill(s->pid, sig);
426    return 1;
427}
428
429int runsv_main(int argc, char **argv);
430int runsv_main(int argc, char **argv)
431{
432    struct stat s;
433    int fd;
434    int r;
435    char buf[256];
436
437    if (!argv[1] || argv[2])
438        bb_show_usage();
439    dir = argv[1];
440
441    xpipe(selfpipe);
442    coe(selfpipe[0]);
443    coe(selfpipe[1]);
444    ndelay_on(selfpipe[0]);
445    ndelay_on(selfpipe[1]);
446
447    sig_block(SIGCHLD);
448    sig_catch(SIGCHLD, s_child);
449    sig_block(SIGTERM);
450    sig_catch(SIGTERM, s_term);
451
452    xchdir(dir);
453    /* bss: svd[0].pid = 0; */
454    if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
455    if (C_NOOP) svd[0].ctrl = C_NOOP;
456    if (W_UP) svd[0].want = W_UP;
457    /* bss: svd[0].islog = 0; */
458    /* bss: svd[1].pid = 0; */
459    gettimeofday_ns(&svd[0].start);
460    if (stat("down", &s) != -1) svd[0].want = W_DOWN;
461
462    if (stat("log", &s) == -1) {
463        if (errno != ENOENT)
464            warn_cannot("stat ./log");
465    } else {
466        if (!S_ISDIR(s.st_mode)) {
467            errno = 0;
468            warn_cannot("stat log/down: log is not a directory");
469        } else {
470            haslog = 1;
471            svd[1].state = S_DOWN;
472            svd[1].ctrl = C_NOOP;
473            svd[1].want = W_UP;
474            svd[1].islog = 1;
475            gettimeofday_ns(&svd[1].start);
476            if (stat("log/down", &s) != -1)
477                svd[1].want = W_DOWN;
478            xpipe(logpipe);
479            coe(logpipe[0]);
480            coe(logpipe[1]);
481        }
482    }
483
484    if (mkdir("supervise", 0700) == -1) {
485        r = readlink("supervise", buf, sizeof(buf));
486        if (r != -1) {
487            if (r == sizeof(buf))
488                fatal2x_cannot("readlink ./supervise", ": name too long");
489            buf[r] = 0;
490            mkdir(buf, 0700);
491        } else {
492            if ((errno != ENOENT) && (errno != EINVAL))
493                fatal_cannot("readlink ./supervise");
494        }
495    }
496    svd[0].fdlock = xopen3("log/supervise/lock"+4,
497            O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
498    if (lock_exnb(svd[0].fdlock) == -1)
499        fatal_cannot("lock supervise/lock");
500    coe(svd[0].fdlock);
501    if (haslog) {
502        if (mkdir("log/supervise", 0700) == -1) {
503            r = readlink("log/supervise", buf, 256);
504            if (r != -1) {
505                if (r == 256)
506                    fatal2x_cannot("readlink ./log/supervise", ": name too long");
507                buf[r] = 0;
508                fd = xopen(".", O_RDONLY|O_NDELAY);
509                xchdir("./log");
510                mkdir(buf, 0700);
511                if (fchdir(fd) == -1)
512                    fatal_cannot("change back to service directory");
513                close(fd);
514            }
515            else {
516                if ((errno != ENOENT) && (errno != EINVAL))
517                    fatal_cannot("readlink ./log/supervise");
518            }
519        }
520        svd[1].fdlock = xopen3("log/supervise/lock",
521                O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
522        if (lock_ex(svd[1].fdlock) == -1)
523            fatal_cannot("lock log/supervise/lock");
524        coe(svd[1].fdlock);
525    }
526
527    mkfifo("log/supervise/control"+4, 0600);
528    svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
529    coe(svd[0].fdcontrol);
530    svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
531    coe(svd[0].fdcontrolwrite);
532    update_status(&svd[0]);
533    if (haslog) {
534        mkfifo("log/supervise/control", 0600);
535        svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
536        coe(svd[1].fdcontrol);
537        svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
538        coe(svd[1].fdcontrolwrite);
539        update_status(&svd[1]);
540    }
541    mkfifo("log/supervise/ok"+4, 0600);
542    fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
543    coe(fd);
544    if (haslog) {
545        mkfifo("log/supervise/ok", 0600);
546        fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
547        coe(fd);
548    }
549    for (;;) {
550        struct pollfd x[3];
551        unsigned deadline;
552        char ch;
553
554        if (haslog)
555            if (!svd[1].pid && svd[1].want == W_UP)
556                startservice(&svd[1]);
557        if (!svd[0].pid)
558            if (svd[0].want == W_UP || svd[0].state == S_FINISH)
559                startservice(&svd[0]);
560
561        x[0].fd = selfpipe[0];
562        x[0].events = POLLIN;
563        x[1].fd = svd[0].fdcontrol;
564        x[1].events = POLLIN;
565        /* x[2] is used only if haslog == 1 */
566        x[2].fd = svd[1].fdcontrol;
567        x[2].events = POLLIN;
568        sig_unblock(SIGTERM);
569        sig_unblock(SIGCHLD);
570        poll(x, 2 + haslog, 3600*1000);
571        sig_block(SIGTERM);
572        sig_block(SIGCHLD);
573
574        while (read(selfpipe[0], &ch, 1) == 1)
575            continue;
576
577        for (;;) {
578            int child;
579            int wstat;
580
581            child = wait_nohang(&wstat);
582            if (!child)
583                break;
584            if ((child == -1) && (errno != EINTR))
585                break;
586            if (child == svd[0].pid) {
587                svd[0].pid = 0;
588                pidchanged = 1;
589                svd[0].ctrl &=~ C_TERM;
590                if (svd[0].state != S_FINISH) {
591                    fd = open_read("finish");
592                    if (fd != -1) {
593                        close(fd);
594                        svd[0].state = S_FINISH;
595                        update_status(&svd[0]);
596                        continue;
597                    }
598                }
599                svd[0].state = S_DOWN;
600                deadline = svd[0].start.tv_sec + 1;
601                gettimeofday_ns(&svd[0].start);
602                update_status(&svd[0]);
603                if (LESS(svd[0].start.tv_sec, deadline))
604                    sleep(1);
605            }
606            if (haslog) {
607                if (child == svd[1].pid) {
608                    svd[1].pid = 0;
609                    pidchanged = 1;
610                    svd[1].state = S_DOWN;
611                    svd[1].ctrl &= ~C_TERM;
612                    deadline = svd[1].start.tv_sec + 1;
613                    gettimeofday_ns(&svd[1].start);
614                    update_status(&svd[1]);
615                    if (LESS(svd[1].start.tv_sec, deadline))
616                        sleep(1);
617                }
618            }
619        }
620        if (read(svd[0].fdcontrol, &ch, 1) == 1)
621            ctrl(&svd[0], ch);
622        if (haslog)
623            if (read(svd[1].fdcontrol, &ch, 1) == 1)
624                ctrl(&svd[1], ch);
625
626        if (sigterm) {
627            ctrl(&svd[0], 'x');
628            sigterm = 0;
629        }
630
631        if (svd[0].want == W_EXIT && svd[0].state == S_DOWN) {
632            if (svd[1].pid == 0)
633                _exit(0);
634            if (svd[1].want != W_EXIT) {
635                svd[1].want = W_EXIT;
636                /* stopservice(&svd[1]); */
637                update_status(&svd[1]);
638                close(logpipe[1]);
639                close(logpipe[0]);
640            }
641        }
642    }
643    /* not reached */
644    return 0;
645}
Note: See TracBrowser for help on using the repository browser.