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 */
gettimeofday_ns(struct timespec * ts)56 static void gettimeofday_ns(struct timespec *ts)
57 {
58 	syscall(__NR_clock_gettime, CLOCK_REALTIME, ts);
59 }
60 #else
gettimeofday_ns(struct timespec * ts)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 
fatal2_cannot(const char * m1,const char * m2)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 }
fatal_cannot(const char * m)127 static void fatal_cannot(const char *m)
128 {
129 	fatal2_cannot(m, "");
130 	/* was exiting 111 */
131 }
fatal2x_cannot(const char * m1,const char * m2)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 }
warn_cannot(const char * m)137 static void warn_cannot(const char *m)
138 {
139 	bb_perror_msg("%s: warning: cannot %s", dir, m);
140 }
141 
s_child(int sig_no UNUSED_PARAM)142 static void s_child(int sig_no UNUSED_PARAM)
143 {
144 	write(selfpipe.wr, "", 1);
145 }
146 
s_term(int sig_no UNUSED_PARAM)147 static void s_term(int sig_no UNUSED_PARAM)
148 {
149 	sigterm = 1;
150 	write(selfpipe.wr, "", 1); /* XXX */
151 }
152 
open_trunc_or_warn(const char * name)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 
update_status(struct svdir * s)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 
custom(struct svdir * s,char c)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 
stopservice(struct svdir * s)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 
startservice(struct svdir * s)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 
ctrl(struct svdir * s,char c)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;
runsv_main(int argc UNUSED_PARAM,char ** argv)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 }
678