daemon.c revision 231910
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 231910 2012-02-19 10:23:51Z trociny $");
33117280Scharnier
34149424Spjd#include <sys/param.h>
35231909Strociny#include <sys/wait.h>
3682547Smike
3782547Smike#include <err.h>
38129983Sphk#include <errno.h>
39149424Spjd#include <libutil.h>
40167700Strhodes#include <login_cap.h>
41231910Strociny#include <pwd.h>
42231910Strociny#include <signal.h>
4382547Smike#include <stdio.h>
4482547Smike#include <stdlib.h>
4582547Smike#include <unistd.h>
4682547Smike
47231910Strocinystatic void dummy_sighandler(int);
48167700Strhodesstatic void restrict_process(const char *);
49231910Strocinystatic void wait_child(pid_t pid, sigset_t *mask);
5082547Smikestatic void usage(void);
5182547Smike
5282547Smikeint
5382547Smikemain(int argc, char *argv[])
5482547Smike{
55167357Strhodes	struct pidfh *pfh = NULL;
56231910Strociny	sigset_t mask, oldmask;
57231909Strociny	int ch, nochdir, noclose;
58167700Strhodes	const char *pidfile, *user;
59231909Strociny	pid_t otherpid, pid;
6082547Smike
6182547Smike	nochdir = noclose = 1;
62167700Strhodes	pidfile = user = NULL;
63168868Speter	while ((ch = getopt(argc, argv, "-cfp:u:")) != -1) {
6482547Smike		switch (ch) {
6582547Smike		case 'c':
6682547Smike			nochdir = 0;
6782547Smike			break;
6882547Smike		case 'f':
6982547Smike			noclose = 0;
7082547Smike			break;
71167700Strhodes		case 'p':
72167700Strhodes			pidfile = optarg;
73167700Strhodes			break;
74167356Strhodes		case 'u':
75167356Strhodes			user = optarg;
76167356Strhodes			break;
7782547Smike		default:
7882547Smike			usage();
7982547Smike		}
8082547Smike	}
8182547Smike	argc -= optind;
8282547Smike	argv += optind;
8382547Smike
8482547Smike	if (argc == 0)
8582547Smike		usage();
86167356Strhodes
87231909Strociny	pfh = NULL;
88129983Sphk	/*
89129983Sphk	 * Try to open the pidfile before calling daemon(3),
90129983Sphk	 * to be able to report the error intelligently
91129983Sphk	 */
92231909Strociny	if (pidfile != NULL) {
93149424Spjd		pfh = pidfile_open(pidfile, 0600, &otherpid);
94149424Spjd		if (pfh == NULL) {
95149424Spjd			if (errno == EEXIST) {
96149424Spjd				errx(3, "process already running, pid: %d",
97149424Spjd				    otherpid);
98149424Spjd			}
99129983Sphk			err(2, "pidfile ``%s''", pidfile);
100149424Spjd		}
101129983Sphk	}
102129983Sphk
10382547Smike	if (daemon(nochdir, noclose) == -1)
10482547Smike		err(1, NULL);
105129983Sphk
106231910Strociny	/*
107231910Strociny	 * If the pidfile option is specified the daemon executes the
108231910Strociny	 * command in a forked process and wait on child exit to
109231910Strociny	 * remove the pidfile. Normally we don't want the monitoring
110231910Strociny	 * daemon to be terminated leaving the running process and the
111231910Strociny	 * stale pidfile, so we catch SIGTERM and pass it to the
112231910Strociny	 * children expecting to get SIGCHLD eventually.
113231910Strociny	 */
114231910Strociny	pid = -1;
115231909Strociny	if (pidfile != NULL) {
116231909Strociny		/*
117231910Strociny		 * Restore default action for SIGTERM in case the
118231910Strociny		 * parent process decided to ignore it.
119231910Strociny		 */
120231910Strociny		if (signal(SIGTERM, SIG_DFL) == SIG_ERR)
121231910Strociny			err(1, "signal");
122231910Strociny		/*
123231910Strociny		 * Because SIGCHLD is ignored by default, setup dummy handler
124231910Strociny		 * for it, so we can mask it.
125231910Strociny		 */
126231910Strociny		if (signal(SIGCHLD, dummy_sighandler) == SIG_ERR)
127231910Strociny			err(1, "signal");
128231910Strociny		/*
129231910Strociny		 * Block interesting signals.
130231910Strociny		 */
131231910Strociny		sigemptyset(&mask);
132231910Strociny		sigaddset(&mask, SIGTERM);
133231910Strociny		sigaddset(&mask, SIGCHLD);
134231910Strociny		if (sigprocmask(SIG_SETMASK, &mask, &oldmask) == -1)
135231910Strociny			err(1, "sigprocmask");
136231910Strociny		/*
137231909Strociny		 * Spawn a child to exec the command, so in the parent
138231909Strociny		 * we could wait for it to exit and remove pidfile.
139231909Strociny		 */
140231909Strociny		pid = fork();
141231909Strociny		if (pid == -1) {
142231909Strociny			pidfile_remove(pfh);
143231909Strociny			err(1, "fork");
144231909Strociny		}
145231909Strociny	}
146231910Strociny	if (pid <= 0) {
147231910Strociny		if (pid == 0) {
148231910Strociny			/* Restore old sigmask in the child. */
149231910Strociny			if (sigprocmask(SIG_SETMASK, &oldmask, NULL) == -1)
150231910Strociny				err(1, "sigprocmask");
151231910Strociny		}
152231909Strociny		/* Now that we are the child, write out the pid. */
153149424Spjd		pidfile_write(pfh);
154129983Sphk
155231909Strociny		if (user != NULL)
156231909Strociny			restrict_process(user);
15782547Smike
158231909Strociny		execvp(argv[0], argv);
159129983Sphk
160231909Strociny		/*
161231909Strociny		 * execvp() failed -- report the error. The child is
162231909Strociny		 * now running, so the exit status doesn't matter.
163231909Strociny		 */
164231909Strociny		err(1, "%s", argv[0]);
165231909Strociny	}
166231909Strociny	setproctitle("%s[%d]", argv[0], pid);
167231910Strociny	wait_child(pid, &mask);
168231909Strociny	pidfile_remove(pfh);
169231909Strociny	exit(0); /* Exit status does not matter. */
17082547Smike}
17182547Smike
17282547Smikestatic void
173231910Strocinydummy_sighandler(int sig __unused)
174231910Strociny{
175231910Strociny	/* Nothing to do. */
176231910Strociny}
177231910Strociny
178231910Strocinystatic void
179167700Strhodesrestrict_process(const char *user)
180167356Strhodes{
181167356Strhodes	struct passwd *pw = NULL;
182167356Strhodes
183167700Strhodes	pw = getpwnam(user);
184167700Strhodes	if (pw == NULL)
185167700Strhodes		errx(1, "unknown user: %s", user);
186167356Strhodes
187167700Strhodes	if (setusercontext(NULL, pw, pw->pw_uid, LOGIN_SETALL) != 0)
188167700Strhodes		errx(1, "failed to set user environment");
189167356Strhodes}
190167356Strhodes
191167356Strhodesstatic void
192231910Strocinywait_child(pid_t pid, sigset_t *mask)
193231909Strociny{
194231910Strociny	int signo;
195231909Strociny
196231910Strociny	for (;;) {
197231910Strociny		if (sigwait(mask, &signo) == -1) {
198231910Strociny			warn("sigwaitinfo");
199231910Strociny			return;
200231909Strociny		}
201231910Strociny		switch (signo) {
202231910Strociny		case SIGCHLD:
203231910Strociny			return;
204231910Strociny		case SIGTERM:
205231910Strociny			if (kill(pid, signo) == -1) {
206231910Strociny				warn("kill");
207231910Strociny				return;
208231910Strociny			}
209231910Strociny			continue;
210231910Strociny		default:
211231910Strociny			warnx("sigwaitinfo: invalid signal: %d", signo);
212231910Strociny			return;
213231910Strociny		}
214231909Strociny	}
215231909Strociny}
216231909Strociny
217231909Strocinystatic void
21882547Smikeusage(void)
21982547Smike{
220129983Sphk	(void)fprintf(stderr,
221167700Strhodes	    "usage: daemon [-cf] [-p pidfile] [-u user] command "
222167356Strhodes		"arguments ...\n");
22382547Smike	exit(1);
22482547Smike}
225