busybox/runit/runsv.c
<<
>>
Prefs
   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 want;
  82        smallint islog;
  83        struct timespec start;
  84        int fdlock;
  85        int fdcontrol;
  86        int fdcontrolwrite;
  87};
  88
  89struct globals {
  90        smallint haslog;
  91        smallint sigterm;
  92        smallint pidchanged;
  93        struct fd_pair selfpipe;
  94        struct fd_pair logpipe;
  95        char *dir;
  96        struct svdir svd[2];
  97};
  98#define G (*(struct globals*)&bb_common_bufsiz1)
  99#define haslog       (G.haslog      )
 100#define sigterm      (G.sigterm     )
 101#define pidchanged   (G.pidchanged  )
 102#define selfpipe     (G.selfpipe    )
 103#define logpipe      (G.logpipe     )
 104#define dir          (G.dir         )
 105#define svd          (G.svd         )
 106#define INIT_G() do { \
 107        pidchanged = 1; \
 108} while (0)
 109
 110static void fatal2_cannot(const char *m1, const char *m2)
 111{
 112        bb_perror_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
 113        /* was exiting 111 */
 114}
 115static void fatal_cannot(const char *m)
 116{
 117        fatal2_cannot(m, "");
 118        /* was exiting 111 */
 119}
 120static void fatal2x_cannot(const char *m1, const char *m2)
 121{
 122        bb_error_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
 123        /* was exiting 111 */
 124}
 125static void warn_cannot(const char *m)
 126{
 127        bb_perror_msg("%s: warning: cannot %s", dir, m);
 128}
 129
 130static void s_child(int sig_no UNUSED_PARAM)
 131{
 132        write(selfpipe.wr, "", 1);
 133}
 134
 135static void s_term(int sig_no UNUSED_PARAM)
 136{
 137        sigterm = 1;
 138        write(selfpipe.wr, "", 1); /* XXX */
 139}
 140
 141static char *add_str(char *p, const char *to_add)
 142{
 143        while ((*p = *to_add) != '\0') {
 144                p++;
 145                to_add++;
 146        }
 147        return p;
 148}
 149
 150static int open_trunc_or_warn(const char *name)
 151{
 152        int fd = open_trunc(name);
 153        if (fd < 0)
 154                bb_perror_msg("%s: warning: cannot open %s",
 155                                dir, name);
 156        return fd;
 157}
 158
 159static void update_status(struct svdir *s)
 160{
 161        ssize_t sz;
 162        int fd;
 163        svstatus_t status;
 164
 165        /* pid */
 166        if (pidchanged) {
 167                fd = open_trunc_or_warn("supervise/pid.new");
 168                if (fd < 0)
 169                        return;
 170                if (s->pid) {
 171                        char spid[sizeof(int)*3 + 2];
 172                        int size = sprintf(spid, "%u\n", (unsigned)s->pid);
 173                        write(fd, spid, size);
 174                }
 175                close(fd);
 176                if (rename_or_warn("supervise/pid.new",
 177                    s->islog ? "log/supervise/pid" : "log/supervise/pid"+4))
 178                        return;
 179                pidchanged = 0;
 180        }
 181
 182        /* stat */
 183        fd = open_trunc_or_warn("supervise/stat.new");
 184        if (fd < -1)
 185                return;
 186
 187        {
 188                char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
 189                char *p = stat_buf;
 190                switch (s->state) {
 191                case S_DOWN:
 192                        p = add_str(p, "down");
 193                        break;
 194                case S_RUN:
 195                        p = add_str(p, "run");
 196                        break;
 197                case S_FINISH:
 198                        p = add_str(p, "finish");
 199                        break;
 200                }
 201                if (s->ctrl & C_PAUSE) p = add_str(p, ", paused");
 202                if (s->ctrl & C_TERM) p = add_str(p, ", got TERM");
 203                if (s->state != S_DOWN)
 204                        switch (s->want) {
 205                        case W_DOWN:
 206                                p = add_str(p, ", want down");
 207                                break;
 208                        case W_EXIT:
 209                                p = add_str(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->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) return 0;
 256        strcpy(a, "control/?");
 257        a[8] = c; /* replace '?' */
 258        if (stat(a, &st) == 0) {
 259                if (st.st_mode & S_IXUSR) {
 260                        pid = vfork();
 261                        if (pid == -1) {
 262                                warn_cannot("vfork for control/?");
 263                                return 0;
 264                        }
 265                        if (!pid) {
 266                                /* child */
 267                                if (haslog && dup2(logpipe.wr, 1) == -1)
 268                                        warn_cannot("setup stdout for control/?");
 269                                execl(a, a, (char *) NULL);
 270                                fatal_cannot("run control/?");
 271                        }
 272                        /* parent */
 273                        if (safe_waitpid(pid, &w, 0) == -1) {
 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        const char *run;
 308
 309        if (s->state == S_FINISH)
 310                run = "./finish";
 311        else {
 312                run = "./run";
 313                custom(s, 'u');
 314        }
 315
 316        if (s->pid != 0)
 317                stopservice(s); /* should never happen */
 318        while ((p = vfork()) == -1) {
 319                warn_cannot("vfork, sleeping");
 320                sleep(5);
 321        }
 322        if (p == 0) {
 323                /* child */
 324                if (haslog) {
 325                        /* NB: bug alert! right order is close, then dup2 */
 326                        if (s->islog) {
 327                                xchdir("./log");
 328                                close(logpipe.wr);
 329                                xdup2(logpipe.rd, 0);
 330                        } else {
 331                                close(logpipe.rd);
 332                                xdup2(logpipe.wr, 1);
 333                        }
 334                }
 335                /* Non-ignored signals revert to SIG_DFL on exec anyway */
 336                /*bb_signals(0
 337                        + (1 << SIGCHLD)
 338                        + (1 << SIGTERM)
 339                        , SIG_DFL);*/
 340                sig_unblock(SIGCHLD);
 341                sig_unblock(SIGTERM);
 342                execl(run, run, (char *) NULL);
 343                fatal2_cannot(s->islog ? "start log/" : "start ", run);
 344        }
 345        /* parent */
 346        if (s->state != S_FINISH) {
 347                gettimeofday_ns(&s->start);
 348                s->state = S_RUN;
 349        }
 350        s->pid = p;
 351        pidchanged = 1;
 352        s->ctrl = C_NOOP;
 353        update_status(s);
 354}
 355
 356static int ctrl(struct svdir *s, char c)
 357{
 358        int sig;
 359
 360        switch (c) {
 361        case 'd': /* down */
 362                s->want = W_DOWN;
 363                update_status(s);
 364                if (s->pid && s->state != S_FINISH)
 365                        stopservice(s);
 366                break;
 367        case 'u': /* up */
 368                s->want = W_UP;
 369                update_status(s);
 370                if (s->pid == 0)
 371                        startservice(s);
 372                break;
 373        case 'x': /* exit */
 374                if (s->islog)
 375                        break;
 376                s->want = W_EXIT;
 377                update_status(s);
 378                /* FALLTHROUGH */
 379        case 't': /* sig term */
 380                if (s->pid && s->state != S_FINISH)
 381                        stopservice(s);
 382                break;
 383        case 'k': /* sig kill */
 384                if (s->pid && !custom(s, c))
 385                        kill(s->pid, SIGKILL);
 386                s->state = S_DOWN;
 387                break;
 388        case 'p': /* sig pause */
 389                if (s->pid && !custom(s, c))
 390                        kill(s->pid, SIGSTOP);
 391                s->ctrl |= C_PAUSE;
 392                update_status(s);
 393                break;
 394        case 'c': /* sig cont */
 395                if (s->pid && !custom(s, c))
 396                        kill(s->pid, SIGCONT);
 397                s->ctrl &= ~C_PAUSE;
 398                update_status(s);
 399                break;
 400        case 'o': /* once */
 401                s->want = W_DOWN;
 402                update_status(s);
 403                if (!s->pid)
 404                        startservice(s);
 405                break;
 406        case 'a': /* sig alarm */
 407                sig = SIGALRM;
 408                goto sendsig;
 409        case 'h': /* sig hup */
 410                sig = SIGHUP;
 411                goto sendsig;
 412        case 'i': /* sig int */
 413                sig = SIGINT;
 414                goto sendsig;
 415        case 'q': /* sig quit */
 416                sig = SIGQUIT;
 417                goto sendsig;
 418        case '1': /* sig usr1 */
 419                sig = SIGUSR1;
 420                goto sendsig;
 421        case '2': /* sig usr2 */
 422                sig = SIGUSR2;
 423                goto sendsig;
 424        }
 425        return 1;
 426 sendsig:
 427        if (s->pid && !custom(s, c))
 428                kill(s->pid, sig);
 429        return 1;
 430}
 431
 432int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 433int runsv_main(int argc UNUSED_PARAM, char **argv)
 434{
 435        struct stat s;
 436        int fd;
 437        int r;
 438        char buf[256];
 439
 440        INIT_G();
 441
 442        if (!argv[1] || argv[2])
 443                bb_show_usage();
 444        dir = argv[1];
 445
 446        xpiped_pair(selfpipe);
 447        close_on_exec_on(selfpipe.rd);
 448        close_on_exec_on(selfpipe.wr);
 449        ndelay_on(selfpipe.rd);
 450        ndelay_on(selfpipe.wr);
 451
 452        sig_block(SIGCHLD);
 453        bb_signals_recursive_norestart(1 << SIGCHLD, s_child);
 454        sig_block(SIGTERM);
 455        bb_signals_recursive_norestart(1 << SIGTERM, s_term);
 456
 457        xchdir(dir);
 458        /* bss: svd[0].pid = 0; */
 459        if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
 460        if (C_NOOP) svd[0].ctrl = C_NOOP;
 461        if (W_UP) svd[0].want = W_UP;
 462        /* bss: svd[0].islog = 0; */
 463        /* bss: svd[1].pid = 0; */
 464        gettimeofday_ns(&svd[0].start);
 465        if (stat("down", &s) != -1) svd[0].want = W_DOWN;
 466
 467        if (stat("log", &s) == -1) {
 468                if (errno != ENOENT)
 469                        warn_cannot("stat ./log");
 470        } else {
 471                if (!S_ISDIR(s.st_mode)) {
 472                        errno = 0;
 473                        warn_cannot("stat log/down: log is not a directory");
 474                } else {
 475                        haslog = 1;
 476                        svd[1].state = S_DOWN;
 477                        svd[1].ctrl = C_NOOP;
 478                        svd[1].want = W_UP;
 479                        svd[1].islog = 1;
 480                        gettimeofday_ns(&svd[1].start);
 481                        if (stat("log/down", &s) != -1)
 482                                svd[1].want = W_DOWN;
 483                        xpiped_pair(logpipe);
 484                        close_on_exec_on(logpipe.rd);
 485                        close_on_exec_on(logpipe.wr);
 486                }
 487        }
 488
 489        if (mkdir("supervise", 0700) == -1) {
 490                r = readlink("supervise", buf, sizeof(buf));
 491                if (r != -1) {
 492                        if (r == sizeof(buf))
 493                                fatal2x_cannot("readlink ./supervise", ": name too long");
 494                        buf[r] = 0;
 495                        mkdir(buf, 0700);
 496                } else {
 497                        if ((errno != ENOENT) && (errno != EINVAL))
 498                                fatal_cannot("readlink ./supervise");
 499                }
 500        }
 501        svd[0].fdlock = xopen3("log/supervise/lock"+4,
 502                        O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
 503        if (lock_exnb(svd[0].fdlock) == -1)
 504                fatal_cannot("lock supervise/lock");
 505        close_on_exec_on(svd[0].fdlock);
 506        if (haslog) {
 507                if (mkdir("log/supervise", 0700) == -1) {
 508                        r = readlink("log/supervise", buf, 256);
 509                        if (r != -1) {
 510                                if (r == 256)
 511                                        fatal2x_cannot("readlink ./log/supervise", ": name too long");
 512                                buf[r] = 0;
 513                                fd = xopen(".", O_RDONLY|O_NDELAY);
 514                                xchdir("./log");
 515                                mkdir(buf, 0700);
 516                                if (fchdir(fd) == -1)
 517                                        fatal_cannot("change back to service directory");
 518                                close(fd);
 519                        }
 520                        else {
 521                                if ((errno != ENOENT) && (errno != EINVAL))
 522                                        fatal_cannot("readlink ./log/supervise");
 523                        }
 524                }
 525                svd[1].fdlock = xopen3("log/supervise/lock",
 526                                O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
 527                if (lock_ex(svd[1].fdlock) == -1)
 528                        fatal_cannot("lock log/supervise/lock");
 529                close_on_exec_on(svd[1].fdlock);
 530        }
 531
 532        mkfifo("log/supervise/control"+4, 0600);
 533        svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
 534        close_on_exec_on(svd[0].fdcontrol);
 535        svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
 536        close_on_exec_on(svd[0].fdcontrolwrite);
 537        update_status(&svd[0]);
 538        if (haslog) {
 539                mkfifo("log/supervise/control", 0600);
 540                svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
 541                close_on_exec_on(svd[1].fdcontrol);
 542                svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
 543                close_on_exec_on(svd[1].fdcontrolwrite);
 544                update_status(&svd[1]);
 545        }
 546        mkfifo("log/supervise/ok"+4, 0600);
 547        fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
 548        close_on_exec_on(fd);
 549        if (haslog) {
 550                mkfifo("log/supervise/ok", 0600);
 551                fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
 552                close_on_exec_on(fd);
 553        }
 554        for (;;) {
 555                struct pollfd x[3];
 556                unsigned deadline;
 557                char ch;
 558
 559                if (haslog)
 560                        if (!svd[1].pid && svd[1].want == W_UP)
 561                                startservice(&svd[1]);
 562                if (!svd[0].pid)
 563                        if (svd[0].want == W_UP || svd[0].state == S_FINISH)
 564                                startservice(&svd[0]);
 565
 566                x[0].fd = selfpipe.rd;
 567                x[0].events = POLLIN;
 568                x[1].fd = svd[0].fdcontrol;
 569                x[1].events = POLLIN;
 570                /* x[2] is used only if haslog == 1 */
 571                x[2].fd = svd[1].fdcontrol;
 572                x[2].events = POLLIN;
 573                sig_unblock(SIGTERM);
 574                sig_unblock(SIGCHLD);
 575                poll(x, 2 + haslog, 3600*1000);
 576                sig_block(SIGTERM);
 577                sig_block(SIGCHLD);
 578
 579                while (read(selfpipe.rd, &ch, 1) == 1)
 580                        continue;
 581
 582                for (;;) {
 583                        pid_t child;
 584                        int wstat;
 585
 586                        child = wait_any_nohang(&wstat);
 587                        if (!child)
 588                                break;
 589                        if ((child == -1) && (errno != EINTR))
 590                                break;
 591                        if (child == svd[0].pid) {
 592                                svd[0].pid = 0;
 593                                pidchanged = 1;
 594                                svd[0].ctrl &=~ C_TERM;
 595                                if (svd[0].state != S_FINISH) {
 596                                        fd = open_read("finish");
 597                                        if (fd != -1) {
 598                                                close(fd);
 599                                                svd[0].state = S_FINISH;
 600                                                update_status(&svd[0]);
 601                                                continue;
 602                                        }
 603                                }
 604                                svd[0].state = S_DOWN;
 605                                deadline = svd[0].start.tv_sec + 1;
 606                                gettimeofday_ns(&svd[0].start);
 607                                update_status(&svd[0]);
 608                                if (LESS(svd[0].start.tv_sec, deadline))
 609                                        sleep(1);
 610                        }
 611                        if (haslog) {
 612                                if (child == svd[1].pid) {
 613                                        svd[1].pid = 0;
 614                                        pidchanged = 1;
 615                                        svd[1].state = S_DOWN;
 616                                        svd[1].ctrl &= ~C_TERM;
 617                                        deadline = svd[1].start.tv_sec + 1;
 618                                        gettimeofday_ns(&svd[1].start);
 619                                        update_status(&svd[1]);
 620                                        if (LESS(svd[1].start.tv_sec, deadline))
 621                                                sleep(1);
 622                                }
 623                        }
 624                } /* for (;;) */
 625                if (read(svd[0].fdcontrol, &ch, 1) == 1)
 626                        ctrl(&svd[0], ch);
 627                if (haslog)
 628                        if (read(svd[1].fdcontrol, &ch, 1) == 1)
 629                                ctrl(&svd[1], ch);
 630
 631                if (sigterm) {
 632                        ctrl(&svd[0], 'x');
 633                        sigterm = 0;
 634                }
 635
 636                if (svd[0].want == W_EXIT && svd[0].state == S_DOWN) {
 637                        if (svd[1].pid == 0)
 638                                _exit(EXIT_SUCCESS);
 639                        if (svd[1].want != W_EXIT) {
 640                                svd[1].want = W_EXIT;
 641                                /* stopservice(&svd[1]); */
 642                                update_status(&svd[1]);
 643                                close(logpipe.wr);
 644                                close(logpipe.rd);
 645                        }
 646                }
 647        } /* for (;;) */
 648        /* not reached */
 649        return 0;
 650}
 651