xref: /dragonfly/sbin/svc/execute.c (revision 78478697)
1 /*
2  * Copyright (c) 2014 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 /*
35  * Handle remote listen/connect and parsing operations.
36  */
37 
38 #include "svc.h"
39 
40 pthread_mutex_t serial_mtx;
41 time_t LastStart;	/* uptime */
42 time_t LastStop;	/* uptime */
43 pid_t DirectPid = -1;
44 runstate_t RunState = RS_STOPPED;
45 command_t *InitCmd;
46 int RestartCounter;
47 
48 static void *logger_thread(void *arg);
49 static void setstate_stopped(command_t *cmd, struct timespec *ts);
50 static int setup_gid(command_t *cmd);
51 static int setup_uid(command_t *cmd);
52 static int setup_jail(command_t *cmd);
53 static int setup_chroot(command_t *cmd);
54 static int setup_devfs(command_t *cmd, const char *dir, int domount);
55 static int escapewrite(FILE *fp, char *buf, int n, int *statep);
56 
57 int
58 execute_init(command_t *cmd)
59 {
60         char buf[32];
61         pid_t pid;
62 	pid_t stoppingpid = -1;
63 	pthread_t logtd;
64 	time_t nextstop = 0;
65 	int lfd;	/* unix domain listen socket */
66 	int pfd;	/* pid file */
67 	int xfd;
68 	int rc;
69 	int fds[2];
70 	char c;
71 
72 	if (cmd->label == NULL || cmd->ext_ac == 0) {
73 		fprintf(cmd->fp, "init requires a label and command\n");
74 		return 1;
75 	}
76 	fprintf(cmd->fp, "initializing new service: %s\n", cmd->label);
77 
78 	if ((xfd = open("/dev/null", O_RDWR)) < 0) {
79 		fprintf(cmd->fp, "Unable to open /dev/null: %s\n",
80 			strerror(errno));
81 		return 1;
82 	}
83 
84 	/*
85 	 * Setup pidfile and unix domain listen socket and lock the
86 	 * pidfile.
87 	 */
88 	rc = setup_pid_and_socket(cmd, &lfd, &pfd);
89 	if (rc)
90 		return rc;
91 
92 	/*
93 	 * Detach the service
94 	 */
95 	if (cmd->foreground) {
96 		/*
97 		 * Stay in foreground.
98 		 */
99 		fds[0] = -1;
100 		fds[1] = -1;
101 		pid = 0;
102 	} else {
103 		if (pipe(fds) < 0) {
104 			fprintf(cmd->fp, "Unable to create pipe: %s\n",
105 				strerror(errno));
106 			close(lfd);
107 			close(pfd);
108 			remove_pid_and_socket(cmd, cmd->label);
109 			return 1;
110 		}
111 		pid = fork();
112 	}
113 
114 	if (pid != 0) {
115                 /*
116                  * Parent
117                  */
118 		close(fds[1]);
119                 if (pid < 0) {
120                         fprintf(cmd->fp, "fork failed: %s\n", strerror(errno));
121 			close(lfd);
122 			close(pfd);
123 			close(fds[0]);
124 			close(fds[1]);
125 			remove_pid_and_socket(cmd, cmd->label);
126 			return 1;
127                 } else {
128 			/*
129 			 * Fill-in pfd before returning.
130 			 */
131 			snprintf(buf, sizeof(buf), "%d\n", (int)pid);
132 			write(pfd, buf, strlen(buf));
133 		}
134 		close(lfd);
135 		close(pfd);
136 
137 		/*
138 		 * Wait for child to completely detach from the tty
139 		 * before returning.
140 		 */
141 		read(fds[0], &c, 1);
142 		close(fds[0]);
143 
144 		return 0;
145         }
146 
147 	/*
148 	 * Forked child is now the service demon.
149 	 *
150 	 * Detach from terminal, scrap tty, set process title.
151 	 */
152 	if (cmd->proctitle) {
153 		setproctitle("%s - %s", cmd->label, cmd->proctitle);
154 	} else {
155 		setproctitle("%s", cmd->label);
156 	}
157 	if (cmd->mountdev) {
158 		if (cmd->jaildir)
159 			setup_devfs(cmd, cmd->jaildir, 1);
160 		else if (cmd->rootdir)
161 			setup_devfs(cmd, cmd->rootdir, 1);
162 	}
163 
164 	if (cmd->foreground == 0) {
165 		close(fds[0]);
166 		fds[0] = -1;
167 	}
168 
169 	if (xfd != 0)				/* scrap tty inputs */
170 		dup2(xfd, 0);
171 	if (cmd->foreground == 0) {
172 		int tfd;
173 
174 		if (xfd != 1)			/* scrap tty outputs */
175 			dup2(xfd, 1);
176 		if (xfd != 2)
177 			dup2(xfd, 2);
178 
179 		if ((tfd = open("/dev/tty", O_RDWR)) >= 0) {
180 			ioctl(tfd, TIOCNOTTY, 0);	/* no controlling tty */
181 			close(tfd);
182 		}
183 		setsid();				/* new session */
184 	}
185 
186 	/*
187 	 * Setup log file.  The log file must not use descriptors 0, 1, or 2.
188 	 */
189 	if (cmd->logfile && strcmp(cmd->logfile, "/dev/null") == 0)
190 		cmd->logfd = -1;
191 	else if (cmd->logfile)
192 		cmd->logfd = open(cmd->logfile, O_WRONLY|O_CREAT|O_APPEND, 0640);
193 	else if (cmd->foreground)
194 		cmd->logfd = dup(1);
195 	else
196 		cmd->logfd = -1;
197 
198 	/*
199 	 * Signal parent that we are completely detached now.
200 	 */
201 	c = 1;
202 	if (cmd->foreground == 0) {
203 		write(fds[1], &c, 1);
204 		close(fds[1]);
205 		fds[1] = -1;
206 	}
207 	InitCmd = cmd;
208 
209 	/*
210 	 * Setup log pipe.  The logger thread copies the pipe to a buffer
211 	 * for the 'log' directive and also writes it to logfd.
212 	 */
213 	pipe(cmd->logfds);
214 	if (cmd->fp != stdout)
215 		fclose(cmd->fp);
216 	cmd->fp = fdopen(cmd->logfds[1], "w");
217 
218 	if (xfd > 2) {
219 		close(xfd);
220 		xfd = -1;
221 	}
222 
223 	pthread_cond_init(&cmd->logcond, NULL);
224 
225 	/*
226 	 * Start accept thread for unix domain listen socket.
227 	 */
228 	pthread_mutex_lock(&serial_mtx);
229 	pthread_create(&logtd, NULL, logger_thread, cmd);
230 	remote_listener(cmd, lfd);
231 
232 	/*
233 	 * Become the reaper for all children recursively.
234 	 */
235 	if (procctl(P_PID, getpid(), PROC_REAP_ACQUIRE, NULL) < 0) {
236 		fprintf(cmd->fp, "svc is unable to become the "
237 				 "reaper for its children\n");
238 		fflush(cmd->fp);
239 	}
240 
241 	/*
242 	 * Initial service start
243 	 */
244 	execute_start(cmd);
245 
246 	/*
247 	 * Main loop is the reaper
248 	 */
249 	for (;;) {
250 		union reaper_info info;
251 		struct timespec ts;
252 		int status;
253 		int dt;
254 		pid_t usepid;
255 
256 		/*
257 		 * If we are running just block doing normal reaping,
258 		 * if we are stopping we have to poll for reaping while
259 		 * we handle stopping.
260 		 */
261 		fflush(cmd->fp);
262 		if (RunState == RS_STARTED) {
263 			pthread_mutex_unlock(&serial_mtx);
264 			pid = wait3(&status, 0, NULL);
265 			pthread_mutex_lock(&serial_mtx);
266 		} else {
267 			pid = wait3(&status, WNOHANG, NULL);
268 		}
269 		clock_gettime(CLOCK_MONOTONIC_FAST, &ts);
270 
271 		if (pid > 0) {
272 			if (pid == DirectPid) {
273 				fprintf(cmd->fp,
274 					"svc %s: lost direct child %d\n",
275 					cmd->label, pid);
276 				fflush(cmd->fp);
277 				DirectPid = -1;
278 				if (cmd->restart_some) {
279 					setstate_stopped(cmd, &ts);
280 				} /* else still considered normal run state */
281 			} else if (cmd->debug) {
282 				/*
283 				 * Reap random disconnected child, but don't
284 				 * spew to the log unless debugging is
285 				 * enabled.
286 				 */
287 				fprintf(cmd->fp,
288 					"svc %s: reap indirect child %d\n",
289 					cmd->label,
290 					(int)pid);
291 			}
292 		}
293 
294 		/*
295 		 * Calculate the pid to potentially act on and/or
296 		 * determine if any children still exist.
297 		 */
298 		if (DirectPid >= 0) {
299 			usepid = DirectPid;
300 		} else if (procctl(P_PID, getpid(),
301 				   PROC_REAP_STATUS, &info) == 0) {
302 			usepid = info.status.pid_head;
303 		} else {
304 			usepid = -1;
305 		}
306 		if (cmd->debug) {
307 			fprintf(stderr, "svc %s: usepid %d\n",
308 				cmd->label, usepid);
309 			fflush(stderr);
310 		}
311 
312 		/*
313 		 * If stoppingpid changes we have to reset the TERM->KILL
314 		 * timer.
315 		 */
316 		if (usepid < 0) {
317 			setstate_stopped(cmd, &ts);
318 		} else if (stoppingpid != usepid &&
319 			   (RunState == RS_STOPPING2 ||
320 			    RunState == RS_STOPPING3)) {
321 			RunState = RS_STOPPING1;
322 		}
323 		stoppingpid = usepid;
324 
325 		/*
326 		 * State machine
327 		 */
328 		switch(RunState) {
329 		case RS_STARTED:
330 			if (usepid < 0)
331 				setstate_stopped(cmd, &ts);
332 			break;
333 		case RS_STOPPED:
334 			dt = (int)(ts.tv_sec - LastStop);
335 
336 			if (cmd->exit_mode) {
337 				/*
338 				 * Service demon was told to exit on service
339 				 * stop (-x passed to init).
340 				 */
341 				fprintf(cmd->fp,
342 					"svc %s: service demon exiting\n",
343 					cmd->label);
344 				remove_pid_and_socket(cmd, cmd->label);
345 				goto exitloop;
346 			} else if (cmd->manual_stop) {
347 				/*
348 				 * Service demon was told to stop via
349 				 * commanded (not automatic) action.  We
350 				 * do not auto-restart the service in
351 				 * this situation.
352 				 */
353 				pthread_mutex_unlock(&serial_mtx);
354 				if (dt < 0 || dt > 60)
355 					sleep(60);
356 				else
357 					sleep(1);
358 				pthread_mutex_lock(&serial_mtx);
359 			} else if (cmd->restart_some || cmd->restart_all) {
360 				/*
361 				 * Handle automatic restarts
362 				 */
363 				if (dt > cmd->restart_timo) {
364 					execute_start(cmd);
365 				} else {
366 					pthread_mutex_unlock(&serial_mtx);
367 					sleep(1);
368 					pthread_mutex_lock(&serial_mtx);
369 				}
370 			} else {
371 				/*
372 				 * No automatic restart was configured,
373 				 * wait for commanded action.
374 				 */
375 				pthread_mutex_unlock(&serial_mtx);
376 				if (dt < 0 || dt > 60)
377 					sleep(60);
378 				else
379 					sleep(1);
380 				pthread_mutex_lock(&serial_mtx);
381 			}
382 			break;
383 		case RS_STOPPING1:
384 			/*
385 			 * Reset TERM->KILL timer
386 			 */
387 			nextstop = ts.tv_sec;
388 			RunState = RS_STOPPING2;
389 			/* fall through */
390 		case RS_STOPPING2:
391 			if (cmd->termkill_timo == 0) {
392 				nextstop = ts.tv_sec - 1;
393 			} else {
394 				kill(stoppingpid, SIGTERM);
395 				fprintf(cmd->fp, "svc %s: sigterm %d\n",
396 					cmd->label, stoppingpid);
397 				sleep(1);
398 			}
399 			RunState = RS_STOPPING3;
400 			/* fall through */
401 		case RS_STOPPING3:
402 			dt = (int)(ts.tv_sec - nextstop);
403 			if (dt > cmd->termkill_timo) {
404 				fprintf(cmd->fp, "svc %s: sigkill %d\n",
405 					cmd->label, stoppingpid);
406 				kill(stoppingpid, SIGKILL);
407 			}
408 			sleep(1);
409 			break;
410 		}
411 	}
412 exitloop:
413 	pthread_mutex_unlock(&serial_mtx);
414 	if (cmd->mountdev) {
415 		if (cmd->jaildir)
416 			setup_devfs(cmd, cmd->jaildir, 0);
417 		else if (cmd->rootdir)
418 			setup_devfs(cmd, cmd->rootdir, 0);
419 	}
420 	exit(0);
421 	/* does not return */
422 }
423 
424 int
425 execute_start(command_t *cmd)
426 {
427 	struct timespec ts;
428 	int maxwait = 60;
429 
430 	while (RunState == RS_STOPPING1 ||
431 	       RunState == RS_STOPPING2 ||
432 	       RunState == RS_STOPPING3) {
433 		fprintf(cmd->fp,
434 			"svc %s: Waiting for previous action to complete\n",
435 			cmd->label);
436 		fflush(cmd->fp);
437 		pthread_mutex_unlock(&serial_mtx);
438 		sleep(1);
439 		pthread_mutex_lock(&serial_mtx);
440 		if (--maxwait == 0) {
441 			fprintf(cmd->fp,
442 				"svc %s: Giving up waiting for action\n",
443 				cmd->label);
444 			fflush(cmd->fp);
445 			break;
446 		}
447 	}
448 	if (RunState == RS_STARTED) {
449 		fprintf(cmd->fp, "svc %s: Already started pid %d\n",
450 			cmd->label, DirectPid);
451 		fflush(cmd->fp);
452 		return 0;
453 	}
454 
455 	clock_gettime(CLOCK_MONOTONIC_FAST, &ts);
456 	if ((DirectPid = fork()) == 0) {
457 		fflush(InitCmd->fp);
458 						/* leave stdin /dev/null */
459 		dup2(fileno(InitCmd->fp), 1);	/* setup stdout */
460 		dup2(fileno(InitCmd->fp), 2);	/* setup stderr */
461 		closefrom(3);
462 
463 		if (cmd->jaildir)		/* jail or chroot */
464 			setup_jail(cmd);
465 		else if (cmd->rootdir)
466 			setup_chroot(cmd);
467 
468 		setup_gid(cmd);
469 		setup_uid(cmd);
470 		execvp(InitCmd->ext_av[0], InitCmd->ext_av);
471 		exit(99);
472 	}
473 	if (DirectPid >= 0) {
474 		RunState = RS_STARTED;
475 		LastStart = ts.tv_sec;
476 	} else {
477 		setstate_stopped(InitCmd, &ts);
478 	}
479 	InitCmd->manual_stop = 0;
480 	fprintf(cmd->fp, "svc %s: Starting pid %d\n", cmd->label, DirectPid);
481 	fflush(cmd->fp);
482 
483 	return 0;
484 }
485 
486 int
487 execute_restart(command_t *cmd)
488 {
489 	int rc;
490 
491 	rc = execute_stop(cmd) + execute_start(cmd);
492 	return rc;
493 }
494 
495 int
496 execute_stop(command_t *cmd)
497 {
498 	union reaper_info info;
499 	struct timespec ts;
500 	int save_restart_some;
501 	int save_restart_all;
502 	int maxwait = 60;
503 
504 	save_restart_some = InitCmd->restart_some;
505 	save_restart_all = InitCmd->restart_all;
506 	if (cmd->commanded)
507 		InitCmd->manual_stop = 1;
508 	if (cmd->commanded && (cmd->restart_some || cmd->restart_all)) {
509 		InitCmd->restart_some = cmd->restart_some;
510 		InitCmd->restart_all = cmd->restart_all;
511 	}
512 	fprintf(cmd->fp, "svc %s: Stopping\n", cmd->label);
513 	fflush(cmd->fp);
514 
515 	/*
516 	 * Start the kill chain going so the master loop's wait3 wakes up.
517 	 */
518 	if (DirectPid >= 0) {
519 		kill(DirectPid, SIGTERM);
520 	} else {
521 		if (procctl(P_PID, getpid(), PROC_REAP_STATUS, &info) == 0 &&
522 		    info.status.pid_head > 0) {
523 			kill(info.status.pid_head, SIGTERM);
524 		}
525 	}
526 
527 	clock_gettime(CLOCK_MONOTONIC_FAST, &ts);
528 	LastStop = ts.tv_sec;
529 	RunState = RS_STOPPING1;
530 
531 	/*
532 	 * If commanded (verses automatic), we are running remote in our
533 	 * own thread and we need to wait for the action to complete.
534 	 */
535 	if (cmd->commanded) {
536 		while (RunState == RS_STOPPING1 ||
537 		       RunState == RS_STOPPING2 ||
538 		       RunState == RS_STOPPING3) {
539 			fprintf(cmd->fp,
540 				"svc %s: Waiting for service to stop\n",
541 				cmd->label);
542 			fflush(cmd->fp);
543 			pthread_mutex_unlock(&serial_mtx);
544 			sleep(1);
545 			pthread_mutex_lock(&serial_mtx);
546 			if (--maxwait == 0) {
547 				fprintf(cmd->fp,
548 					"svc %s: Giving up waiting for stop\n",
549 					cmd->label);
550 				fflush(cmd->fp);
551 				break;
552 			}
553 		}
554 		if (cmd->restart_some || cmd->restart_all) {
555 			InitCmd->restart_some = save_restart_some;
556 			InitCmd->restart_all = save_restart_all;
557 		}
558 	}
559 
560 	return 0;
561 }
562 
563 int
564 execute_exit(command_t *cmd)
565 {
566 	if (cmd->commanded) {
567 		InitCmd->restart_some = 0;
568 		InitCmd->restart_all = 1;	/* kill all children */
569 		InitCmd->exit_mode = 1;		/* exit after stop */
570 	} else {
571 		cmd->exit_mode = 1;
572 	}
573 	fprintf(cmd->fp, "svc %s: Stopping and Exiting\n", cmd->label);
574 	execute_stop(cmd);
575 
576 	return 0;
577 }
578 
579 int
580 execute_list(command_t *cmd)
581 {
582 	fprintf(cmd->fp, "%-16s\n", cmd->label);
583 
584 	return 0;
585 }
586 
587 int
588 execute_status(command_t *cmd)
589 {
590 	const char *state;
591 
592 	switch(RunState) {
593 	case RS_STOPPED:
594 		if (InitCmd && InitCmd->exit_mode)
595 			state = "stopped (exiting)";
596 		else if (InitCmd && InitCmd->manual_stop)
597 			state = "stopped (manual)";
598 		else
599 			state = "stopped";
600 		break;
601 	case RS_STARTED:
602 		state = "running";
603 		break;
604 	case RS_STOPPING1:
605 	case RS_STOPPING2:
606 	case RS_STOPPING3:
607 		state = "killing";
608 		break;
609 	default:
610 		state = "unknown";
611 		break;
612 	}
613 
614 	fprintf(cmd->fp, "%-16s %s\n", cmd->label, state);
615 
616 	return 0;
617 }
618 
619 int
620 execute_log(command_t *cmd)
621 {
622 	int lbsize = (int)sizeof(cmd->logbuf);
623 	int lbmask = lbsize - 1;
624 	int windex;
625 	int n;
626 	int lastnl;
627 	int dotstate;
628 	char buf[LOGCHUNK];
629 
630 	assert(InitCmd);
631 
632 	/*
633 	 * mode 0 - Dump everything then exit
634 	 * mode 1 - Dump everything then block/loop
635 	 * mode 2 - Skeep to end then block/loop
636 	 */
637 	if (cmd->tail_mode == 2)
638 		windex = InitCmd->logwindex;
639 	else
640 		windex = InitCmd->logwindex - InitCmd->logcount;
641 	lastnl = 1;
642 	dotstate = 0;	/* 0=start-of-line 1=middle-of-line 2=dot */
643 
644 	for (;;) {
645 		/*
646 		 * Calculate the amount of data we missed and determine
647 		 * if some data was lost.
648 		 */
649 		n = InitCmd->logwindex - windex;
650 		if (n < 0 || n > InitCmd->logcount) {
651 			windex = InitCmd->logwindex - InitCmd->logcount;
652 			pthread_mutex_unlock(&serial_mtx);
653 			fprintf(cmd->fp, "\n(LOG DATA LOST)\n");
654 			pthread_mutex_lock(&serial_mtx);
655 			continue;
656 		}
657 
658 		/*
659 		 * Circular buffer and copy size limitations.  If no
660 		 * data ready, wait for some.
661 		 */
662 		if (n > lbsize - (windex & lbmask))
663 			n = lbsize - (windex & lbmask);
664 		if (n > LOGCHUNK)
665 			n = LOGCHUNK;
666 		if (n == 0) {
667 			if (cmd->tail_mode == 0)
668 				break;
669 			pthread_cond_wait(&InitCmd->logcond, &serial_mtx);
670 			continue;
671 		}
672 		bcopy(InitCmd->logbuf + (windex & lbmask), buf, n);
673 
674 		/*
675 		 * Dump log output, escape any '.' on a line by itself.
676 		 */
677 		pthread_mutex_unlock(&serial_mtx);
678 		n = escapewrite(cmd->fp, buf, n, &dotstate);
679 		fflush(cmd->fp);
680 		if (n > 0)
681 			lastnl = (buf[n-1] == '\n');
682 		pthread_mutex_lock(&serial_mtx);
683 
684 		if (n < 0)
685 			break;
686 		windex += n;
687 	}
688 	if (lastnl == 0) {
689 		pthread_mutex_unlock(&serial_mtx);
690 		fprintf(cmd->fp, "\n");
691 		pthread_mutex_lock(&serial_mtx);
692 	}
693 	return 0;
694 }
695 
696 /*
697  * Change or reopen logfile.
698  */
699 int
700 execute_logfile(command_t *cmd)
701 {
702 	char *logfile;
703 	int fd;
704 	int rc;
705 
706 	assert(InitCmd);
707 
708 	logfile = cmd->logfile;
709 	if (cmd->ext_av && cmd->ext_av[0])
710 		logfile = cmd->ext_av[0];
711 	if (logfile == NULL)
712 		logfile = InitCmd->logfile;
713 
714 	rc = 0;
715 	if (logfile) {
716 		if (InitCmd->logfile &&
717 		    strcmp(InitCmd->logfile, logfile) == 0) {
718 			fprintf(cmd->fp, "svc %s: Reopen logfile %s\n",
719 				cmd->label, logfile);
720 		} else {
721 			fprintf(cmd->fp, "svc %s: Change logfile to %s\n",
722 				cmd->label, logfile);
723 		}
724 		if (InitCmd->logfd >= 0) {
725 			close(InitCmd->logfd);
726 			InitCmd->logfd = -1;
727 		}
728 		if (strcmp(logfile, "/dev/null") == 0) {
729 			sreplace(&InitCmd->logfile, logfile);
730 		} else {
731 			fd = open(logfile, O_WRONLY|O_CREAT|O_APPEND, 0640);
732 			if (fd >= 0) {
733 				InitCmd->logfd = fd;
734 				sreplace(&InitCmd->logfile, logfile);
735 			} else {
736 				fprintf(cmd->fp,
737 					"svc %s: Unable to open/create "
738 					"\"%s\": %s\n",
739 					cmd->label,
740 					logfile, strerror(errno));
741 				rc = 1;
742 			}
743 		}
744 	}
745 	return rc;
746 }
747 
748 int
749 execute_help(command_t *cmd)
750 {
751 	fprintf(cmd->fp,
752 		"svc [options] directive [label [additional_args]]\n"
753 		"\n"
754 		"Directives: init start stop stopall restart exit\n"
755 		"            kill list status log logf tailf logfile\n"
756 		"            help\n"
757 	);
758 	return 0;
759 }
760 
761 static
762 void *
763 logger_thread(void *arg)
764 {
765 	command_t *cmd = arg;
766 	int lbsize = (int)sizeof(cmd->logbuf);
767 	int lbmask = lbsize - 1;
768 	int windex;
769 	int n;
770 
771 	pthread_detach(pthread_self());
772 	pthread_mutex_lock(&serial_mtx);
773 	for (;;) {
774 		/*
775 		 * slip circular buffer to make room for new data.
776 		 */
777 		n = cmd->logcount - (lbsize - LOGCHUNK);
778 		if (n > 0) {
779 			cmd->logcount -= n;
780 			cmd->logwindex += n;
781 		}
782 		windex = cmd->logwindex & lbmask;
783 		n = lbsize - windex;
784 		if (n > LOGCHUNK)
785 			n = LOGCHUNK;
786 		pthread_mutex_unlock(&serial_mtx);
787 		n = read(cmd->logfds[0], cmd->logbuf + windex, n);
788 		pthread_mutex_lock(&serial_mtx);
789 		if (n > 0) {
790 			if (cmd->logfd >= 0)
791 				write(cmd->logfd, cmd->logbuf + windex, n);
792 			cmd->logcount += n;
793 			cmd->logwindex += n;
794 			pthread_cond_signal(&cmd->logcond);
795 		}
796 		if (n == 0 || (n < 0 && errno != EINTR))
797 			break;
798 	}
799 	pthread_mutex_unlock(&serial_mtx);
800 	return NULL;
801 }
802 
803 /*
804  * Put us in the STOPPED state if we are not already there, and
805  * handle post-stop options (aka sync).
806  */
807 static
808 void
809 setstate_stopped(command_t *cmd, struct timespec *ts)
810 {
811 	if (RunState != RS_STOPPED) {
812 		RunState = RS_STOPPED;
813 		LastStop = ts->tv_sec;
814 		if (cmd->sync_mode)	/* support -s option */
815 			sync();
816 	}
817 }
818 
819 static
820 int
821 setup_gid(command_t *cmd)
822 {
823 	int i;
824 
825 	if (cmd->gid_mode &&
826 	    setgid(cmd->grent.gr_gid) < 0) {
827 		fprintf(cmd->fp, "unable to setgid to \"%s\": %s\n",
828 			cmd->grent.gr_name, strerror(errno));
829 		return 1;
830 	}
831 
832 	/*
833 	 * -G overrides all group ids.
834 	 */
835 	if (cmd->ngroups) {
836 		if (setgroups(cmd->ngroups, cmd->groups) < 0) {
837 			fprintf(cmd->fp, "unable to setgroups to (");
838 			for (i = 0; i < cmd->ngroups; ++i) {
839 				if (i)
840 					fprintf(cmd->fp, ", ");
841 				fprintf(cmd->fp, "%d", cmd->groups[i]);
842 			}
843 			fprintf(cmd->fp, "): %s\n", strerror(errno));
844 			return 1;
845 		}
846 	}
847 	return 0;
848 }
849 
850 static
851 int
852 setup_uid(command_t *cmd)
853 {
854 	fprintf(stderr, "UIDMODE %d %d\n", cmd->uid_mode, cmd->pwent.pw_uid);
855 	if (cmd->uid_mode &&
856 	    cmd->gid_mode == 0 &&
857 	    cmd->ngroups == 0 &&
858 	    setgid(cmd->pwent.pw_gid) < 0) {
859 		fprintf(cmd->fp, "unable to setgid for user \"%s\": %s\n",
860 			cmd->pwent.pw_name,
861 			strerror(errno));
862 		return 1;
863 	}
864 	if (cmd->uid_mode &&
865 	    setuid(cmd->pwent.pw_uid) < 0) {
866 		fprintf(cmd->fp, "unable to setuid for user \"%s\": %s\n",
867 			cmd->pwent.pw_name,
868 			strerror(errno));
869 		return 1;
870 	}
871 	return 0;
872 }
873 
874 static
875 int
876 setup_jail(command_t *cmd)
877 {
878 	struct jail info;
879 	char hostbuf[256];
880 
881 	if (gethostname(hostbuf, sizeof(hostbuf) - 1) < 0) {
882 		fprintf(cmd->fp, "gethostname() failed: %s\n", strerror(errno));
883 		return 1;
884 	}
885 	/* make sure it is zero terminated */
886 	hostbuf[sizeof(hostbuf) -1] = 0;
887 
888 	bzero(&info, sizeof(info));
889 	info.version = 1;
890 	info.path = cmd->jaildir;
891 	info.hostname = hostbuf;
892 	/* info.n_ips, sockaddr_storage ips[] */
893 
894 	if (jail(&info) < 0) {
895 		fprintf(cmd->fp, "unable to create jail \"%s\": %s\n",
896 			cmd->rootdir,
897 			strerror(errno));
898 		return 1;
899 	}
900 	return 0;
901 }
902 
903 static
904 int
905 setup_chroot(command_t *cmd)
906 {
907 	if (chroot(cmd->rootdir) < 0) {
908 		fprintf(cmd->fp, "unable to chroot to \"%s\": %s\n",
909 			cmd->rootdir,
910 			strerror(errno));
911 		return 1;
912 	}
913 	return 0;
914 }
915 
916 static
917 int
918 setup_devfs(command_t *cmd, const char *dir, int domount)
919 {
920 	struct devfs_mount_info info;
921 	struct statfs fs;
922 	int rc = 0;
923 	char *path;
924 
925 	bzero(&info, sizeof(info));
926 	info.flags = 0;
927 	asprintf(&path, "%s/dev", dir);
928 
929 	if (domount) {
930 		if (statfs(path, &fs) == 0 &&
931 		    strcmp(fs.f_fstypename, "devfs") == 0) {
932 			fprintf(cmd->fp, "devfs already mounted\n");
933 		} else
934 		if (mount("devfs", path, MNT_NOEXEC|MNT_NOSUID, &info) < 0) {
935 			fprintf(cmd->fp, "cannot mount devfs on %s: %s\n",
936 				path, strerror(errno));
937 			rc = 1;
938 		}
939 	} else {
940 		if (statfs(path, &fs) < 0 ||
941 		    strcmp(fs.f_fstypename, "devfs") != 0) {
942 			fprintf(cmd->fp, "devfs already unmounted\n");
943 		} else
944 		if (unmount(path, 0) < 0) {
945 			fprintf(cmd->fp, "cannot unmount devfs from %s: %s\n",
946 				path, strerror(errno));
947 			rc = 1;
948 		}
949 	}
950 	free(path);
951 	return rc;
952 }
953 
954 /*
955  * Escape writes.  A '.' on a line by itself must be escaped to '..'.
956  */
957 static
958 int
959 escapewrite(FILE *fp, char *buf, int n, int *statep)
960 {
961 	int b;
962 	int i;
963 	int r;
964 	char c;
965 
966 	b = 0;
967 	r = 0;
968 	i = 0;
969 	while (i < n) {
970 		for (i = b; i < n; ++i) {
971 			c = buf[i];
972 
973 			switch(*statep) {
974 			case 0:
975 				/*
976 				 * beginning of line
977 				 */
978 				if (c == '.')
979 					*statep = 2;
980 				else if (c != '\n')
981 					*statep = 1;
982 				break;
983 			case 1:
984 				/*
985 				 * middle of line
986 				 */
987 				if (c == '\n')
988 					*statep = 0;
989 				break;
990 			case 2:
991 				/*
992 				 * dot was output at beginning of line
993 				 */
994 				if (c == '\n')
995 					*statep = 3;
996 				else
997 					*statep = 1;
998 				break;
999 			default:
1000 				break;
1001 			}
1002 			if (*statep == 3)	/* flush with escape */
1003 				break;
1004 		}
1005 		if (i != b) {
1006 			n = fwrite(buf, 1, i - b, fp);
1007 			if (n > 0)
1008 				r += n;
1009 			if (n < 0)
1010 				r = -1;
1011 		}
1012 		if (*statep == 3) {		/* added escape */
1013 			n = fwrite(".", 1, 1, fp);
1014 			/* escapes not counted in r */
1015 			*statep = 1;
1016 			if (n < 0)
1017 				r = -1;
1018 		}
1019 		if (r < 0)
1020 			break;
1021 	}
1022 	return r;
1023 }
1024