daemon.c revision 236550
182547Smike/*-
282547Smike * Copyright (c) 1999 Berkeley Software Design, Inc. All rights reserved.
382547Smike *
482547Smike * Redistribution and use in source and binary forms, with or without
582547Smike * modification, are permitted provided that the following conditions
682547Smike * are met:
782547Smike * 1. Redistributions of source code must retain the above copyright
882547Smike *    notice, this list of conditions and the following disclaimer.
982547Smike * 2. Redistributions in binary form must reproduce the above copyright
1082547Smike *    notice, this list of conditions and the following disclaimer in the
1182547Smike *    documentation and/or other materials provided with the distribution.
1282547Smike * 3. Berkeley Software Design Inc's name may not be used to endorse or
1382547Smike *    promote products derived from this software without specific prior
1482547Smike *    written permission.
1582547Smike *
1682547Smike * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN INC ``AS IS'' AND
1782547Smike * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1882547Smike * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1982547Smike * ARE DISCLAIMED.  IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN INC BE LIABLE
2082547Smike * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2182547Smike * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2282547Smike * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2382547Smike * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2482547Smike * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2582547Smike * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2682547Smike * SUCH DAMAGE.
2782547Smike *
2882547Smike *	From BSDI: daemon.c,v 1.2 1996/08/15 01:11:09 jch Exp
2982547Smike */
3082547Smike
31117280Scharnier#include <sys/cdefs.h>
32117280Scharnier__FBSDID("$FreeBSD: head/usr.sbin/daemon/daemon.c 236550 2012-06-04 09:22:22Z trociny $");
33117280Scharnier
34149424Spjd#include <sys/param.h>
35231912Strociny#include <sys/mman.h>
36231909Strociny#include <sys/wait.h>
3782547Smike
3882547Smike#include <err.h>
39129983Sphk#include <errno.h>
40149424Spjd#include <libutil.h>
41167700Strhodes#include <login_cap.h>
42231910Strociny#include <pwd.h>
43231910Strociny#include <signal.h>
4482547Smike#include <stdio.h>
4582547Smike#include <stdlib.h>
4682547Smike#include <unistd.h>
4782547Smike
48231910Strocinystatic void dummy_sighandler(int);
49167700Strhodesstatic void restrict_process(const char *);
50231911Strocinystatic int  wait_child(pid_t pid, sigset_t *mask);
5182547Smikestatic void usage(void);
5282547Smike
5382547Smikeint
5482547Smikemain(int argc, char *argv[])
5582547Smike{
56167357Strhodes	struct pidfh *pfh = NULL;
57231910Strociny	sigset_t mask, oldmask;
58231911Strociny	int ch, nochdir, noclose, restart;
59167700Strhodes	const char *pidfile, *user;
60231909Strociny	pid_t otherpid, pid;
6182547Smike
6282547Smike	nochdir = noclose = 1;
63231911Strociny	restart = 0;
64167700Strhodes	pidfile = user = NULL;
65231911Strociny	while ((ch = getopt(argc, argv, "-cfp:ru:")) != -1) {
6682547Smike		switch (ch) {
6782547Smike		case 'c':
6882547Smike			nochdir = 0;
6982547Smike			break;
7082547Smike		case 'f':
7182547Smike			noclose = 0;
7282547Smike			break;
73167700Strhodes		case 'p':
74167700Strhodes			pidfile = optarg;
75167700Strhodes			break;
76231911Strociny		case 'r':
77231911Strociny			restart = 1;
78231911Strociny			break;
79167356Strhodes		case 'u':
80167356Strhodes			user = optarg;
81167356Strhodes			break;
8282547Smike		default:
8382547Smike			usage();
8482547Smike		}
8582547Smike	}
8682547Smike	argc -= optind;
8782547Smike	argv += optind;
8882547Smike
8982547Smike	if (argc == 0)
9082547Smike		usage();
91167356Strhodes
92231909Strociny	pfh = NULL;
93129983Sphk	/*
94129983Sphk	 * Try to open the pidfile before calling daemon(3),
95129983Sphk	 * to be able to report the error intelligently
96129983Sphk	 */
97231909Strociny	if (pidfile != NULL) {
98149424Spjd		pfh = pidfile_open(pidfile, 0600, &otherpid);
99149424Spjd		if (pfh == NULL) {
100149424Spjd			if (errno == EEXIST) {
101149424Spjd				errx(3, "process already running, pid: %d",
102149424Spjd				    otherpid);
103149424Spjd			}
104129983Sphk			err(2, "pidfile ``%s''", pidfile);
105149424Spjd		}
106129983Sphk	}
107129983Sphk
10882547Smike	if (daemon(nochdir, noclose) == -1)
10982547Smike		err(1, NULL);
110129983Sphk
111231910Strociny	/*
112231911Strociny	 * If the pidfile or restart option is specified the daemon
113231911Strociny	 * executes the command in a forked process and wait on child
114231911Strociny	 * exit to remove the pidfile or restart the command. Normally
115231911Strociny	 * we don't want the monitoring daemon to be terminated
116231911Strociny	 * leaving the running process and the stale pidfile, so we
117231911Strociny	 * catch SIGTERM and forward it to the children expecting to
118231911Strociny	 * get SIGCHLD eventually.
119231910Strociny	 */
120231910Strociny	pid = -1;
121231911Strociny	if (pidfile != NULL || restart) {
122231909Strociny		/*
123231910Strociny		 * Restore default action for SIGTERM in case the
124231910Strociny		 * parent process decided to ignore it.
125231910Strociny		 */
126231910Strociny		if (signal(SIGTERM, SIG_DFL) == SIG_ERR)
127231910Strociny			err(1, "signal");
128231910Strociny		/*
129231910Strociny		 * Because SIGCHLD is ignored by default, setup dummy handler
130231910Strociny		 * for it, so we can mask it.
131231910Strociny		 */
132231910Strociny		if (signal(SIGCHLD, dummy_sighandler) == SIG_ERR)
133231910Strociny			err(1, "signal");
134231910Strociny		/*
135231910Strociny		 * Block interesting signals.
136231910Strociny		 */
137231910Strociny		sigemptyset(&mask);
138231910Strociny		sigaddset(&mask, SIGTERM);
139231910Strociny		sigaddset(&mask, SIGCHLD);
140231910Strociny		if (sigprocmask(SIG_SETMASK, &mask, &oldmask) == -1)
141231910Strociny			err(1, "sigprocmask");
142231912Strociny		/*
143231912Strociny		 * Try to protect against pageout kill. Ignore the
144231912Strociny		 * error, madvise(2) will fail only if a process does
145231912Strociny		 * not have superuser privileges.
146231912Strociny		 */
147231912Strociny		(void)madvise(NULL, 0, MADV_PROTECT);
148231911Strocinyrestart:
149231910Strociny		/*
150231909Strociny		 * Spawn a child to exec the command, so in the parent
151231909Strociny		 * we could wait for it to exit and remove pidfile.
152231909Strociny		 */
153231909Strociny		pid = fork();
154231909Strociny		if (pid == -1) {
155231909Strociny			pidfile_remove(pfh);
156231909Strociny			err(1, "fork");
157231909Strociny		}
158231909Strociny	}
159231910Strociny	if (pid <= 0) {
160231910Strociny		if (pid == 0) {
161231910Strociny			/* Restore old sigmask in the child. */
162231910Strociny			if (sigprocmask(SIG_SETMASK, &oldmask, NULL) == -1)
163231910Strociny				err(1, "sigprocmask");
164231910Strociny		}
165231909Strociny		/* Now that we are the child, write out the pid. */
166149424Spjd		pidfile_write(pfh);
167129983Sphk
168231909Strociny		if (user != NULL)
169231909Strociny			restrict_process(user);
17082547Smike
171231909Strociny		execvp(argv[0], argv);
172129983Sphk
173231909Strociny		/*
174231909Strociny		 * execvp() failed -- report the error. The child is
175231909Strociny		 * now running, so the exit status doesn't matter.
176231909Strociny		 */
177231909Strociny		err(1, "%s", argv[0]);
178231909Strociny	}
179231909Strociny	setproctitle("%s[%d]", argv[0], pid);
180231911Strociny	if (wait_child(pid, &mask) == 0 && restart) {
181231911Strociny		sleep(1);
182231911Strociny		goto restart;
183231911Strociny	}
184231909Strociny	pidfile_remove(pfh);
185231909Strociny	exit(0); /* Exit status does not matter. */
18682547Smike}
18782547Smike
18882547Smikestatic void
189231910Strocinydummy_sighandler(int sig __unused)
190231910Strociny{
191231910Strociny	/* Nothing to do. */
192231910Strociny}
193231910Strociny
194231910Strocinystatic void
195167700Strhodesrestrict_process(const char *user)
196167356Strhodes{
197167356Strhodes	struct passwd *pw = NULL;
198167356Strhodes
199167700Strhodes	pw = getpwnam(user);
200167700Strhodes	if (pw == NULL)
201167700Strhodes		errx(1, "unknown user: %s", user);
202167356Strhodes
203167700Strhodes	if (setusercontext(NULL, pw, pw->pw_uid, LOGIN_SETALL) != 0)
204167700Strhodes		errx(1, "failed to set user environment");
205167356Strhodes}
206167356Strhodes
207231911Strocinystatic int
208231910Strocinywait_child(pid_t pid, sigset_t *mask)
209231909Strociny{
210231911Strociny	int terminate, signo;
211231909Strociny
212231911Strociny	terminate = 0;
213231910Strociny	for (;;) {
214231910Strociny		if (sigwait(mask, &signo) == -1) {
215231910Strociny			warn("sigwaitinfo");
216231911Strociny			return (-1);
217231909Strociny		}
218231910Strociny		switch (signo) {
219231910Strociny		case SIGCHLD:
220236550Strociny			if (waitpid(pid, NULL, WNOHANG) == -1) {
221236550Strociny				warn("waitpid");
222236550Strociny				return (-1);
223236550Strociny			}
224231911Strociny			return (terminate);
225231910Strociny		case SIGTERM:
226231911Strociny			terminate = 1;
227231910Strociny			if (kill(pid, signo) == -1) {
228231910Strociny				warn("kill");
229231911Strociny				return (-1);
230231910Strociny			}
231231910Strociny			continue;
232231910Strociny		default:
233231910Strociny			warnx("sigwaitinfo: invalid signal: %d", signo);
234231911Strociny			return (-1);
235231910Strociny		}
236231909Strociny	}
237231909Strociny}
238231909Strociny
239231909Strocinystatic void
24082547Smikeusage(void)
24182547Smike{
242129983Sphk	(void)fprintf(stderr,
243167700Strhodes	    "usage: daemon [-cf] [-p pidfile] [-u user] command "
244167356Strhodes		"arguments ...\n");
24582547Smike	exit(1);
24682547Smike}
247