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};
  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        int fd = open_trunc(name);
 155        if (fd < 0)
 156                bb_perror_msg("%s: warning: cannot open %s",
 157                                dir, name);
 158        return fd;
 159}
 160
 161static void update_status(struct svdir *s)
 162{
 163        ssize_t sz;
 164        int fd;
 165        svstatus_t status;
 166
 167        /* pid */
 168        if (pidchanged) {
 169                fd = open_trunc_or_warn("supervise/pid.new");
 170                if (fd < 0)
 171                        return;
 172                if (s->pid) {
 173                        char spid[sizeof(int)*3 + 2];
 174                        int size = sprintf(spid, "%u\n", (unsigned)s->pid);
 175                        write(fd, spid, size);
 176                }
 177                close(fd);
 178                if (rename_or_warn("supervise/pid.new",
 179                    s->islog ? "log/supervise/pid" : "log/supervise/pid"+4))
 180                        return;
 181                pidchanged = 0;
 182        }
 183
 184        /* stat */
 185        fd = open_trunc_or_warn("supervise/stat.new");
 186        if (fd < -1)
 187                return;
 188
 189        {
 190                char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
 191                char *p = stat_buf;
 192                switch (s->state) {
 193                case S_DOWN:
 194                        p = bb_stpcpy(p, "down");
 195                        break;
 196                case S_RUN:
 197                        p = bb_stpcpy(p, "run");
 198                        break;
 199                case S_FINISH:
 200                        p = bb_stpcpy(p, "finish");
 201                        break;
 202                }
 203                if (s->ctrl & C_PAUSE)
 204                        p = bb_stpcpy(p, ", paused");
 205                if (s->ctrl & C_TERM)
 206                        p = bb_stpcpy(p, ", got TERM");
 207                if (s->state != S_DOWN)
 208                        switch (s->sd_want) {
 209                        case W_DOWN:
 210                                p = bb_stpcpy(p, ", want down");
 211                                break;
 212                        case W_EXIT:
 213                                p = bb_stpcpy(p, ", want exit");
 214                                break;
 215                        }
 216                *p++ = '\n';
 217                write(fd, stat_buf, p - stat_buf);
 218                close(fd);
 219        }
 220
 221        rename_or_warn("supervise/stat.new",
 222                s->islog ? "log/supervise/stat" : "log/supervise/stat"+4);
 223
 224        /* supervise compatibility */
 225        memset(&status, 0, sizeof(status));
 226        status.time_be64 = SWAP_BE64(s->start.tv_sec + 0x400000000000000aULL);
 227        status.time_nsec_be32 = SWAP_BE32(s->start.tv_nsec);
 228        status.pid_le32 = SWAP_LE32(s->pid);
 229        if (s->ctrl & C_PAUSE)
 230                status.paused = 1;
 231        if (s->sd_want == W_UP)
 232                status.want = 'u';
 233        else
 234                status.want = 'd';
 235        if (s->ctrl & C_TERM)
 236                status.got_term = 1;
 237        status.run_or_finish = s->state;
 238        fd = open_trunc_or_warn("supervise/status.new");
 239        if (fd < 0)
 240                return;
 241        sz = write(fd, &status, sizeof(status));
 242        close(fd);
 243        if (sz != sizeof(status)) {
 244                warn_cannot("write supervise/status.new");
 245                unlink("supervise/status.new");
 246                return;
 247        }
 248        rename_or_warn("supervise/status.new",
 249                s->islog ? "log/supervise/status" : "log/supervise/status"+4);
 250}
 251
 252static unsigned custom(struct svdir *s, char c)
 253{
 254        pid_t pid;
 255        int w;
 256        char a[10];
 257        struct stat st;
 258
 259        if (s->islog)
 260                return 0;
 261        strcpy(a, "control/?");
 262        a[8] = c; /* replace '?' */
 263        if (stat(a, &st) == 0) {
 264                if (st.st_mode & S_IXUSR) {
 265                        pid = vfork();
 266                        if (pid == -1) {
 267                                warn_cannot("vfork for control/?");
 268                                return 0;
 269                        }
 270                        if (pid == 0) {
 271                                /* child */
 272                                if (haslog && dup2(logpipe.wr, 1) == -1)
 273                                        warn_cannot("setup stdout for control/?");
 274                                execl(a, a, (char *) NULL);
 275                                fatal_cannot("run control/?");
 276                        }
 277                        /* parent */
 278                        if (safe_waitpid(pid, &w, 0) == -1) {
 279                                warn_cannot("wait for child control/?");
 280                                return 0;
 281                        }
 282                        return WEXITSTATUS(w) == 0;
 283                }
 284        } else {
 285                if (errno != ENOENT)
 286                        warn_cannot("stat control/?");
 287        }
 288        return 0;
 289}
 290
 291static void stopservice(struct svdir *s)
 292{
 293        if (s->pid && !custom(s, 't')) {
 294                kill(s->pid, SIGTERM);
 295                s->ctrl |= C_TERM;
 296                update_status(s);
 297        }
 298        if (s->sd_want == W_DOWN) {
 299                kill(s->pid, SIGCONT);
 300                custom(s, 'd');
 301                return;
 302        }
 303        if (s->sd_want == W_EXIT) {
 304                kill(s->pid, SIGCONT);
 305                custom(s, 'x');
 306        }
 307}
 308
 309static void startservice(struct svdir *s)
 310{
 311        int p;
 312        const char *arg[4];
 313        char exitcode[sizeof(int)*3 + 2];
 314        char sigcode[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                        sprintf(exitcode, "%u", (int) WEXITSTATUS(s->wstat));
 328                        arg[1] = exitcode;
 329                }
 330                //arg[2] = "0";
 331                //if (WIFSIGNALED(s->wstat)) {
 332                        sprintf(sigcode, "%u", (int) WTERMSIG(s->wstat));
 333                        arg[2] = sigcode;
 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        if (!argv[1] || argv[2])
 469                bb_show_usage();
 470        dir = argv[1];
 471
 472        xpiped_pair(selfpipe);
 473        close_on_exec_on(selfpipe.rd);
 474        close_on_exec_on(selfpipe.wr);
 475        ndelay_on(selfpipe.rd);
 476        ndelay_on(selfpipe.wr);
 477
 478        sig_block(SIGCHLD);
 479        bb_signals_recursive_norestart(1 << SIGCHLD, s_child);
 480        sig_block(SIGTERM);
 481        bb_signals_recursive_norestart(1 << SIGTERM, s_term);
 482
 483        xchdir(dir);
 484        /* bss: svd[0].pid = 0; */
 485        if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
 486        if (C_NOOP) svd[0].ctrl = C_NOOP;
 487        if (W_UP) svd[0].sd_want = W_UP;
 488        /* bss: svd[0].islog = 0; */
 489        /* bss: svd[1].pid = 0; */
 490        gettimeofday_ns(&svd[0].start);
 491        if (stat("down", &s) != -1)
 492                svd[0].sd_want = W_DOWN;
 493
 494        if (stat("log", &s) == -1) {
 495                if (errno != ENOENT)
 496                        warn_cannot("stat ./log");
 497        } else {
 498                if (!S_ISDIR(s.st_mode)) {
 499                        errno = 0;
 500                        warn_cannot("stat log/down: log is not a directory");
 501                } else {
 502                        haslog = 1;
 503                        svd[1].state = S_DOWN;
 504                        svd[1].ctrl = C_NOOP;
 505                        svd[1].sd_want = W_UP;
 506                        svd[1].islog = 1;
 507                        gettimeofday_ns(&svd[1].start);
 508                        if (stat("log/down", &s) != -1)
 509                                svd[1].sd_want = W_DOWN;
 510                        xpiped_pair(logpipe);
 511                        close_on_exec_on(logpipe.rd);
 512                        close_on_exec_on(logpipe.wr);
 513                }
 514        }
 515
 516        if (mkdir("supervise", 0700) == -1) {
 517                r = readlink("supervise", buf, sizeof(buf));
 518                if (r != -1) {
 519                        if (r == sizeof(buf))
 520                                fatal2x_cannot("readlink ./supervise", ": name too long");
 521                        buf[r] = 0;
 522                        mkdir(buf, 0700);
 523                } else {
 524                        if ((errno != ENOENT) && (errno != EINVAL))
 525                                fatal_cannot("readlink ./supervise");
 526                }
 527        }
 528        svd[0].fdlock = xopen3("log/supervise/lock"+4,
 529                        O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
 530        if (lock_exnb(svd[0].fdlock) == -1)
 531                fatal_cannot("lock supervise/lock");
 532        close_on_exec_on(svd[0].fdlock);
 533        if (haslog) {
 534                if (mkdir("log/supervise", 0700) == -1) {
 535                        r = readlink("log/supervise", buf, 256);
 536                        if (r != -1) {
 537                                if (r == 256)
 538                                        fatal2x_cannot("readlink ./log/supervise", ": name too long");
 539                                buf[r] = 0;
 540                                fd = xopen(".", O_RDONLY|O_NDELAY);
 541                                xchdir("./log");
 542                                mkdir(buf, 0700);
 543                                if (fchdir(fd) == -1)
 544                                        fatal_cannot("change back to service directory");
 545                                close(fd);
 546                        }
 547                        else {
 548                                if ((errno != ENOENT) && (errno != EINVAL))
 549                                        fatal_cannot("readlink ./log/supervise");
 550                        }
 551                }
 552                svd[1].fdlock = xopen3("log/supervise/lock",
 553                                O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
 554                if (lock_ex(svd[1].fdlock) == -1)
 555                        fatal_cannot("lock log/supervise/lock");
 556                close_on_exec_on(svd[1].fdlock);
 557        }
 558
 559        mkfifo("log/supervise/control"+4, 0600);
 560        svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
 561        close_on_exec_on(svd[0].fdcontrol);
 562        svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
 563        close_on_exec_on(svd[0].fdcontrolwrite);
 564        update_status(&svd[0]);
 565        if (haslog) {
 566                mkfifo("log/supervise/control", 0600);
 567                svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
 568                close_on_exec_on(svd[1].fdcontrol);
 569                svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
 570                close_on_exec_on(svd[1].fdcontrolwrite);
 571                update_status(&svd[1]);
 572        }
 573        mkfifo("log/supervise/ok"+4, 0600);
 574        fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
 575        close_on_exec_on(fd);
 576        if (haslog) {
 577                mkfifo("log/supervise/ok", 0600);
 578                fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
 579                close_on_exec_on(fd);
 580        }
 581        for (;;) {
 582                struct pollfd x[3];
 583                unsigned deadline;
 584                char ch;
 585
 586                if (haslog)
 587                        if (!svd[1].pid && svd[1].sd_want == W_UP)
 588                                startservice(&svd[1]);
 589                if (!svd[0].pid)
 590                        if (svd[0].sd_want == W_UP || svd[0].state == S_FINISH)
 591                                startservice(&svd[0]);
 592
 593                x[0].fd = selfpipe.rd;
 594                x[0].events = POLLIN;
 595                x[1].fd = svd[0].fdcontrol;
 596                x[1].events = POLLIN;
 597                /* x[2] is used only if haslog == 1 */
 598                x[2].fd = svd[1].fdcontrol;
 599                x[2].events = POLLIN;
 600                sig_unblock(SIGTERM);
 601                sig_unblock(SIGCHLD);
 602                poll(x, 2 + haslog, 3600*1000);
 603                sig_block(SIGTERM);
 604                sig_block(SIGCHLD);
 605
 606                while (read(selfpipe.rd, &ch, 1) == 1)
 607                        continue;
 608
 609                for (;;) {
 610                        pid_t child;
 611                        int wstat;
 612
 613                        child = wait_any_nohang(&wstat);
 614                        if (!child)
 615                                break;
 616                        if ((child == -1) && (errno != EINTR))
 617                                break;
 618                        if (child == svd[0].pid) {
 619                                svd[0].wstat = wstat;
 620                                svd[0].pid = 0;
 621                                pidchanged = 1;
 622                                svd[0].ctrl &= ~C_TERM;
 623                                if (svd[0].state != S_FINISH) {
 624                                        fd = open_read("finish");
 625                                        if (fd != -1) {
 626                                                close(fd);
 627                                                svd[0].state = S_FINISH;
 628                                                update_status(&svd[0]);
 629                                                continue;
 630                                        }
 631                                }
 632                                svd[0].state = S_DOWN;
 633                                deadline = svd[0].start.tv_sec + 1;
 634                                gettimeofday_ns(&svd[0].start);
 635                                update_status(&svd[0]);
 636                                if (LESS(svd[0].start.tv_sec, deadline))
 637                                        sleep(1);
 638                        }
 639                        if (haslog) {
 640                                if (child == svd[1].pid) {
 641                                        svd[0].wstat = wstat;
 642                                        svd[1].pid = 0;
 643                                        pidchanged = 1;
 644                                        svd[1].state = S_DOWN;
 645                                        svd[1].ctrl &= ~C_TERM;
 646                                        deadline = svd[1].start.tv_sec + 1;
 647                                        gettimeofday_ns(&svd[1].start);
 648                                        update_status(&svd[1]);
 649                                        if (LESS(svd[1].start.tv_sec, deadline))
 650                                                sleep(1);
 651                                }
 652                        }
 653                } /* for (;;) */
 654                if (read(svd[0].fdcontrol, &ch, 1) == 1)
 655                        ctrl(&svd[0], ch);
 656                if (haslog)
 657                        if (read(svd[1].fdcontrol, &ch, 1) == 1)
 658                                ctrl(&svd[1], ch);
 659
 660                if (sigterm) {
 661                        ctrl(&svd[0], 'x');
 662                        sigterm = 0;
 663                }
 664
 665                if (svd[0].sd_want == W_EXIT && svd[0].state == S_DOWN) {
 666                        if (svd[1].pid == 0)
 667                                _exit(EXIT_SUCCESS);
 668                        if (svd[1].sd_want != W_EXIT) {
 669                                svd[1].sd_want = W_EXIT;
 670                                /* stopservice(&svd[1]); */
 671                                update_status(&svd[1]);
 672                                close(logpipe.wr);
 673                                close(logpipe.rd);
 674                        }
 675                }
 676        } /* for (;;) */
 677        /* not reached */
 678        return 0;
 679}
 680