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> 34c6262cb6SPawel Jakub Dawidek #include <sys/param.h> 358935a399SIhor Antonov #include <sys/event.h> 3653c49998SMikolaj Golub #include <sys/mman.h> 372ad43027SMikolaj Golub #include <sys/wait.h> 38bd06a3ecSMike Barcroft 3953d49b37SJilles Tjoelker #include <fcntl.h> 40bd06a3ecSMike Barcroft #include <err.h> 41846be7bdSPoul-Henning Kamp #include <errno.h> 420a402ad2SIhor Antonov #include <getopt.h> 43c6262cb6SPawel Jakub Dawidek #include <libutil.h> 44e6d4b388STom Rhodes #include <login_cap.h> 456b3ad1d7SMaxim Sobolev #include <paths.h> 46195fc497SMikolaj Golub #include <pwd.h> 47195fc497SMikolaj Golub #include <signal.h> 48bd06a3ecSMike Barcroft #include <stdio.h> 49203df05bSIhor Antonov #include <stdbool.h> 50bd06a3ecSMike Barcroft #include <stdlib.h> 51bd06a3ecSMike Barcroft #include <unistd.h> 5253d49b37SJilles Tjoelker #include <string.h> 5353d49b37SJilles Tjoelker #include <strings.h> 5453d49b37SJilles Tjoelker #define SYSLOG_NAMES 5553d49b37SJilles Tjoelker #include <syslog.h> 5653d49b37SJilles Tjoelker #include <time.h> 5753d49b37SJilles Tjoelker #include <assert.h> 58bd06a3ecSMike Barcroft 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 68298a392eSIhor Antonov struct daemon_state { 69298a392eSIhor Antonov int pipe_fd[2]; 704c41f4a0SIhor Antonov char **argv; 71298a392eSIhor Antonov const char *child_pidfile; 72298a392eSIhor Antonov const char *parent_pidfile; 73e70444c6SIhor Antonov const char *output_filename; 746f063672SIhor Antonov const char *syslog_tag; 75298a392eSIhor Antonov const char *title; 76298a392eSIhor Antonov const char *user; 77298a392eSIhor Antonov struct pidfh *parent_pidfh; 78298a392eSIhor Antonov struct pidfh *child_pidfh; 798935a399SIhor Antonov enum daemon_mode mode; 808935a399SIhor Antonov int pid; 81298a392eSIhor Antonov int keep_cur_workdir; 82298a392eSIhor Antonov int restart_delay; 83298a392eSIhor Antonov int stdmask; 84e70444c6SIhor Antonov int syslog_priority; 856f063672SIhor Antonov int syslog_facility; 86129ec8f4SIhor Antonov int keep_fds_open; 87e70444c6SIhor Antonov int output_fd; 88298a392eSIhor Antonov bool restart_enabled; 89f2f9d31dSIhor Antonov bool syslog_enabled; 90298a392eSIhor Antonov bool log_reopen; 9153d49b37SJilles Tjoelker }; 9253d49b37SJilles Tjoelker 93e6d4b388STom Rhodes static void restrict_process(const char *); 944cd407ecSMaxim Sobolev static int open_log(const char *); 95298a392eSIhor Antonov static void reopen_log(struct daemon_state *); 96298a392eSIhor Antonov static bool listen_child(int, struct daemon_state *); 9753d49b37SJilles Tjoelker static int get_log_mapping(const char *, const CODE *); 98298a392eSIhor Antonov static void open_pid_files(struct daemon_state *); 99298a392eSIhor Antonov static void do_output(const unsigned char *, size_t, struct daemon_state *); 1008935a399SIhor Antonov static void daemon_sleep(struct daemon_state *); 101298a392eSIhor Antonov static void daemon_state_init(struct daemon_state *); 1024c41f4a0SIhor Antonov static void daemon_eventloop(struct daemon_state *); 103cf6356fdSIhor Antonov static void daemon_terminate(struct daemon_state *); 1048935a399SIhor Antonov static void daemon_exec(struct daemon_state *); 1058935a399SIhor Antonov static bool daemon_is_child_dead(struct daemon_state *); 1068935a399SIhor Antonov static void daemon_set_child_pipe(struct daemon_state *); 10753d49b37SJilles Tjoelker 1080a402ad2SIhor Antonov static const char shortopts[] = "+cfHSp:P:ru:o:s:l:t:m:R:T:h"; 1090a402ad2SIhor Antonov 1100a402ad2SIhor Antonov static const struct option longopts[] = { 1110a402ad2SIhor Antonov { "change-dir", no_argument, NULL, 'c' }, 1120a402ad2SIhor Antonov { "close-fds", no_argument, NULL, 'f' }, 1130a402ad2SIhor Antonov { "sighup", no_argument, NULL, 'H' }, 1140a402ad2SIhor Antonov { "syslog", no_argument, NULL, 'S' }, 1150a402ad2SIhor Antonov { "output-file", required_argument, NULL, 'o' }, 1160a402ad2SIhor Antonov { "output-mask", required_argument, NULL, 'm' }, 1170a402ad2SIhor Antonov { "child-pidfile", required_argument, NULL, 'p' }, 1180a402ad2SIhor Antonov { "supervisor-pidfile", required_argument, NULL, 'P' }, 1190a402ad2SIhor Antonov { "restart", no_argument, NULL, 'r' }, 1200a402ad2SIhor Antonov { "restart-delay", required_argument, NULL, 'R' }, 1210a402ad2SIhor Antonov { "title", required_argument, NULL, 't' }, 1220a402ad2SIhor Antonov { "user", required_argument, NULL, 'u' }, 1230a402ad2SIhor Antonov { "syslog-priority", required_argument, NULL, 's' }, 1240a402ad2SIhor Antonov { "syslog-facility", required_argument, NULL, 'l' }, 1250a402ad2SIhor Antonov { "syslog-tag", required_argument, NULL, 'T' }, 1260a402ad2SIhor Antonov { "help", no_argument, NULL, 'h' }, 1270a402ad2SIhor Antonov { NULL, 0, NULL, 0 } 1280a402ad2SIhor Antonov }; 1290a402ad2SIhor Antonov 1300a402ad2SIhor Antonov static _Noreturn void 1310a402ad2SIhor Antonov usage(int exitcode) 1320a402ad2SIhor Antonov { 1330a402ad2SIhor Antonov (void)fprintf(stderr, 1340a402ad2SIhor Antonov "usage: daemon [-cfHrS] [-p child_pidfile] [-P supervisor_pidfile]\n" 1350a402ad2SIhor Antonov " [-u user] [-o output_file] [-t title]\n" 1360a402ad2SIhor Antonov " [-l syslog_facility] [-s syslog_priority]\n" 1370a402ad2SIhor Antonov " [-T syslog_tag] [-m output_mask] [-R restart_delay_secs]\n" 1380a402ad2SIhor Antonov "command arguments ...\n"); 1390a402ad2SIhor Antonov 1400a402ad2SIhor Antonov (void)fprintf(stderr, 1410a402ad2SIhor Antonov " --change-dir -c Change the current working directory to root\n" 1420a402ad2SIhor Antonov " --close-fds -f Set stdin, stdout, stderr to /dev/null\n" 1430a402ad2SIhor Antonov " --sighup -H Close and re-open output file on SIGHUP\n" 1440a402ad2SIhor Antonov " --syslog -S Send output to syslog\n" 1450a402ad2SIhor Antonov " --output-file -o <file> Append output of the child process to file\n" 1460a402ad2SIhor Antonov " --output-mask -m <mask> What to send to syslog/file\n" 1470a402ad2SIhor Antonov " 1=stdout, 2=stderr, 3=both\n" 1480a402ad2SIhor Antonov " --child-pidfile -p <file> Write PID of the child process to file\n" 1490a402ad2SIhor Antonov " --supervisor-pidfile -P <file> Write PID of the supervisor process to file\n" 1500a402ad2SIhor Antonov " --restart -r Restart child if it terminates (1 sec delay)\n" 1510a402ad2SIhor Antonov " --restart-delay -R <N> Restart child if it terminates after N sec\n" 1520a402ad2SIhor Antonov " --title -t <title> Set the title of the supervisor process\n" 1530a402ad2SIhor Antonov " --user -u <user> Drop privileges, run as given user\n" 1540a402ad2SIhor Antonov " --syslog-priority -s <prio> Set syslog priority\n" 1550a402ad2SIhor Antonov " --syslog-facility -l <flty> Set syslog facility\n" 1560a402ad2SIhor Antonov " --syslog-tag -T <tag> Set syslog tag\n" 1570a402ad2SIhor Antonov " --help -h Show this help\n"); 1580a402ad2SIhor Antonov 1590a402ad2SIhor Antonov exit(exitcode); 1600a402ad2SIhor Antonov } 1610a402ad2SIhor Antonov 162bd06a3ecSMike Barcroft int 163bd06a3ecSMike Barcroft main(int argc, char *argv[]) 164bd06a3ecSMike Barcroft { 165e745dc22SIhor Antonov char *p = NULL; 166e745dc22SIhor Antonov int ch = 0; 167298a392eSIhor Antonov struct daemon_state state; 168bd06a3ecSMike Barcroft 169298a392eSIhor Antonov daemon_state_init(&state); 1709ee1faeeSIhor Antonov 1718935a399SIhor Antonov /* Signals are processed via kqueue */ 1728935a399SIhor Antonov signal(SIGHUP, SIG_IGN); 1738935a399SIhor Antonov signal(SIGTERM, SIG_IGN); 1748935a399SIhor Antonov 1759ee1faeeSIhor Antonov /* 176f907027bSIhor Antonov * Supervision mode is enabled if one of the following options are used: 177f907027bSIhor Antonov * --child-pidfile -p 178f907027bSIhor Antonov * --supervisor-pidfile -P 179f907027bSIhor Antonov * --restart -r / --restart-delay -R 180f907027bSIhor Antonov * --syslog -S 181f907027bSIhor Antonov * --syslog-facility -l 182f907027bSIhor Antonov * --syslog-priority -s 183f907027bSIhor Antonov * --syslog-tag -T 184f907027bSIhor Antonov * 185f907027bSIhor Antonov * In supervision mode daemon executes the command in a forked process 186f907027bSIhor Antonov * and observes the child by waiting for SIGCHILD. In supervision mode 187f907027bSIhor Antonov * daemon must never exit before the child, this is necessary to prevent 188f907027bSIhor Antonov * orphaning the child and leaving a stale pid file. 189f907027bSIhor Antonov * To achieve this daemon catches SIGTERM and 190f907027bSIhor Antonov * forwards it to the child, expecting to get SIGCHLD eventually. 191f907027bSIhor Antonov */ 1920a402ad2SIhor Antonov while ((ch = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) { 193bd06a3ecSMike Barcroft switch (ch) { 194bd06a3ecSMike Barcroft case 'c': 195298a392eSIhor Antonov state.keep_cur_workdir = 0; 196bd06a3ecSMike Barcroft break; 197bd06a3ecSMike Barcroft case 'f': 198298a392eSIhor Antonov state.keep_fds_open = 0; 199bd06a3ecSMike Barcroft break; 2004cd407ecSMaxim Sobolev case 'H': 201298a392eSIhor Antonov state.log_reopen = true; 2024cd407ecSMaxim Sobolev break; 20353d49b37SJilles Tjoelker case 'l': 204298a392eSIhor Antonov state.syslog_facility = get_log_mapping(optarg, 20539ea4280SIhor Antonov facilitynames); 206298a392eSIhor Antonov if (state.syslog_facility == -1) { 20753d49b37SJilles Tjoelker errx(5, "unrecognized syslog facility"); 2086b4ef4b1SIhor Antonov } 209298a392eSIhor Antonov state.syslog_enabled = true; 2108935a399SIhor Antonov state.mode = MODE_SUPERVISE; 21153d49b37SJilles Tjoelker break; 21253d49b37SJilles Tjoelker case 'm': 213298a392eSIhor Antonov state.stdmask = strtol(optarg, &p, 10); 214298a392eSIhor Antonov if (p == optarg || state.stdmask < 0 || state.stdmask > 3) { 21553d49b37SJilles Tjoelker errx(6, "unrecognized listening mask"); 2166b4ef4b1SIhor Antonov } 21753d49b37SJilles Tjoelker break; 21853d49b37SJilles Tjoelker case 'o': 219298a392eSIhor Antonov state.output_filename = optarg; 220f907027bSIhor Antonov /* 221f907027bSIhor Antonov * TODO: setting output filename doesn't have to turn 222f907027bSIhor Antonov * the supervision mode on. For non-supervised mode 223f907027bSIhor Antonov * daemon could open the specified file and set it's 224f907027bSIhor Antonov * descriptor as both stderr and stout before execve() 225f907027bSIhor Antonov */ 2268935a399SIhor Antonov state.mode = MODE_SUPERVISE; 22753d49b37SJilles Tjoelker break; 228846be7bdSPoul-Henning Kamp case 'p': 229298a392eSIhor Antonov state.child_pidfile = optarg; 2308935a399SIhor Antonov state.mode = MODE_SUPERVISE; 231846be7bdSPoul-Henning Kamp break; 23232b17786SJohn-Mark Gurney case 'P': 233298a392eSIhor Antonov state.parent_pidfile = optarg; 2348935a399SIhor Antonov state.mode = MODE_SUPERVISE; 23532b17786SJohn-Mark Gurney break; 236b6193c24SMikolaj Golub case 'r': 237298a392eSIhor Antonov state.restart_enabled = true; 2388935a399SIhor Antonov state.mode = MODE_SUPERVISE; 239b6193c24SMikolaj Golub break; 24037820b87SIan Lepore case 'R': 241298a392eSIhor Antonov state.restart_enabled = true; 242298a392eSIhor Antonov state.restart_delay = strtol(optarg, &p, 0); 243298a392eSIhor Antonov if (p == optarg || state.restart_delay < 1) { 24437820b87SIan Lepore errx(6, "invalid restart delay"); 2456b4ef4b1SIhor Antonov } 24637820b87SIan Lepore break; 24753d49b37SJilles Tjoelker case 's': 248298a392eSIhor Antonov state.syslog_priority = get_log_mapping(optarg, 24939ea4280SIhor Antonov prioritynames); 250298a392eSIhor Antonov if (state.syslog_priority == -1) { 25153d49b37SJilles Tjoelker errx(4, "unrecognized syslog priority"); 2526b4ef4b1SIhor Antonov } 253298a392eSIhor Antonov state.syslog_enabled = true; 2548935a399SIhor Antonov state.mode = MODE_SUPERVISE; 25553d49b37SJilles Tjoelker break; 25653d49b37SJilles Tjoelker case 'S': 257298a392eSIhor Antonov state.syslog_enabled = true; 2588935a399SIhor Antonov state.mode = MODE_SUPERVISE; 25953d49b37SJilles Tjoelker break; 260112bfcf5SConrad Meyer case 't': 261298a392eSIhor Antonov state.title = optarg; 262112bfcf5SConrad Meyer break; 26353d49b37SJilles Tjoelker case 'T': 264298a392eSIhor Antonov state.syslog_tag = optarg; 265298a392eSIhor Antonov state.syslog_enabled = true; 2668935a399SIhor Antonov state.mode = MODE_SUPERVISE; 26753d49b37SJilles Tjoelker break; 268e6d4b388STom Rhodes case 'u': 269298a392eSIhor Antonov state.user = optarg; 270e6d4b388STom Rhodes break; 2710a402ad2SIhor Antonov case 'h': 2720a402ad2SIhor Antonov usage(0); 2730a402ad2SIhor Antonov __builtin_unreachable(); 274bd06a3ecSMike Barcroft default: 2750a402ad2SIhor Antonov usage(1); 276bd06a3ecSMike Barcroft } 277bd06a3ecSMike Barcroft } 278bd06a3ecSMike Barcroft argc -= optind; 279bd06a3ecSMike Barcroft argv += optind; 2804c41f4a0SIhor Antonov state.argv = argv; 281bd06a3ecSMike Barcroft 2826b4ef4b1SIhor Antonov if (argc == 0) { 2830a402ad2SIhor Antonov usage(1); 2846b4ef4b1SIhor Antonov } 28512d7249eSTom Rhodes 286298a392eSIhor Antonov if (!state.title) { 287298a392eSIhor Antonov state.title = argv[0]; 2886b4ef4b1SIhor Antonov } 28953d49b37SJilles Tjoelker 290298a392eSIhor Antonov if (state.output_filename) { 291298a392eSIhor Antonov state.output_fd = open_log(state.output_filename); 292298a392eSIhor Antonov if (state.output_fd == -1) { 29353d49b37SJilles Tjoelker err(7, "open"); 29453d49b37SJilles Tjoelker } 2956b4ef4b1SIhor Antonov } 29653d49b37SJilles Tjoelker 297298a392eSIhor Antonov if (state.syslog_enabled) { 298298a392eSIhor Antonov openlog(state.syslog_tag, LOG_PID | LOG_NDELAY, 299298a392eSIhor Antonov state.syslog_facility); 3006b4ef4b1SIhor Antonov } 30153d49b37SJilles Tjoelker 302846be7bdSPoul-Henning Kamp /* 303846be7bdSPoul-Henning Kamp * Try to open the pidfile before calling daemon(3), 304846be7bdSPoul-Henning Kamp * to be able to report the error intelligently 305846be7bdSPoul-Henning Kamp */ 306298a392eSIhor Antonov open_pid_files(&state); 3078935a399SIhor Antonov 3088935a399SIhor Antonov /* 3098935a399SIhor Antonov * TODO: add feature to avoid backgrounding 3108935a399SIhor Antonov * i.e. --foreground, -f 3118935a399SIhor Antonov */ 312298a392eSIhor Antonov if (daemon(state.keep_cur_workdir, state.keep_fds_open) == -1) { 3139da0ef13SMikolaj Golub warn("daemon"); 314cf6356fdSIhor Antonov daemon_terminate(&state); 3159da0ef13SMikolaj Golub } 3168935a399SIhor Antonov 3178935a399SIhor Antonov if (state.mode == MODE_DAEMON) { 3188935a399SIhor Antonov daemon_exec(&state); 3198935a399SIhor Antonov } 3208935a399SIhor Antonov 3219da0ef13SMikolaj Golub /* Write out parent pidfile if needed. */ 322298a392eSIhor Antonov pidfile_write(state.parent_pidfh); 323203df05bSIhor Antonov 3244c41f4a0SIhor Antonov do { 3258935a399SIhor Antonov state.mode = MODE_SUPERVISE; 3264c41f4a0SIhor Antonov daemon_eventloop(&state); 3278935a399SIhor Antonov daemon_sleep(&state); 3288935a399SIhor Antonov } while (state.restart_enabled); 3294c41f4a0SIhor Antonov 3304c41f4a0SIhor Antonov daemon_terminate(&state); 3314c41f4a0SIhor Antonov } 3324c41f4a0SIhor Antonov 3338935a399SIhor Antonov static void 3348935a399SIhor Antonov daemon_exec(struct daemon_state *state) 3358935a399SIhor Antonov { 3368935a399SIhor Antonov pidfile_write(state->child_pidfh); 3374c41f4a0SIhor Antonov 3388935a399SIhor Antonov if (state->user != NULL) { 3398935a399SIhor Antonov restrict_process(state->user); 3408935a399SIhor Antonov } 3418935a399SIhor Antonov 3428935a399SIhor Antonov /* Ignored signals remain ignored after execve, unignore them */ 3438935a399SIhor Antonov signal(SIGHUP, SIG_DFL); 3448935a399SIhor Antonov signal(SIGTERM, SIG_DFL); 3458935a399SIhor Antonov execvp(state->argv[0], state->argv); 3468935a399SIhor Antonov /* execvp() failed - report error and exit this process */ 3478935a399SIhor Antonov err(1, "%s", state->argv[0]); 3488935a399SIhor Antonov } 3498935a399SIhor Antonov 3508935a399SIhor Antonov /* Main event loop: fork the child and watch for events. 3518935a399SIhor Antonov * After SIGTERM is recieved and propagated to the child there are 3524c41f4a0SIhor Antonov * several options on what to do next: 3534c41f4a0SIhor Antonov * - read until EOF 3544c41f4a0SIhor Antonov * - read until EOF but only for a while 3554c41f4a0SIhor Antonov * - bail immediately 3564c41f4a0SIhor Antonov * Currently the third option is used, because otherwise there is no 3574c41f4a0SIhor Antonov * guarantee that read() won't block indefinitely if the child refuses 3584c41f4a0SIhor Antonov * to depart. To handle the second option, a different approach 3594c41f4a0SIhor Antonov * would be needed (procctl()?). 3604c41f4a0SIhor Antonov */ 3614c41f4a0SIhor Antonov static void 3624c41f4a0SIhor Antonov daemon_eventloop(struct daemon_state *state) 3634c41f4a0SIhor Antonov { 3648935a399SIhor Antonov struct kevent event; 3658935a399SIhor Antonov int kq; 3668935a399SIhor Antonov int ret; 3678935a399SIhor Antonov 3688935a399SIhor Antonov /* 3698935a399SIhor Antonov * Try to protect against pageout kill. Ignore the 3708935a399SIhor Antonov * error, madvise(2) will fail only if a process does 3718935a399SIhor Antonov * not have superuser privileges. 3728935a399SIhor Antonov */ 3738935a399SIhor Antonov (void)madvise(NULL, 0, MADV_PROTECT); 3748935a399SIhor Antonov 3754c41f4a0SIhor Antonov if (pipe(state->pipe_fd)) { 37653d49b37SJilles Tjoelker err(1, "pipe"); 3776b4ef4b1SIhor Antonov } 3788935a399SIhor Antonov 3798935a399SIhor Antonov kq = kqueuex(KQUEUE_CLOEXEC); 3808935a399SIhor Antonov EV_SET(&event, state->pipe_fd[0], EVFILT_READ, EV_ADD|EV_CLEAR, 0, 0, 3818935a399SIhor Antonov NULL); 3828935a399SIhor Antonov if (kevent(kq, &event, 1, NULL, 0, NULL) == -1) { 3838935a399SIhor Antonov err(EXIT_FAILURE, "failed to register kevent"); 38475f61ca9SIhor Antonov } 38575f61ca9SIhor Antonov 3868935a399SIhor Antonov EV_SET(&event, SIGHUP, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); 3878935a399SIhor Antonov if (kevent(kq, &event, 1, NULL, 0, NULL) == -1) { 3888935a399SIhor Antonov err(EXIT_FAILURE, "failed to register kevent"); 3898935a399SIhor Antonov } 3908935a399SIhor Antonov 3918935a399SIhor Antonov EV_SET(&event, SIGTERM, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); 3928935a399SIhor Antonov if (kevent(kq, &event, 1, NULL, 0, NULL) == -1) { 3938935a399SIhor Antonov err(EXIT_FAILURE, "failed to register kevent"); 3948935a399SIhor Antonov } 3958935a399SIhor Antonov 3968935a399SIhor Antonov EV_SET(&event, SIGCHLD, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); 3978935a399SIhor Antonov if (kevent(kq, &event, 1, NULL, 0, NULL) == -1) { 3988935a399SIhor Antonov err(EXIT_FAILURE, "failed to register kevent"); 3998935a399SIhor Antonov } 4008935a399SIhor Antonov memset(&event, 0, sizeof(struct kevent)); 4018935a399SIhor Antonov 4028935a399SIhor Antonov /* Spawn a child to exec the command. */ 4038935a399SIhor Antonov state->pid = fork(); 4048935a399SIhor Antonov 40575f61ca9SIhor Antonov /* fork failed, this can only happen when supervision is enabled */ 4068935a399SIhor Antonov switch (state->pid) { 4078935a399SIhor Antonov case -1: 4089da0ef13SMikolaj Golub warn("fork"); 4098935a399SIhor Antonov state->mode = MODE_NOCHILD; 4108935a399SIhor Antonov return; 4118935a399SIhor Antonov /* fork succeeded, this is child's branch */ 4128935a399SIhor Antonov case 0: 4138935a399SIhor Antonov close(kq); 4148935a399SIhor Antonov daemon_set_child_pipe(state); 4158935a399SIhor Antonov daemon_exec(state); 41653d49b37SJilles Tjoelker break; 417cd1e6e70SIhor Antonov } 418cd1e6e70SIhor Antonov 4198935a399SIhor Antonov /* case: pid > 0; fork succeeded */ 4208935a399SIhor Antonov close(state->pipe_fd[1]); 4218935a399SIhor Antonov state->pipe_fd[1] = -1; 4228935a399SIhor Antonov setproctitle("%s[%d]", state->title, (int)state->pid); 423*cec8e6baSDag-Erling Smørgrav setbuf(stdout, NULL); 424cd1e6e70SIhor Antonov 4258935a399SIhor Antonov while (state->mode != MODE_NOCHILD) { 4268935a399SIhor Antonov ret = kevent(kq, NULL, 0, &event, 1, NULL); 4278935a399SIhor Antonov switch (ret) { 4288935a399SIhor Antonov case -1: 429494e7dfdSKyle Evans if (errno == EINTR) 430494e7dfdSKyle Evans continue; 4318935a399SIhor Antonov err(EXIT_FAILURE, "kevent wait"); 4328935a399SIhor Antonov case 0: 433cd1e6e70SIhor Antonov continue; 43453d49b37SJilles Tjoelker } 435cd1e6e70SIhor Antonov 4368935a399SIhor Antonov if (event.flags & EV_ERROR) { 4378935a399SIhor Antonov errx(EXIT_FAILURE, "Event error: %s", 4388935a399SIhor Antonov strerror(event.data)); 439cd1e6e70SIhor Antonov } 440cd1e6e70SIhor Antonov 4418935a399SIhor Antonov switch (event.filter) { 4428935a399SIhor Antonov case EVFILT_SIGNAL: 443cd1e6e70SIhor Antonov 4448935a399SIhor Antonov switch (event.ident) { 4458935a399SIhor Antonov case SIGCHLD: 4468935a399SIhor Antonov if (daemon_is_child_dead(state)) { 4478935a399SIhor Antonov /* child is dead, read all until EOF */ 4488935a399SIhor Antonov state->pid = -1; 4498935a399SIhor Antonov state->mode = MODE_NOCHILD; 4508935a399SIhor Antonov while (listen_child(state->pipe_fd[0], 4518935a399SIhor Antonov state)) 4528935a399SIhor Antonov ; 4538935a399SIhor Antonov } 4548935a399SIhor Antonov continue; 4558935a399SIhor Antonov case SIGTERM: 4568935a399SIhor Antonov if (state->mode != MODE_SUPERVISE) { 4578935a399SIhor Antonov /* user is impatient */ 4588935a399SIhor Antonov /* TODO: warn about repeated SIGTERM? */ 4598935a399SIhor Antonov continue; 460cd1e6e70SIhor Antonov } 461cd1e6e70SIhor Antonov 4628935a399SIhor Antonov state->mode = MODE_TERMINATING; 4638935a399SIhor Antonov state->restart_enabled = false; 4648935a399SIhor Antonov if (state->pid > 0) { 4658935a399SIhor Antonov kill(state->pid, SIGTERM); 46653d49b37SJilles Tjoelker } 4674c41f4a0SIhor Antonov /* 4688935a399SIhor Antonov * TODO set kevent timer to exit 4698935a399SIhor Antonov * unconditionally after some time 4704c41f4a0SIhor Antonov */ 4718935a399SIhor Antonov continue; 4728935a399SIhor Antonov case SIGHUP: 4738935a399SIhor Antonov if (state->log_reopen && state->output_fd >= 0) { 4748935a399SIhor Antonov reopen_log(state); 4758935a399SIhor Antonov } 4768935a399SIhor Antonov continue; 4778935a399SIhor Antonov } 4788935a399SIhor Antonov break; 4798935a399SIhor Antonov 4808935a399SIhor Antonov case EVFILT_READ: 4818935a399SIhor Antonov /* 4828935a399SIhor Antonov * detecting EOF is no longer necessary 4838935a399SIhor Antonov * if child closes the pipe daemon will stop getting 4848935a399SIhor Antonov * EVFILT_READ events 4858935a399SIhor Antonov */ 4868935a399SIhor Antonov 4878935a399SIhor Antonov if (event.data > 0) { 4888935a399SIhor Antonov (void)listen_child(state->pipe_fd[0], state); 4898935a399SIhor Antonov } 4908935a399SIhor Antonov continue; 4918935a399SIhor Antonov default: 4928935a399SIhor Antonov continue; 4938935a399SIhor Antonov } 49453d49b37SJilles Tjoelker } 4954c41f4a0SIhor Antonov 4968935a399SIhor Antonov close(kq); 4978935a399SIhor Antonov close(state->pipe_fd[0]); 4988935a399SIhor Antonov state->pipe_fd[0] = -1; 499bd06a3ecSMike Barcroft } 500bd06a3ecSMike Barcroft 501bd06a3ecSMike Barcroft static void 5028935a399SIhor Antonov daemon_sleep(struct daemon_state *state) 503195fc497SMikolaj Golub { 5048935a399SIhor Antonov struct timespec ts = { state->restart_delay, 0 }; 50509a3675dSConrad Meyer 5068935a399SIhor Antonov if (!state->restart_enabled) { 5078935a399SIhor Antonov return; 5088935a399SIhor Antonov } 5098935a399SIhor Antonov while (nanosleep(&ts, &ts) == -1) { 5106b4ef4b1SIhor Antonov if (errno != EINTR) { 51153d49b37SJilles Tjoelker err(1, "nanosleep"); 51253d49b37SJilles Tjoelker } 51353d49b37SJilles Tjoelker } 5146b4ef4b1SIhor Antonov } 51553d49b37SJilles Tjoelker 51653d49b37SJilles Tjoelker static void 517298a392eSIhor Antonov open_pid_files(struct daemon_state *state) 51853d49b37SJilles Tjoelker { 51953d49b37SJilles Tjoelker pid_t fpid; 52053d49b37SJilles Tjoelker int serrno; 52153d49b37SJilles Tjoelker 522298a392eSIhor Antonov if (state->child_pidfile) { 523298a392eSIhor Antonov state->child_pidfh = pidfile_open(state->child_pidfile, 0600, &fpid); 524298a392eSIhor Antonov if (state->child_pidfh == NULL) { 52553d49b37SJilles Tjoelker if (errno == EEXIST) { 52653d49b37SJilles Tjoelker errx(3, "process already running, pid: %d", 52753d49b37SJilles Tjoelker fpid); 52853d49b37SJilles Tjoelker } 529298a392eSIhor Antonov err(2, "pidfile ``%s''", state->child_pidfile); 53053d49b37SJilles Tjoelker } 53153d49b37SJilles Tjoelker } 53253d49b37SJilles Tjoelker /* Do the same for the actual daemon process. */ 533298a392eSIhor Antonov if (state->parent_pidfile) { 534298a392eSIhor Antonov state->parent_pidfh= pidfile_open(state->parent_pidfile, 0600, &fpid); 535298a392eSIhor Antonov if (state->parent_pidfh == NULL) { 53653d49b37SJilles Tjoelker serrno = errno; 537298a392eSIhor Antonov pidfile_remove(state->child_pidfh); 53853d49b37SJilles Tjoelker errno = serrno; 53953d49b37SJilles Tjoelker if (errno == EEXIST) { 54053d49b37SJilles Tjoelker errx(3, "process already running, pid: %d", 54153d49b37SJilles Tjoelker fpid); 54253d49b37SJilles Tjoelker } 543298a392eSIhor Antonov err(2, "ppidfile ``%s''", state->parent_pidfile); 54453d49b37SJilles Tjoelker } 54553d49b37SJilles Tjoelker } 54653d49b37SJilles Tjoelker } 54753d49b37SJilles Tjoelker 54853d49b37SJilles Tjoelker static int 54953d49b37SJilles Tjoelker get_log_mapping(const char *str, const CODE *c) 55053d49b37SJilles Tjoelker { 55153d49b37SJilles Tjoelker const CODE *cp; 55253d49b37SJilles Tjoelker for (cp = c; cp->c_name; cp++) 5536b4ef4b1SIhor Antonov if (strcmp(cp->c_name, str) == 0) { 55453d49b37SJilles Tjoelker return cp->c_val; 5556b4ef4b1SIhor Antonov } 55653d49b37SJilles Tjoelker return -1; 557195fc497SMikolaj Golub } 558195fc497SMikolaj Golub 559195fc497SMikolaj Golub static void 560e6d4b388STom Rhodes restrict_process(const char *user) 56112d7249eSTom Rhodes { 56212d7249eSTom Rhodes struct passwd *pw = NULL; 56312d7249eSTom Rhodes 564e6d4b388STom Rhodes pw = getpwnam(user); 5656b4ef4b1SIhor Antonov if (pw == NULL) { 566e6d4b388STom Rhodes errx(1, "unknown user: %s", user); 5676b4ef4b1SIhor Antonov } 56812d7249eSTom Rhodes 5696b4ef4b1SIhor Antonov if (setusercontext(NULL, pw, pw->pw_uid, LOGIN_SETALL) != 0) { 570e6d4b388STom Rhodes errx(1, "failed to set user environment"); 5716b4ef4b1SIhor Antonov } 5726b3ad1d7SMaxim Sobolev 5736b3ad1d7SMaxim Sobolev setenv("USER", pw->pw_name, 1); 5746b3ad1d7SMaxim Sobolev setenv("HOME", pw->pw_dir, 1); 5756b3ad1d7SMaxim Sobolev setenv("SHELL", *pw->pw_shell ? pw->pw_shell : _PATH_BSHELL, 1); 57612d7249eSTom Rhodes } 57712d7249eSTom Rhodes 57853d49b37SJilles Tjoelker /* 57953d49b37SJilles Tjoelker * We try to collect whole lines terminated by '\n'. Otherwise we collect a 58053d49b37SJilles Tjoelker * full buffer, and then output it. 58153d49b37SJilles Tjoelker * 582bc43a9a7SIhor Antonov * Return value of false is assumed to mean EOF or error, and true indicates to 58353d49b37SJilles Tjoelker * continue reading. 5848935a399SIhor Antonov * 5858935a399SIhor Antonov * TODO: simplify signature - state contains pipefd 58653d49b37SJilles Tjoelker */ 587bc43a9a7SIhor Antonov static bool 588298a392eSIhor Antonov listen_child(int fd, struct daemon_state *state) 5892ad43027SMikolaj Golub { 59053d49b37SJilles Tjoelker static unsigned char buf[LBUF_SIZE]; 59153d49b37SJilles Tjoelker static size_t bytes_read = 0; 59253d49b37SJilles Tjoelker int rv; 5932ad43027SMikolaj Golub 594cf6356fdSIhor Antonov assert(state != NULL); 59553d49b37SJilles Tjoelker assert(bytes_read < LBUF_SIZE - 1); 59653d49b37SJilles Tjoelker 59753d49b37SJilles Tjoelker rv = read(fd, buf + bytes_read, LBUF_SIZE - bytes_read - 1); 59853d49b37SJilles Tjoelker if (rv > 0) { 59953d49b37SJilles Tjoelker unsigned char *cp; 60053d49b37SJilles Tjoelker 60153d49b37SJilles Tjoelker bytes_read += rv; 60253d49b37SJilles Tjoelker assert(bytes_read <= LBUF_SIZE - 1); 60353d49b37SJilles Tjoelker /* Always NUL-terminate just in case. */ 60453d49b37SJilles Tjoelker buf[LBUF_SIZE - 1] = '\0'; 60553d49b37SJilles Tjoelker /* 60653d49b37SJilles Tjoelker * Chomp line by line until we run out of buffer. 60753d49b37SJilles Tjoelker * This does not take NUL characters into account. 60853d49b37SJilles Tjoelker */ 60953d49b37SJilles Tjoelker while ((cp = memchr(buf, '\n', bytes_read)) != NULL) { 61053d49b37SJilles Tjoelker size_t bytes_line = cp - buf + 1; 61153d49b37SJilles Tjoelker assert(bytes_line <= bytes_read); 612298a392eSIhor Antonov do_output(buf, bytes_line, state); 61353d49b37SJilles Tjoelker bytes_read -= bytes_line; 61453d49b37SJilles Tjoelker memmove(buf, cp + 1, bytes_read); 615195fc497SMikolaj Golub } 61653d49b37SJilles Tjoelker /* Wait until the buffer is full. */ 6176b4ef4b1SIhor Antonov if (bytes_read < LBUF_SIZE - 1) { 618bc43a9a7SIhor Antonov return true; 6196b4ef4b1SIhor Antonov } 620298a392eSIhor Antonov do_output(buf, bytes_read, state); 62153d49b37SJilles Tjoelker bytes_read = 0; 622bc43a9a7SIhor Antonov return true; 62353d49b37SJilles Tjoelker } else if (rv == -1) { 62453d49b37SJilles Tjoelker /* EINTR should trigger another read. */ 62553d49b37SJilles Tjoelker if (errno == EINTR) { 626bc43a9a7SIhor Antonov return true; 62753d49b37SJilles Tjoelker } else { 62853d49b37SJilles Tjoelker warn("read"); 629bc43a9a7SIhor Antonov return false; 630c60d51f9SMikolaj Golub } 63153d49b37SJilles Tjoelker } 63253d49b37SJilles Tjoelker /* Upon EOF, we have to flush what's left of the buffer. */ 63353d49b37SJilles Tjoelker if (bytes_read > 0) { 634298a392eSIhor Antonov do_output(buf, bytes_read, state); 63553d49b37SJilles Tjoelker bytes_read = 0; 63653d49b37SJilles Tjoelker } 637bc43a9a7SIhor Antonov return false; 63853d49b37SJilles Tjoelker } 63953d49b37SJilles Tjoelker 64053d49b37SJilles Tjoelker /* 64153d49b37SJilles Tjoelker * The default behavior is to stay silent if the user wants to redirect 64253d49b37SJilles Tjoelker * output to a file and/or syslog. If neither are provided, then we bounce 64353d49b37SJilles Tjoelker * everything back to parent's stdout. 64453d49b37SJilles Tjoelker */ 64553d49b37SJilles Tjoelker static void 646298a392eSIhor Antonov do_output(const unsigned char *buf, size_t len, struct daemon_state *state) 64753d49b37SJilles Tjoelker { 64853d49b37SJilles Tjoelker assert(len <= LBUF_SIZE); 649cf6356fdSIhor Antonov assert(state != NULL); 65053d49b37SJilles Tjoelker 6516b4ef4b1SIhor Antonov if (len < 1) { 65253d49b37SJilles Tjoelker return; 6536b4ef4b1SIhor Antonov } 654298a392eSIhor Antonov if (state->syslog_enabled) { 655298a392eSIhor Antonov syslog(state->syslog_priority, "%.*s", (int)len, buf); 6566b4ef4b1SIhor Antonov } 657298a392eSIhor Antonov if (state->output_fd != -1) { 658298a392eSIhor Antonov if (write(state->output_fd, buf, len) == -1) 65953d49b37SJilles Tjoelker warn("write"); 66053d49b37SJilles Tjoelker } 661298a392eSIhor Antonov if (state->keep_fds_open && 662298a392eSIhor Antonov !state->syslog_enabled && 663298a392eSIhor Antonov state->output_fd == -1) { 66453d49b37SJilles Tjoelker printf("%.*s", (int)len, buf); 66553d49b37SJilles Tjoelker } 6666b4ef4b1SIhor Antonov } 66753d49b37SJilles Tjoelker 6684cd407ecSMaxim Sobolev static int 6694cd407ecSMaxim Sobolev open_log(const char *outfn) 6704cd407ecSMaxim Sobolev { 6714cd407ecSMaxim Sobolev 6724cd407ecSMaxim Sobolev return open(outfn, O_CREAT | O_WRONLY | O_APPEND | O_CLOEXEC, 0600); 6734cd407ecSMaxim Sobolev } 6744cd407ecSMaxim Sobolev 6754cd407ecSMaxim Sobolev static void 676298a392eSIhor Antonov reopen_log(struct daemon_state *state) 6774cd407ecSMaxim Sobolev { 6784cd407ecSMaxim Sobolev int outfd; 6794cd407ecSMaxim Sobolev 680298a392eSIhor Antonov outfd = open_log(state->output_filename); 681298a392eSIhor Antonov if (state->output_fd >= 0) { 682298a392eSIhor Antonov close(state->output_fd); 6836b4ef4b1SIhor Antonov } 684298a392eSIhor Antonov state->output_fd = outfd; 6854cd407ecSMaxim Sobolev } 6864cd407ecSMaxim Sobolev 687298a392eSIhor Antonov static void 688298a392eSIhor Antonov daemon_state_init(struct daemon_state *state) 689298a392eSIhor Antonov { 690298a392eSIhor Antonov *state = (struct daemon_state) { 691298a392eSIhor Antonov .pipe_fd = { -1, -1 }, 6924c41f4a0SIhor Antonov .argv = NULL, 693298a392eSIhor Antonov .parent_pidfh = NULL, 694298a392eSIhor Antonov .child_pidfh = NULL, 695298a392eSIhor Antonov .child_pidfile = NULL, 696298a392eSIhor Antonov .parent_pidfile = NULL, 697298a392eSIhor Antonov .title = NULL, 698298a392eSIhor Antonov .user = NULL, 6998935a399SIhor Antonov .mode = MODE_DAEMON, 700298a392eSIhor Antonov .restart_enabled = false, 7018935a399SIhor Antonov .pid = 0, 702298a392eSIhor Antonov .keep_cur_workdir = 1, 703298a392eSIhor Antonov .restart_delay = 1, 704298a392eSIhor Antonov .stdmask = STDOUT_FILENO | STDERR_FILENO, 705298a392eSIhor Antonov .syslog_enabled = false, 706298a392eSIhor Antonov .log_reopen = false, 707298a392eSIhor Antonov .syslog_priority = LOG_NOTICE, 708298a392eSIhor Antonov .syslog_tag = "daemon", 709298a392eSIhor Antonov .syslog_facility = LOG_DAEMON, 710298a392eSIhor Antonov .keep_fds_open = 1, 711298a392eSIhor Antonov .output_fd = -1, 712298a392eSIhor Antonov .output_filename = NULL, 713298a392eSIhor Antonov }; 714298a392eSIhor Antonov } 715cf6356fdSIhor Antonov 716cf6356fdSIhor Antonov static _Noreturn void 717cf6356fdSIhor Antonov daemon_terminate(struct daemon_state *state) 718cf6356fdSIhor Antonov { 719cf6356fdSIhor Antonov assert(state != NULL); 7208935a399SIhor Antonov 7218935a399SIhor Antonov if (state->output_fd >= 0) { 722cf6356fdSIhor Antonov close(state->output_fd); 7238935a399SIhor Antonov } 7248935a399SIhor Antonov if (state->pipe_fd[0] >= 0) { 725cf6356fdSIhor Antonov close(state->pipe_fd[0]); 7268935a399SIhor Antonov } 7278935a399SIhor Antonov 7288935a399SIhor Antonov if (state->pipe_fd[1] >= 0) { 729cf6356fdSIhor Antonov close(state->pipe_fd[1]); 7308935a399SIhor Antonov } 731cf6356fdSIhor Antonov if (state->syslog_enabled) { 732cf6356fdSIhor Antonov closelog(); 733cf6356fdSIhor Antonov } 734cf6356fdSIhor Antonov pidfile_remove(state->child_pidfh); 735cf6356fdSIhor Antonov pidfile_remove(state->parent_pidfh); 736cf6356fdSIhor Antonov 737cf6356fdSIhor Antonov /* 738cf6356fdSIhor Antonov * Note that the exit value here doesn't matter in the case of a clean 739cf6356fdSIhor Antonov * exit; daemon(3) already detached us from the caller, nothing is left 740cf6356fdSIhor Antonov * to care about this one. 741cf6356fdSIhor Antonov */ 742cf6356fdSIhor Antonov exit(1); 743cf6356fdSIhor Antonov } 7448935a399SIhor Antonov 7458935a399SIhor Antonov /* 7468935a399SIhor Antonov * Returns true if SIGCHILD came from state->pid 7478935a399SIhor Antonov * This function could hang if SIGCHILD was emittied for a reason other than 7488935a399SIhor Antonov * child dying (e.g., ptrace attach). 7498935a399SIhor Antonov */ 7508935a399SIhor Antonov static bool 7518935a399SIhor Antonov daemon_is_child_dead(struct daemon_state *state) 7528935a399SIhor Antonov { 7538935a399SIhor Antonov for (;;) { 7548935a399SIhor Antonov int who = waitpid(-1, NULL, WNOHANG); 7558935a399SIhor Antonov if (state->pid == who) { 7568935a399SIhor Antonov return true; 7578935a399SIhor Antonov } 7588935a399SIhor Antonov if (who == -1 && errno != EINTR) { 7598935a399SIhor Antonov warn("waitpid"); 7608935a399SIhor Antonov return false; 7618935a399SIhor Antonov } 7628935a399SIhor Antonov } 7638935a399SIhor Antonov } 7648935a399SIhor Antonov 7658935a399SIhor Antonov static void 7668935a399SIhor Antonov daemon_set_child_pipe(struct daemon_state *state) 7678935a399SIhor Antonov { 7688935a399SIhor Antonov if (state->stdmask & STDERR_FILENO) { 7698935a399SIhor Antonov if (dup2(state->pipe_fd[1], STDERR_FILENO) == -1) { 7708935a399SIhor Antonov err(1, "dup2"); 7718935a399SIhor Antonov } 7728935a399SIhor Antonov } 7738935a399SIhor Antonov if (state->stdmask & STDOUT_FILENO) { 7748935a399SIhor Antonov if (dup2(state->pipe_fd[1], STDOUT_FILENO) == -1) { 7758935a399SIhor Antonov err(1, "dup2"); 7768935a399SIhor Antonov } 7778935a399SIhor Antonov } 7788935a399SIhor Antonov if (state->pipe_fd[1] != STDERR_FILENO && 7798935a399SIhor Antonov state->pipe_fd[1] != STDOUT_FILENO) { 7808935a399SIhor Antonov close(state->pipe_fd[1]); 7818935a399SIhor Antonov } 7828935a399SIhor Antonov 7838935a399SIhor Antonov /* The child gets dup'd pipes. */ 7848935a399SIhor Antonov close(state->pipe_fd[0]); 7858935a399SIhor Antonov } 786