daemon.c revision 231912
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 231912 2012-02-19 10:38:55Z 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: 220231911Strociny return (terminate); 221231910Strociny case SIGTERM: 222231911Strociny terminate = 1; 223231910Strociny if (kill(pid, signo) == -1) { 224231910Strociny warn("kill"); 225231911Strociny return (-1); 226231910Strociny } 227231910Strociny continue; 228231910Strociny default: 229231910Strociny warnx("sigwaitinfo: invalid signal: %d", signo); 230231911Strociny return (-1); 231231910Strociny } 232231909Strociny } 233231909Strociny} 234231909Strociny 235231909Strocinystatic void 23682547Smikeusage(void) 23782547Smike{ 238129983Sphk (void)fprintf(stderr, 239167700Strhodes "usage: daemon [-cf] [-p pidfile] [-u user] command " 240167356Strhodes "arguments ...\n"); 24182547Smike exit(1); 24282547Smike} 243