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