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