1bd06a3ecSMike Barcroft /*- 21de7b4b8SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause 31de7b4b8SPedro F. Giffuni * 4bd06a3ecSMike Barcroft * Copyright (c) 1999 Berkeley Software Design, Inc. All rights reserved. 5bd06a3ecSMike Barcroft * 6bd06a3ecSMike Barcroft * Redistribution and use in source and binary forms, with or without 7bd06a3ecSMike Barcroft * modification, are permitted provided that the following conditions 8bd06a3ecSMike Barcroft * are met: 9bd06a3ecSMike Barcroft * 1. Redistributions of source code must retain the above copyright 10bd06a3ecSMike Barcroft * notice, this list of conditions and the following disclaimer. 11bd06a3ecSMike Barcroft * 2. Redistributions in binary form must reproduce the above copyright 12bd06a3ecSMike Barcroft * notice, this list of conditions and the following disclaimer in the 13bd06a3ecSMike Barcroft * documentation and/or other materials provided with the distribution. 14bd06a3ecSMike Barcroft * 3. Berkeley Software Design Inc's name may not be used to endorse or 15bd06a3ecSMike Barcroft * promote products derived from this software without specific prior 16bd06a3ecSMike Barcroft * written permission. 17bd06a3ecSMike Barcroft * 18bd06a3ecSMike Barcroft * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN INC ``AS IS'' AND 19bd06a3ecSMike Barcroft * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20bd06a3ecSMike Barcroft * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21bd06a3ecSMike Barcroft * ARE DISCLAIMED. IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN INC BE LIABLE 22bd06a3ecSMike Barcroft * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23bd06a3ecSMike Barcroft * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24bd06a3ecSMike Barcroft * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25bd06a3ecSMike Barcroft * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26bd06a3ecSMike Barcroft * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27bd06a3ecSMike Barcroft * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28bd06a3ecSMike Barcroft * SUCH DAMAGE. 29bd06a3ecSMike Barcroft * 30bd06a3ecSMike Barcroft * From BSDI: daemon.c,v 1.2 1996/08/15 01:11:09 jch Exp 31bd06a3ecSMike Barcroft */ 32bd06a3ecSMike Barcroft 3354ede02dSPhilippe Charnier #include <sys/cdefs.h> 3454ede02dSPhilippe Charnier __FBSDID("$FreeBSD$"); 3554ede02dSPhilippe Charnier 36c6262cb6SPawel Jakub Dawidek #include <sys/param.h> 3753c49998SMikolaj Golub #include <sys/mman.h> 382ad43027SMikolaj Golub #include <sys/wait.h> 39bd06a3ecSMike Barcroft 4053d49b37SJilles Tjoelker #include <fcntl.h> 41bd06a3ecSMike Barcroft #include <err.h> 42846be7bdSPoul-Henning Kamp #include <errno.h> 43c6262cb6SPawel Jakub Dawidek #include <libutil.h> 44e6d4b388STom Rhodes #include <login_cap.h> 45195fc497SMikolaj Golub #include <pwd.h> 46195fc497SMikolaj Golub #include <signal.h> 47bd06a3ecSMike Barcroft #include <stdio.h> 48bd06a3ecSMike Barcroft #include <stdlib.h> 49bd06a3ecSMike Barcroft #include <unistd.h> 5053d49b37SJilles Tjoelker #include <string.h> 5153d49b37SJilles Tjoelker #include <strings.h> 5253d49b37SJilles Tjoelker #define SYSLOG_NAMES 5353d49b37SJilles Tjoelker #include <syslog.h> 5453d49b37SJilles Tjoelker #include <time.h> 5553d49b37SJilles Tjoelker #include <assert.h> 56bd06a3ecSMike Barcroft 5753d49b37SJilles Tjoelker #define LBUF_SIZE 4096 5853d49b37SJilles Tjoelker 5953d49b37SJilles Tjoelker struct log_params { 6053d49b37SJilles Tjoelker int dosyslog; 6153d49b37SJilles Tjoelker int logpri; 6253d49b37SJilles Tjoelker int noclose; 6353d49b37SJilles Tjoelker int outfd; 6453d49b37SJilles Tjoelker }; 6553d49b37SJilles Tjoelker 66e6d4b388STom Rhodes static void restrict_process(const char *); 6753d49b37SJilles Tjoelker static void handle_term(int); 6853d49b37SJilles Tjoelker static void handle_chld(int); 6953d49b37SJilles Tjoelker static int listen_child(int, struct log_params *); 7053d49b37SJilles Tjoelker static int get_log_mapping(const char *, const CODE *); 7153d49b37SJilles Tjoelker static void open_pid_files(const char *, const char *, struct pidfh **, 7253d49b37SJilles Tjoelker struct pidfh **); 7353d49b37SJilles Tjoelker static void do_output(const unsigned char *, size_t, struct log_params *); 7453d49b37SJilles Tjoelker static void daemon_sleep(time_t, long); 75bd06a3ecSMike Barcroft static void usage(void); 76bd06a3ecSMike Barcroft 7753d49b37SJilles Tjoelker static volatile sig_atomic_t terminate = 0, child_gone = 0, pid = 0; 7853d49b37SJilles Tjoelker 79bd06a3ecSMike Barcroft int 80bd06a3ecSMike Barcroft main(int argc, char *argv[]) 81bd06a3ecSMike Barcroft { 8253d49b37SJilles Tjoelker const char *pidfile, *ppidfile, *title, *user, *outfn, *logtag; 8353d49b37SJilles Tjoelker int ch, nochdir, noclose, restart, dosyslog, child_eof; 8453d49b37SJilles Tjoelker sigset_t mask_susp, mask_orig, mask_read, mask_term; 8553d49b37SJilles Tjoelker struct log_params logpar; 8653d49b37SJilles Tjoelker int pfd[2] = { -1, -1 }, outfd = -1; 8753d49b37SJilles Tjoelker int stdmask, logpri, logfac; 8832b17786SJohn-Mark Gurney struct pidfh *ppfh, *pfh; 8953d49b37SJilles Tjoelker char *p; 90bd06a3ecSMike Barcroft 9153d49b37SJilles Tjoelker memset(&logpar, 0, sizeof(logpar)); 9253d49b37SJilles Tjoelker stdmask = STDOUT_FILENO | STDERR_FILENO; 9353d49b37SJilles Tjoelker ppidfile = pidfile = user = NULL; 94bd06a3ecSMike Barcroft nochdir = noclose = 1; 9553d49b37SJilles Tjoelker logpri = LOG_NOTICE; 9653d49b37SJilles Tjoelker logfac = LOG_DAEMON; 9753d49b37SJilles Tjoelker logtag = "daemon"; 98b6193c24SMikolaj Golub restart = 0; 9953d49b37SJilles Tjoelker dosyslog = 0; 10053d49b37SJilles Tjoelker outfn = NULL; 10153d49b37SJilles Tjoelker title = NULL; 102*37820b87SIan Lepore while ((ch = getopt(argc, argv, "cfSp:P:ru:o:s:l:t:l:m:R:T:")) != -1) { 103bd06a3ecSMike Barcroft switch (ch) { 104bd06a3ecSMike Barcroft case 'c': 105bd06a3ecSMike Barcroft nochdir = 0; 106bd06a3ecSMike Barcroft break; 107bd06a3ecSMike Barcroft case 'f': 108bd06a3ecSMike Barcroft noclose = 0; 109bd06a3ecSMike Barcroft break; 11053d49b37SJilles Tjoelker case 'l': 11153d49b37SJilles Tjoelker logfac = get_log_mapping(optarg, facilitynames); 11253d49b37SJilles Tjoelker if (logfac == -1) 11353d49b37SJilles Tjoelker errx(5, "unrecognized syslog facility"); 11453d49b37SJilles Tjoelker dosyslog = 1; 11553d49b37SJilles Tjoelker break; 11653d49b37SJilles Tjoelker case 'm': 11753d49b37SJilles Tjoelker stdmask = strtol(optarg, &p, 10); 11853d49b37SJilles Tjoelker if (p == optarg || stdmask < 0 || stdmask > 3) 11953d49b37SJilles Tjoelker errx(6, "unrecognized listening mask"); 12053d49b37SJilles Tjoelker break; 12153d49b37SJilles Tjoelker case 'o': 12253d49b37SJilles Tjoelker outfn = optarg; 12353d49b37SJilles Tjoelker break; 124846be7bdSPoul-Henning Kamp case 'p': 125846be7bdSPoul-Henning Kamp pidfile = optarg; 126846be7bdSPoul-Henning Kamp break; 12732b17786SJohn-Mark Gurney case 'P': 12832b17786SJohn-Mark Gurney ppidfile = optarg; 12932b17786SJohn-Mark Gurney break; 130b6193c24SMikolaj Golub case 'r': 131b6193c24SMikolaj Golub restart = 1; 132b6193c24SMikolaj Golub break; 133*37820b87SIan Lepore case 'R': 134*37820b87SIan Lepore restart = strtol(optarg, &p, 0); 135*37820b87SIan Lepore if (p == optarg || restart < 1) 136*37820b87SIan Lepore errx(6, "invalid restart delay"); 137*37820b87SIan Lepore break; 13853d49b37SJilles Tjoelker case 's': 13953d49b37SJilles Tjoelker logpri = get_log_mapping(optarg, prioritynames); 14053d49b37SJilles Tjoelker if (logpri == -1) 14153d49b37SJilles Tjoelker errx(4, "unrecognized syslog priority"); 14253d49b37SJilles Tjoelker dosyslog = 1; 14353d49b37SJilles Tjoelker break; 14453d49b37SJilles Tjoelker case 'S': 14553d49b37SJilles Tjoelker dosyslog = 1; 14653d49b37SJilles Tjoelker break; 147112bfcf5SConrad Meyer case 't': 148112bfcf5SConrad Meyer title = optarg; 149112bfcf5SConrad Meyer break; 15053d49b37SJilles Tjoelker case 'T': 15153d49b37SJilles Tjoelker logtag = optarg; 15253d49b37SJilles Tjoelker dosyslog = 1; 15353d49b37SJilles Tjoelker break; 154e6d4b388STom Rhodes case 'u': 155e6d4b388STom Rhodes user = optarg; 156e6d4b388STom Rhodes break; 157bd06a3ecSMike Barcroft default: 158bd06a3ecSMike Barcroft usage(); 159bd06a3ecSMike Barcroft } 160bd06a3ecSMike Barcroft } 161bd06a3ecSMike Barcroft argc -= optind; 162bd06a3ecSMike Barcroft argv += optind; 163bd06a3ecSMike Barcroft 164bd06a3ecSMike Barcroft if (argc == 0) 165bd06a3ecSMike Barcroft usage(); 16612d7249eSTom Rhodes 16753d49b37SJilles Tjoelker if (!title) 16853d49b37SJilles Tjoelker title = argv[0]; 16953d49b37SJilles Tjoelker 17053d49b37SJilles Tjoelker if (outfn) { 17153d49b37SJilles Tjoelker outfd = open(outfn, O_CREAT | O_WRONLY | O_APPEND | O_CLOEXEC, 0600); 17253d49b37SJilles Tjoelker if (outfd == -1) 17353d49b37SJilles Tjoelker err(7, "open"); 17453d49b37SJilles Tjoelker } 17553d49b37SJilles Tjoelker 17653d49b37SJilles Tjoelker if (dosyslog) 17753d49b37SJilles Tjoelker openlog(logtag, LOG_PID | LOG_NDELAY, logfac); 17853d49b37SJilles Tjoelker 17932b17786SJohn-Mark Gurney ppfh = pfh = NULL; 180846be7bdSPoul-Henning Kamp /* 181846be7bdSPoul-Henning Kamp * Try to open the pidfile before calling daemon(3), 182846be7bdSPoul-Henning Kamp * to be able to report the error intelligently 183846be7bdSPoul-Henning Kamp */ 18453d49b37SJilles Tjoelker open_pid_files(pidfile, ppidfile, &pfh, &ppfh); 1859da0ef13SMikolaj Golub if (daemon(nochdir, noclose) == -1) { 1869da0ef13SMikolaj Golub warn("daemon"); 1879da0ef13SMikolaj Golub goto exit; 1889da0ef13SMikolaj Golub } 1899da0ef13SMikolaj Golub /* Write out parent pidfile if needed. */ 1909da0ef13SMikolaj Golub pidfile_write(ppfh); 191195fc497SMikolaj Golub /* 192b6193c24SMikolaj Golub * If the pidfile or restart option is specified the daemon 193b6193c24SMikolaj Golub * executes the command in a forked process and wait on child 194b6193c24SMikolaj Golub * exit to remove the pidfile or restart the command. Normally 195b6193c24SMikolaj Golub * we don't want the monitoring daemon to be terminated 196b6193c24SMikolaj Golub * leaving the running process and the stale pidfile, so we 197b6193c24SMikolaj Golub * catch SIGTERM and forward it to the children expecting to 19853d49b37SJilles Tjoelker * get SIGCHLD eventually. We also must fork() to obtain a 19953d49b37SJilles Tjoelker * readable pipe with the child for writing to a log file 20053d49b37SJilles Tjoelker * and syslog. 201195fc497SMikolaj Golub */ 202195fc497SMikolaj Golub pid = -1; 20353d49b37SJilles Tjoelker if (pidfile || ppidfile || restart || outfd != -1 || dosyslog) { 20453d49b37SJilles Tjoelker struct sigaction act_term, act_chld; 20553d49b37SJilles Tjoelker 20653d49b37SJilles Tjoelker /* Avoid PID racing with SIGCHLD and SIGTERM. */ 20753d49b37SJilles Tjoelker memset(&act_term, 0, sizeof(act_term)); 20853d49b37SJilles Tjoelker act_term.sa_handler = handle_term; 20953d49b37SJilles Tjoelker sigemptyset(&act_term.sa_mask); 21053d49b37SJilles Tjoelker sigaddset(&act_term.sa_mask, SIGCHLD); 21153d49b37SJilles Tjoelker 21253d49b37SJilles Tjoelker memset(&act_chld, 0, sizeof(act_chld)); 21353d49b37SJilles Tjoelker act_chld.sa_handler = handle_chld; 21453d49b37SJilles Tjoelker sigemptyset(&act_chld.sa_mask); 21553d49b37SJilles Tjoelker sigaddset(&act_chld.sa_mask, SIGTERM); 21653d49b37SJilles Tjoelker 21753d49b37SJilles Tjoelker /* Block these when avoiding racing before sigsuspend(). */ 21853d49b37SJilles Tjoelker sigemptyset(&mask_susp); 21953d49b37SJilles Tjoelker sigaddset(&mask_susp, SIGTERM); 22053d49b37SJilles Tjoelker sigaddset(&mask_susp, SIGCHLD); 22153d49b37SJilles Tjoelker /* Block SIGTERM when we lack a valid child PID. */ 22253d49b37SJilles Tjoelker sigemptyset(&mask_term); 22353d49b37SJilles Tjoelker sigaddset(&mask_term, SIGTERM); 2242ad43027SMikolaj Golub /* 22553d49b37SJilles Tjoelker * When reading, we wish to avoid SIGCHLD. SIGTERM 22653d49b37SJilles Tjoelker * has to be caught, otherwise we'll be stuck until 22753d49b37SJilles Tjoelker * the read() returns - if it returns. 228195fc497SMikolaj Golub */ 22953d49b37SJilles Tjoelker sigemptyset(&mask_read); 23053d49b37SJilles Tjoelker sigaddset(&mask_read, SIGCHLD); 23153d49b37SJilles Tjoelker /* Block SIGTERM to avoid racing until we have forked. */ 23253d49b37SJilles Tjoelker if (sigprocmask(SIG_BLOCK, &mask_term, &mask_orig)) { 2339da0ef13SMikolaj Golub warn("sigprocmask"); 2349da0ef13SMikolaj Golub goto exit; 2359da0ef13SMikolaj Golub } 23653d49b37SJilles Tjoelker if (sigaction(SIGTERM, &act_term, NULL) == -1) { 23753d49b37SJilles Tjoelker warn("sigaction"); 23853d49b37SJilles Tjoelker goto exit; 23953d49b37SJilles Tjoelker } 24053d49b37SJilles Tjoelker if (sigaction(SIGCHLD, &act_chld, NULL) == -1) { 24153d49b37SJilles Tjoelker warn("sigaction"); 24253d49b37SJilles Tjoelker goto exit; 24353d49b37SJilles Tjoelker } 24453c49998SMikolaj Golub /* 24553c49998SMikolaj Golub * Try to protect against pageout kill. Ignore the 24653c49998SMikolaj Golub * error, madvise(2) will fail only if a process does 24753c49998SMikolaj Golub * not have superuser privileges. 24853c49998SMikolaj Golub */ 24953c49998SMikolaj Golub (void)madvise(NULL, 0, MADV_PROTECT); 25053d49b37SJilles Tjoelker logpar.outfd = outfd; 25153d49b37SJilles Tjoelker logpar.dosyslog = dosyslog; 25253d49b37SJilles Tjoelker logpar.logpri = logpri; 25353d49b37SJilles Tjoelker logpar.noclose = noclose; 254b6193c24SMikolaj Golub restart: 25553d49b37SJilles Tjoelker if (pipe(pfd)) 25653d49b37SJilles Tjoelker err(1, "pipe"); 257195fc497SMikolaj Golub /* 25853d49b37SJilles Tjoelker * Spawn a child to exec the command. 2592ad43027SMikolaj Golub */ 26053d49b37SJilles Tjoelker child_gone = 0; 2612ad43027SMikolaj Golub pid = fork(); 2622ad43027SMikolaj Golub if (pid == -1) { 2639da0ef13SMikolaj Golub warn("fork"); 2649da0ef13SMikolaj Golub goto exit; 26553d49b37SJilles Tjoelker } else if (pid > 0) { 26653d49b37SJilles Tjoelker /* 26753d49b37SJilles Tjoelker * Unblock SIGTERM after we know we have a valid 26853d49b37SJilles Tjoelker * child PID to signal. 26953d49b37SJilles Tjoelker */ 27053d49b37SJilles Tjoelker if (sigprocmask(SIG_UNBLOCK, &mask_term, NULL)) { 27153d49b37SJilles Tjoelker warn("sigprocmask"); 27253d49b37SJilles Tjoelker goto exit; 27353d49b37SJilles Tjoelker } 27453d49b37SJilles Tjoelker close(pfd[1]); 27553d49b37SJilles Tjoelker pfd[1] = -1; 2762ad43027SMikolaj Golub } 2772ad43027SMikolaj Golub } 278195fc497SMikolaj Golub if (pid <= 0) { 2792ad43027SMikolaj Golub /* Now that we are the child, write out the pid. */ 280c6262cb6SPawel Jakub Dawidek pidfile_write(pfh); 281846be7bdSPoul-Henning Kamp 2822ad43027SMikolaj Golub if (user != NULL) 2832ad43027SMikolaj Golub restrict_process(user); 28453d49b37SJilles Tjoelker /* 28553d49b37SJilles Tjoelker * When forking, the child gets the original sigmask, 28653d49b37SJilles Tjoelker * and dup'd pipes. 28753d49b37SJilles Tjoelker */ 28853d49b37SJilles Tjoelker if (pid == 0) { 28953d49b37SJilles Tjoelker close(pfd[0]); 29053d49b37SJilles Tjoelker if (sigprocmask(SIG_SETMASK, &mask_orig, NULL)) 29153d49b37SJilles Tjoelker err(1, "sigprogmask"); 29253d49b37SJilles Tjoelker if (stdmask & STDERR_FILENO) { 29353d49b37SJilles Tjoelker if (dup2(pfd[1], STDERR_FILENO) == -1) 29453d49b37SJilles Tjoelker err(1, "dup2"); 29553d49b37SJilles Tjoelker } 29653d49b37SJilles Tjoelker if (stdmask & STDOUT_FILENO) { 29753d49b37SJilles Tjoelker if (dup2(pfd[1], STDOUT_FILENO) == -1) 29853d49b37SJilles Tjoelker err(1, "dup2"); 29953d49b37SJilles Tjoelker } 30053d49b37SJilles Tjoelker if (pfd[1] != STDERR_FILENO && 30153d49b37SJilles Tjoelker pfd[1] != STDOUT_FILENO) 30253d49b37SJilles Tjoelker close(pfd[1]); 30353d49b37SJilles Tjoelker } 304bd06a3ecSMike Barcroft execvp(argv[0], argv); 305846be7bdSPoul-Henning Kamp /* 3062ad43027SMikolaj Golub * execvp() failed -- report the error. The child is 3072ad43027SMikolaj Golub * now running, so the exit status doesn't matter. 308846be7bdSPoul-Henning Kamp */ 3092ad43027SMikolaj Golub err(1, "%s", argv[0]); 3102ad43027SMikolaj Golub } 31153d49b37SJilles Tjoelker setproctitle("%s[%d]", title, (int)pid); 31253d49b37SJilles Tjoelker /* 31353d49b37SJilles Tjoelker * As we have closed the write end of pipe for parent process, 31453d49b37SJilles Tjoelker * we might detect the child's exit by reading EOF. The child 31553d49b37SJilles Tjoelker * might have closed its stdout and stderr, so we must wait for 31653d49b37SJilles Tjoelker * the SIGCHLD to ensure that the process is actually gone. 31753d49b37SJilles Tjoelker */ 31853d49b37SJilles Tjoelker child_eof = 0; 31953d49b37SJilles Tjoelker for (;;) { 32053d49b37SJilles Tjoelker /* 32153d49b37SJilles Tjoelker * We block SIGCHLD when listening, but SIGTERM we accept 32253d49b37SJilles Tjoelker * so the read() won't block if we wish to depart. 32353d49b37SJilles Tjoelker * 32453d49b37SJilles Tjoelker * Upon receiving SIGTERM, we have several options after 32553d49b37SJilles Tjoelker * sending the SIGTERM to our child: 32653d49b37SJilles Tjoelker * - read until EOF 32753d49b37SJilles Tjoelker * - read until EOF but only for a while 32853d49b37SJilles Tjoelker * - bail immediately 32953d49b37SJilles Tjoelker * 33053d49b37SJilles Tjoelker * We go for the third, as otherwise we have no guarantee 33153d49b37SJilles Tjoelker * that we won't block indefinitely if the child refuses 33253d49b37SJilles Tjoelker * to depart. To handle the second option, a different 33353d49b37SJilles Tjoelker * approach would be needed (procctl()?) 33453d49b37SJilles Tjoelker */ 33553d49b37SJilles Tjoelker if (child_gone && child_eof) { 33653d49b37SJilles Tjoelker break; 33753d49b37SJilles Tjoelker } else if (terminate) { 33853d49b37SJilles Tjoelker goto exit; 33953d49b37SJilles Tjoelker } else if (!child_eof) { 34053d49b37SJilles Tjoelker if (sigprocmask(SIG_BLOCK, &mask_read, NULL)) { 34153d49b37SJilles Tjoelker warn("sigprocmask"); 34253d49b37SJilles Tjoelker goto exit; 34353d49b37SJilles Tjoelker } 34453d49b37SJilles Tjoelker child_eof = !listen_child(pfd[0], &logpar); 34553d49b37SJilles Tjoelker if (sigprocmask(SIG_UNBLOCK, &mask_read, NULL)) { 34653d49b37SJilles Tjoelker warn("sigprocmask"); 34753d49b37SJilles Tjoelker goto exit; 34853d49b37SJilles Tjoelker } 34953d49b37SJilles Tjoelker } else { 35053d49b37SJilles Tjoelker if (sigprocmask(SIG_BLOCK, &mask_susp, NULL)) { 35153d49b37SJilles Tjoelker warn("sigprocmask"); 35253d49b37SJilles Tjoelker goto exit; 35353d49b37SJilles Tjoelker } 35453d49b37SJilles Tjoelker while (!terminate && !child_gone) 35553d49b37SJilles Tjoelker sigsuspend(&mask_orig); 35653d49b37SJilles Tjoelker if (sigprocmask(SIG_UNBLOCK, &mask_susp, NULL)) { 35753d49b37SJilles Tjoelker warn("sigprocmask"); 35853d49b37SJilles Tjoelker goto exit; 35953d49b37SJilles Tjoelker } 36053d49b37SJilles Tjoelker } 36153d49b37SJilles Tjoelker } 36253d49b37SJilles Tjoelker if (sigprocmask(SIG_BLOCK, &mask_term, NULL)) { 36353d49b37SJilles Tjoelker warn("sigprocmask"); 36453d49b37SJilles Tjoelker goto exit; 36553d49b37SJilles Tjoelker } 36653d49b37SJilles Tjoelker if (restart && !terminate) { 367*37820b87SIan Lepore daemon_sleep(restart, 0); 36853d49b37SJilles Tjoelker close(pfd[0]); 36953d49b37SJilles Tjoelker pfd[0] = -1; 370b6193c24SMikolaj Golub goto restart; 371b6193c24SMikolaj Golub } 3729da0ef13SMikolaj Golub exit: 37353d49b37SJilles Tjoelker close(outfd); 37453d49b37SJilles Tjoelker close(pfd[0]); 37553d49b37SJilles Tjoelker close(pfd[1]); 37653d49b37SJilles Tjoelker if (dosyslog) 37753d49b37SJilles Tjoelker closelog(); 378c6262cb6SPawel Jakub Dawidek pidfile_remove(pfh); 37932b17786SJohn-Mark Gurney pidfile_remove(ppfh); 3809da0ef13SMikolaj Golub exit(1); /* If daemon(3) succeeded exit status does not matter. */ 381bd06a3ecSMike Barcroft } 382bd06a3ecSMike Barcroft 383bd06a3ecSMike Barcroft static void 38453d49b37SJilles Tjoelker daemon_sleep(time_t secs, long nsecs) 385195fc497SMikolaj Golub { 38653d49b37SJilles Tjoelker struct timespec ts = { secs, nsecs }; 38753d49b37SJilles Tjoelker while (nanosleep(&ts, &ts) == -1) { 38853d49b37SJilles Tjoelker if (errno != EINTR) 38953d49b37SJilles Tjoelker err(1, "nanosleep"); 39053d49b37SJilles Tjoelker } 39153d49b37SJilles Tjoelker } 39253d49b37SJilles Tjoelker 39353d49b37SJilles Tjoelker static void 39453d49b37SJilles Tjoelker open_pid_files(const char *pidfile, const char *ppidfile, 39553d49b37SJilles Tjoelker struct pidfh **pfh, struct pidfh **ppfh) 39653d49b37SJilles Tjoelker { 39753d49b37SJilles Tjoelker pid_t fpid; 39853d49b37SJilles Tjoelker int serrno; 39953d49b37SJilles Tjoelker 40053d49b37SJilles Tjoelker if (pidfile) { 40153d49b37SJilles Tjoelker *pfh = pidfile_open(pidfile, 0600, &fpid); 40253d49b37SJilles Tjoelker if (*pfh == NULL) { 40353d49b37SJilles Tjoelker if (errno == EEXIST) { 40453d49b37SJilles Tjoelker errx(3, "process already running, pid: %d", 40553d49b37SJilles Tjoelker fpid); 40653d49b37SJilles Tjoelker } 40753d49b37SJilles Tjoelker err(2, "pidfile ``%s''", pidfile); 40853d49b37SJilles Tjoelker } 40953d49b37SJilles Tjoelker } 41053d49b37SJilles Tjoelker /* Do the same for the actual daemon process. */ 41153d49b37SJilles Tjoelker if (ppidfile) { 41253d49b37SJilles Tjoelker *ppfh = pidfile_open(ppidfile, 0600, &fpid); 41353d49b37SJilles Tjoelker if (*ppfh == NULL) { 41453d49b37SJilles Tjoelker serrno = errno; 41553d49b37SJilles Tjoelker pidfile_remove(*pfh); 41653d49b37SJilles Tjoelker errno = serrno; 41753d49b37SJilles Tjoelker if (errno == EEXIST) { 41853d49b37SJilles Tjoelker errx(3, "process already running, pid: %d", 41953d49b37SJilles Tjoelker fpid); 42053d49b37SJilles Tjoelker } 42153d49b37SJilles Tjoelker err(2, "ppidfile ``%s''", ppidfile); 42253d49b37SJilles Tjoelker } 42353d49b37SJilles Tjoelker } 42453d49b37SJilles Tjoelker } 42553d49b37SJilles Tjoelker 42653d49b37SJilles Tjoelker static int 42753d49b37SJilles Tjoelker get_log_mapping(const char *str, const CODE *c) 42853d49b37SJilles Tjoelker { 42953d49b37SJilles Tjoelker const CODE *cp; 43053d49b37SJilles Tjoelker for (cp = c; cp->c_name; cp++) 43153d49b37SJilles Tjoelker if (strcmp(cp->c_name, str) == 0) 43253d49b37SJilles Tjoelker return cp->c_val; 43353d49b37SJilles Tjoelker return -1; 434195fc497SMikolaj Golub } 435195fc497SMikolaj Golub 436195fc497SMikolaj Golub static void 437e6d4b388STom Rhodes restrict_process(const char *user) 43812d7249eSTom Rhodes { 43912d7249eSTom Rhodes struct passwd *pw = NULL; 44012d7249eSTom Rhodes 441e6d4b388STom Rhodes pw = getpwnam(user); 442e6d4b388STom Rhodes if (pw == NULL) 443e6d4b388STom Rhodes errx(1, "unknown user: %s", user); 44412d7249eSTom Rhodes 445e6d4b388STom Rhodes if (setusercontext(NULL, pw, pw->pw_uid, LOGIN_SETALL) != 0) 446e6d4b388STom Rhodes errx(1, "failed to set user environment"); 44712d7249eSTom Rhodes } 44812d7249eSTom Rhodes 44953d49b37SJilles Tjoelker /* 45053d49b37SJilles Tjoelker * We try to collect whole lines terminated by '\n'. Otherwise we collect a 45153d49b37SJilles Tjoelker * full buffer, and then output it. 45253d49b37SJilles Tjoelker * 45353d49b37SJilles Tjoelker * Return value of 0 is assumed to mean EOF or error, and 1 indicates to 45453d49b37SJilles Tjoelker * continue reading. 45553d49b37SJilles Tjoelker */ 456b6193c24SMikolaj Golub static int 45753d49b37SJilles Tjoelker listen_child(int fd, struct log_params *logpar) 4582ad43027SMikolaj Golub { 45953d49b37SJilles Tjoelker static unsigned char buf[LBUF_SIZE]; 46053d49b37SJilles Tjoelker static size_t bytes_read = 0; 46153d49b37SJilles Tjoelker int rv; 4622ad43027SMikolaj Golub 46353d49b37SJilles Tjoelker assert(logpar); 46453d49b37SJilles Tjoelker assert(bytes_read < LBUF_SIZE - 1); 46553d49b37SJilles Tjoelker 46653d49b37SJilles Tjoelker rv = read(fd, buf + bytes_read, LBUF_SIZE - bytes_read - 1); 46753d49b37SJilles Tjoelker if (rv > 0) { 46853d49b37SJilles Tjoelker unsigned char *cp; 46953d49b37SJilles Tjoelker 47053d49b37SJilles Tjoelker bytes_read += rv; 47153d49b37SJilles Tjoelker assert(bytes_read <= LBUF_SIZE - 1); 47253d49b37SJilles Tjoelker /* Always NUL-terminate just in case. */ 47353d49b37SJilles Tjoelker buf[LBUF_SIZE - 1] = '\0'; 47453d49b37SJilles Tjoelker /* 47553d49b37SJilles Tjoelker * Chomp line by line until we run out of buffer. 47653d49b37SJilles Tjoelker * This does not take NUL characters into account. 47753d49b37SJilles Tjoelker */ 47853d49b37SJilles Tjoelker while ((cp = memchr(buf, '\n', bytes_read)) != NULL) { 47953d49b37SJilles Tjoelker size_t bytes_line = cp - buf + 1; 48053d49b37SJilles Tjoelker assert(bytes_line <= bytes_read); 48153d49b37SJilles Tjoelker do_output(buf, bytes_line, logpar); 48253d49b37SJilles Tjoelker bytes_read -= bytes_line; 48353d49b37SJilles Tjoelker memmove(buf, cp + 1, bytes_read); 484195fc497SMikolaj Golub } 48553d49b37SJilles Tjoelker /* Wait until the buffer is full. */ 48653d49b37SJilles Tjoelker if (bytes_read < LBUF_SIZE - 1) 48753d49b37SJilles Tjoelker return 1; 48853d49b37SJilles Tjoelker do_output(buf, bytes_read, logpar); 48953d49b37SJilles Tjoelker bytes_read = 0; 49053d49b37SJilles Tjoelker return 1; 49153d49b37SJilles Tjoelker } else if (rv == -1) { 49253d49b37SJilles Tjoelker /* EINTR should trigger another read. */ 49353d49b37SJilles Tjoelker if (errno == EINTR) { 49453d49b37SJilles Tjoelker return 1; 49553d49b37SJilles Tjoelker } else { 49653d49b37SJilles Tjoelker warn("read"); 49753d49b37SJilles Tjoelker return 0; 498c60d51f9SMikolaj Golub } 49953d49b37SJilles Tjoelker } 50053d49b37SJilles Tjoelker /* Upon EOF, we have to flush what's left of the buffer. */ 50153d49b37SJilles Tjoelker if (bytes_read > 0) { 50253d49b37SJilles Tjoelker do_output(buf, bytes_read, logpar); 50353d49b37SJilles Tjoelker bytes_read = 0; 50453d49b37SJilles Tjoelker } 50553d49b37SJilles Tjoelker return 0; 50653d49b37SJilles Tjoelker } 50753d49b37SJilles Tjoelker 50853d49b37SJilles Tjoelker /* 50953d49b37SJilles Tjoelker * The default behavior is to stay silent if the user wants to redirect 51053d49b37SJilles Tjoelker * output to a file and/or syslog. If neither are provided, then we bounce 51153d49b37SJilles Tjoelker * everything back to parent's stdout. 51253d49b37SJilles Tjoelker */ 51353d49b37SJilles Tjoelker static void 51453d49b37SJilles Tjoelker do_output(const unsigned char *buf, size_t len, struct log_params *logpar) 51553d49b37SJilles Tjoelker { 51653d49b37SJilles Tjoelker assert(len <= LBUF_SIZE); 51753d49b37SJilles Tjoelker assert(logpar); 51853d49b37SJilles Tjoelker 51953d49b37SJilles Tjoelker if (len < 1) 52053d49b37SJilles Tjoelker return; 52153d49b37SJilles Tjoelker if (logpar->dosyslog) 52253d49b37SJilles Tjoelker syslog(logpar->logpri, "%.*s", (int)len, buf); 52353d49b37SJilles Tjoelker if (logpar->outfd != -1) { 52453d49b37SJilles Tjoelker if (write(logpar->outfd, buf, len) == -1) 52553d49b37SJilles Tjoelker warn("write"); 52653d49b37SJilles Tjoelker } 52753d49b37SJilles Tjoelker if (logpar->noclose && !logpar->dosyslog && logpar->outfd == -1) 52853d49b37SJilles Tjoelker printf("%.*s", (int)len, buf); 52953d49b37SJilles Tjoelker } 53053d49b37SJilles Tjoelker 53153d49b37SJilles Tjoelker /* 53253d49b37SJilles Tjoelker * We use the global PID acquired directly from fork. If there is no valid 53353d49b37SJilles Tjoelker * child pid, the handler should be blocked and/or child_gone == 1. 53453d49b37SJilles Tjoelker */ 53553d49b37SJilles Tjoelker static void 53653d49b37SJilles Tjoelker handle_term(int signo) 53753d49b37SJilles Tjoelker { 53853d49b37SJilles Tjoelker if (pid > 0 && !child_gone) 53953d49b37SJilles Tjoelker kill(pid, signo); 540b6193c24SMikolaj Golub terminate = 1; 541195fc497SMikolaj Golub } 54253d49b37SJilles Tjoelker 54353d49b37SJilles Tjoelker static void 54453d49b37SJilles Tjoelker handle_chld(int signo) 54553d49b37SJilles Tjoelker { 54653d49b37SJilles Tjoelker (void)signo; 54753d49b37SJilles Tjoelker for (;;) { 54853d49b37SJilles Tjoelker int rv = waitpid(-1, NULL, WNOHANG); 54953d49b37SJilles Tjoelker if (pid == rv) { 55053d49b37SJilles Tjoelker child_gone = 1; 55153d49b37SJilles Tjoelker break; 55253d49b37SJilles Tjoelker } else if (rv == -1 && errno != EINTR) { 55353d49b37SJilles Tjoelker warn("waitpid"); 55453d49b37SJilles Tjoelker return; 5552ad43027SMikolaj Golub } 5562ad43027SMikolaj Golub } 5572ad43027SMikolaj Golub } 5582ad43027SMikolaj Golub 5592ad43027SMikolaj Golub static void 560bd06a3ecSMike Barcroft usage(void) 561bd06a3ecSMike Barcroft { 56253d49b37SJilles Tjoelker (void)fprintf(stderr, 56353d49b37SJilles Tjoelker "usage: daemon [-cfrS] [-p child_pidfile] [-P supervisor_pidfile]\n" 56453d49b37SJilles Tjoelker " [-u user] [-o output_file] [-t title]\n" 56553d49b37SJilles Tjoelker " [-l syslog_facility] [-s syslog_priority]\n" 566*37820b87SIan Lepore " [-T syslog_tag] [-m output_mask] [-R restart_delay_secs]\n" 56753d49b37SJilles Tjoelker "command arguments ...\n"); 568bd06a3ecSMike Barcroft exit(1); 569bd06a3ecSMike Barcroft } 570