1bd06a3ecSMike Barcroft /*- 2bd06a3ecSMike Barcroft * Copyright (c) 1999 Berkeley Software Design, Inc. All rights reserved. 3bd06a3ecSMike Barcroft * 4bd06a3ecSMike Barcroft * Redistribution and use in source and binary forms, with or without 5bd06a3ecSMike Barcroft * modification, are permitted provided that the following conditions 6bd06a3ecSMike Barcroft * are met: 7bd06a3ecSMike Barcroft * 1. Redistributions of source code must retain the above copyright 8bd06a3ecSMike Barcroft * notice, this list of conditions and the following disclaimer. 9bd06a3ecSMike Barcroft * 2. Redistributions in binary form must reproduce the above copyright 10bd06a3ecSMike Barcroft * notice, this list of conditions and the following disclaimer in the 11bd06a3ecSMike Barcroft * documentation and/or other materials provided with the distribution. 12bd06a3ecSMike Barcroft * 3. Berkeley Software Design Inc's name may not be used to endorse or 13bd06a3ecSMike Barcroft * promote products derived from this software without specific prior 14bd06a3ecSMike Barcroft * written permission. 15bd06a3ecSMike Barcroft * 16bd06a3ecSMike Barcroft * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN INC ``AS IS'' AND 17bd06a3ecSMike Barcroft * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18bd06a3ecSMike Barcroft * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19bd06a3ecSMike Barcroft * ARE DISCLAIMED. IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN INC BE LIABLE 20bd06a3ecSMike Barcroft * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21bd06a3ecSMike Barcroft * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22bd06a3ecSMike Barcroft * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23bd06a3ecSMike Barcroft * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24bd06a3ecSMike Barcroft * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25bd06a3ecSMike Barcroft * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26bd06a3ecSMike Barcroft * SUCH DAMAGE. 27bd06a3ecSMike Barcroft * 28bd06a3ecSMike Barcroft * From BSDI: daemon.c,v 1.2 1996/08/15 01:11:09 jch Exp 29bd06a3ecSMike Barcroft */ 30bd06a3ecSMike Barcroft 3154ede02dSPhilippe Charnier #include <sys/cdefs.h> 3254ede02dSPhilippe Charnier __FBSDID("$FreeBSD$"); 3354ede02dSPhilippe Charnier 34c6262cb6SPawel Jakub Dawidek #include <sys/param.h> 3553c49998SMikolaj Golub #include <sys/mman.h> 362ad43027SMikolaj Golub #include <sys/wait.h> 37bd06a3ecSMike Barcroft 38*53d49b37SJilles Tjoelker #include <fcntl.h> 39bd06a3ecSMike Barcroft #include <err.h> 40846be7bdSPoul-Henning Kamp #include <errno.h> 41c6262cb6SPawel Jakub Dawidek #include <libutil.h> 42e6d4b388STom Rhodes #include <login_cap.h> 43195fc497SMikolaj Golub #include <pwd.h> 44195fc497SMikolaj Golub #include <signal.h> 45bd06a3ecSMike Barcroft #include <stdio.h> 46bd06a3ecSMike Barcroft #include <stdlib.h> 47bd06a3ecSMike Barcroft #include <unistd.h> 48*53d49b37SJilles Tjoelker #include <string.h> 49*53d49b37SJilles Tjoelker #include <strings.h> 50*53d49b37SJilles Tjoelker #define SYSLOG_NAMES 51*53d49b37SJilles Tjoelker #include <syslog.h> 52*53d49b37SJilles Tjoelker #include <time.h> 53*53d49b37SJilles Tjoelker #include <assert.h> 54bd06a3ecSMike Barcroft 55*53d49b37SJilles Tjoelker #define LBUF_SIZE 4096 56*53d49b37SJilles Tjoelker 57*53d49b37SJilles Tjoelker struct log_params { 58*53d49b37SJilles Tjoelker int dosyslog; 59*53d49b37SJilles Tjoelker int logpri; 60*53d49b37SJilles Tjoelker int noclose; 61*53d49b37SJilles Tjoelker int outfd; 62*53d49b37SJilles Tjoelker }; 63*53d49b37SJilles Tjoelker 64e6d4b388STom Rhodes static void restrict_process(const char *); 65*53d49b37SJilles Tjoelker static void handle_term(int); 66*53d49b37SJilles Tjoelker static void handle_chld(int); 67*53d49b37SJilles Tjoelker static int listen_child(int, struct log_params *); 68*53d49b37SJilles Tjoelker static int get_log_mapping(const char *, const CODE *); 69*53d49b37SJilles Tjoelker static void open_pid_files(const char *, const char *, struct pidfh **, 70*53d49b37SJilles Tjoelker struct pidfh **); 71*53d49b37SJilles Tjoelker static void do_output(const unsigned char *, size_t, struct log_params *); 72*53d49b37SJilles Tjoelker static void daemon_sleep(time_t, long); 73bd06a3ecSMike Barcroft static void usage(void); 74bd06a3ecSMike Barcroft 75*53d49b37SJilles Tjoelker static volatile sig_atomic_t terminate = 0, child_gone = 0, pid = 0; 76*53d49b37SJilles Tjoelker 77bd06a3ecSMike Barcroft int 78bd06a3ecSMike Barcroft main(int argc, char *argv[]) 79bd06a3ecSMike Barcroft { 80*53d49b37SJilles Tjoelker const char *pidfile, *ppidfile, *title, *user, *outfn, *logtag; 81*53d49b37SJilles Tjoelker int ch, nochdir, noclose, restart, dosyslog, child_eof; 82*53d49b37SJilles Tjoelker sigset_t mask_susp, mask_orig, mask_read, mask_term; 83*53d49b37SJilles Tjoelker struct log_params logpar; 84*53d49b37SJilles Tjoelker int pfd[2] = { -1, -1 }, outfd = -1; 85*53d49b37SJilles Tjoelker int stdmask, logpri, logfac; 8632b17786SJohn-Mark Gurney struct pidfh *ppfh, *pfh; 87*53d49b37SJilles Tjoelker char *p; 88bd06a3ecSMike Barcroft 89*53d49b37SJilles Tjoelker memset(&logpar, 0, sizeof(logpar)); 90*53d49b37SJilles Tjoelker stdmask = STDOUT_FILENO | STDERR_FILENO; 91*53d49b37SJilles Tjoelker ppidfile = pidfile = user = NULL; 92bd06a3ecSMike Barcroft nochdir = noclose = 1; 93*53d49b37SJilles Tjoelker logpri = LOG_NOTICE; 94*53d49b37SJilles Tjoelker logfac = LOG_DAEMON; 95*53d49b37SJilles Tjoelker logtag = "daemon"; 96b6193c24SMikolaj Golub restart = 0; 97*53d49b37SJilles Tjoelker dosyslog = 0; 98*53d49b37SJilles Tjoelker outfn = NULL; 99*53d49b37SJilles Tjoelker title = NULL; 100*53d49b37SJilles Tjoelker while ((ch = getopt(argc, argv, "cfSp:P:ru:o:s:l:t:l:m:T:")) != -1) { 101bd06a3ecSMike Barcroft switch (ch) { 102bd06a3ecSMike Barcroft case 'c': 103bd06a3ecSMike Barcroft nochdir = 0; 104bd06a3ecSMike Barcroft break; 105bd06a3ecSMike Barcroft case 'f': 106bd06a3ecSMike Barcroft noclose = 0; 107bd06a3ecSMike Barcroft break; 108*53d49b37SJilles Tjoelker case 'l': 109*53d49b37SJilles Tjoelker logfac = get_log_mapping(optarg, facilitynames); 110*53d49b37SJilles Tjoelker if (logfac == -1) 111*53d49b37SJilles Tjoelker errx(5, "unrecognized syslog facility"); 112*53d49b37SJilles Tjoelker dosyslog = 1; 113*53d49b37SJilles Tjoelker break; 114*53d49b37SJilles Tjoelker case 'm': 115*53d49b37SJilles Tjoelker stdmask = strtol(optarg, &p, 10); 116*53d49b37SJilles Tjoelker if (p == optarg || stdmask < 0 || stdmask > 3) 117*53d49b37SJilles Tjoelker errx(6, "unrecognized listening mask"); 118*53d49b37SJilles Tjoelker break; 119*53d49b37SJilles Tjoelker case 'o': 120*53d49b37SJilles Tjoelker outfn = optarg; 121*53d49b37SJilles Tjoelker break; 122846be7bdSPoul-Henning Kamp case 'p': 123846be7bdSPoul-Henning Kamp pidfile = optarg; 124846be7bdSPoul-Henning Kamp break; 12532b17786SJohn-Mark Gurney case 'P': 12632b17786SJohn-Mark Gurney ppidfile = optarg; 12732b17786SJohn-Mark Gurney break; 128b6193c24SMikolaj Golub case 'r': 129b6193c24SMikolaj Golub restart = 1; 130b6193c24SMikolaj Golub break; 131*53d49b37SJilles Tjoelker case 's': 132*53d49b37SJilles Tjoelker logpri = get_log_mapping(optarg, prioritynames); 133*53d49b37SJilles Tjoelker if (logpri == -1) 134*53d49b37SJilles Tjoelker errx(4, "unrecognized syslog priority"); 135*53d49b37SJilles Tjoelker dosyslog = 1; 136*53d49b37SJilles Tjoelker break; 137*53d49b37SJilles Tjoelker case 'S': 138*53d49b37SJilles Tjoelker dosyslog = 1; 139*53d49b37SJilles Tjoelker break; 140112bfcf5SConrad Meyer case 't': 141112bfcf5SConrad Meyer title = optarg; 142112bfcf5SConrad Meyer break; 143*53d49b37SJilles Tjoelker case 'T': 144*53d49b37SJilles Tjoelker logtag = optarg; 145*53d49b37SJilles Tjoelker dosyslog = 1; 146*53d49b37SJilles Tjoelker break; 147e6d4b388STom Rhodes case 'u': 148e6d4b388STom Rhodes user = optarg; 149e6d4b388STom Rhodes break; 150bd06a3ecSMike Barcroft default: 151bd06a3ecSMike Barcroft usage(); 152bd06a3ecSMike Barcroft } 153bd06a3ecSMike Barcroft } 154bd06a3ecSMike Barcroft argc -= optind; 155bd06a3ecSMike Barcroft argv += optind; 156bd06a3ecSMike Barcroft 157bd06a3ecSMike Barcroft if (argc == 0) 158bd06a3ecSMike Barcroft usage(); 15912d7249eSTom Rhodes 160*53d49b37SJilles Tjoelker if (!title) 161*53d49b37SJilles Tjoelker title = argv[0]; 162*53d49b37SJilles Tjoelker 163*53d49b37SJilles Tjoelker if (outfn) { 164*53d49b37SJilles Tjoelker outfd = open(outfn, O_CREAT | O_WRONLY | O_APPEND | O_CLOEXEC, 0600); 165*53d49b37SJilles Tjoelker if (outfd == -1) 166*53d49b37SJilles Tjoelker err(7, "open"); 167*53d49b37SJilles Tjoelker } 168*53d49b37SJilles Tjoelker 169*53d49b37SJilles Tjoelker if (dosyslog) 170*53d49b37SJilles Tjoelker openlog(logtag, LOG_PID | LOG_NDELAY, logfac); 171*53d49b37SJilles Tjoelker 17232b17786SJohn-Mark Gurney ppfh = pfh = NULL; 173846be7bdSPoul-Henning Kamp /* 174846be7bdSPoul-Henning Kamp * Try to open the pidfile before calling daemon(3), 175846be7bdSPoul-Henning Kamp * to be able to report the error intelligently 176846be7bdSPoul-Henning Kamp */ 177*53d49b37SJilles Tjoelker open_pid_files(pidfile, ppidfile, &pfh, &ppfh); 1789da0ef13SMikolaj Golub if (daemon(nochdir, noclose) == -1) { 1799da0ef13SMikolaj Golub warn("daemon"); 1809da0ef13SMikolaj Golub goto exit; 1819da0ef13SMikolaj Golub } 1829da0ef13SMikolaj Golub /* Write out parent pidfile if needed. */ 1839da0ef13SMikolaj Golub pidfile_write(ppfh); 184195fc497SMikolaj Golub /* 185b6193c24SMikolaj Golub * If the pidfile or restart option is specified the daemon 186b6193c24SMikolaj Golub * executes the command in a forked process and wait on child 187b6193c24SMikolaj Golub * exit to remove the pidfile or restart the command. Normally 188b6193c24SMikolaj Golub * we don't want the monitoring daemon to be terminated 189b6193c24SMikolaj Golub * leaving the running process and the stale pidfile, so we 190b6193c24SMikolaj Golub * catch SIGTERM and forward it to the children expecting to 191*53d49b37SJilles Tjoelker * get SIGCHLD eventually. We also must fork() to obtain a 192*53d49b37SJilles Tjoelker * readable pipe with the child for writing to a log file 193*53d49b37SJilles Tjoelker * and syslog. 194195fc497SMikolaj Golub */ 195195fc497SMikolaj Golub pid = -1; 196*53d49b37SJilles Tjoelker if (pidfile || ppidfile || restart || outfd != -1 || dosyslog) { 197*53d49b37SJilles Tjoelker struct sigaction act_term, act_chld; 198*53d49b37SJilles Tjoelker 199*53d49b37SJilles Tjoelker /* Avoid PID racing with SIGCHLD and SIGTERM. */ 200*53d49b37SJilles Tjoelker memset(&act_term, 0, sizeof(act_term)); 201*53d49b37SJilles Tjoelker act_term.sa_handler = handle_term; 202*53d49b37SJilles Tjoelker sigemptyset(&act_term.sa_mask); 203*53d49b37SJilles Tjoelker sigaddset(&act_term.sa_mask, SIGCHLD); 204*53d49b37SJilles Tjoelker 205*53d49b37SJilles Tjoelker memset(&act_chld, 0, sizeof(act_chld)); 206*53d49b37SJilles Tjoelker act_chld.sa_handler = handle_chld; 207*53d49b37SJilles Tjoelker sigemptyset(&act_chld.sa_mask); 208*53d49b37SJilles Tjoelker sigaddset(&act_chld.sa_mask, SIGTERM); 209*53d49b37SJilles Tjoelker 210*53d49b37SJilles Tjoelker /* Block these when avoiding racing before sigsuspend(). */ 211*53d49b37SJilles Tjoelker sigemptyset(&mask_susp); 212*53d49b37SJilles Tjoelker sigaddset(&mask_susp, SIGTERM); 213*53d49b37SJilles Tjoelker sigaddset(&mask_susp, SIGCHLD); 214*53d49b37SJilles Tjoelker /* Block SIGTERM when we lack a valid child PID. */ 215*53d49b37SJilles Tjoelker sigemptyset(&mask_term); 216*53d49b37SJilles Tjoelker sigaddset(&mask_term, SIGTERM); 2172ad43027SMikolaj Golub /* 218*53d49b37SJilles Tjoelker * When reading, we wish to avoid SIGCHLD. SIGTERM 219*53d49b37SJilles Tjoelker * has to be caught, otherwise we'll be stuck until 220*53d49b37SJilles Tjoelker * the read() returns - if it returns. 221195fc497SMikolaj Golub */ 222*53d49b37SJilles Tjoelker sigemptyset(&mask_read); 223*53d49b37SJilles Tjoelker sigaddset(&mask_read, SIGCHLD); 224*53d49b37SJilles Tjoelker /* Block SIGTERM to avoid racing until we have forked. */ 225*53d49b37SJilles Tjoelker if (sigprocmask(SIG_BLOCK, &mask_term, &mask_orig)) { 2269da0ef13SMikolaj Golub warn("sigprocmask"); 2279da0ef13SMikolaj Golub goto exit; 2289da0ef13SMikolaj Golub } 229*53d49b37SJilles Tjoelker if (sigaction(SIGTERM, &act_term, NULL) == -1) { 230*53d49b37SJilles Tjoelker warn("sigaction"); 231*53d49b37SJilles Tjoelker goto exit; 232*53d49b37SJilles Tjoelker } 233*53d49b37SJilles Tjoelker if (sigaction(SIGCHLD, &act_chld, NULL) == -1) { 234*53d49b37SJilles Tjoelker warn("sigaction"); 235*53d49b37SJilles Tjoelker goto exit; 236*53d49b37SJilles Tjoelker } 23753c49998SMikolaj Golub /* 23853c49998SMikolaj Golub * Try to protect against pageout kill. Ignore the 23953c49998SMikolaj Golub * error, madvise(2) will fail only if a process does 24053c49998SMikolaj Golub * not have superuser privileges. 24153c49998SMikolaj Golub */ 24253c49998SMikolaj Golub (void)madvise(NULL, 0, MADV_PROTECT); 243*53d49b37SJilles Tjoelker logpar.outfd = outfd; 244*53d49b37SJilles Tjoelker logpar.dosyslog = dosyslog; 245*53d49b37SJilles Tjoelker logpar.logpri = logpri; 246*53d49b37SJilles Tjoelker logpar.noclose = noclose; 247b6193c24SMikolaj Golub restart: 248*53d49b37SJilles Tjoelker if (pipe(pfd)) 249*53d49b37SJilles Tjoelker err(1, "pipe"); 250195fc497SMikolaj Golub /* 251*53d49b37SJilles Tjoelker * Spawn a child to exec the command. 2522ad43027SMikolaj Golub */ 253*53d49b37SJilles Tjoelker child_gone = 0; 2542ad43027SMikolaj Golub pid = fork(); 2552ad43027SMikolaj Golub if (pid == -1) { 2569da0ef13SMikolaj Golub warn("fork"); 2579da0ef13SMikolaj Golub goto exit; 258*53d49b37SJilles Tjoelker } else if (pid > 0) { 259*53d49b37SJilles Tjoelker /* 260*53d49b37SJilles Tjoelker * Unblock SIGTERM after we know we have a valid 261*53d49b37SJilles Tjoelker * child PID to signal. 262*53d49b37SJilles Tjoelker */ 263*53d49b37SJilles Tjoelker if (sigprocmask(SIG_UNBLOCK, &mask_term, NULL)) { 264*53d49b37SJilles Tjoelker warn("sigprocmask"); 265*53d49b37SJilles Tjoelker goto exit; 266*53d49b37SJilles Tjoelker } 267*53d49b37SJilles Tjoelker close(pfd[1]); 268*53d49b37SJilles Tjoelker pfd[1] = -1; 2692ad43027SMikolaj Golub } 2702ad43027SMikolaj Golub } 271195fc497SMikolaj Golub if (pid <= 0) { 2722ad43027SMikolaj Golub /* Now that we are the child, write out the pid. */ 273c6262cb6SPawel Jakub Dawidek pidfile_write(pfh); 274846be7bdSPoul-Henning Kamp 2752ad43027SMikolaj Golub if (user != NULL) 2762ad43027SMikolaj Golub restrict_process(user); 277*53d49b37SJilles Tjoelker /* 278*53d49b37SJilles Tjoelker * When forking, the child gets the original sigmask, 279*53d49b37SJilles Tjoelker * and dup'd pipes. 280*53d49b37SJilles Tjoelker */ 281*53d49b37SJilles Tjoelker if (pid == 0) { 282*53d49b37SJilles Tjoelker close(pfd[0]); 283*53d49b37SJilles Tjoelker if (sigprocmask(SIG_SETMASK, &mask_orig, NULL)) 284*53d49b37SJilles Tjoelker err(1, "sigprogmask"); 285*53d49b37SJilles Tjoelker if (stdmask & STDERR_FILENO) { 286*53d49b37SJilles Tjoelker if (dup2(pfd[1], STDERR_FILENO) == -1) 287*53d49b37SJilles Tjoelker err(1, "dup2"); 288*53d49b37SJilles Tjoelker } 289*53d49b37SJilles Tjoelker if (stdmask & STDOUT_FILENO) { 290*53d49b37SJilles Tjoelker if (dup2(pfd[1], STDOUT_FILENO) == -1) 291*53d49b37SJilles Tjoelker err(1, "dup2"); 292*53d49b37SJilles Tjoelker } 293*53d49b37SJilles Tjoelker if (pfd[1] != STDERR_FILENO && 294*53d49b37SJilles Tjoelker pfd[1] != STDOUT_FILENO) 295*53d49b37SJilles Tjoelker close(pfd[1]); 296*53d49b37SJilles Tjoelker } 297bd06a3ecSMike Barcroft execvp(argv[0], argv); 298846be7bdSPoul-Henning Kamp /* 2992ad43027SMikolaj Golub * execvp() failed -- report the error. The child is 3002ad43027SMikolaj Golub * now running, so the exit status doesn't matter. 301846be7bdSPoul-Henning Kamp */ 3022ad43027SMikolaj Golub err(1, "%s", argv[0]); 3032ad43027SMikolaj Golub } 304*53d49b37SJilles Tjoelker setproctitle("%s[%d]", title, (int)pid); 305*53d49b37SJilles Tjoelker /* 306*53d49b37SJilles Tjoelker * As we have closed the write end of pipe for parent process, 307*53d49b37SJilles Tjoelker * we might detect the child's exit by reading EOF. The child 308*53d49b37SJilles Tjoelker * might have closed its stdout and stderr, so we must wait for 309*53d49b37SJilles Tjoelker * the SIGCHLD to ensure that the process is actually gone. 310*53d49b37SJilles Tjoelker */ 311*53d49b37SJilles Tjoelker child_eof = 0; 312*53d49b37SJilles Tjoelker for (;;) { 313*53d49b37SJilles Tjoelker /* 314*53d49b37SJilles Tjoelker * We block SIGCHLD when listening, but SIGTERM we accept 315*53d49b37SJilles Tjoelker * so the read() won't block if we wish to depart. 316*53d49b37SJilles Tjoelker * 317*53d49b37SJilles Tjoelker * Upon receiving SIGTERM, we have several options after 318*53d49b37SJilles Tjoelker * sending the SIGTERM to our child: 319*53d49b37SJilles Tjoelker * - read until EOF 320*53d49b37SJilles Tjoelker * - read until EOF but only for a while 321*53d49b37SJilles Tjoelker * - bail immediately 322*53d49b37SJilles Tjoelker * 323*53d49b37SJilles Tjoelker * We go for the third, as otherwise we have no guarantee 324*53d49b37SJilles Tjoelker * that we won't block indefinitely if the child refuses 325*53d49b37SJilles Tjoelker * to depart. To handle the second option, a different 326*53d49b37SJilles Tjoelker * approach would be needed (procctl()?) 327*53d49b37SJilles Tjoelker */ 328*53d49b37SJilles Tjoelker if (child_gone && child_eof) { 329*53d49b37SJilles Tjoelker break; 330*53d49b37SJilles Tjoelker } else if (terminate) { 331*53d49b37SJilles Tjoelker goto exit; 332*53d49b37SJilles Tjoelker } else if (!child_eof) { 333*53d49b37SJilles Tjoelker if (sigprocmask(SIG_BLOCK, &mask_read, NULL)) { 334*53d49b37SJilles Tjoelker warn("sigprocmask"); 335*53d49b37SJilles Tjoelker goto exit; 336*53d49b37SJilles Tjoelker } 337*53d49b37SJilles Tjoelker child_eof = !listen_child(pfd[0], &logpar); 338*53d49b37SJilles Tjoelker if (sigprocmask(SIG_UNBLOCK, &mask_read, NULL)) { 339*53d49b37SJilles Tjoelker warn("sigprocmask"); 340*53d49b37SJilles Tjoelker goto exit; 341*53d49b37SJilles Tjoelker } 342*53d49b37SJilles Tjoelker } else { 343*53d49b37SJilles Tjoelker if (sigprocmask(SIG_BLOCK, &mask_susp, NULL)) { 344*53d49b37SJilles Tjoelker warn("sigprocmask"); 345*53d49b37SJilles Tjoelker goto exit; 346*53d49b37SJilles Tjoelker } 347*53d49b37SJilles Tjoelker while (!terminate && !child_gone) 348*53d49b37SJilles Tjoelker sigsuspend(&mask_orig); 349*53d49b37SJilles Tjoelker if (sigprocmask(SIG_UNBLOCK, &mask_susp, NULL)) { 350*53d49b37SJilles Tjoelker warn("sigprocmask"); 351*53d49b37SJilles Tjoelker goto exit; 352*53d49b37SJilles Tjoelker } 353*53d49b37SJilles Tjoelker } 354*53d49b37SJilles Tjoelker } 355*53d49b37SJilles Tjoelker if (sigprocmask(SIG_BLOCK, &mask_term, NULL)) { 356*53d49b37SJilles Tjoelker warn("sigprocmask"); 357*53d49b37SJilles Tjoelker goto exit; 358*53d49b37SJilles Tjoelker } 359*53d49b37SJilles Tjoelker if (restart && !terminate) { 360*53d49b37SJilles Tjoelker daemon_sleep(1, 0); 361*53d49b37SJilles Tjoelker close(pfd[0]); 362*53d49b37SJilles Tjoelker pfd[0] = -1; 363b6193c24SMikolaj Golub goto restart; 364b6193c24SMikolaj Golub } 3659da0ef13SMikolaj Golub exit: 366*53d49b37SJilles Tjoelker close(outfd); 367*53d49b37SJilles Tjoelker close(pfd[0]); 368*53d49b37SJilles Tjoelker close(pfd[1]); 369*53d49b37SJilles Tjoelker if (dosyslog) 370*53d49b37SJilles Tjoelker closelog(); 371c6262cb6SPawel Jakub Dawidek pidfile_remove(pfh); 37232b17786SJohn-Mark Gurney pidfile_remove(ppfh); 3739da0ef13SMikolaj Golub exit(1); /* If daemon(3) succeeded exit status does not matter. */ 374bd06a3ecSMike Barcroft } 375bd06a3ecSMike Barcroft 376bd06a3ecSMike Barcroft static void 377*53d49b37SJilles Tjoelker daemon_sleep(time_t secs, long nsecs) 378195fc497SMikolaj Golub { 379*53d49b37SJilles Tjoelker struct timespec ts = { secs, nsecs }; 380*53d49b37SJilles Tjoelker while (nanosleep(&ts, &ts) == -1) { 381*53d49b37SJilles Tjoelker if (errno != EINTR) 382*53d49b37SJilles Tjoelker err(1, "nanosleep"); 383*53d49b37SJilles Tjoelker } 384*53d49b37SJilles Tjoelker } 385*53d49b37SJilles Tjoelker 386*53d49b37SJilles Tjoelker static void 387*53d49b37SJilles Tjoelker open_pid_files(const char *pidfile, const char *ppidfile, 388*53d49b37SJilles Tjoelker struct pidfh **pfh, struct pidfh **ppfh) 389*53d49b37SJilles Tjoelker { 390*53d49b37SJilles Tjoelker pid_t fpid; 391*53d49b37SJilles Tjoelker int serrno; 392*53d49b37SJilles Tjoelker 393*53d49b37SJilles Tjoelker if (pidfile) { 394*53d49b37SJilles Tjoelker *pfh = pidfile_open(pidfile, 0600, &fpid); 395*53d49b37SJilles Tjoelker if (*pfh == NULL) { 396*53d49b37SJilles Tjoelker if (errno == EEXIST) { 397*53d49b37SJilles Tjoelker errx(3, "process already running, pid: %d", 398*53d49b37SJilles Tjoelker fpid); 399*53d49b37SJilles Tjoelker } 400*53d49b37SJilles Tjoelker err(2, "pidfile ``%s''", pidfile); 401*53d49b37SJilles Tjoelker } 402*53d49b37SJilles Tjoelker } 403*53d49b37SJilles Tjoelker /* Do the same for the actual daemon process. */ 404*53d49b37SJilles Tjoelker if (ppidfile) { 405*53d49b37SJilles Tjoelker *ppfh = pidfile_open(ppidfile, 0600, &fpid); 406*53d49b37SJilles Tjoelker if (*ppfh == NULL) { 407*53d49b37SJilles Tjoelker serrno = errno; 408*53d49b37SJilles Tjoelker pidfile_remove(*pfh); 409*53d49b37SJilles Tjoelker errno = serrno; 410*53d49b37SJilles Tjoelker if (errno == EEXIST) { 411*53d49b37SJilles Tjoelker errx(3, "process already running, pid: %d", 412*53d49b37SJilles Tjoelker fpid); 413*53d49b37SJilles Tjoelker } 414*53d49b37SJilles Tjoelker err(2, "ppidfile ``%s''", ppidfile); 415*53d49b37SJilles Tjoelker } 416*53d49b37SJilles Tjoelker } 417*53d49b37SJilles Tjoelker } 418*53d49b37SJilles Tjoelker 419*53d49b37SJilles Tjoelker static int 420*53d49b37SJilles Tjoelker get_log_mapping(const char *str, const CODE *c) 421*53d49b37SJilles Tjoelker { 422*53d49b37SJilles Tjoelker const CODE *cp; 423*53d49b37SJilles Tjoelker for (cp = c; cp->c_name; cp++) 424*53d49b37SJilles Tjoelker if (strcmp(cp->c_name, str) == 0) 425*53d49b37SJilles Tjoelker return cp->c_val; 426*53d49b37SJilles Tjoelker return -1; 427195fc497SMikolaj Golub } 428195fc497SMikolaj Golub 429195fc497SMikolaj Golub static void 430e6d4b388STom Rhodes restrict_process(const char *user) 43112d7249eSTom Rhodes { 43212d7249eSTom Rhodes struct passwd *pw = NULL; 43312d7249eSTom Rhodes 434e6d4b388STom Rhodes pw = getpwnam(user); 435e6d4b388STom Rhodes if (pw == NULL) 436e6d4b388STom Rhodes errx(1, "unknown user: %s", user); 43712d7249eSTom Rhodes 438e6d4b388STom Rhodes if (setusercontext(NULL, pw, pw->pw_uid, LOGIN_SETALL) != 0) 439e6d4b388STom Rhodes errx(1, "failed to set user environment"); 44012d7249eSTom Rhodes } 44112d7249eSTom Rhodes 442*53d49b37SJilles Tjoelker /* 443*53d49b37SJilles Tjoelker * We try to collect whole lines terminated by '\n'. Otherwise we collect a 444*53d49b37SJilles Tjoelker * full buffer, and then output it. 445*53d49b37SJilles Tjoelker * 446*53d49b37SJilles Tjoelker * Return value of 0 is assumed to mean EOF or error, and 1 indicates to 447*53d49b37SJilles Tjoelker * continue reading. 448*53d49b37SJilles Tjoelker */ 449b6193c24SMikolaj Golub static int 450*53d49b37SJilles Tjoelker listen_child(int fd, struct log_params *logpar) 4512ad43027SMikolaj Golub { 452*53d49b37SJilles Tjoelker static unsigned char buf[LBUF_SIZE]; 453*53d49b37SJilles Tjoelker static size_t bytes_read = 0; 454*53d49b37SJilles Tjoelker int rv; 4552ad43027SMikolaj Golub 456*53d49b37SJilles Tjoelker assert(logpar); 457*53d49b37SJilles Tjoelker assert(bytes_read < LBUF_SIZE - 1); 458*53d49b37SJilles Tjoelker 459*53d49b37SJilles Tjoelker rv = read(fd, buf + bytes_read, LBUF_SIZE - bytes_read - 1); 460*53d49b37SJilles Tjoelker if (rv > 0) { 461*53d49b37SJilles Tjoelker unsigned char *cp; 462*53d49b37SJilles Tjoelker 463*53d49b37SJilles Tjoelker bytes_read += rv; 464*53d49b37SJilles Tjoelker assert(bytes_read <= LBUF_SIZE - 1); 465*53d49b37SJilles Tjoelker /* Always NUL-terminate just in case. */ 466*53d49b37SJilles Tjoelker buf[LBUF_SIZE - 1] = '\0'; 467*53d49b37SJilles Tjoelker /* 468*53d49b37SJilles Tjoelker * Chomp line by line until we run out of buffer. 469*53d49b37SJilles Tjoelker * This does not take NUL characters into account. 470*53d49b37SJilles Tjoelker */ 471*53d49b37SJilles Tjoelker while ((cp = memchr(buf, '\n', bytes_read)) != NULL) { 472*53d49b37SJilles Tjoelker size_t bytes_line = cp - buf + 1; 473*53d49b37SJilles Tjoelker assert(bytes_line <= bytes_read); 474*53d49b37SJilles Tjoelker do_output(buf, bytes_line, logpar); 475*53d49b37SJilles Tjoelker bytes_read -= bytes_line; 476*53d49b37SJilles Tjoelker memmove(buf, cp + 1, bytes_read); 477195fc497SMikolaj Golub } 478*53d49b37SJilles Tjoelker /* Wait until the buffer is full. */ 479*53d49b37SJilles Tjoelker if (bytes_read < LBUF_SIZE - 1) 480*53d49b37SJilles Tjoelker return 1; 481*53d49b37SJilles Tjoelker do_output(buf, bytes_read, logpar); 482*53d49b37SJilles Tjoelker bytes_read = 0; 483*53d49b37SJilles Tjoelker return 1; 484*53d49b37SJilles Tjoelker } else if (rv == -1) { 485*53d49b37SJilles Tjoelker /* EINTR should trigger another read. */ 486*53d49b37SJilles Tjoelker if (errno == EINTR) { 487*53d49b37SJilles Tjoelker return 1; 488*53d49b37SJilles Tjoelker } else { 489*53d49b37SJilles Tjoelker warn("read"); 490*53d49b37SJilles Tjoelker return 0; 491c60d51f9SMikolaj Golub } 492*53d49b37SJilles Tjoelker } 493*53d49b37SJilles Tjoelker /* Upon EOF, we have to flush what's left of the buffer. */ 494*53d49b37SJilles Tjoelker if (bytes_read > 0) { 495*53d49b37SJilles Tjoelker do_output(buf, bytes_read, logpar); 496*53d49b37SJilles Tjoelker bytes_read = 0; 497*53d49b37SJilles Tjoelker } 498*53d49b37SJilles Tjoelker return 0; 499*53d49b37SJilles Tjoelker } 500*53d49b37SJilles Tjoelker 501*53d49b37SJilles Tjoelker /* 502*53d49b37SJilles Tjoelker * The default behavior is to stay silent if the user wants to redirect 503*53d49b37SJilles Tjoelker * output to a file and/or syslog. If neither are provided, then we bounce 504*53d49b37SJilles Tjoelker * everything back to parent's stdout. 505*53d49b37SJilles Tjoelker */ 506*53d49b37SJilles Tjoelker static void 507*53d49b37SJilles Tjoelker do_output(const unsigned char *buf, size_t len, struct log_params *logpar) 508*53d49b37SJilles Tjoelker { 509*53d49b37SJilles Tjoelker assert(len <= LBUF_SIZE); 510*53d49b37SJilles Tjoelker assert(logpar); 511*53d49b37SJilles Tjoelker 512*53d49b37SJilles Tjoelker if (len < 1) 513*53d49b37SJilles Tjoelker return; 514*53d49b37SJilles Tjoelker if (logpar->dosyslog) 515*53d49b37SJilles Tjoelker syslog(logpar->logpri, "%.*s", (int)len, buf); 516*53d49b37SJilles Tjoelker if (logpar->outfd != -1) { 517*53d49b37SJilles Tjoelker if (write(logpar->outfd, buf, len) == -1) 518*53d49b37SJilles Tjoelker warn("write"); 519*53d49b37SJilles Tjoelker } 520*53d49b37SJilles Tjoelker if (logpar->noclose && !logpar->dosyslog && logpar->outfd == -1) 521*53d49b37SJilles Tjoelker printf("%.*s", (int)len, buf); 522*53d49b37SJilles Tjoelker } 523*53d49b37SJilles Tjoelker 524*53d49b37SJilles Tjoelker /* 525*53d49b37SJilles Tjoelker * We use the global PID acquired directly from fork. If there is no valid 526*53d49b37SJilles Tjoelker * child pid, the handler should be blocked and/or child_gone == 1. 527*53d49b37SJilles Tjoelker */ 528*53d49b37SJilles Tjoelker static void 529*53d49b37SJilles Tjoelker handle_term(int signo) 530*53d49b37SJilles Tjoelker { 531*53d49b37SJilles Tjoelker if (pid > 0 && !child_gone) 532*53d49b37SJilles Tjoelker kill(pid, signo); 533b6193c24SMikolaj Golub terminate = 1; 534195fc497SMikolaj Golub } 535*53d49b37SJilles Tjoelker 536*53d49b37SJilles Tjoelker static void 537*53d49b37SJilles Tjoelker handle_chld(int signo) 538*53d49b37SJilles Tjoelker { 539*53d49b37SJilles Tjoelker (void)signo; 540*53d49b37SJilles Tjoelker for (;;) { 541*53d49b37SJilles Tjoelker int rv = waitpid(-1, NULL, WNOHANG); 542*53d49b37SJilles Tjoelker if (pid == rv) { 543*53d49b37SJilles Tjoelker child_gone = 1; 544*53d49b37SJilles Tjoelker break; 545*53d49b37SJilles Tjoelker } else if (rv == -1 && errno != EINTR) { 546*53d49b37SJilles Tjoelker warn("waitpid"); 547*53d49b37SJilles Tjoelker return; 5482ad43027SMikolaj Golub } 5492ad43027SMikolaj Golub } 5502ad43027SMikolaj Golub } 5512ad43027SMikolaj Golub 5522ad43027SMikolaj Golub static void 553bd06a3ecSMike Barcroft usage(void) 554bd06a3ecSMike Barcroft { 555*53d49b37SJilles Tjoelker (void)fprintf(stderr, 556*53d49b37SJilles Tjoelker "usage: daemon [-cfrS] [-p child_pidfile] [-P supervisor_pidfile]\n" 557*53d49b37SJilles Tjoelker " [-u user] [-o output_file] [-t title]\n" 558*53d49b37SJilles Tjoelker " [-l syslog_facility] [-s syslog_priority]\n" 559*53d49b37SJilles Tjoelker " [-T syslog_tag] [-m output_mask]\n" 560*53d49b37SJilles Tjoelker "command arguments ...\n"); 561bd06a3ecSMike Barcroft exit(1); 562bd06a3ecSMike Barcroft } 563