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