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