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