daemon.c revision 255707
189051Sjake/*-
289051Sjake * Copyright (c) 1999 Berkeley Software Design, Inc. All rights reserved.
389051Sjake *
489051Sjake * Redistribution and use in source and binary forms, with or without
589051Sjake * modification, are permitted provided that the following conditions
689051Sjake * are met:
789051Sjake * 1. Redistributions of source code must retain the above copyright
889051Sjake *    notice, this list of conditions and the following disclaimer.
989051Sjake * 2. Redistributions in binary form must reproduce the above copyright
1089051Sjake *    notice, this list of conditions and the following disclaimer in the
1189051Sjake *    documentation and/or other materials provided with the distribution.
1289051Sjake * 3. Berkeley Software Design Inc's name may not be used to endorse or
1389051Sjake *    promote products derived from this software without specific prior
1489051Sjake *    written permission.
1589051Sjake *
1689051Sjake * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN INC ``AS IS'' AND
1789051Sjake * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1889051Sjake * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1989051Sjake * ARE DISCLAIMED.  IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN INC BE LIABLE
2089051Sjake * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2189051Sjake * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2289051Sjake * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2389051Sjake * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2489051Sjake * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2589051Sjake * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2689051Sjake * SUCH DAMAGE.
2789051Sjake *
2889051Sjake *	From BSDI: daemon.c,v 1.2 1996/08/15 01:11:09 jch Exp
2989051Sjake */
3089051Sjake
3189051Sjake#include <sys/cdefs.h>
3289051Sjake__FBSDID("$FreeBSD: head/usr.sbin/daemon/daemon.c 255707 2013-09-19 18:00:05Z trociny $");
3389051Sjake
3489051Sjake#include <sys/param.h>
3589051Sjake#include <sys/mman.h>
3689051Sjake#include <sys/wait.h>
3789051Sjake
3889051Sjake#include <err.h>
3989051Sjake#include <errno.h>
4089051Sjake#include <libutil.h>
4189051Sjake#include <login_cap.h>
4289051Sjake#include <pwd.h>
4389051Sjake#include <signal.h>
4489051Sjake#include <stdio.h>
4589051Sjake#include <stdlib.h>
4689051Sjake#include <unistd.h>
4789051Sjake
4889051Sjakestatic void dummy_sighandler(int);
4989051Sjakestatic void restrict_process(const char *);
5089051Sjakestatic int  wait_child(pid_t pid, sigset_t *mask);
5189051Sjakestatic void usage(void);
5289051Sjake
5389051Sjakeint
5489051Sjakemain(int argc, char *argv[])
5589051Sjake{
5689051Sjake	struct pidfh  *ppfh, *pfh;
5789051Sjake	sigset_t mask, oldmask;
58107103Sjhb	int ch, nochdir, noclose, restart, serrno;
59107103Sjhb	const char *pidfile, *ppidfile,  *user;
6089051Sjake	pid_t otherpid, pid;
6189051Sjake
6289051Sjake	nochdir = noclose = 1;
6389051Sjake	restart = 0;
6489051Sjake	ppidfile = pidfile = user = NULL;
6589051Sjake	while ((ch = getopt(argc, argv, "cfp:P:ru:")) != -1) {
6689051Sjake		switch (ch) {
6789051Sjake		case 'c':
6889051Sjake			nochdir = 0;
6989051Sjake			break;
7089051Sjake		case 'f':
7189051Sjake			noclose = 0;
7289051Sjake			break;
7389051Sjake		case 'p':
7489051Sjake			pidfile = optarg;
7589051Sjake			break;
7689051Sjake		case 'P':
7789051Sjake			ppidfile = optarg;
7889051Sjake			break;
7992205Sjake		case 'r':
8092205Sjake			restart = 1;
8189051Sjake			break;
8292205Sjake		case 'u':
83119696Smarcel			user = optarg;
8489051Sjake			break;
8597511Sjake		default:
8692205Sjake			usage();
8789051Sjake		}
8895132Sjake	}
8991617Sjake	argc -= optind;
9089051Sjake	argv += optind;
9189051Sjake
9289051Sjake	if (argc == 0)
9389051Sjake		usage();
9489051Sjake
9589051Sjake	ppfh = pfh = NULL;
9689051Sjake	/*
9789051Sjake	 * Try to open the pidfile before calling daemon(3),
9889051Sjake	 * to be able to report the error intelligently
9989051Sjake	 */
10089051Sjake	if (pidfile != NULL) {
10191783Sjake		pfh = pidfile_open(pidfile, 0600, &otherpid);
10297001Sjake		if (pfh == NULL) {
10391783Sjake			if (errno == EEXIST) {
10489051Sjake				errx(3, "process already running, pid: %d",
105108187Sjake				    otherpid);
106108187Sjake			}
10791617Sjake			err(2, "pidfile ``%s''", pidfile);
10891617Sjake		}
10989051Sjake	}
11089051Sjake	/* Do the same for actual daemon process. */
11192205Sjake	if (ppidfile != NULL) {
11292205Sjake		ppfh = pidfile_open(ppidfile, 0600, &otherpid);
11391617Sjake		if (ppfh == NULL) {
11491617Sjake			serrno = errno;
11591617Sjake			pidfile_remove(pfh);
11691617Sjake			errno = serrno;
11791617Sjake			if (errno == EEXIST) {
11891617Sjake				errx(3, "process already running, pid: %d",
11991617Sjake				     otherpid);
12091617Sjake			}
12191617Sjake			err(2, "ppidfile ``%s''", ppidfile);
12291617Sjake		}
12391617Sjake	}
12491617Sjake
12591617Sjake	if (daemon(nochdir, noclose) == -1) {
12691617Sjake		warn("daemon");
12791617Sjake		goto exit;
12891617Sjake	}
12991617Sjake	/* Write out parent pidfile if needed. */
13097511Sjake	pidfile_write(ppfh);
131102042Sjake
13297511Sjake	/*
13397511Sjake	 * If the pidfile or restart option is specified the daemon
13497511Sjake	 * executes the command in a forked process and wait on child
13591617Sjake	 * exit to remove the pidfile or restart the command. Normally
13691617Sjake	 * we don't want the monitoring daemon to be terminated
13791617Sjake	 * leaving the running process and the stale pidfile, so we
13891617Sjake	 * catch SIGTERM and forward it to the children expecting to
13991617Sjake	 * get SIGCHLD eventually.
14089051Sjake	 */
14189051Sjake	pid = -1;
14289051Sjake	if (pidfile != NULL || restart) {
143122947Sjhb		/*
144122947Sjhb		 * Restore default action for SIGTERM in case the
14589051Sjake		 * parent process decided to ignore it.
14689051Sjake		 */
14789051Sjake		if (signal(SIGTERM, SIG_DFL) == SIG_ERR) {
14889051Sjake			warn("signal");
14989051Sjake			goto exit;
15089051Sjake		}
15189051Sjake		/*
15289051Sjake		 * Because SIGCHLD is ignored by default, setup dummy handler
15389051Sjake		 * for it, so we can mask it.
15489051Sjake		 */
15589051Sjake		if (signal(SIGCHLD, dummy_sighandler) == SIG_ERR) {
15689051Sjake			warn("signal");
15789051Sjake			goto exit;
15889051Sjake		}
15989051Sjake		/*
16089051Sjake		 * Block interesting signals.
16189051Sjake		 */
16293683Stmm		sigemptyset(&mask);
16389051Sjake		sigaddset(&mask, SIGTERM);
16489051Sjake		sigaddset(&mask, SIGCHLD);
165122947Sjhb		if (sigprocmask(SIG_SETMASK, &mask, &oldmask) == -1) {
166122947Sjhb			warn("sigprocmask");
167122947Sjhb			goto exit;
168122947Sjhb		}
169122947Sjhb		/*
170122947Sjhb		 * Try to protect against pageout kill. Ignore the
171122947Sjhb		 * error, madvise(2) will fail only if a process does
17291617Sjake		 * not have superuser privileges.
17391617Sjake		 */
17491617Sjake		(void)madvise(NULL, 0, MADV_PROTECT);
17591617Sjakerestart:
17691617Sjake		/*
17791617Sjake		 * Spawn a child to exec the command, so in the parent
17891617Sjake		 * we could wait for it to exit and remove pidfile.
17991617Sjake		 */
18091617Sjake		pid = fork();
18191617Sjake		if (pid == -1) {
18291617Sjake			warn("fork");
18391617Sjake			goto exit;
18491617Sjake		}
18591617Sjake	}
18691617Sjake	if (pid <= 0) {
18791617Sjake		if (pid == 0) {
18891617Sjake			/* Restore old sigmask in the child. */
18991617Sjake			if (sigprocmask(SIG_SETMASK, &oldmask, NULL) == -1)
19091617Sjake				err(1, "sigprocmask");
19191617Sjake		}
19291617Sjake		/* Now that we are the child, write out the pid. */
19391617Sjake		pidfile_write(pfh);
19491617Sjake
19591617Sjake		if (user != NULL)
19691617Sjake			restrict_process(user);
19789051Sjake
19892205Sjake		execvp(argv[0], argv);
19992205Sjake
20092205Sjake		/*
20192205Sjake		 * execvp() failed -- report the error. The child is
20292205Sjake		 * now running, so the exit status doesn't matter.
20392205Sjake		 */
20492205Sjake		err(1, "%s", argv[0]);
20592205Sjake	}
20692205Sjake
20792205Sjake	setproctitle("%s[%d]", argv[0], pid);
20892205Sjake	if (wait_child(pid, &mask) == 0 && restart) {
20992205Sjake		sleep(1);
21092205Sjake		goto restart;
21192205Sjake	}
21292205Sjakeexit:
21392205Sjake	pidfile_remove(pfh);
21492205Sjake	pidfile_remove(ppfh);
21592205Sjake	exit(1); /* If daemon(3) succeeded exit status does not matter. */
21692205Sjake}
21792205Sjake
21889051Sjakestatic void
21989051Sjakedummy_sighandler(int sig __unused)
22089051Sjake{
22189051Sjake	/* Nothing to do. */
22289051Sjake}
22389051Sjake
22489051Sjakestatic void
22589051Sjakerestrict_process(const char *user)
22689051Sjake{
22789051Sjake	struct passwd *pw = NULL;
22889051Sjake
22991617Sjake	pw = getpwnam(user);
23091617Sjake	if (pw == NULL)
23189051Sjake		errx(1, "unknown user: %s", user);
23291617Sjake
23389051Sjake	if (setusercontext(NULL, pw, pw->pw_uid, LOGIN_SETALL) != 0)
234108187Sjake		errx(1, "failed to set user environment");
23589051Sjake}
23689051Sjake
23789051Sjakestatic int
23889051Sjakewait_child(pid_t pid, sigset_t *mask)
23989051Sjake{
24089051Sjake	int terminate, signo;
24189051Sjake
24289051Sjake	terminate = 0;
24389051Sjake	for (;;) {
24489051Sjake		if (sigwait(mask, &signo) == -1) {
24589051Sjake			warn("sigwaitinfo");
24689051Sjake			return (-1);
24799936Sjake		}
24899936Sjake		switch (signo) {
24989051Sjake		case SIGCHLD:
25089051Sjake			if (waitpid(pid, NULL, WNOHANG) == -1) {
25189051Sjake				warn("waitpid");
25291617Sjake				return (-1);
25391617Sjake			}
25491617Sjake			return (terminate);
25589051Sjake		case SIGTERM:
25691617Sjake			terminate = 1;
25791617Sjake			if (kill(pid, signo) == -1) {
25891617Sjake				warn("kill");
25991617Sjake				return (-1);
26091617Sjake			}
26191617Sjake			continue;
26291617Sjake		default:
26391617Sjake			warnx("sigwaitinfo: invalid signal: %d", signo);
26491617Sjake			return (-1);
26591617Sjake		}
26691617Sjake	}
26791617Sjake}
26889051Sjake
26991617Sjakestatic void
27091617Sjakeusage(void)
27191617Sjake{
27291617Sjake	(void)fprintf(stderr,
27389051Sjake	    "usage: daemon [-cfr] [-p child_pidfile] [-P supervisor_pidfile] "
27491617Sjake	    "[-u user]\n              command arguments ...\n");
27589051Sjake	exit(1);
276101898Sjake}
27789051Sjake