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 338935a399SIhor Antonov #include <sys/event.h> 3453c49998SMikolaj Golub #include <sys/mman.h> 352ad43027SMikolaj Golub #include <sys/wait.h> 36bd06a3ecSMike Barcroft 3753d49b37SJilles Tjoelker #include <fcntl.h> 38bd06a3ecSMike Barcroft #include <err.h> 39846be7bdSPoul-Henning Kamp #include <errno.h> 400a402ad2SIhor Antonov #include <getopt.h> 41c6262cb6SPawel Jakub Dawidek #include <libutil.h> 42e6d4b388STom Rhodes #include <login_cap.h> 436b3ad1d7SMaxim Sobolev #include <paths.h> 44195fc497SMikolaj Golub #include <pwd.h> 45195fc497SMikolaj Golub #include <signal.h> 46bd06a3ecSMike Barcroft #include <stdio.h> 47203df05bSIhor Antonov #include <stdbool.h> 48bd06a3ecSMike Barcroft #include <stdlib.h> 49bd06a3ecSMike Barcroft #include <unistd.h> 5053d49b37SJilles Tjoelker #include <string.h> 5153d49b37SJilles Tjoelker #define SYSLOG_NAMES 5253d49b37SJilles Tjoelker #include <syslog.h> 5353d49b37SJilles Tjoelker #include <time.h> 5453d49b37SJilles Tjoelker #include <assert.h> 55bd06a3ecSMike Barcroft 56a6f795ccSIhor Antonov /* 1 year in seconds */ 57a6f795ccSIhor Antonov #define MAX_RESTART_DELAY 60*60*24*365 58a6f795ccSIhor Antonov 5953d49b37SJilles Tjoelker #define LBUF_SIZE 4096 6053d49b37SJilles Tjoelker 618935a399SIhor Antonov enum daemon_mode { 628935a399SIhor Antonov MODE_DAEMON = 0, /* simply daemonize, no supervision */ 638935a399SIhor Antonov MODE_SUPERVISE, /* initial supervision state */ 648935a399SIhor Antonov MODE_TERMINATING, /* user requested termination */ 658935a399SIhor Antonov MODE_NOCHILD, /* child is terminated, final state of the event loop */ 668935a399SIhor Antonov }; 678935a399SIhor Antonov 68407e3790SIhor Antonov 69298a392eSIhor Antonov struct daemon_state { 7024fd3e96SIhor Antonov unsigned char buf[LBUF_SIZE]; 7124fd3e96SIhor Antonov size_t pos; 724c41f4a0SIhor Antonov char **argv; 73298a392eSIhor Antonov const char *child_pidfile; 74298a392eSIhor Antonov const char *parent_pidfile; 75e70444c6SIhor Antonov const char *output_filename; 766f063672SIhor Antonov const char *syslog_tag; 77298a392eSIhor Antonov const char *title; 78298a392eSIhor Antonov const char *user; 79298a392eSIhor Antonov struct pidfh *parent_pidfh; 80298a392eSIhor Antonov struct pidfh *child_pidfh; 818935a399SIhor Antonov enum daemon_mode mode; 828935a399SIhor Antonov int pid; 83407e3790SIhor Antonov int pipe_rd; 84407e3790SIhor Antonov int pipe_wr; 85298a392eSIhor Antonov int keep_cur_workdir; 86298a392eSIhor Antonov int restart_delay; 87298a392eSIhor Antonov int stdmask; 88e70444c6SIhor Antonov int syslog_priority; 896f063672SIhor Antonov int syslog_facility; 90129ec8f4SIhor Antonov int keep_fds_open; 91e70444c6SIhor Antonov int output_fd; 92298a392eSIhor Antonov bool restart_enabled; 93f2f9d31dSIhor Antonov bool syslog_enabled; 94298a392eSIhor Antonov bool log_reopen; 9553d49b37SJilles Tjoelker }; 9653d49b37SJilles Tjoelker 97e6d4b388STom Rhodes static void restrict_process(const char *); 984cd407ecSMaxim Sobolev static int open_log(const char *); 99298a392eSIhor Antonov static void reopen_log(struct daemon_state *); 1006ac7c9f0SIhor Antonov static bool listen_child(struct daemon_state *); 10153d49b37SJilles Tjoelker static int get_log_mapping(const char *, const CODE *); 102298a392eSIhor Antonov static void open_pid_files(struct daemon_state *); 103298a392eSIhor Antonov static void do_output(const unsigned char *, size_t, struct daemon_state *); 1048935a399SIhor Antonov static void daemon_sleep(struct daemon_state *); 105298a392eSIhor Antonov static void daemon_state_init(struct daemon_state *); 1064c41f4a0SIhor Antonov static void daemon_eventloop(struct daemon_state *); 107cf6356fdSIhor Antonov static void daemon_terminate(struct daemon_state *); 1088935a399SIhor Antonov static void daemon_exec(struct daemon_state *); 1098935a399SIhor Antonov static bool daemon_is_child_dead(struct daemon_state *); 1108935a399SIhor Antonov static void daemon_set_child_pipe(struct daemon_state *); 11153d49b37SJilles Tjoelker 1120a402ad2SIhor Antonov static const char shortopts[] = "+cfHSp:P:ru:o:s:l:t:m:R:T:h"; 1130a402ad2SIhor Antonov 1140a402ad2SIhor Antonov static const struct option longopts[] = { 1150a402ad2SIhor Antonov { "change-dir", no_argument, NULL, 'c' }, 1160a402ad2SIhor Antonov { "close-fds", no_argument, NULL, 'f' }, 1170a402ad2SIhor Antonov { "sighup", no_argument, NULL, 'H' }, 1180a402ad2SIhor Antonov { "syslog", no_argument, NULL, 'S' }, 1190a402ad2SIhor Antonov { "output-file", required_argument, NULL, 'o' }, 1200a402ad2SIhor Antonov { "output-mask", required_argument, NULL, 'm' }, 1210a402ad2SIhor Antonov { "child-pidfile", required_argument, NULL, 'p' }, 1220a402ad2SIhor Antonov { "supervisor-pidfile", required_argument, NULL, 'P' }, 1230a402ad2SIhor Antonov { "restart", no_argument, NULL, 'r' }, 1240a402ad2SIhor Antonov { "restart-delay", required_argument, NULL, 'R' }, 1250a402ad2SIhor Antonov { "title", required_argument, NULL, 't' }, 1260a402ad2SIhor Antonov { "user", required_argument, NULL, 'u' }, 1270a402ad2SIhor Antonov { "syslog-priority", required_argument, NULL, 's' }, 1280a402ad2SIhor Antonov { "syslog-facility", required_argument, NULL, 'l' }, 1290a402ad2SIhor Antonov { "syslog-tag", required_argument, NULL, 'T' }, 1300a402ad2SIhor Antonov { "help", no_argument, NULL, 'h' }, 1310a402ad2SIhor Antonov { NULL, 0, NULL, 0 } 1320a402ad2SIhor Antonov }; 1330a402ad2SIhor Antonov 1340a402ad2SIhor Antonov static _Noreturn void 1350a402ad2SIhor Antonov usage(int exitcode) 1360a402ad2SIhor Antonov { 1370a402ad2SIhor Antonov (void)fprintf(stderr, 1380a402ad2SIhor Antonov "usage: daemon [-cfHrS] [-p child_pidfile] [-P supervisor_pidfile]\n" 1390a402ad2SIhor Antonov " [-u user] [-o output_file] [-t title]\n" 1400a402ad2SIhor Antonov " [-l syslog_facility] [-s syslog_priority]\n" 1410a402ad2SIhor Antonov " [-T syslog_tag] [-m output_mask] [-R restart_delay_secs]\n" 1420a402ad2SIhor Antonov "command arguments ...\n"); 1430a402ad2SIhor Antonov 1440a402ad2SIhor Antonov (void)fprintf(stderr, 1450a402ad2SIhor Antonov " --change-dir -c Change the current working directory to root\n" 1460a402ad2SIhor Antonov " --close-fds -f Set stdin, stdout, stderr to /dev/null\n" 1470a402ad2SIhor Antonov " --sighup -H Close and re-open output file on SIGHUP\n" 1480a402ad2SIhor Antonov " --syslog -S Send output to syslog\n" 1490a402ad2SIhor Antonov " --output-file -o <file> Append output of the child process to file\n" 1500a402ad2SIhor Antonov " --output-mask -m <mask> What to send to syslog/file\n" 1510a402ad2SIhor Antonov " 1=stdout, 2=stderr, 3=both\n" 1520a402ad2SIhor Antonov " --child-pidfile -p <file> Write PID of the child process to file\n" 1530a402ad2SIhor Antonov " --supervisor-pidfile -P <file> Write PID of the supervisor process to file\n" 1540a402ad2SIhor Antonov " --restart -r Restart child if it terminates (1 sec delay)\n" 1550a402ad2SIhor Antonov " --restart-delay -R <N> Restart child if it terminates after N sec\n" 1560a402ad2SIhor Antonov " --title -t <title> Set the title of the supervisor process\n" 1570a402ad2SIhor Antonov " --user -u <user> Drop privileges, run as given user\n" 1580a402ad2SIhor Antonov " --syslog-priority -s <prio> Set syslog priority\n" 1590a402ad2SIhor Antonov " --syslog-facility -l <flty> Set syslog facility\n" 1600a402ad2SIhor Antonov " --syslog-tag -T <tag> Set syslog tag\n" 1610a402ad2SIhor Antonov " --help -h Show this help\n"); 1620a402ad2SIhor Antonov 1630a402ad2SIhor Antonov exit(exitcode); 1640a402ad2SIhor Antonov } 1650a402ad2SIhor Antonov 166bd06a3ecSMike Barcroft int 167bd06a3ecSMike Barcroft main(int argc, char *argv[]) 168bd06a3ecSMike Barcroft { 169a6f795ccSIhor Antonov const char *e = NULL; 170e745dc22SIhor Antonov int ch = 0; 171298a392eSIhor Antonov struct daemon_state state; 172bd06a3ecSMike Barcroft 173298a392eSIhor Antonov daemon_state_init(&state); 1749ee1faeeSIhor Antonov 1758935a399SIhor Antonov /* Signals are processed via kqueue */ 1768935a399SIhor Antonov signal(SIGHUP, SIG_IGN); 1778935a399SIhor Antonov signal(SIGTERM, SIG_IGN); 1788935a399SIhor Antonov 1799ee1faeeSIhor Antonov /* 180f907027bSIhor Antonov * Supervision mode is enabled if one of the following options are used: 181f907027bSIhor Antonov * --child-pidfile -p 182f907027bSIhor Antonov * --supervisor-pidfile -P 183f907027bSIhor Antonov * --restart -r / --restart-delay -R 184f907027bSIhor Antonov * --syslog -S 185f907027bSIhor Antonov * --syslog-facility -l 186f907027bSIhor Antonov * --syslog-priority -s 187f907027bSIhor Antonov * --syslog-tag -T 188f907027bSIhor Antonov * 189f907027bSIhor Antonov * In supervision mode daemon executes the command in a forked process 190f907027bSIhor Antonov * and observes the child by waiting for SIGCHILD. In supervision mode 191f907027bSIhor Antonov * daemon must never exit before the child, this is necessary to prevent 192f907027bSIhor Antonov * orphaning the child and leaving a stale pid file. 193f907027bSIhor Antonov * To achieve this daemon catches SIGTERM and 194f907027bSIhor Antonov * forwards it to the child, expecting to get SIGCHLD eventually. 195f907027bSIhor Antonov */ 1960a402ad2SIhor Antonov while ((ch = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) { 197bd06a3ecSMike Barcroft switch (ch) { 198bd06a3ecSMike Barcroft case 'c': 199298a392eSIhor Antonov state.keep_cur_workdir = 0; 200bd06a3ecSMike Barcroft break; 201bd06a3ecSMike Barcroft case 'f': 202298a392eSIhor Antonov state.keep_fds_open = 0; 203bd06a3ecSMike Barcroft break; 2044cd407ecSMaxim Sobolev case 'H': 205298a392eSIhor Antonov state.log_reopen = true; 2064cd407ecSMaxim Sobolev break; 20753d49b37SJilles Tjoelker case 'l': 208298a392eSIhor Antonov state.syslog_facility = get_log_mapping(optarg, 20939ea4280SIhor Antonov facilitynames); 210298a392eSIhor Antonov if (state.syslog_facility == -1) { 21153d49b37SJilles Tjoelker errx(5, "unrecognized syslog facility"); 2126b4ef4b1SIhor Antonov } 213298a392eSIhor Antonov state.syslog_enabled = true; 2148935a399SIhor Antonov state.mode = MODE_SUPERVISE; 21553d49b37SJilles Tjoelker break; 21653d49b37SJilles Tjoelker case 'm': 217a6f795ccSIhor Antonov state.stdmask = (int)strtonum(optarg, 0, 3, &e); 218a6f795ccSIhor Antonov if (e != NULL) { 219a6f795ccSIhor Antonov errx(6, "unrecognized listening mask: %s", e); 2206b4ef4b1SIhor Antonov } 22153d49b37SJilles Tjoelker break; 22253d49b37SJilles Tjoelker case 'o': 223298a392eSIhor Antonov state.output_filename = optarg; 224f907027bSIhor Antonov /* 225f907027bSIhor Antonov * TODO: setting output filename doesn't have to turn 226f907027bSIhor Antonov * the supervision mode on. For non-supervised mode 227f907027bSIhor Antonov * daemon could open the specified file and set it's 228f907027bSIhor Antonov * descriptor as both stderr and stout before execve() 229f907027bSIhor Antonov */ 2308935a399SIhor Antonov state.mode = MODE_SUPERVISE; 23153d49b37SJilles Tjoelker break; 232846be7bdSPoul-Henning Kamp case 'p': 233298a392eSIhor Antonov state.child_pidfile = optarg; 2348935a399SIhor Antonov state.mode = MODE_SUPERVISE; 235846be7bdSPoul-Henning Kamp break; 23632b17786SJohn-Mark Gurney case 'P': 237298a392eSIhor Antonov state.parent_pidfile = optarg; 2388935a399SIhor Antonov state.mode = MODE_SUPERVISE; 23932b17786SJohn-Mark Gurney break; 240b6193c24SMikolaj Golub case 'r': 241298a392eSIhor Antonov state.restart_enabled = true; 2428935a399SIhor Antonov state.mode = MODE_SUPERVISE; 243b6193c24SMikolaj Golub break; 24437820b87SIan Lepore case 'R': 245298a392eSIhor Antonov state.restart_enabled = true; 246a6f795ccSIhor Antonov state.restart_delay = (int)strtonum(optarg, 1, 247a6f795ccSIhor Antonov MAX_RESTART_DELAY, &e); 248a6f795ccSIhor Antonov if (e != NULL) { 249a6f795ccSIhor Antonov errx(6, "invalid restart delay: %s", e); 2506b4ef4b1SIhor Antonov } 25137820b87SIan Lepore break; 25253d49b37SJilles Tjoelker case 's': 253298a392eSIhor Antonov state.syslog_priority = get_log_mapping(optarg, 25439ea4280SIhor Antonov prioritynames); 255298a392eSIhor Antonov if (state.syslog_priority == -1) { 25653d49b37SJilles Tjoelker errx(4, "unrecognized syslog priority"); 2576b4ef4b1SIhor Antonov } 258298a392eSIhor Antonov state.syslog_enabled = true; 2598935a399SIhor Antonov state.mode = MODE_SUPERVISE; 26053d49b37SJilles Tjoelker break; 26153d49b37SJilles Tjoelker case 'S': 262298a392eSIhor Antonov state.syslog_enabled = true; 2638935a399SIhor Antonov state.mode = MODE_SUPERVISE; 26453d49b37SJilles Tjoelker break; 265112bfcf5SConrad Meyer case 't': 266298a392eSIhor Antonov state.title = optarg; 267112bfcf5SConrad Meyer break; 26853d49b37SJilles Tjoelker case 'T': 269298a392eSIhor Antonov state.syslog_tag = optarg; 270298a392eSIhor Antonov state.syslog_enabled = true; 2718935a399SIhor Antonov state.mode = MODE_SUPERVISE; 27253d49b37SJilles Tjoelker break; 273e6d4b388STom Rhodes case 'u': 274298a392eSIhor Antonov state.user = optarg; 275e6d4b388STom Rhodes break; 2760a402ad2SIhor Antonov case 'h': 2770a402ad2SIhor Antonov usage(0); 278*f7a10a77SCollin Funk __unreachable(); 279bd06a3ecSMike Barcroft default: 2800a402ad2SIhor Antonov usage(1); 281bd06a3ecSMike Barcroft } 282bd06a3ecSMike Barcroft } 283bd06a3ecSMike Barcroft argc -= optind; 284bd06a3ecSMike Barcroft argv += optind; 2854c41f4a0SIhor Antonov state.argv = argv; 286bd06a3ecSMike Barcroft 2876b4ef4b1SIhor Antonov if (argc == 0) { 2880a402ad2SIhor Antonov usage(1); 2896b4ef4b1SIhor Antonov } 29012d7249eSTom Rhodes 291298a392eSIhor Antonov if (!state.title) { 292298a392eSIhor Antonov state.title = argv[0]; 2936b4ef4b1SIhor Antonov } 29453d49b37SJilles Tjoelker 295298a392eSIhor Antonov if (state.output_filename) { 296298a392eSIhor Antonov state.output_fd = open_log(state.output_filename); 297298a392eSIhor Antonov if (state.output_fd == -1) { 29853d49b37SJilles Tjoelker err(7, "open"); 29953d49b37SJilles Tjoelker } 3006b4ef4b1SIhor Antonov } 30153d49b37SJilles Tjoelker 302298a392eSIhor Antonov if (state.syslog_enabled) { 303298a392eSIhor Antonov openlog(state.syslog_tag, LOG_PID | LOG_NDELAY, 304298a392eSIhor Antonov state.syslog_facility); 3056b4ef4b1SIhor Antonov } 30653d49b37SJilles Tjoelker 307846be7bdSPoul-Henning Kamp /* 308846be7bdSPoul-Henning Kamp * Try to open the pidfile before calling daemon(3), 309846be7bdSPoul-Henning Kamp * to be able to report the error intelligently 310846be7bdSPoul-Henning Kamp */ 311298a392eSIhor Antonov open_pid_files(&state); 3128935a399SIhor Antonov 3138935a399SIhor Antonov /* 3148935a399SIhor Antonov * TODO: add feature to avoid backgrounding 3158935a399SIhor Antonov * i.e. --foreground, -f 3168935a399SIhor Antonov */ 317298a392eSIhor Antonov if (daemon(state.keep_cur_workdir, state.keep_fds_open) == -1) { 3189da0ef13SMikolaj Golub warn("daemon"); 319cf6356fdSIhor Antonov daemon_terminate(&state); 3209da0ef13SMikolaj Golub } 3218935a399SIhor Antonov 3228935a399SIhor Antonov if (state.mode == MODE_DAEMON) { 3238935a399SIhor Antonov daemon_exec(&state); 3248935a399SIhor Antonov } 3258935a399SIhor Antonov 3269da0ef13SMikolaj Golub /* Write out parent pidfile if needed. */ 327298a392eSIhor Antonov pidfile_write(state.parent_pidfh); 328203df05bSIhor Antonov 3294c41f4a0SIhor Antonov do { 3308935a399SIhor Antonov state.mode = MODE_SUPERVISE; 3314c41f4a0SIhor Antonov daemon_eventloop(&state); 3328935a399SIhor Antonov daemon_sleep(&state); 3338935a399SIhor Antonov } while (state.restart_enabled); 3344c41f4a0SIhor Antonov 3354c41f4a0SIhor Antonov daemon_terminate(&state); 3364c41f4a0SIhor Antonov } 3374c41f4a0SIhor Antonov 3388935a399SIhor Antonov static void 3398935a399SIhor Antonov daemon_exec(struct daemon_state *state) 3408935a399SIhor Antonov { 3418935a399SIhor Antonov pidfile_write(state->child_pidfh); 3424c41f4a0SIhor Antonov 3438935a399SIhor Antonov if (state->user != NULL) { 3448935a399SIhor Antonov restrict_process(state->user); 3458935a399SIhor Antonov } 3468935a399SIhor Antonov 3478935a399SIhor Antonov /* Ignored signals remain ignored after execve, unignore them */ 3488935a399SIhor Antonov signal(SIGHUP, SIG_DFL); 3498935a399SIhor Antonov signal(SIGTERM, SIG_DFL); 3508935a399SIhor Antonov execvp(state->argv[0], state->argv); 3518935a399SIhor Antonov /* execvp() failed - report error and exit this process */ 3528935a399SIhor Antonov err(1, "%s", state->argv[0]); 3538935a399SIhor Antonov } 3548935a399SIhor Antonov 3558935a399SIhor Antonov /* Main event loop: fork the child and watch for events. 356a6f795ccSIhor Antonov * After SIGTERM is received and propagated to the child there are 3574c41f4a0SIhor Antonov * several options on what to do next: 3584c41f4a0SIhor Antonov * - read until EOF 3594c41f4a0SIhor Antonov * - read until EOF but only for a while 3604c41f4a0SIhor Antonov * - bail immediately 3614c41f4a0SIhor Antonov * Currently the third option is used, because otherwise there is no 3624c41f4a0SIhor Antonov * guarantee that read() won't block indefinitely if the child refuses 3634c41f4a0SIhor Antonov * to depart. To handle the second option, a different approach 3644c41f4a0SIhor Antonov * would be needed (procctl()?). 3654c41f4a0SIhor Antonov */ 3664c41f4a0SIhor Antonov static void 3674c41f4a0SIhor Antonov daemon_eventloop(struct daemon_state *state) 3684c41f4a0SIhor Antonov { 3698935a399SIhor Antonov struct kevent event; 3708935a399SIhor Antonov int kq; 3718935a399SIhor Antonov int ret; 372407e3790SIhor Antonov int pipe_fd[2]; 3738935a399SIhor Antonov 3748935a399SIhor Antonov /* 3758935a399SIhor Antonov * Try to protect against pageout kill. Ignore the 3768935a399SIhor Antonov * error, madvise(2) will fail only if a process does 3778935a399SIhor Antonov * not have superuser privileges. 3788935a399SIhor Antonov */ 3798935a399SIhor Antonov (void)madvise(NULL, 0, MADV_PROTECT); 3808935a399SIhor Antonov 381407e3790SIhor Antonov if (pipe(pipe_fd)) { 38253d49b37SJilles Tjoelker err(1, "pipe"); 3836b4ef4b1SIhor Antonov } 384407e3790SIhor Antonov state->pipe_rd = pipe_fd[0]; 385407e3790SIhor Antonov state->pipe_wr = pipe_fd[1]; 3868935a399SIhor Antonov 3878935a399SIhor Antonov kq = kqueuex(KQUEUE_CLOEXEC); 388407e3790SIhor Antonov EV_SET(&event, state->pipe_rd, EVFILT_READ, EV_ADD|EV_CLEAR, 0, 0, 3898935a399SIhor Antonov NULL); 3908935a399SIhor Antonov if (kevent(kq, &event, 1, NULL, 0, NULL) == -1) { 3918935a399SIhor Antonov err(EXIT_FAILURE, "failed to register kevent"); 39275f61ca9SIhor Antonov } 39375f61ca9SIhor Antonov 3948935a399SIhor Antonov EV_SET(&event, SIGHUP, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); 3958935a399SIhor Antonov if (kevent(kq, &event, 1, NULL, 0, NULL) == -1) { 3968935a399SIhor Antonov err(EXIT_FAILURE, "failed to register kevent"); 3978935a399SIhor Antonov } 3988935a399SIhor Antonov 3998935a399SIhor Antonov EV_SET(&event, SIGTERM, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); 4008935a399SIhor Antonov if (kevent(kq, &event, 1, NULL, 0, NULL) == -1) { 4018935a399SIhor Antonov err(EXIT_FAILURE, "failed to register kevent"); 4028935a399SIhor Antonov } 4038935a399SIhor Antonov 4048935a399SIhor Antonov EV_SET(&event, SIGCHLD, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); 4058935a399SIhor Antonov if (kevent(kq, &event, 1, NULL, 0, NULL) == -1) { 4068935a399SIhor Antonov err(EXIT_FAILURE, "failed to register kevent"); 4078935a399SIhor Antonov } 4088935a399SIhor Antonov memset(&event, 0, sizeof(struct kevent)); 4098935a399SIhor Antonov 4108935a399SIhor Antonov /* Spawn a child to exec the command. */ 4118935a399SIhor Antonov state->pid = fork(); 4128935a399SIhor Antonov 41375f61ca9SIhor Antonov /* fork failed, this can only happen when supervision is enabled */ 4148935a399SIhor Antonov switch (state->pid) { 4158935a399SIhor Antonov case -1: 4169da0ef13SMikolaj Golub warn("fork"); 4178935a399SIhor Antonov state->mode = MODE_NOCHILD; 4188935a399SIhor Antonov return; 4198935a399SIhor Antonov /* fork succeeded, this is child's branch */ 4208935a399SIhor Antonov case 0: 4218935a399SIhor Antonov close(kq); 4228935a399SIhor Antonov daemon_set_child_pipe(state); 4238935a399SIhor Antonov daemon_exec(state); 42453d49b37SJilles Tjoelker break; 425cd1e6e70SIhor Antonov } 426cd1e6e70SIhor Antonov 4278935a399SIhor Antonov /* case: pid > 0; fork succeeded */ 428407e3790SIhor Antonov close(state->pipe_wr); 429407e3790SIhor Antonov state->pipe_wr = -1; 4308935a399SIhor Antonov setproctitle("%s[%d]", state->title, (int)state->pid); 431cec8e6baSDag-Erling Smørgrav setbuf(stdout, NULL); 432cd1e6e70SIhor Antonov 4338935a399SIhor Antonov while (state->mode != MODE_NOCHILD) { 4348935a399SIhor Antonov ret = kevent(kq, NULL, 0, &event, 1, NULL); 4358935a399SIhor Antonov switch (ret) { 4368935a399SIhor Antonov case -1: 437494e7dfdSKyle Evans if (errno == EINTR) 438494e7dfdSKyle Evans continue; 4398935a399SIhor Antonov err(EXIT_FAILURE, "kevent wait"); 4408935a399SIhor Antonov case 0: 441cd1e6e70SIhor Antonov continue; 44253d49b37SJilles Tjoelker } 443cd1e6e70SIhor Antonov 4448935a399SIhor Antonov if (event.flags & EV_ERROR) { 4458935a399SIhor Antonov errx(EXIT_FAILURE, "Event error: %s", 446a6f795ccSIhor Antonov strerror((int)event.data)); 447cd1e6e70SIhor Antonov } 448cd1e6e70SIhor Antonov 4498935a399SIhor Antonov switch (event.filter) { 4508935a399SIhor Antonov case EVFILT_SIGNAL: 451cd1e6e70SIhor Antonov 4528935a399SIhor Antonov switch (event.ident) { 4538935a399SIhor Antonov case SIGCHLD: 4548935a399SIhor Antonov if (daemon_is_child_dead(state)) { 4558935a399SIhor Antonov /* child is dead, read all until EOF */ 4568935a399SIhor Antonov state->pid = -1; 4578935a399SIhor Antonov state->mode = MODE_NOCHILD; 458e0645579SKyle Evans while (listen_child(state)) { 459e0645579SKyle Evans continue; 460e0645579SKyle Evans } 4618935a399SIhor Antonov } 4628935a399SIhor Antonov continue; 4638935a399SIhor Antonov case SIGTERM: 4648935a399SIhor Antonov if (state->mode != MODE_SUPERVISE) { 4658935a399SIhor Antonov /* user is impatient */ 4668935a399SIhor Antonov /* TODO: warn about repeated SIGTERM? */ 4678935a399SIhor Antonov continue; 468cd1e6e70SIhor Antonov } 469cd1e6e70SIhor Antonov 4708935a399SIhor Antonov state->mode = MODE_TERMINATING; 4718935a399SIhor Antonov state->restart_enabled = false; 4728935a399SIhor Antonov if (state->pid > 0) { 4738935a399SIhor Antonov kill(state->pid, SIGTERM); 47453d49b37SJilles Tjoelker } 4754c41f4a0SIhor Antonov /* 4768935a399SIhor Antonov * TODO set kevent timer to exit 4778935a399SIhor Antonov * unconditionally after some time 4784c41f4a0SIhor Antonov */ 4798935a399SIhor Antonov continue; 4808935a399SIhor Antonov case SIGHUP: 4818935a399SIhor Antonov if (state->log_reopen && state->output_fd >= 0) { 4828935a399SIhor Antonov reopen_log(state); 4838935a399SIhor Antonov } 4848935a399SIhor Antonov continue; 4858935a399SIhor Antonov } 4868935a399SIhor Antonov break; 4878935a399SIhor Antonov 4888935a399SIhor Antonov case EVFILT_READ: 4898935a399SIhor Antonov /* 4908935a399SIhor Antonov * detecting EOF is no longer necessary 4918935a399SIhor Antonov * if child closes the pipe daemon will stop getting 4928935a399SIhor Antonov * EVFILT_READ events 4938935a399SIhor Antonov */ 4948935a399SIhor Antonov 4958935a399SIhor Antonov if (event.data > 0) { 4966ac7c9f0SIhor Antonov (void)listen_child(state); 4978935a399SIhor Antonov } 4988935a399SIhor Antonov continue; 4998935a399SIhor Antonov default: 5008935a399SIhor Antonov continue; 5018935a399SIhor Antonov } 50253d49b37SJilles Tjoelker } 5034c41f4a0SIhor Antonov 5048935a399SIhor Antonov close(kq); 505407e3790SIhor Antonov close(state->pipe_rd); 506407e3790SIhor Antonov state->pipe_rd = -1; 507bd06a3ecSMike Barcroft } 508bd06a3ecSMike Barcroft 509bd06a3ecSMike Barcroft static void 5108935a399SIhor Antonov daemon_sleep(struct daemon_state *state) 511195fc497SMikolaj Golub { 5128935a399SIhor Antonov struct timespec ts = { state->restart_delay, 0 }; 51309a3675dSConrad Meyer 5148935a399SIhor Antonov if (!state->restart_enabled) { 5158935a399SIhor Antonov return; 5168935a399SIhor Antonov } 5178935a399SIhor Antonov while (nanosleep(&ts, &ts) == -1) { 5186b4ef4b1SIhor Antonov if (errno != EINTR) { 51953d49b37SJilles Tjoelker err(1, "nanosleep"); 52053d49b37SJilles Tjoelker } 52153d49b37SJilles Tjoelker } 5226b4ef4b1SIhor Antonov } 52353d49b37SJilles Tjoelker 52453d49b37SJilles Tjoelker static void 525298a392eSIhor Antonov open_pid_files(struct daemon_state *state) 52653d49b37SJilles Tjoelker { 52753d49b37SJilles Tjoelker pid_t fpid; 52853d49b37SJilles Tjoelker int serrno; 52953d49b37SJilles Tjoelker 530298a392eSIhor Antonov if (state->child_pidfile) { 531298a392eSIhor Antonov state->child_pidfh = pidfile_open(state->child_pidfile, 0600, &fpid); 532298a392eSIhor Antonov if (state->child_pidfh == NULL) { 53353d49b37SJilles Tjoelker if (errno == EEXIST) { 53453d49b37SJilles Tjoelker errx(3, "process already running, pid: %d", 53553d49b37SJilles Tjoelker fpid); 53653d49b37SJilles Tjoelker } 537298a392eSIhor Antonov err(2, "pidfile ``%s''", state->child_pidfile); 53853d49b37SJilles Tjoelker } 53953d49b37SJilles Tjoelker } 54053d49b37SJilles Tjoelker /* Do the same for the actual daemon process. */ 541298a392eSIhor Antonov if (state->parent_pidfile) { 542298a392eSIhor Antonov state->parent_pidfh= pidfile_open(state->parent_pidfile, 0600, &fpid); 543298a392eSIhor Antonov if (state->parent_pidfh == NULL) { 54453d49b37SJilles Tjoelker serrno = errno; 545298a392eSIhor Antonov pidfile_remove(state->child_pidfh); 54653d49b37SJilles Tjoelker errno = serrno; 54753d49b37SJilles Tjoelker if (errno == EEXIST) { 54853d49b37SJilles Tjoelker errx(3, "process already running, pid: %d", 54953d49b37SJilles Tjoelker fpid); 55053d49b37SJilles Tjoelker } 551298a392eSIhor Antonov err(2, "ppidfile ``%s''", state->parent_pidfile); 55253d49b37SJilles Tjoelker } 55353d49b37SJilles Tjoelker } 55453d49b37SJilles Tjoelker } 55553d49b37SJilles Tjoelker 55653d49b37SJilles Tjoelker static int 55753d49b37SJilles Tjoelker get_log_mapping(const char *str, const CODE *c) 55853d49b37SJilles Tjoelker { 55953d49b37SJilles Tjoelker const CODE *cp; 56053d49b37SJilles Tjoelker for (cp = c; cp->c_name; cp++) 5616b4ef4b1SIhor Antonov if (strcmp(cp->c_name, str) == 0) { 56253d49b37SJilles Tjoelker return cp->c_val; 5636b4ef4b1SIhor Antonov } 56453d49b37SJilles Tjoelker return -1; 565195fc497SMikolaj Golub } 566195fc497SMikolaj Golub 567195fc497SMikolaj Golub static void 568e6d4b388STom Rhodes restrict_process(const char *user) 56912d7249eSTom Rhodes { 57012d7249eSTom Rhodes struct passwd *pw = NULL; 57112d7249eSTom Rhodes 572e6d4b388STom Rhodes pw = getpwnam(user); 5736b4ef4b1SIhor Antonov if (pw == NULL) { 574e6d4b388STom Rhodes errx(1, "unknown user: %s", user); 5756b4ef4b1SIhor Antonov } 57612d7249eSTom Rhodes 5776b4ef4b1SIhor Antonov if (setusercontext(NULL, pw, pw->pw_uid, LOGIN_SETALL) != 0) { 578e6d4b388STom Rhodes errx(1, "failed to set user environment"); 5796b4ef4b1SIhor Antonov } 5806b3ad1d7SMaxim Sobolev 5816b3ad1d7SMaxim Sobolev setenv("USER", pw->pw_name, 1); 5826b3ad1d7SMaxim Sobolev setenv("HOME", pw->pw_dir, 1); 5836b3ad1d7SMaxim Sobolev setenv("SHELL", *pw->pw_shell ? pw->pw_shell : _PATH_BSHELL, 1); 58412d7249eSTom Rhodes } 58512d7249eSTom Rhodes 58653d49b37SJilles Tjoelker /* 58753d49b37SJilles Tjoelker * We try to collect whole lines terminated by '\n'. Otherwise we collect a 58853d49b37SJilles Tjoelker * full buffer, and then output it. 58953d49b37SJilles Tjoelker * 590bc43a9a7SIhor Antonov * Return value of false is assumed to mean EOF or error, and true indicates to 59153d49b37SJilles Tjoelker * continue reading. 59253d49b37SJilles Tjoelker */ 593bc43a9a7SIhor Antonov static bool 5946ac7c9f0SIhor Antonov listen_child(struct daemon_state *state) 5952ad43027SMikolaj Golub { 596a6f795ccSIhor Antonov ssize_t rv; 5975745a584SIhor Antonov unsigned char *cp; 5982ad43027SMikolaj Golub 599cf6356fdSIhor Antonov assert(state != NULL); 60024fd3e96SIhor Antonov assert(state->pos < LBUF_SIZE - 1); 60153d49b37SJilles Tjoelker 602e0645579SKyle Evans rv = read(state->pipe_rd, state->buf + state->pos, 603e0645579SKyle Evans LBUF_SIZE - state->pos - 1); 60453d49b37SJilles Tjoelker if (rv > 0) { 60524fd3e96SIhor Antonov state->pos += rv; 60624fd3e96SIhor Antonov assert(state->pos <= LBUF_SIZE - 1); 60753d49b37SJilles Tjoelker /* Always NUL-terminate just in case. */ 60824fd3e96SIhor Antonov state->buf[LBUF_SIZE - 1] = '\0'; 6095745a584SIhor Antonov 61053d49b37SJilles Tjoelker /* 6115745a584SIhor Antonov * Find position of the last newline in the buffer. 6125745a584SIhor Antonov * The buffer is guaranteed to have one or more complete lines 6135745a584SIhor Antonov * if at least one newline was found when searching in reverse. 6145745a584SIhor Antonov * All complete lines are flushed. 61553d49b37SJilles Tjoelker * This does not take NUL characters into account. 61653d49b37SJilles Tjoelker */ 6175745a584SIhor Antonov cp = memrchr(state->buf, '\n', state->pos); 6185745a584SIhor Antonov if (cp != NULL) { 61924fd3e96SIhor Antonov size_t bytes_line = cp - state->buf + 1; 62024fd3e96SIhor Antonov assert(bytes_line <= state->pos); 62124fd3e96SIhor Antonov do_output(state->buf, bytes_line, state); 62224fd3e96SIhor Antonov state->pos -= bytes_line; 62324fd3e96SIhor Antonov memmove(state->buf, cp + 1, state->pos); 624195fc497SMikolaj Golub } 62553d49b37SJilles Tjoelker /* Wait until the buffer is full. */ 62624fd3e96SIhor Antonov if (state->pos < LBUF_SIZE - 1) { 627bc43a9a7SIhor Antonov return true; 6286b4ef4b1SIhor Antonov } 62924fd3e96SIhor Antonov do_output(state->buf, state->pos, state); 63024fd3e96SIhor Antonov state->pos = 0; 631bc43a9a7SIhor Antonov return true; 63253d49b37SJilles Tjoelker } else if (rv == -1) { 63353d49b37SJilles Tjoelker /* EINTR should trigger another read. */ 63453d49b37SJilles Tjoelker if (errno == EINTR) { 635bc43a9a7SIhor Antonov return true; 63653d49b37SJilles Tjoelker } else { 63753d49b37SJilles Tjoelker warn("read"); 638bc43a9a7SIhor Antonov return false; 639c60d51f9SMikolaj Golub } 64053d49b37SJilles Tjoelker } 64153d49b37SJilles Tjoelker /* Upon EOF, we have to flush what's left of the buffer. */ 64224fd3e96SIhor Antonov if (state->pos > 0) { 64324fd3e96SIhor Antonov do_output(state->buf, state->pos, state); 64424fd3e96SIhor Antonov state->pos = 0; 64553d49b37SJilles Tjoelker } 646bc43a9a7SIhor Antonov return false; 64753d49b37SJilles Tjoelker } 64853d49b37SJilles Tjoelker 64953d49b37SJilles Tjoelker /* 65053d49b37SJilles Tjoelker * The default behavior is to stay silent if the user wants to redirect 65153d49b37SJilles Tjoelker * output to a file and/or syslog. If neither are provided, then we bounce 65253d49b37SJilles Tjoelker * everything back to parent's stdout. 65353d49b37SJilles Tjoelker */ 65453d49b37SJilles Tjoelker static void 655298a392eSIhor Antonov do_output(const unsigned char *buf, size_t len, struct daemon_state *state) 65653d49b37SJilles Tjoelker { 65753d49b37SJilles Tjoelker assert(len <= LBUF_SIZE); 658cf6356fdSIhor Antonov assert(state != NULL); 65953d49b37SJilles Tjoelker 6606b4ef4b1SIhor Antonov if (len < 1) { 66153d49b37SJilles Tjoelker return; 6626b4ef4b1SIhor Antonov } 663298a392eSIhor Antonov if (state->syslog_enabled) { 664298a392eSIhor Antonov syslog(state->syslog_priority, "%.*s", (int)len, buf); 6656b4ef4b1SIhor Antonov } 666298a392eSIhor Antonov if (state->output_fd != -1) { 667298a392eSIhor Antonov if (write(state->output_fd, buf, len) == -1) 66853d49b37SJilles Tjoelker warn("write"); 66953d49b37SJilles Tjoelker } 670298a392eSIhor Antonov if (state->keep_fds_open && 671298a392eSIhor Antonov !state->syslog_enabled && 672298a392eSIhor Antonov state->output_fd == -1) { 67353d49b37SJilles Tjoelker printf("%.*s", (int)len, buf); 67453d49b37SJilles Tjoelker } 6756b4ef4b1SIhor Antonov } 67653d49b37SJilles Tjoelker 6774cd407ecSMaxim Sobolev static int 6784cd407ecSMaxim Sobolev open_log(const char *outfn) 6794cd407ecSMaxim Sobolev { 6804cd407ecSMaxim Sobolev 6814cd407ecSMaxim Sobolev return open(outfn, O_CREAT | O_WRONLY | O_APPEND | O_CLOEXEC, 0600); 6824cd407ecSMaxim Sobolev } 6834cd407ecSMaxim Sobolev 6844cd407ecSMaxim Sobolev static void 685298a392eSIhor Antonov reopen_log(struct daemon_state *state) 6864cd407ecSMaxim Sobolev { 6874cd407ecSMaxim Sobolev int outfd; 6884cd407ecSMaxim Sobolev 689298a392eSIhor Antonov outfd = open_log(state->output_filename); 690298a392eSIhor Antonov if (state->output_fd >= 0) { 691298a392eSIhor Antonov close(state->output_fd); 6926b4ef4b1SIhor Antonov } 693298a392eSIhor Antonov state->output_fd = outfd; 6944cd407ecSMaxim Sobolev } 6954cd407ecSMaxim Sobolev 696298a392eSIhor Antonov static void 697298a392eSIhor Antonov daemon_state_init(struct daemon_state *state) 698298a392eSIhor Antonov { 699298a392eSIhor Antonov *state = (struct daemon_state) { 70024fd3e96SIhor Antonov .buf = {0}, 70124fd3e96SIhor Antonov .pos = 0, 7024c41f4a0SIhor Antonov .argv = NULL, 703298a392eSIhor Antonov .parent_pidfh = NULL, 704298a392eSIhor Antonov .child_pidfh = NULL, 705298a392eSIhor Antonov .child_pidfile = NULL, 706298a392eSIhor Antonov .parent_pidfile = NULL, 707298a392eSIhor Antonov .title = NULL, 708298a392eSIhor Antonov .user = NULL, 7098935a399SIhor Antonov .mode = MODE_DAEMON, 710298a392eSIhor Antonov .restart_enabled = false, 7118935a399SIhor Antonov .pid = 0, 712407e3790SIhor Antonov .pipe_rd = -1, 713407e3790SIhor Antonov .pipe_wr = -1, 714298a392eSIhor Antonov .keep_cur_workdir = 1, 715298a392eSIhor Antonov .restart_delay = 1, 716298a392eSIhor Antonov .stdmask = STDOUT_FILENO | STDERR_FILENO, 717298a392eSIhor Antonov .syslog_enabled = false, 718298a392eSIhor Antonov .log_reopen = false, 719298a392eSIhor Antonov .syslog_priority = LOG_NOTICE, 720298a392eSIhor Antonov .syslog_tag = "daemon", 721298a392eSIhor Antonov .syslog_facility = LOG_DAEMON, 722298a392eSIhor Antonov .keep_fds_open = 1, 723298a392eSIhor Antonov .output_fd = -1, 724298a392eSIhor Antonov .output_filename = NULL, 725298a392eSIhor Antonov }; 726298a392eSIhor Antonov } 727cf6356fdSIhor Antonov 728cf6356fdSIhor Antonov static _Noreturn void 729cf6356fdSIhor Antonov daemon_terminate(struct daemon_state *state) 730cf6356fdSIhor Antonov { 731cf6356fdSIhor Antonov assert(state != NULL); 7328935a399SIhor Antonov 7338935a399SIhor Antonov if (state->output_fd >= 0) { 734cf6356fdSIhor Antonov close(state->output_fd); 7358935a399SIhor Antonov } 736407e3790SIhor Antonov if (state->pipe_rd >= 0) { 737407e3790SIhor Antonov close(state->pipe_rd); 7388935a399SIhor Antonov } 7398935a399SIhor Antonov 740407e3790SIhor Antonov if (state->pipe_wr >= 0) { 741407e3790SIhor Antonov close(state->pipe_wr); 7428935a399SIhor Antonov } 743cf6356fdSIhor Antonov if (state->syslog_enabled) { 744cf6356fdSIhor Antonov closelog(); 745cf6356fdSIhor Antonov } 746cf6356fdSIhor Antonov pidfile_remove(state->child_pidfh); 747cf6356fdSIhor Antonov pidfile_remove(state->parent_pidfh); 748cf6356fdSIhor Antonov 749cf6356fdSIhor Antonov /* 750cf6356fdSIhor Antonov * Note that the exit value here doesn't matter in the case of a clean 751cf6356fdSIhor Antonov * exit; daemon(3) already detached us from the caller, nothing is left 752cf6356fdSIhor Antonov * to care about this one. 753cf6356fdSIhor Antonov */ 754cf6356fdSIhor Antonov exit(1); 755cf6356fdSIhor Antonov } 7568935a399SIhor Antonov 7578935a399SIhor Antonov /* 7588eaa6be8SKonstantin Belousov * Returns true if SIGCHILD came from state->pid due to its exit. 7598935a399SIhor Antonov */ 7608935a399SIhor Antonov static bool 7618935a399SIhor Antonov daemon_is_child_dead(struct daemon_state *state) 7628935a399SIhor Antonov { 7638eaa6be8SKonstantin Belousov int status; 7648eaa6be8SKonstantin Belousov 7658935a399SIhor Antonov for (;;) { 7668eaa6be8SKonstantin Belousov int who = waitpid(-1, &status, WNOHANG); 7678eaa6be8SKonstantin Belousov if (state->pid == who && (WIFEXITED(status) || 7688eaa6be8SKonstantin Belousov WIFSIGNALED(status))) { 7698935a399SIhor Antonov return true; 7708935a399SIhor Antonov } 7718eaa6be8SKonstantin Belousov if (who == 0) { 7728eaa6be8SKonstantin Belousov return false; 7738eaa6be8SKonstantin Belousov } 7748935a399SIhor Antonov if (who == -1 && errno != EINTR) { 7758935a399SIhor Antonov warn("waitpid"); 7768935a399SIhor Antonov return false; 7778935a399SIhor Antonov } 7788935a399SIhor Antonov } 7798935a399SIhor Antonov } 7808935a399SIhor Antonov 7818935a399SIhor Antonov static void 7828935a399SIhor Antonov daemon_set_child_pipe(struct daemon_state *state) 7838935a399SIhor Antonov { 7848935a399SIhor Antonov if (state->stdmask & STDERR_FILENO) { 785407e3790SIhor Antonov if (dup2(state->pipe_wr, STDERR_FILENO) == -1) { 7868935a399SIhor Antonov err(1, "dup2"); 7878935a399SIhor Antonov } 7888935a399SIhor Antonov } 7898935a399SIhor Antonov if (state->stdmask & STDOUT_FILENO) { 790407e3790SIhor Antonov if (dup2(state->pipe_wr, STDOUT_FILENO) == -1) { 7918935a399SIhor Antonov err(1, "dup2"); 7928935a399SIhor Antonov } 7938935a399SIhor Antonov } 794407e3790SIhor Antonov if (state->pipe_wr != STDERR_FILENO && 795407e3790SIhor Antonov state->pipe_wr != STDOUT_FILENO) { 796407e3790SIhor Antonov close(state->pipe_wr); 7978935a399SIhor Antonov } 7988935a399SIhor Antonov 7998935a399SIhor Antonov /* The child gets dup'd pipes. */ 800407e3790SIhor Antonov close(state->pipe_rd); 8018935a399SIhor Antonov } 802