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