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$");
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{
56255521Sjmg	struct pidfh  *ppfh, *pfh;
57231910Strociny	sigset_t mask, oldmask;
58255707Strociny	int ch, nochdir, noclose, restart, serrno;
59255521Sjmg	const char *pidfile, *ppidfile,  *user;
60231909Strociny	pid_t otherpid, pid;
6182547Smike
6282547Smike	nochdir = noclose = 1;
63231911Strociny	restart = 0;
64255521Sjmg	ppidfile = pidfile = user = NULL;
65255521Sjmg	while ((ch = getopt(argc, argv, "cfp:P: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;
76255521Sjmg		case 'P':
77255521Sjmg			ppidfile = optarg;
78255521Sjmg			break;
79231911Strociny		case 'r':
80231911Strociny			restart = 1;
81231911Strociny			break;
82167356Strhodes		case 'u':
83167356Strhodes			user = optarg;
84167356Strhodes			break;
8582547Smike		default:
8682547Smike			usage();
8782547Smike		}
8882547Smike	}
8982547Smike	argc -= optind;
9082547Smike	argv += optind;
9182547Smike
9282547Smike	if (argc == 0)
9382547Smike		usage();
94167356Strhodes
95255521Sjmg	ppfh = pfh = NULL;
96129983Sphk	/*
97129983Sphk	 * Try to open the pidfile before calling daemon(3),
98129983Sphk	 * to be able to report the error intelligently
99129983Sphk	 */
100231909Strociny	if (pidfile != NULL) {
101149424Spjd		pfh = pidfile_open(pidfile, 0600, &otherpid);
102149424Spjd		if (pfh == NULL) {
103149424Spjd			if (errno == EEXIST) {
104149424Spjd				errx(3, "process already running, pid: %d",
105149424Spjd				    otherpid);
106149424Spjd			}
107129983Sphk			err(2, "pidfile ``%s''", pidfile);
108149424Spjd		}
109129983Sphk	}
110255707Strociny	/* Do the same for actual daemon process. */
111255521Sjmg	if (ppidfile != NULL) {
112255521Sjmg		ppfh = pidfile_open(ppidfile, 0600, &otherpid);
113255521Sjmg		if (ppfh == NULL) {
114255707Strociny			serrno = errno;
115255707Strociny			pidfile_remove(pfh);
116255707Strociny			errno = serrno;
117255521Sjmg			if (errno == EEXIST) {
118255521Sjmg				errx(3, "process already running, pid: %d",
119255521Sjmg				     otherpid);
120255521Sjmg			}
121255521Sjmg			err(2, "ppidfile ``%s''", ppidfile);
122255521Sjmg		}
123255521Sjmg	}
124129983Sphk
125255707Strociny	if (daemon(nochdir, noclose) == -1) {
126255707Strociny		warn("daemon");
127255707Strociny		goto exit;
128255707Strociny	}
129255707Strociny	/* Write out parent pidfile if needed. */
130255707Strociny	pidfile_write(ppfh);
131129983Sphk
132231910Strociny	/*
133231911Strociny	 * If the pidfile or restart option is specified the daemon
134231911Strociny	 * executes the command in a forked process and wait on child
135231911Strociny	 * exit to remove the pidfile or restart the command. Normally
136231911Strociny	 * we don't want the monitoring daemon to be terminated
137231911Strociny	 * leaving the running process and the stale pidfile, so we
138231911Strociny	 * catch SIGTERM and forward it to the children expecting to
139231911Strociny	 * get SIGCHLD eventually.
140231910Strociny	 */
141231910Strociny	pid = -1;
142231911Strociny	if (pidfile != NULL || restart) {
143231909Strociny		/*
144231910Strociny		 * Restore default action for SIGTERM in case the
145231910Strociny		 * parent process decided to ignore it.
146231910Strociny		 */
147255707Strociny		if (signal(SIGTERM, SIG_DFL) == SIG_ERR) {
148255707Strociny			warn("signal");
149255707Strociny			goto exit;
150255707Strociny		}
151231910Strociny		/*
152231910Strociny		 * Because SIGCHLD is ignored by default, setup dummy handler
153231910Strociny		 * for it, so we can mask it.
154231910Strociny		 */
155255707Strociny		if (signal(SIGCHLD, dummy_sighandler) == SIG_ERR) {
156255707Strociny			warn("signal");
157255707Strociny			goto exit;
158255707Strociny		}
159231910Strociny		/*
160231910Strociny		 * Block interesting signals.
161231910Strociny		 */
162231910Strociny		sigemptyset(&mask);
163231910Strociny		sigaddset(&mask, SIGTERM);
164231910Strociny		sigaddset(&mask, SIGCHLD);
165255707Strociny		if (sigprocmask(SIG_SETMASK, &mask, &oldmask) == -1) {
166255707Strociny			warn("sigprocmask");
167255707Strociny			goto exit;
168255707Strociny		}
169231912Strociny		/*
170231912Strociny		 * Try to protect against pageout kill. Ignore the
171231912Strociny		 * error, madvise(2) will fail only if a process does
172231912Strociny		 * not have superuser privileges.
173231912Strociny		 */
174231912Strociny		(void)madvise(NULL, 0, MADV_PROTECT);
175231911Strocinyrestart:
176231910Strociny		/*
177231909Strociny		 * Spawn a child to exec the command, so in the parent
178231909Strociny		 * we could wait for it to exit and remove pidfile.
179231909Strociny		 */
180231909Strociny		pid = fork();
181231909Strociny		if (pid == -1) {
182255707Strociny			warn("fork");
183255707Strociny			goto exit;
184231909Strociny		}
185231909Strociny	}
186231910Strociny	if (pid <= 0) {
187231910Strociny		if (pid == 0) {
188231910Strociny			/* Restore old sigmask in the child. */
189231910Strociny			if (sigprocmask(SIG_SETMASK, &oldmask, NULL) == -1)
190231910Strociny				err(1, "sigprocmask");
191231910Strociny		}
192231909Strociny		/* Now that we are the child, write out the pid. */
193149424Spjd		pidfile_write(pfh);
194129983Sphk
195231909Strociny		if (user != NULL)
196231909Strociny			restrict_process(user);
19782547Smike
198231909Strociny		execvp(argv[0], argv);
199129983Sphk
200231909Strociny		/*
201231909Strociny		 * execvp() failed -- report the error. The child is
202231909Strociny		 * now running, so the exit status doesn't matter.
203231909Strociny		 */
204231909Strociny		err(1, "%s", argv[0]);
205231909Strociny	}
206255521Sjmg
207231909Strociny	setproctitle("%s[%d]", argv[0], pid);
208231911Strociny	if (wait_child(pid, &mask) == 0 && restart) {
209231911Strociny		sleep(1);
210231911Strociny		goto restart;
211231911Strociny	}
212255707Strocinyexit:
213231909Strociny	pidfile_remove(pfh);
214255521Sjmg	pidfile_remove(ppfh);
215255707Strociny	exit(1); /* If daemon(3) succeeded exit status does not matter. */
21682547Smike}
21782547Smike
21882547Smikestatic void
219231910Strocinydummy_sighandler(int sig __unused)
220231910Strociny{
221231910Strociny	/* Nothing to do. */
222231910Strociny}
223231910Strociny
224231910Strocinystatic void
225167700Strhodesrestrict_process(const char *user)
226167356Strhodes{
227167356Strhodes	struct passwd *pw = NULL;
228167356Strhodes
229167700Strhodes	pw = getpwnam(user);
230167700Strhodes	if (pw == NULL)
231167700Strhodes		errx(1, "unknown user: %s", user);
232167356Strhodes
233167700Strhodes	if (setusercontext(NULL, pw, pw->pw_uid, LOGIN_SETALL) != 0)
234167700Strhodes		errx(1, "failed to set user environment");
235167356Strhodes}
236167356Strhodes
237231911Strocinystatic int
238231910Strocinywait_child(pid_t pid, sigset_t *mask)
239231909Strociny{
240231911Strociny	int terminate, signo;
241231909Strociny
242231911Strociny	terminate = 0;
243231910Strociny	for (;;) {
244231910Strociny		if (sigwait(mask, &signo) == -1) {
245231910Strociny			warn("sigwaitinfo");
246231911Strociny			return (-1);
247231909Strociny		}
248231910Strociny		switch (signo) {
249231910Strociny		case SIGCHLD:
250236550Strociny			if (waitpid(pid, NULL, WNOHANG) == -1) {
251236550Strociny				warn("waitpid");
252236550Strociny				return (-1);
253236550Strociny			}
254231911Strociny			return (terminate);
255231910Strociny		case SIGTERM:
256231911Strociny			terminate = 1;
257231910Strociny			if (kill(pid, signo) == -1) {
258231910Strociny				warn("kill");
259231911Strociny				return (-1);
260231910Strociny			}
261231910Strociny			continue;
262231910Strociny		default:
263231910Strociny			warnx("sigwaitinfo: invalid signal: %d", signo);
264231911Strociny			return (-1);
265231910Strociny		}
266231909Strociny	}
267231909Strociny}
268231909Strociny
269231909Strocinystatic void
27082547Smikeusage(void)
27182547Smike{
272129983Sphk	(void)fprintf(stderr,
273255521Sjmg	    "usage: daemon [-cfr] [-p child_pidfile] [-P supervisor_pidfile] "
274255521Sjmg	    "[-u user]\n              command arguments ...\n");
27582547Smike	exit(1);
27682547Smike}
277