1 /*- 2 * Copyright (c) 1999 Berkeley Software Design, Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 3. Berkeley Software Design Inc's name may not be used to endorse or 13 * promote products derived from this software without specific prior 14 * written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN INC ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN INC BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 * From BSDI: daemon.c,v 1.2 1996/08/15 01:11:09 jch Exp 29 */ 30 31 #include <sys/cdefs.h> 32 __FBSDID("$FreeBSD$"); 33 34 #include <sys/param.h> 35 #include <sys/mman.h> 36 #include <sys/wait.h> 37 38 #include <err.h> 39 #include <errno.h> 40 #include <libutil.h> 41 #include <login_cap.h> 42 #include <pwd.h> 43 #include <signal.h> 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <unistd.h> 47 48 static void dummy_sighandler(int); 49 static void restrict_process(const char *); 50 static int wait_child(pid_t pid, sigset_t *mask); 51 static void usage(void); 52 53 int 54 main(int argc, char *argv[]) 55 { 56 struct pidfh *ppfh, *pfh; 57 sigset_t mask, oldmask; 58 int ch, nochdir, noclose, restart, serrno; 59 const char *pidfile, *ppidfile, *title, *user; 60 pid_t otherpid, pid; 61 62 nochdir = noclose = 1; 63 restart = 0; 64 ppidfile = pidfile = title = user = NULL; 65 while ((ch = getopt(argc, argv, "cfp:P:rt:u:")) != -1) { 66 switch (ch) { 67 case 'c': 68 nochdir = 0; 69 break; 70 case 'f': 71 noclose = 0; 72 break; 73 case 'p': 74 pidfile = optarg; 75 break; 76 case 'P': 77 ppidfile = optarg; 78 break; 79 case 'r': 80 restart = 1; 81 break; 82 case 't': 83 title = optarg; 84 break; 85 case 'u': 86 user = optarg; 87 break; 88 default: 89 usage(); 90 } 91 } 92 argc -= optind; 93 argv += optind; 94 95 if (argc == 0) 96 usage(); 97 98 ppfh = pfh = NULL; 99 /* 100 * Try to open the pidfile before calling daemon(3), 101 * to be able to report the error intelligently 102 */ 103 if (pidfile != NULL) { 104 pfh = pidfile_open(pidfile, 0600, &otherpid); 105 if (pfh == NULL) { 106 if (errno == EEXIST) { 107 errx(3, "process already running, pid: %d", 108 otherpid); 109 } 110 err(2, "pidfile ``%s''", pidfile); 111 } 112 } 113 /* Do the same for actual daemon process. */ 114 if (ppidfile != NULL) { 115 ppfh = pidfile_open(ppidfile, 0600, &otherpid); 116 if (ppfh == NULL) { 117 serrno = errno; 118 pidfile_remove(pfh); 119 errno = serrno; 120 if (errno == EEXIST) { 121 errx(3, "process already running, pid: %d", 122 otherpid); 123 } 124 err(2, "ppidfile ``%s''", ppidfile); 125 } 126 } 127 128 if (daemon(nochdir, noclose) == -1) { 129 warn("daemon"); 130 goto exit; 131 } 132 /* Write out parent pidfile if needed. */ 133 pidfile_write(ppfh); 134 135 /* 136 * If the pidfile or restart option is specified the daemon 137 * executes the command in a forked process and wait on child 138 * exit to remove the pidfile or restart the command. Normally 139 * we don't want the monitoring daemon to be terminated 140 * leaving the running process and the stale pidfile, so we 141 * catch SIGTERM and forward it to the children expecting to 142 * get SIGCHLD eventually. 143 */ 144 pid = -1; 145 if (pidfile != NULL || ppidfile != NULL || restart) { 146 /* 147 * Restore default action for SIGTERM in case the 148 * parent process decided to ignore it. 149 */ 150 if (signal(SIGTERM, SIG_DFL) == SIG_ERR) { 151 warn("signal"); 152 goto exit; 153 } 154 /* 155 * Because SIGCHLD is ignored by default, setup dummy handler 156 * for it, so we can mask it. 157 */ 158 if (signal(SIGCHLD, dummy_sighandler) == SIG_ERR) { 159 warn("signal"); 160 goto exit; 161 } 162 /* 163 * Block interesting signals. 164 */ 165 sigemptyset(&mask); 166 sigaddset(&mask, SIGTERM); 167 sigaddset(&mask, SIGCHLD); 168 if (sigprocmask(SIG_SETMASK, &mask, &oldmask) == -1) { 169 warn("sigprocmask"); 170 goto exit; 171 } 172 /* 173 * Try to protect against pageout kill. Ignore the 174 * error, madvise(2) will fail only if a process does 175 * not have superuser privileges. 176 */ 177 (void)madvise(NULL, 0, MADV_PROTECT); 178 restart: 179 /* 180 * Spawn a child to exec the command, so in the parent 181 * we could wait for it to exit and remove pidfile. 182 */ 183 pid = fork(); 184 if (pid == -1) { 185 warn("fork"); 186 goto exit; 187 } 188 } 189 if (pid <= 0) { 190 if (pid == 0) { 191 /* Restore old sigmask in the child. */ 192 if (sigprocmask(SIG_SETMASK, &oldmask, NULL) == -1) 193 err(1, "sigprocmask"); 194 } 195 /* Now that we are the child, write out the pid. */ 196 pidfile_write(pfh); 197 198 if (user != NULL) 199 restrict_process(user); 200 201 execvp(argv[0], argv); 202 203 /* 204 * execvp() failed -- report the error. The child is 205 * now running, so the exit status doesn't matter. 206 */ 207 err(1, "%s", argv[0]); 208 } 209 210 if (title != NULL) 211 setproctitle("%s[%d]", title, pid); 212 else 213 setproctitle("%s[%d]", argv[0], pid); 214 if (wait_child(pid, &mask) == 0 && restart) { 215 sleep(1); 216 goto restart; 217 } 218 exit: 219 pidfile_remove(pfh); 220 pidfile_remove(ppfh); 221 exit(1); /* If daemon(3) succeeded exit status does not matter. */ 222 } 223 224 static void 225 dummy_sighandler(int sig __unused) 226 { 227 /* Nothing to do. */ 228 } 229 230 static void 231 restrict_process(const char *user) 232 { 233 struct passwd *pw = NULL; 234 235 pw = getpwnam(user); 236 if (pw == NULL) 237 errx(1, "unknown user: %s", user); 238 239 if (setusercontext(NULL, pw, pw->pw_uid, LOGIN_SETALL) != 0) 240 errx(1, "failed to set user environment"); 241 } 242 243 static int 244 wait_child(pid_t pid, sigset_t *mask) 245 { 246 int terminate, signo; 247 248 terminate = 0; 249 for (;;) { 250 if (sigwait(mask, &signo) == -1) { 251 warn("sigwaitinfo"); 252 return (-1); 253 } 254 switch (signo) { 255 case SIGCHLD: 256 if (waitpid(pid, NULL, WNOHANG) == -1) { 257 warn("waitpid"); 258 return (-1); 259 } 260 return (terminate); 261 case SIGTERM: 262 terminate = 1; 263 if (kill(pid, signo) == -1) { 264 warn("kill"); 265 return (-1); 266 } 267 continue; 268 default: 269 warnx("sigwaitinfo: invalid signal: %d", signo); 270 return (-1); 271 } 272 } 273 } 274 275 static void 276 usage(void) 277 { 278 (void)fprintf(stderr, "%s\n\t%s\n", 279 "usage: daemon [-cfr] [-p child_pidfile] [-P supervisor_pidfile]", 280 "[-t title] [-u user] command arguments ..."); 281 exit(1); 282 } 283