a67280b4b8188acebec92b7bcf8da0d958fb9d8f
[oweals/busybox.git] / runit / runsv.c
1 /*
2 Copyright (c) 2001-2006, Gerrit Pape
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, 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
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED
17 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 ADVISED 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.2 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 */
56 static void gettimeofday_ns(struct timespec *ts)
57 {
58         syscall(__NR_clock_gettime, CLOCK_REALTIME, ts);
59 }
60 #else
61 static 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
87 struct 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
100 struct 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
122 static 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 }
127 static void fatal_cannot(const char *m)
128 {
129         fatal2_cannot(m, "");
130         /* was exiting 111 */
131 }
132 static 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 }
137 static void warn2_cannot(const char *m1, const char *m2)
138 {
139         bb_perror_msg("%s: warning: can't %s%s", dir, m1, m2);
140 }
141 static void warn_cannot(const char *m)
142 {
143         warn2_cannot(m, "");
144 }
145
146 static void s_child(int sig_no UNUSED_PARAM)
147 {
148         write(selfpipe.wr, "", 1);
149 }
150
151 static void s_term(int sig_no UNUSED_PARAM)
152 {
153         sigterm = 1;
154         write(selfpipe.wr, "", 1); /* XXX */
155 }
156
157 static 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
167 static 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
270 static 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
309 static 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
327 static 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
398 static 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
474 static 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
489 int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
490 int 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 }