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> 378935a399SIhor Antonov #include <sys/event.h> 3853c49998SMikolaj Golub #include <sys/mman.h> 392ad43027SMikolaj Golub #include <sys/wait.h> 40bd06a3ecSMike Barcroft 4153d49b37SJilles Tjoelker #include <fcntl.h> 42bd06a3ecSMike Barcroft #include <err.h> 43846be7bdSPoul-Henning Kamp #include <errno.h> 440a402ad2SIhor Antonov #include <getopt.h> 45c6262cb6SPawel Jakub Dawidek #include <libutil.h> 46e6d4b388STom Rhodes #include <login_cap.h> 476b3ad1d7SMaxim Sobolev #include <paths.h> 48195fc497SMikolaj Golub #include <pwd.h> 49195fc497SMikolaj Golub #include <signal.h> 50bd06a3ecSMike Barcroft #include <stdio.h> 51203df05bSIhor Antonov #include <stdbool.h> 52bd06a3ecSMike Barcroft #include <stdlib.h> 53bd06a3ecSMike Barcroft #include <unistd.h> 5453d49b37SJilles Tjoelker #include <string.h> 5553d49b37SJilles Tjoelker #include <strings.h> 5653d49b37SJilles Tjoelker #define SYSLOG_NAMES 5753d49b37SJilles Tjoelker #include <syslog.h> 5853d49b37SJilles Tjoelker #include <time.h> 5953d49b37SJilles Tjoelker #include <assert.h> 60bd06a3ecSMike Barcroft 6153d49b37SJilles Tjoelker #define LBUF_SIZE 4096 6253d49b37SJilles Tjoelker 638935a399SIhor Antonov enum daemon_mode { 648935a399SIhor Antonov MODE_DAEMON = 0, /* simply daemonize, no supervision */ 658935a399SIhor Antonov MODE_SUPERVISE, /* initial supervision state */ 668935a399SIhor Antonov MODE_TERMINATING, /* user requested termination */ 678935a399SIhor Antonov MODE_NOCHILD, /* child is terminated, final state of the event loop */ 688935a399SIhor Antonov }; 698935a399SIhor Antonov 70298a392eSIhor Antonov struct daemon_state { 71298a392eSIhor Antonov int pipe_fd[2]; 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; 83298a392eSIhor Antonov int keep_cur_workdir; 84298a392eSIhor Antonov int restart_delay; 85298a392eSIhor Antonov int stdmask; 86e70444c6SIhor Antonov int syslog_priority; 876f063672SIhor Antonov int syslog_facility; 88129ec8f4SIhor Antonov int keep_fds_open; 89e70444c6SIhor Antonov int output_fd; 90298a392eSIhor Antonov bool restart_enabled; 91f2f9d31dSIhor Antonov bool syslog_enabled; 92298a392eSIhor Antonov bool log_reopen; 9353d49b37SJilles Tjoelker }; 9453d49b37SJilles Tjoelker 95e6d4b388STom Rhodes static void restrict_process(const char *); 964cd407ecSMaxim Sobolev static int open_log(const char *); 97298a392eSIhor Antonov static void reopen_log(struct daemon_state *); 98298a392eSIhor Antonov static bool listen_child(int, struct daemon_state *); 9953d49b37SJilles Tjoelker static int get_log_mapping(const char *, const CODE *); 100298a392eSIhor Antonov static void open_pid_files(struct daemon_state *); 101298a392eSIhor Antonov static void do_output(const unsigned char *, size_t, struct daemon_state *); 1028935a399SIhor Antonov static void daemon_sleep(struct daemon_state *); 103298a392eSIhor Antonov static void daemon_state_init(struct daemon_state *); 1044c41f4a0SIhor Antonov static void daemon_eventloop(struct daemon_state *); 105cf6356fdSIhor Antonov static void daemon_terminate(struct daemon_state *); 1068935a399SIhor Antonov static void daemon_exec(struct daemon_state *); 1078935a399SIhor Antonov static bool daemon_is_child_dead(struct daemon_state *); 1088935a399SIhor Antonov static void daemon_set_child_pipe(struct daemon_state *); 10953d49b37SJilles Tjoelker 1100a402ad2SIhor Antonov static const char shortopts[] = "+cfHSp:P:ru:o:s:l:t:m:R:T:h"; 1110a402ad2SIhor Antonov 1120a402ad2SIhor Antonov static const struct option longopts[] = { 1130a402ad2SIhor Antonov { "change-dir", no_argument, NULL, 'c' }, 1140a402ad2SIhor Antonov { "close-fds", no_argument, NULL, 'f' }, 1150a402ad2SIhor Antonov { "sighup", no_argument, NULL, 'H' }, 1160a402ad2SIhor Antonov { "syslog", no_argument, NULL, 'S' }, 1170a402ad2SIhor Antonov { "output-file", required_argument, NULL, 'o' }, 1180a402ad2SIhor Antonov { "output-mask", required_argument, NULL, 'm' }, 1190a402ad2SIhor Antonov { "child-pidfile", required_argument, NULL, 'p' }, 1200a402ad2SIhor Antonov { "supervisor-pidfile", required_argument, NULL, 'P' }, 1210a402ad2SIhor Antonov { "restart", no_argument, NULL, 'r' }, 1220a402ad2SIhor Antonov { "restart-delay", required_argument, NULL, 'R' }, 1230a402ad2SIhor Antonov { "title", required_argument, NULL, 't' }, 1240a402ad2SIhor Antonov { "user", required_argument, NULL, 'u' }, 1250a402ad2SIhor Antonov { "syslog-priority", required_argument, NULL, 's' }, 1260a402ad2SIhor Antonov { "syslog-facility", required_argument, NULL, 'l' }, 1270a402ad2SIhor Antonov { "syslog-tag", required_argument, NULL, 'T' }, 1280a402ad2SIhor Antonov { "help", no_argument, NULL, 'h' }, 1290a402ad2SIhor Antonov { NULL, 0, NULL, 0 } 1300a402ad2SIhor Antonov }; 1310a402ad2SIhor Antonov 1320a402ad2SIhor Antonov static _Noreturn void 1330a402ad2SIhor Antonov usage(int exitcode) 1340a402ad2SIhor Antonov { 1350a402ad2SIhor Antonov (void)fprintf(stderr, 1360a402ad2SIhor Antonov "usage: daemon [-cfHrS] [-p child_pidfile] [-P supervisor_pidfile]\n" 1370a402ad2SIhor Antonov " [-u user] [-o output_file] [-t title]\n" 1380a402ad2SIhor Antonov " [-l syslog_facility] [-s syslog_priority]\n" 1390a402ad2SIhor Antonov " [-T syslog_tag] [-m output_mask] [-R restart_delay_secs]\n" 1400a402ad2SIhor Antonov "command arguments ...\n"); 1410a402ad2SIhor Antonov 1420a402ad2SIhor Antonov (void)fprintf(stderr, 1430a402ad2SIhor Antonov " --change-dir -c Change the current working directory to root\n" 1440a402ad2SIhor Antonov " --close-fds -f Set stdin, stdout, stderr to /dev/null\n" 1450a402ad2SIhor Antonov " --sighup -H Close and re-open output file on SIGHUP\n" 1460a402ad2SIhor Antonov " --syslog -S Send output to syslog\n" 1470a402ad2SIhor Antonov " --output-file -o <file> Append output of the child process to file\n" 1480a402ad2SIhor Antonov " --output-mask -m <mask> What to send to syslog/file\n" 1490a402ad2SIhor Antonov " 1=stdout, 2=stderr, 3=both\n" 1500a402ad2SIhor Antonov " --child-pidfile -p <file> Write PID of the child process to file\n" 1510a402ad2SIhor Antonov " --supervisor-pidfile -P <file> Write PID of the supervisor process to file\n" 1520a402ad2SIhor Antonov " --restart -r Restart child if it terminates (1 sec delay)\n" 1530a402ad2SIhor Antonov " --restart-delay -R <N> Restart child if it terminates after N sec\n" 1540a402ad2SIhor Antonov " --title -t <title> Set the title of the supervisor process\n" 1550a402ad2SIhor Antonov " --user -u <user> Drop privileges, run as given user\n" 1560a402ad2SIhor Antonov " --syslog-priority -s <prio> Set syslog priority\n" 1570a402ad2SIhor Antonov " --syslog-facility -l <flty> Set syslog facility\n" 1580a402ad2SIhor Antonov " --syslog-tag -T <tag> Set syslog tag\n" 1590a402ad2SIhor Antonov " --help -h Show this help\n"); 1600a402ad2SIhor Antonov 1610a402ad2SIhor Antonov exit(exitcode); 1620a402ad2SIhor Antonov } 1630a402ad2SIhor Antonov 164bd06a3ecSMike Barcroft int 165bd06a3ecSMike Barcroft main(int argc, char *argv[]) 166bd06a3ecSMike Barcroft { 167e745dc22SIhor Antonov char *p = NULL; 168e745dc22SIhor Antonov int ch = 0; 169298a392eSIhor Antonov struct daemon_state state; 170bd06a3ecSMike Barcroft 171298a392eSIhor Antonov daemon_state_init(&state); 1729ee1faeeSIhor Antonov 1738935a399SIhor Antonov /* Signals are processed via kqueue */ 1748935a399SIhor Antonov signal(SIGHUP, SIG_IGN); 1758935a399SIhor Antonov signal(SIGTERM, SIG_IGN); 1768935a399SIhor Antonov 1779ee1faeeSIhor Antonov /* 178f907027bSIhor Antonov * Supervision mode is enabled if one of the following options are used: 179f907027bSIhor Antonov * --child-pidfile -p 180f907027bSIhor Antonov * --supervisor-pidfile -P 181f907027bSIhor Antonov * --restart -r / --restart-delay -R 182f907027bSIhor Antonov * --syslog -S 183f907027bSIhor Antonov * --syslog-facility -l 184f907027bSIhor Antonov * --syslog-priority -s 185f907027bSIhor Antonov * --syslog-tag -T 186f907027bSIhor Antonov * 187f907027bSIhor Antonov * In supervision mode daemon executes the command in a forked process 188f907027bSIhor Antonov * and observes the child by waiting for SIGCHILD. In supervision mode 189f907027bSIhor Antonov * daemon must never exit before the child, this is necessary to prevent 190f907027bSIhor Antonov * orphaning the child and leaving a stale pid file. 191f907027bSIhor Antonov * To achieve this daemon catches SIGTERM and 192f907027bSIhor Antonov * forwards it to the child, expecting to get SIGCHLD eventually. 193f907027bSIhor Antonov */ 1940a402ad2SIhor Antonov while ((ch = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) { 195bd06a3ecSMike Barcroft switch (ch) { 196bd06a3ecSMike Barcroft case 'c': 197298a392eSIhor Antonov state.keep_cur_workdir = 0; 198bd06a3ecSMike Barcroft break; 199bd06a3ecSMike Barcroft case 'f': 200298a392eSIhor Antonov state.keep_fds_open = 0; 201bd06a3ecSMike Barcroft break; 2024cd407ecSMaxim Sobolev case 'H': 203298a392eSIhor Antonov state.log_reopen = true; 2044cd407ecSMaxim Sobolev break; 20553d49b37SJilles Tjoelker case 'l': 206298a392eSIhor Antonov state.syslog_facility = get_log_mapping(optarg, 20739ea4280SIhor Antonov facilitynames); 208298a392eSIhor Antonov if (state.syslog_facility == -1) { 20953d49b37SJilles Tjoelker errx(5, "unrecognized syslog facility"); 2106b4ef4b1SIhor Antonov } 211298a392eSIhor Antonov state.syslog_enabled = true; 2128935a399SIhor Antonov state.mode = MODE_SUPERVISE; 21353d49b37SJilles Tjoelker break; 21453d49b37SJilles Tjoelker case 'm': 215298a392eSIhor Antonov state.stdmask = strtol(optarg, &p, 10); 216298a392eSIhor Antonov if (p == optarg || state.stdmask < 0 || state.stdmask > 3) { 21753d49b37SJilles Tjoelker errx(6, "unrecognized listening mask"); 2186b4ef4b1SIhor Antonov } 21953d49b37SJilles Tjoelker break; 22053d49b37SJilles Tjoelker case 'o': 221298a392eSIhor Antonov state.output_filename = optarg; 222f907027bSIhor Antonov /* 223f907027bSIhor Antonov * TODO: setting output filename doesn't have to turn 224f907027bSIhor Antonov * the supervision mode on. For non-supervised mode 225f907027bSIhor Antonov * daemon could open the specified file and set it's 226f907027bSIhor Antonov * descriptor as both stderr and stout before execve() 227f907027bSIhor Antonov */ 2288935a399SIhor Antonov state.mode = MODE_SUPERVISE; 22953d49b37SJilles Tjoelker break; 230846be7bdSPoul-Henning Kamp case 'p': 231298a392eSIhor Antonov state.child_pidfile = optarg; 2328935a399SIhor Antonov state.mode = MODE_SUPERVISE; 233846be7bdSPoul-Henning Kamp break; 23432b17786SJohn-Mark Gurney case 'P': 235298a392eSIhor Antonov state.parent_pidfile = optarg; 2368935a399SIhor Antonov state.mode = MODE_SUPERVISE; 23732b17786SJohn-Mark Gurney break; 238b6193c24SMikolaj Golub case 'r': 239298a392eSIhor Antonov state.restart_enabled = true; 2408935a399SIhor Antonov state.mode = MODE_SUPERVISE; 241b6193c24SMikolaj Golub break; 24237820b87SIan Lepore case 'R': 243298a392eSIhor Antonov state.restart_enabled = true; 244298a392eSIhor Antonov state.restart_delay = strtol(optarg, &p, 0); 245298a392eSIhor Antonov if (p == optarg || state.restart_delay < 1) { 24637820b87SIan Lepore errx(6, "invalid restart delay"); 2476b4ef4b1SIhor Antonov } 24837820b87SIan Lepore break; 24953d49b37SJilles Tjoelker case 's': 250298a392eSIhor Antonov state.syslog_priority = get_log_mapping(optarg, 25139ea4280SIhor Antonov prioritynames); 252298a392eSIhor Antonov if (state.syslog_priority == -1) { 25353d49b37SJilles Tjoelker errx(4, "unrecognized syslog priority"); 2546b4ef4b1SIhor Antonov } 255298a392eSIhor Antonov state.syslog_enabled = true; 2568935a399SIhor Antonov state.mode = MODE_SUPERVISE; 25753d49b37SJilles Tjoelker break; 25853d49b37SJilles Tjoelker case 'S': 259298a392eSIhor Antonov state.syslog_enabled = true; 2608935a399SIhor Antonov state.mode = MODE_SUPERVISE; 26153d49b37SJilles Tjoelker break; 262112bfcf5SConrad Meyer case 't': 263298a392eSIhor Antonov state.title = optarg; 264112bfcf5SConrad Meyer break; 26553d49b37SJilles Tjoelker case 'T': 266298a392eSIhor Antonov state.syslog_tag = optarg; 267298a392eSIhor Antonov state.syslog_enabled = true; 2688935a399SIhor Antonov state.mode = MODE_SUPERVISE; 26953d49b37SJilles Tjoelker break; 270e6d4b388STom Rhodes case 'u': 271298a392eSIhor Antonov state.user = optarg; 272e6d4b388STom Rhodes break; 2730a402ad2SIhor Antonov case 'h': 2740a402ad2SIhor Antonov usage(0); 2750a402ad2SIhor Antonov __builtin_unreachable(); 276bd06a3ecSMike Barcroft default: 2770a402ad2SIhor Antonov usage(1); 278bd06a3ecSMike Barcroft } 279bd06a3ecSMike Barcroft } 280bd06a3ecSMike Barcroft argc -= optind; 281bd06a3ecSMike Barcroft argv += optind; 2824c41f4a0SIhor Antonov state.argv = argv; 283bd06a3ecSMike Barcroft 2846b4ef4b1SIhor Antonov if (argc == 0) { 2850a402ad2SIhor Antonov usage(1); 2866b4ef4b1SIhor Antonov } 28712d7249eSTom Rhodes 288298a392eSIhor Antonov if (!state.title) { 289298a392eSIhor Antonov state.title = argv[0]; 2906b4ef4b1SIhor Antonov } 29153d49b37SJilles Tjoelker 292298a392eSIhor Antonov if (state.output_filename) { 293298a392eSIhor Antonov state.output_fd = open_log(state.output_filename); 294298a392eSIhor Antonov if (state.output_fd == -1) { 29553d49b37SJilles Tjoelker err(7, "open"); 29653d49b37SJilles Tjoelker } 2976b4ef4b1SIhor Antonov } 29853d49b37SJilles Tjoelker 299298a392eSIhor Antonov if (state.syslog_enabled) { 300298a392eSIhor Antonov openlog(state.syslog_tag, LOG_PID | LOG_NDELAY, 301298a392eSIhor Antonov state.syslog_facility); 3026b4ef4b1SIhor Antonov } 30353d49b37SJilles Tjoelker 304846be7bdSPoul-Henning Kamp /* 305846be7bdSPoul-Henning Kamp * Try to open the pidfile before calling daemon(3), 306846be7bdSPoul-Henning Kamp * to be able to report the error intelligently 307846be7bdSPoul-Henning Kamp */ 308298a392eSIhor Antonov open_pid_files(&state); 3098935a399SIhor Antonov 3108935a399SIhor Antonov /* 3118935a399SIhor Antonov * TODO: add feature to avoid backgrounding 3128935a399SIhor Antonov * i.e. --foreground, -f 3138935a399SIhor Antonov */ 314298a392eSIhor Antonov if (daemon(state.keep_cur_workdir, state.keep_fds_open) == -1) { 3159da0ef13SMikolaj Golub warn("daemon"); 316cf6356fdSIhor Antonov daemon_terminate(&state); 3179da0ef13SMikolaj Golub } 3188935a399SIhor Antonov 3198935a399SIhor Antonov if (state.mode == MODE_DAEMON) { 3208935a399SIhor Antonov daemon_exec(&state); 3218935a399SIhor Antonov } 3228935a399SIhor Antonov 3239da0ef13SMikolaj Golub /* Write out parent pidfile if needed. */ 324298a392eSIhor Antonov pidfile_write(state.parent_pidfh); 325203df05bSIhor Antonov 3264c41f4a0SIhor Antonov do { 3278935a399SIhor Antonov state.mode = MODE_SUPERVISE; 3284c41f4a0SIhor Antonov daemon_eventloop(&state); 3298935a399SIhor Antonov daemon_sleep(&state); 3308935a399SIhor Antonov } while (state.restart_enabled); 3314c41f4a0SIhor Antonov 3324c41f4a0SIhor Antonov daemon_terminate(&state); 3334c41f4a0SIhor Antonov } 3344c41f4a0SIhor Antonov 3358935a399SIhor Antonov static void 3368935a399SIhor Antonov daemon_exec(struct daemon_state *state) 3378935a399SIhor Antonov { 3388935a399SIhor Antonov pidfile_write(state->child_pidfh); 3394c41f4a0SIhor Antonov 3408935a399SIhor Antonov if (state->user != NULL) { 3418935a399SIhor Antonov restrict_process(state->user); 3428935a399SIhor Antonov } 3438935a399SIhor Antonov 3448935a399SIhor Antonov /* Ignored signals remain ignored after execve, unignore them */ 3458935a399SIhor Antonov signal(SIGHUP, SIG_DFL); 3468935a399SIhor Antonov signal(SIGTERM, SIG_DFL); 3478935a399SIhor Antonov execvp(state->argv[0], state->argv); 3488935a399SIhor Antonov /* execvp() failed - report error and exit this process */ 3498935a399SIhor Antonov err(1, "%s", state->argv[0]); 3508935a399SIhor Antonov } 3518935a399SIhor Antonov 3528935a399SIhor Antonov /* Main event loop: fork the child and watch for events. 3538935a399SIhor Antonov * After SIGTERM is recieved and propagated to the child there are 3544c41f4a0SIhor Antonov * several options on what to do next: 3554c41f4a0SIhor Antonov * - read until EOF 3564c41f4a0SIhor Antonov * - read until EOF but only for a while 3574c41f4a0SIhor Antonov * - bail immediately 3584c41f4a0SIhor Antonov * Currently the third option is used, because otherwise there is no 3594c41f4a0SIhor Antonov * guarantee that read() won't block indefinitely if the child refuses 3604c41f4a0SIhor Antonov * to depart. To handle the second option, a different approach 3614c41f4a0SIhor Antonov * would be needed (procctl()?). 3624c41f4a0SIhor Antonov */ 3634c41f4a0SIhor Antonov static void 3644c41f4a0SIhor Antonov daemon_eventloop(struct daemon_state *state) 3654c41f4a0SIhor Antonov { 3668935a399SIhor Antonov struct kevent event; 3678935a399SIhor Antonov int kq; 3688935a399SIhor Antonov int ret; 3698935a399SIhor Antonov 3708935a399SIhor Antonov /* 3718935a399SIhor Antonov * Try to protect against pageout kill. Ignore the 3728935a399SIhor Antonov * error, madvise(2) will fail only if a process does 3738935a399SIhor Antonov * not have superuser privileges. 3748935a399SIhor Antonov */ 3758935a399SIhor Antonov (void)madvise(NULL, 0, MADV_PROTECT); 3768935a399SIhor Antonov 3774c41f4a0SIhor Antonov if (pipe(state->pipe_fd)) { 37853d49b37SJilles Tjoelker err(1, "pipe"); 3796b4ef4b1SIhor Antonov } 3808935a399SIhor Antonov 3818935a399SIhor Antonov kq = kqueuex(KQUEUE_CLOEXEC); 3828935a399SIhor Antonov EV_SET(&event, state->pipe_fd[0], EVFILT_READ, EV_ADD|EV_CLEAR, 0, 0, 3838935a399SIhor Antonov NULL); 3848935a399SIhor Antonov if (kevent(kq, &event, 1, NULL, 0, NULL) == -1) { 3858935a399SIhor Antonov err(EXIT_FAILURE, "failed to register kevent"); 38675f61ca9SIhor Antonov } 38775f61ca9SIhor Antonov 3888935a399SIhor Antonov EV_SET(&event, SIGHUP, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); 3898935a399SIhor Antonov if (kevent(kq, &event, 1, NULL, 0, NULL) == -1) { 3908935a399SIhor Antonov err(EXIT_FAILURE, "failed to register kevent"); 3918935a399SIhor Antonov } 3928935a399SIhor Antonov 3938935a399SIhor Antonov EV_SET(&event, SIGTERM, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); 3948935a399SIhor Antonov if (kevent(kq, &event, 1, NULL, 0, NULL) == -1) { 3958935a399SIhor Antonov err(EXIT_FAILURE, "failed to register kevent"); 3968935a399SIhor Antonov } 3978935a399SIhor Antonov 3988935a399SIhor Antonov EV_SET(&event, SIGCHLD, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); 3998935a399SIhor Antonov if (kevent(kq, &event, 1, NULL, 0, NULL) == -1) { 4008935a399SIhor Antonov err(EXIT_FAILURE, "failed to register kevent"); 4018935a399SIhor Antonov } 4028935a399SIhor Antonov memset(&event, 0, sizeof(struct kevent)); 4038935a399SIhor Antonov 4048935a399SIhor Antonov /* Spawn a child to exec the command. */ 4058935a399SIhor Antonov state->pid = fork(); 4068935a399SIhor Antonov 40775f61ca9SIhor Antonov /* fork failed, this can only happen when supervision is enabled */ 4088935a399SIhor Antonov switch (state->pid) { 4098935a399SIhor Antonov case -1: 4109da0ef13SMikolaj Golub warn("fork"); 4118935a399SIhor Antonov state->mode = MODE_NOCHILD; 4128935a399SIhor Antonov return; 4138935a399SIhor Antonov /* fork succeeded, this is child's branch */ 4148935a399SIhor Antonov case 0: 4158935a399SIhor Antonov close(kq); 4168935a399SIhor Antonov daemon_set_child_pipe(state); 4178935a399SIhor Antonov daemon_exec(state); 41853d49b37SJilles Tjoelker break; 419cd1e6e70SIhor Antonov } 420cd1e6e70SIhor Antonov 4218935a399SIhor Antonov /* case: pid > 0; fork succeeded */ 4228935a399SIhor Antonov close(state->pipe_fd[1]); 4238935a399SIhor Antonov state->pipe_fd[1] = -1; 4248935a399SIhor Antonov setproctitle("%s[%d]", state->title, (int)state->pid); 425cd1e6e70SIhor Antonov 4268935a399SIhor Antonov while (state->mode != MODE_NOCHILD) { 4278935a399SIhor Antonov ret = kevent(kq, NULL, 0, &event, 1, NULL); 4288935a399SIhor Antonov switch (ret) { 4298935a399SIhor Antonov case -1: 430*494e7dfdSKyle Evans if (errno == EINTR) 431*494e7dfdSKyle Evans continue; 4328935a399SIhor Antonov err(EXIT_FAILURE, "kevent wait"); 4338935a399SIhor Antonov case 0: 434cd1e6e70SIhor Antonov continue; 43553d49b37SJilles Tjoelker } 436cd1e6e70SIhor Antonov 4378935a399SIhor Antonov if (event.flags & EV_ERROR) { 4388935a399SIhor Antonov errx(EXIT_FAILURE, "Event error: %s", 4398935a399SIhor Antonov strerror(event.data)); 440cd1e6e70SIhor Antonov } 441cd1e6e70SIhor Antonov 4428935a399SIhor Antonov switch (event.filter) { 4438935a399SIhor Antonov case EVFILT_SIGNAL: 444cd1e6e70SIhor Antonov 4458935a399SIhor Antonov switch (event.ident) { 4468935a399SIhor Antonov case SIGCHLD: 4478935a399SIhor Antonov if (daemon_is_child_dead(state)) { 4488935a399SIhor Antonov /* child is dead, read all until EOF */ 4498935a399SIhor Antonov state->pid = -1; 4508935a399SIhor Antonov state->mode = MODE_NOCHILD; 4518935a399SIhor Antonov while (listen_child(state->pipe_fd[0], 4528935a399SIhor Antonov state)) 4538935a399SIhor Antonov ; 4548935a399SIhor Antonov } 4558935a399SIhor Antonov continue; 4568935a399SIhor Antonov case SIGTERM: 4578935a399SIhor Antonov if (state->mode != MODE_SUPERVISE) { 4588935a399SIhor Antonov /* user is impatient */ 4598935a399SIhor Antonov /* TODO: warn about repeated SIGTERM? */ 4608935a399SIhor Antonov continue; 461cd1e6e70SIhor Antonov } 462cd1e6e70SIhor Antonov 4638935a399SIhor Antonov state->mode = MODE_TERMINATING; 4648935a399SIhor Antonov state->restart_enabled = false; 4658935a399SIhor Antonov if (state->pid > 0) { 4668935a399SIhor Antonov kill(state->pid, SIGTERM); 46753d49b37SJilles Tjoelker } 4684c41f4a0SIhor Antonov /* 4698935a399SIhor Antonov * TODO set kevent timer to exit 4708935a399SIhor Antonov * unconditionally after some time 4714c41f4a0SIhor Antonov */ 4728935a399SIhor Antonov continue; 4738935a399SIhor Antonov case SIGHUP: 4748935a399SIhor Antonov if (state->log_reopen && state->output_fd >= 0) { 4758935a399SIhor Antonov reopen_log(state); 4768935a399SIhor Antonov } 4778935a399SIhor Antonov continue; 4788935a399SIhor Antonov } 4798935a399SIhor Antonov break; 4808935a399SIhor Antonov 4818935a399SIhor Antonov case EVFILT_READ: 4828935a399SIhor Antonov /* 4838935a399SIhor Antonov * detecting EOF is no longer necessary 4848935a399SIhor Antonov * if child closes the pipe daemon will stop getting 4858935a399SIhor Antonov * EVFILT_READ events 4868935a399SIhor Antonov */ 4878935a399SIhor Antonov 4888935a399SIhor Antonov if (event.data > 0) { 4898935a399SIhor Antonov (void)listen_child(state->pipe_fd[0], state); 4908935a399SIhor Antonov } 4918935a399SIhor Antonov continue; 4928935a399SIhor Antonov default: 4938935a399SIhor Antonov continue; 4948935a399SIhor Antonov } 49553d49b37SJilles Tjoelker } 4964c41f4a0SIhor Antonov 4978935a399SIhor Antonov close(kq); 4988935a399SIhor Antonov close(state->pipe_fd[0]); 4998935a399SIhor Antonov state->pipe_fd[0] = -1; 500bd06a3ecSMike Barcroft } 501bd06a3ecSMike Barcroft 502bd06a3ecSMike Barcroft static void 5038935a399SIhor Antonov daemon_sleep(struct daemon_state *state) 504195fc497SMikolaj Golub { 5058935a399SIhor Antonov struct timespec ts = { state->restart_delay, 0 }; 50609a3675dSConrad Meyer 5078935a399SIhor Antonov if (!state->restart_enabled) { 5088935a399SIhor Antonov return; 5098935a399SIhor Antonov } 5108935a399SIhor Antonov while (nanosleep(&ts, &ts) == -1) { 5116b4ef4b1SIhor Antonov if (errno != EINTR) { 51253d49b37SJilles Tjoelker err(1, "nanosleep"); 51353d49b37SJilles Tjoelker } 51453d49b37SJilles Tjoelker } 5156b4ef4b1SIhor Antonov } 51653d49b37SJilles Tjoelker 51753d49b37SJilles Tjoelker static void 518298a392eSIhor Antonov open_pid_files(struct daemon_state *state) 51953d49b37SJilles Tjoelker { 52053d49b37SJilles Tjoelker pid_t fpid; 52153d49b37SJilles Tjoelker int serrno; 52253d49b37SJilles Tjoelker 523298a392eSIhor Antonov if (state->child_pidfile) { 524298a392eSIhor Antonov state->child_pidfh = pidfile_open(state->child_pidfile, 0600, &fpid); 525298a392eSIhor Antonov if (state->child_pidfh == NULL) { 52653d49b37SJilles Tjoelker if (errno == EEXIST) { 52753d49b37SJilles Tjoelker errx(3, "process already running, pid: %d", 52853d49b37SJilles Tjoelker fpid); 52953d49b37SJilles Tjoelker } 530298a392eSIhor Antonov err(2, "pidfile ``%s''", state->child_pidfile); 53153d49b37SJilles Tjoelker } 53253d49b37SJilles Tjoelker } 53353d49b37SJilles Tjoelker /* Do the same for the actual daemon process. */ 534298a392eSIhor Antonov if (state->parent_pidfile) { 535298a392eSIhor Antonov state->parent_pidfh= pidfile_open(state->parent_pidfile, 0600, &fpid); 536298a392eSIhor Antonov if (state->parent_pidfh == NULL) { 53753d49b37SJilles Tjoelker serrno = errno; 538298a392eSIhor Antonov pidfile_remove(state->child_pidfh); 53953d49b37SJilles Tjoelker errno = serrno; 54053d49b37SJilles Tjoelker if (errno == EEXIST) { 54153d49b37SJilles Tjoelker errx(3, "process already running, pid: %d", 54253d49b37SJilles Tjoelker fpid); 54353d49b37SJilles Tjoelker } 544298a392eSIhor Antonov err(2, "ppidfile ``%s''", state->parent_pidfile); 54553d49b37SJilles Tjoelker } 54653d49b37SJilles Tjoelker } 54753d49b37SJilles Tjoelker } 54853d49b37SJilles Tjoelker 54953d49b37SJilles Tjoelker static int 55053d49b37SJilles Tjoelker get_log_mapping(const char *str, const CODE *c) 55153d49b37SJilles Tjoelker { 55253d49b37SJilles Tjoelker const CODE *cp; 55353d49b37SJilles Tjoelker for (cp = c; cp->c_name; cp++) 5546b4ef4b1SIhor Antonov if (strcmp(cp->c_name, str) == 0) { 55553d49b37SJilles Tjoelker return cp->c_val; 5566b4ef4b1SIhor Antonov } 55753d49b37SJilles Tjoelker return -1; 558195fc497SMikolaj Golub } 559195fc497SMikolaj Golub 560195fc497SMikolaj Golub static void 561e6d4b388STom Rhodes restrict_process(const char *user) 56212d7249eSTom Rhodes { 56312d7249eSTom Rhodes struct passwd *pw = NULL; 56412d7249eSTom Rhodes 565e6d4b388STom Rhodes pw = getpwnam(user); 5666b4ef4b1SIhor Antonov if (pw == NULL) { 567e6d4b388STom Rhodes errx(1, "unknown user: %s", user); 5686b4ef4b1SIhor Antonov } 56912d7249eSTom Rhodes 5706b4ef4b1SIhor Antonov if (setusercontext(NULL, pw, pw->pw_uid, LOGIN_SETALL) != 0) { 571e6d4b388STom Rhodes errx(1, "failed to set user environment"); 5726b4ef4b1SIhor Antonov } 5736b3ad1d7SMaxim Sobolev 5746b3ad1d7SMaxim Sobolev setenv("USER", pw->pw_name, 1); 5756b3ad1d7SMaxim Sobolev setenv("HOME", pw->pw_dir, 1); 5766b3ad1d7SMaxim Sobolev setenv("SHELL", *pw->pw_shell ? pw->pw_shell : _PATH_BSHELL, 1); 57712d7249eSTom Rhodes } 57812d7249eSTom Rhodes 57953d49b37SJilles Tjoelker /* 58053d49b37SJilles Tjoelker * We try to collect whole lines terminated by '\n'. Otherwise we collect a 58153d49b37SJilles Tjoelker * full buffer, and then output it. 58253d49b37SJilles Tjoelker * 583bc43a9a7SIhor Antonov * Return value of false is assumed to mean EOF or error, and true indicates to 58453d49b37SJilles Tjoelker * continue reading. 5858935a399SIhor Antonov * 5868935a399SIhor Antonov * TODO: simplify signature - state contains pipefd 58753d49b37SJilles Tjoelker */ 588bc43a9a7SIhor Antonov static bool 589298a392eSIhor Antonov listen_child(int fd, struct daemon_state *state) 5902ad43027SMikolaj Golub { 59153d49b37SJilles Tjoelker static unsigned char buf[LBUF_SIZE]; 59253d49b37SJilles Tjoelker static size_t bytes_read = 0; 59353d49b37SJilles Tjoelker int rv; 5942ad43027SMikolaj Golub 595cf6356fdSIhor Antonov assert(state != NULL); 59653d49b37SJilles Tjoelker assert(bytes_read < LBUF_SIZE - 1); 59753d49b37SJilles Tjoelker 59853d49b37SJilles Tjoelker rv = read(fd, buf + bytes_read, LBUF_SIZE - bytes_read - 1); 59953d49b37SJilles Tjoelker if (rv > 0) { 60053d49b37SJilles Tjoelker unsigned char *cp; 60153d49b37SJilles Tjoelker 60253d49b37SJilles Tjoelker bytes_read += rv; 60353d49b37SJilles Tjoelker assert(bytes_read <= LBUF_SIZE - 1); 60453d49b37SJilles Tjoelker /* Always NUL-terminate just in case. */ 60553d49b37SJilles Tjoelker buf[LBUF_SIZE - 1] = '\0'; 60653d49b37SJilles Tjoelker /* 60753d49b37SJilles Tjoelker * Chomp line by line until we run out of buffer. 60853d49b37SJilles Tjoelker * This does not take NUL characters into account. 60953d49b37SJilles Tjoelker */ 61053d49b37SJilles Tjoelker while ((cp = memchr(buf, '\n', bytes_read)) != NULL) { 61153d49b37SJilles Tjoelker size_t bytes_line = cp - buf + 1; 61253d49b37SJilles Tjoelker assert(bytes_line <= bytes_read); 613298a392eSIhor Antonov do_output(buf, bytes_line, state); 61453d49b37SJilles Tjoelker bytes_read -= bytes_line; 61553d49b37SJilles Tjoelker memmove(buf, cp + 1, bytes_read); 616195fc497SMikolaj Golub } 61753d49b37SJilles Tjoelker /* Wait until the buffer is full. */ 6186b4ef4b1SIhor Antonov if (bytes_read < LBUF_SIZE - 1) { 619bc43a9a7SIhor Antonov return true; 6206b4ef4b1SIhor Antonov } 621298a392eSIhor Antonov do_output(buf, bytes_read, state); 62253d49b37SJilles Tjoelker bytes_read = 0; 623bc43a9a7SIhor Antonov return true; 62453d49b37SJilles Tjoelker } else if (rv == -1) { 62553d49b37SJilles Tjoelker /* EINTR should trigger another read. */ 62653d49b37SJilles Tjoelker if (errno == EINTR) { 627bc43a9a7SIhor Antonov return true; 62853d49b37SJilles Tjoelker } else { 62953d49b37SJilles Tjoelker warn("read"); 630bc43a9a7SIhor Antonov return false; 631c60d51f9SMikolaj Golub } 63253d49b37SJilles Tjoelker } 63353d49b37SJilles Tjoelker /* Upon EOF, we have to flush what's left of the buffer. */ 63453d49b37SJilles Tjoelker if (bytes_read > 0) { 635298a392eSIhor Antonov do_output(buf, bytes_read, state); 63653d49b37SJilles Tjoelker bytes_read = 0; 63753d49b37SJilles Tjoelker } 638bc43a9a7SIhor Antonov return false; 63953d49b37SJilles Tjoelker } 64053d49b37SJilles Tjoelker 64153d49b37SJilles Tjoelker /* 64253d49b37SJilles Tjoelker * The default behavior is to stay silent if the user wants to redirect 64353d49b37SJilles Tjoelker * output to a file and/or syslog. If neither are provided, then we bounce 64453d49b37SJilles Tjoelker * everything back to parent's stdout. 64553d49b37SJilles Tjoelker */ 64653d49b37SJilles Tjoelker static void 647298a392eSIhor Antonov do_output(const unsigned char *buf, size_t len, struct daemon_state *state) 64853d49b37SJilles Tjoelker { 64953d49b37SJilles Tjoelker assert(len <= LBUF_SIZE); 650cf6356fdSIhor Antonov assert(state != NULL); 65153d49b37SJilles Tjoelker 6526b4ef4b1SIhor Antonov if (len < 1) { 65353d49b37SJilles Tjoelker return; 6546b4ef4b1SIhor Antonov } 655298a392eSIhor Antonov if (state->syslog_enabled) { 656298a392eSIhor Antonov syslog(state->syslog_priority, "%.*s", (int)len, buf); 6576b4ef4b1SIhor Antonov } 658298a392eSIhor Antonov if (state->output_fd != -1) { 659298a392eSIhor Antonov if (write(state->output_fd, buf, len) == -1) 66053d49b37SJilles Tjoelker warn("write"); 66153d49b37SJilles Tjoelker } 662298a392eSIhor Antonov if (state->keep_fds_open && 663298a392eSIhor Antonov !state->syslog_enabled && 664298a392eSIhor Antonov state->output_fd == -1) { 66553d49b37SJilles Tjoelker printf("%.*s", (int)len, buf); 66653d49b37SJilles Tjoelker } 6676b4ef4b1SIhor Antonov } 66853d49b37SJilles Tjoelker 6694cd407ecSMaxim Sobolev static int 6704cd407ecSMaxim Sobolev open_log(const char *outfn) 6714cd407ecSMaxim Sobolev { 6724cd407ecSMaxim Sobolev 6734cd407ecSMaxim Sobolev return open(outfn, O_CREAT | O_WRONLY | O_APPEND | O_CLOEXEC, 0600); 6744cd407ecSMaxim Sobolev } 6754cd407ecSMaxim Sobolev 6764cd407ecSMaxim Sobolev static void 677298a392eSIhor Antonov reopen_log(struct daemon_state *state) 6784cd407ecSMaxim Sobolev { 6794cd407ecSMaxim Sobolev int outfd; 6804cd407ecSMaxim Sobolev 681298a392eSIhor Antonov outfd = open_log(state->output_filename); 682298a392eSIhor Antonov if (state->output_fd >= 0) { 683298a392eSIhor Antonov close(state->output_fd); 6846b4ef4b1SIhor Antonov } 685298a392eSIhor Antonov state->output_fd = outfd; 6864cd407ecSMaxim Sobolev } 6874cd407ecSMaxim Sobolev 688298a392eSIhor Antonov static void 689298a392eSIhor Antonov daemon_state_init(struct daemon_state *state) 690298a392eSIhor Antonov { 691298a392eSIhor Antonov *state = (struct daemon_state) { 692298a392eSIhor Antonov .pipe_fd = { -1, -1 }, 6934c41f4a0SIhor Antonov .argv = NULL, 694298a392eSIhor Antonov .parent_pidfh = NULL, 695298a392eSIhor Antonov .child_pidfh = NULL, 696298a392eSIhor Antonov .child_pidfile = NULL, 697298a392eSIhor Antonov .parent_pidfile = NULL, 698298a392eSIhor Antonov .title = NULL, 699298a392eSIhor Antonov .user = NULL, 7008935a399SIhor Antonov .mode = MODE_DAEMON, 701298a392eSIhor Antonov .restart_enabled = false, 7028935a399SIhor Antonov .pid = 0, 703298a392eSIhor Antonov .keep_cur_workdir = 1, 704298a392eSIhor Antonov .restart_delay = 1, 705298a392eSIhor Antonov .stdmask = STDOUT_FILENO | STDERR_FILENO, 706298a392eSIhor Antonov .syslog_enabled = false, 707298a392eSIhor Antonov .log_reopen = false, 708298a392eSIhor Antonov .syslog_priority = LOG_NOTICE, 709298a392eSIhor Antonov .syslog_tag = "daemon", 710298a392eSIhor Antonov .syslog_facility = LOG_DAEMON, 711298a392eSIhor Antonov .keep_fds_open = 1, 712298a392eSIhor Antonov .output_fd = -1, 713298a392eSIhor Antonov .output_filename = NULL, 714298a392eSIhor Antonov }; 715298a392eSIhor Antonov } 716cf6356fdSIhor Antonov 717cf6356fdSIhor Antonov static _Noreturn void 718cf6356fdSIhor Antonov daemon_terminate(struct daemon_state *state) 719cf6356fdSIhor Antonov { 720cf6356fdSIhor Antonov assert(state != NULL); 7218935a399SIhor Antonov 7228935a399SIhor Antonov if (state->output_fd >= 0) { 723cf6356fdSIhor Antonov close(state->output_fd); 7248935a399SIhor Antonov } 7258935a399SIhor Antonov if (state->pipe_fd[0] >= 0) { 726cf6356fdSIhor Antonov close(state->pipe_fd[0]); 7278935a399SIhor Antonov } 7288935a399SIhor Antonov 7298935a399SIhor Antonov if (state->pipe_fd[1] >= 0) { 730cf6356fdSIhor Antonov close(state->pipe_fd[1]); 7318935a399SIhor Antonov } 732cf6356fdSIhor Antonov if (state->syslog_enabled) { 733cf6356fdSIhor Antonov closelog(); 734cf6356fdSIhor Antonov } 735cf6356fdSIhor Antonov pidfile_remove(state->child_pidfh); 736cf6356fdSIhor Antonov pidfile_remove(state->parent_pidfh); 737cf6356fdSIhor Antonov 738cf6356fdSIhor Antonov /* 739cf6356fdSIhor Antonov * Note that the exit value here doesn't matter in the case of a clean 740cf6356fdSIhor Antonov * exit; daemon(3) already detached us from the caller, nothing is left 741cf6356fdSIhor Antonov * to care about this one. 742cf6356fdSIhor Antonov */ 743cf6356fdSIhor Antonov exit(1); 744cf6356fdSIhor Antonov } 7458935a399SIhor Antonov 7468935a399SIhor Antonov /* 7478935a399SIhor Antonov * Returns true if SIGCHILD came from state->pid 7488935a399SIhor Antonov * This function could hang if SIGCHILD was emittied for a reason other than 7498935a399SIhor Antonov * child dying (e.g., ptrace attach). 7508935a399SIhor Antonov */ 7518935a399SIhor Antonov static bool 7528935a399SIhor Antonov daemon_is_child_dead(struct daemon_state *state) 7538935a399SIhor Antonov { 7548935a399SIhor Antonov for (;;) { 7558935a399SIhor Antonov int who = waitpid(-1, NULL, WNOHANG); 7568935a399SIhor Antonov if (state->pid == who) { 7578935a399SIhor Antonov return true; 7588935a399SIhor Antonov } 7598935a399SIhor Antonov if (who == -1 && errno != EINTR) { 7608935a399SIhor Antonov warn("waitpid"); 7618935a399SIhor Antonov return false; 7628935a399SIhor Antonov } 7638935a399SIhor Antonov } 7648935a399SIhor Antonov } 7658935a399SIhor Antonov 7668935a399SIhor Antonov static void 7678935a399SIhor Antonov daemon_set_child_pipe(struct daemon_state *state) 7688935a399SIhor Antonov { 7698935a399SIhor Antonov if (state->stdmask & STDERR_FILENO) { 7708935a399SIhor Antonov if (dup2(state->pipe_fd[1], STDERR_FILENO) == -1) { 7718935a399SIhor Antonov err(1, "dup2"); 7728935a399SIhor Antonov } 7738935a399SIhor Antonov } 7748935a399SIhor Antonov if (state->stdmask & STDOUT_FILENO) { 7758935a399SIhor Antonov if (dup2(state->pipe_fd[1], STDOUT_FILENO) == -1) { 7768935a399SIhor Antonov err(1, "dup2"); 7778935a399SIhor Antonov } 7788935a399SIhor Antonov } 7798935a399SIhor Antonov if (state->pipe_fd[1] != STDERR_FILENO && 7808935a399SIhor Antonov state->pipe_fd[1] != STDOUT_FILENO) { 7818935a399SIhor Antonov close(state->pipe_fd[1]); 7828935a399SIhor Antonov } 7838935a399SIhor Antonov 7848935a399SIhor Antonov /* The child gets dup'd pipes. */ 7858935a399SIhor Antonov close(state->pipe_fd[0]); 7868935a399SIhor Antonov } 787