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