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 597618c9e1SJuraj Lutter /* Maximum number of restarts */ 607618c9e1SJuraj Lutter #define MAX_RESTART_COUNT 128 617618c9e1SJuraj Lutter 6253d49b37SJilles Tjoelker #define LBUF_SIZE 4096 6353d49b37SJilles Tjoelker 648935a399SIhor Antonov enum daemon_mode { 658935a399SIhor Antonov MODE_DAEMON = 0, /* simply daemonize, no supervision */ 668935a399SIhor Antonov MODE_SUPERVISE, /* initial supervision state */ 678935a399SIhor Antonov MODE_TERMINATING, /* user requested termination */ 688935a399SIhor Antonov MODE_NOCHILD, /* child is terminated, final state of the event loop */ 698935a399SIhor Antonov }; 708935a399SIhor Antonov 71407e3790SIhor Antonov 72298a392eSIhor Antonov struct daemon_state { 7324fd3e96SIhor Antonov unsigned char buf[LBUF_SIZE]; 7424fd3e96SIhor Antonov size_t pos; 754c41f4a0SIhor Antonov char **argv; 76298a392eSIhor Antonov const char *child_pidfile; 77298a392eSIhor Antonov const char *parent_pidfile; 78e70444c6SIhor Antonov const char *output_filename; 796f063672SIhor Antonov const char *syslog_tag; 80298a392eSIhor Antonov const char *title; 81298a392eSIhor Antonov const char *user; 82298a392eSIhor Antonov struct pidfh *parent_pidfh; 83298a392eSIhor Antonov struct pidfh *child_pidfh; 848935a399SIhor Antonov enum daemon_mode mode; 858935a399SIhor Antonov int pid; 86407e3790SIhor Antonov int pipe_rd; 87407e3790SIhor Antonov int pipe_wr; 88298a392eSIhor Antonov int keep_cur_workdir; 89298a392eSIhor Antonov int restart_delay; 90298a392eSIhor Antonov int stdmask; 91e70444c6SIhor Antonov int syslog_priority; 926f063672SIhor Antonov int syslog_facility; 93129ec8f4SIhor Antonov int keep_fds_open; 94e70444c6SIhor Antonov int output_fd; 95298a392eSIhor Antonov bool restart_enabled; 96f2f9d31dSIhor Antonov bool syslog_enabled; 97298a392eSIhor Antonov bool log_reopen; 987618c9e1SJuraj Lutter int restart_count; 997618c9e1SJuraj Lutter int restarted_count; 10053d49b37SJilles Tjoelker }; 10153d49b37SJilles Tjoelker 102e6d4b388STom Rhodes static void restrict_process(const char *); 1034cd407ecSMaxim Sobolev static int open_log(const char *); 104298a392eSIhor Antonov static void reopen_log(struct daemon_state *); 1056ac7c9f0SIhor Antonov static bool listen_child(struct daemon_state *); 10653d49b37SJilles Tjoelker static int get_log_mapping(const char *, const CODE *); 107298a392eSIhor Antonov static void open_pid_files(struct daemon_state *); 108298a392eSIhor Antonov static void do_output(const unsigned char *, size_t, struct daemon_state *); 1098935a399SIhor Antonov static void daemon_sleep(struct daemon_state *); 110298a392eSIhor Antonov static void daemon_state_init(struct daemon_state *); 1114c41f4a0SIhor Antonov static void daemon_eventloop(struct daemon_state *); 112cf6356fdSIhor Antonov static void daemon_terminate(struct daemon_state *); 1138935a399SIhor Antonov static void daemon_exec(struct daemon_state *); 1148935a399SIhor Antonov static bool daemon_is_child_dead(struct daemon_state *); 1158935a399SIhor Antonov static void daemon_set_child_pipe(struct daemon_state *); 11653d49b37SJilles Tjoelker 117*aa8722ccSKyle Evans static int pidfile_truncate(struct pidfh *); 118*aa8722ccSKyle Evans 1197618c9e1SJuraj Lutter static const char shortopts[] = "+cfHSp:P:ru:o:s:l:t:m:R:T:C:h"; 1200a402ad2SIhor Antonov 1210a402ad2SIhor Antonov static const struct option longopts[] = { 1220a402ad2SIhor Antonov { "change-dir", no_argument, NULL, 'c' }, 1230a402ad2SIhor Antonov { "close-fds", no_argument, NULL, 'f' }, 1240a402ad2SIhor Antonov { "sighup", no_argument, NULL, 'H' }, 1250a402ad2SIhor Antonov { "syslog", no_argument, NULL, 'S' }, 1260a402ad2SIhor Antonov { "output-file", required_argument, NULL, 'o' }, 1270a402ad2SIhor Antonov { "output-mask", required_argument, NULL, 'm' }, 1280a402ad2SIhor Antonov { "child-pidfile", required_argument, NULL, 'p' }, 1290a402ad2SIhor Antonov { "supervisor-pidfile", required_argument, NULL, 'P' }, 1300a402ad2SIhor Antonov { "restart", no_argument, NULL, 'r' }, 1317618c9e1SJuraj Lutter { "restart-count", required_argument, NULL, 'C' }, 1320a402ad2SIhor Antonov { "restart-delay", required_argument, NULL, 'R' }, 1330a402ad2SIhor Antonov { "title", required_argument, NULL, 't' }, 1340a402ad2SIhor Antonov { "user", required_argument, NULL, 'u' }, 1350a402ad2SIhor Antonov { "syslog-priority", required_argument, NULL, 's' }, 1360a402ad2SIhor Antonov { "syslog-facility", required_argument, NULL, 'l' }, 1370a402ad2SIhor Antonov { "syslog-tag", required_argument, NULL, 'T' }, 1380a402ad2SIhor Antonov { "help", no_argument, NULL, 'h' }, 1390a402ad2SIhor Antonov { NULL, 0, NULL, 0 } 1400a402ad2SIhor Antonov }; 1410a402ad2SIhor Antonov 1420a402ad2SIhor Antonov static _Noreturn void 1430a402ad2SIhor Antonov usage(int exitcode) 1440a402ad2SIhor Antonov { 1450a402ad2SIhor Antonov (void)fprintf(stderr, 1460a402ad2SIhor Antonov "usage: daemon [-cfHrS] [-p child_pidfile] [-P supervisor_pidfile]\n" 1470a402ad2SIhor Antonov " [-u user] [-o output_file] [-t title]\n" 1480a402ad2SIhor Antonov " [-l syslog_facility] [-s syslog_priority]\n" 1490a402ad2SIhor Antonov " [-T syslog_tag] [-m output_mask] [-R restart_delay_secs]\n" 1507618c9e1SJuraj Lutter " [-C restart_count]\n" 1510a402ad2SIhor Antonov "command arguments ...\n"); 1520a402ad2SIhor Antonov 1530a402ad2SIhor Antonov (void)fprintf(stderr, 1540a402ad2SIhor Antonov " --change-dir -c Change the current working directory to root\n" 1550a402ad2SIhor Antonov " --close-fds -f Set stdin, stdout, stderr to /dev/null\n" 1560a402ad2SIhor Antonov " --sighup -H Close and re-open output file on SIGHUP\n" 1570a402ad2SIhor Antonov " --syslog -S Send output to syslog\n" 1580a402ad2SIhor Antonov " --output-file -o <file> Append output of the child process to file\n" 1590a402ad2SIhor Antonov " --output-mask -m <mask> What to send to syslog/file\n" 1600a402ad2SIhor Antonov " 1=stdout, 2=stderr, 3=both\n" 1610a402ad2SIhor Antonov " --child-pidfile -p <file> Write PID of the child process to file\n" 1620a402ad2SIhor Antonov " --supervisor-pidfile -P <file> Write PID of the supervisor process to file\n" 1630a402ad2SIhor Antonov " --restart -r Restart child if it terminates (1 sec delay)\n" 1647618c9e1SJuraj Lutter " --restart-count -C <N> Restart child at most N times, then exit\n" 1650a402ad2SIhor Antonov " --restart-delay -R <N> Restart child if it terminates after N sec\n" 1660a402ad2SIhor Antonov " --title -t <title> Set the title of the supervisor process\n" 1670a402ad2SIhor Antonov " --user -u <user> Drop privileges, run as given user\n" 1680a402ad2SIhor Antonov " --syslog-priority -s <prio> Set syslog priority\n" 1690a402ad2SIhor Antonov " --syslog-facility -l <flty> Set syslog facility\n" 1700a402ad2SIhor Antonov " --syslog-tag -T <tag> Set syslog tag\n" 1710a402ad2SIhor Antonov " --help -h Show this help\n"); 1720a402ad2SIhor Antonov 1730a402ad2SIhor Antonov exit(exitcode); 1740a402ad2SIhor Antonov } 1750a402ad2SIhor Antonov 176bd06a3ecSMike Barcroft int 177bd06a3ecSMike Barcroft main(int argc, char *argv[]) 178bd06a3ecSMike Barcroft { 179a6f795ccSIhor Antonov const char *e = NULL; 180e745dc22SIhor Antonov int ch = 0; 181298a392eSIhor Antonov struct daemon_state state; 182bd06a3ecSMike Barcroft 183298a392eSIhor Antonov daemon_state_init(&state); 1849ee1faeeSIhor Antonov 1858935a399SIhor Antonov /* Signals are processed via kqueue */ 1868935a399SIhor Antonov signal(SIGHUP, SIG_IGN); 1878935a399SIhor Antonov signal(SIGTERM, SIG_IGN); 1888935a399SIhor Antonov 1899ee1faeeSIhor Antonov /* 190f907027bSIhor Antonov * Supervision mode is enabled if one of the following options are used: 191f907027bSIhor Antonov * --child-pidfile -p 192f907027bSIhor Antonov * --supervisor-pidfile -P 193f907027bSIhor Antonov * --restart -r / --restart-delay -R 194f907027bSIhor Antonov * --syslog -S 195f907027bSIhor Antonov * --syslog-facility -l 196f907027bSIhor Antonov * --syslog-priority -s 197f907027bSIhor Antonov * --syslog-tag -T 198f907027bSIhor Antonov * 199f907027bSIhor Antonov * In supervision mode daemon executes the command in a forked process 200f907027bSIhor Antonov * and observes the child by waiting for SIGCHILD. In supervision mode 201f907027bSIhor Antonov * daemon must never exit before the child, this is necessary to prevent 202f907027bSIhor Antonov * orphaning the child and leaving a stale pid file. 203f907027bSIhor Antonov * To achieve this daemon catches SIGTERM and 204f907027bSIhor Antonov * forwards it to the child, expecting to get SIGCHLD eventually. 205f907027bSIhor Antonov */ 2060a402ad2SIhor Antonov while ((ch = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) { 207bd06a3ecSMike Barcroft switch (ch) { 208bd06a3ecSMike Barcroft case 'c': 209298a392eSIhor Antonov state.keep_cur_workdir = 0; 210bd06a3ecSMike Barcroft break; 2117618c9e1SJuraj Lutter case 'C': 2127618c9e1SJuraj Lutter state.restart_count = (int)strtonum(optarg, 0, 2137618c9e1SJuraj Lutter MAX_RESTART_COUNT, &e); 2147618c9e1SJuraj Lutter if (e != NULL) { 2157618c9e1SJuraj Lutter errx(6, "invalid restart count: %s", e); 2167618c9e1SJuraj Lutter } 2177618c9e1SJuraj Lutter break; 218bd06a3ecSMike Barcroft case 'f': 219298a392eSIhor Antonov state.keep_fds_open = 0; 220bd06a3ecSMike Barcroft break; 2214cd407ecSMaxim Sobolev case 'H': 222298a392eSIhor Antonov state.log_reopen = true; 2234cd407ecSMaxim Sobolev break; 22453d49b37SJilles Tjoelker case 'l': 225298a392eSIhor Antonov state.syslog_facility = get_log_mapping(optarg, 22639ea4280SIhor Antonov facilitynames); 227298a392eSIhor Antonov if (state.syslog_facility == -1) { 22853d49b37SJilles Tjoelker errx(5, "unrecognized syslog facility"); 2296b4ef4b1SIhor Antonov } 230298a392eSIhor Antonov state.syslog_enabled = true; 2318935a399SIhor Antonov state.mode = MODE_SUPERVISE; 23253d49b37SJilles Tjoelker break; 23353d49b37SJilles Tjoelker case 'm': 234a6f795ccSIhor Antonov state.stdmask = (int)strtonum(optarg, 0, 3, &e); 235a6f795ccSIhor Antonov if (e != NULL) { 236a6f795ccSIhor Antonov errx(6, "unrecognized listening mask: %s", e); 2376b4ef4b1SIhor Antonov } 23853d49b37SJilles Tjoelker break; 23953d49b37SJilles Tjoelker case 'o': 240298a392eSIhor Antonov state.output_filename = optarg; 241f907027bSIhor Antonov /* 242f907027bSIhor Antonov * TODO: setting output filename doesn't have to turn 243f907027bSIhor Antonov * the supervision mode on. For non-supervised mode 244f907027bSIhor Antonov * daemon could open the specified file and set it's 245f907027bSIhor Antonov * descriptor as both stderr and stout before execve() 246f907027bSIhor Antonov */ 2478935a399SIhor Antonov state.mode = MODE_SUPERVISE; 24853d49b37SJilles Tjoelker break; 249846be7bdSPoul-Henning Kamp case 'p': 250298a392eSIhor Antonov state.child_pidfile = optarg; 2518935a399SIhor Antonov state.mode = MODE_SUPERVISE; 252846be7bdSPoul-Henning Kamp break; 25332b17786SJohn-Mark Gurney case 'P': 254298a392eSIhor Antonov state.parent_pidfile = optarg; 2558935a399SIhor Antonov state.mode = MODE_SUPERVISE; 25632b17786SJohn-Mark Gurney break; 257b6193c24SMikolaj Golub case 'r': 258298a392eSIhor Antonov state.restart_enabled = true; 2598935a399SIhor Antonov state.mode = MODE_SUPERVISE; 260b6193c24SMikolaj Golub break; 26137820b87SIan Lepore case 'R': 262298a392eSIhor Antonov state.restart_enabled = true; 263a6f795ccSIhor Antonov state.restart_delay = (int)strtonum(optarg, 1, 264a6f795ccSIhor Antonov MAX_RESTART_DELAY, &e); 265a6f795ccSIhor Antonov if (e != NULL) { 266a6f795ccSIhor Antonov errx(6, "invalid restart delay: %s", e); 2676b4ef4b1SIhor Antonov } 268bbc6e6c5SMathieu state.mode = MODE_SUPERVISE; 26937820b87SIan Lepore break; 27053d49b37SJilles Tjoelker case 's': 271298a392eSIhor Antonov state.syslog_priority = get_log_mapping(optarg, 27239ea4280SIhor Antonov prioritynames); 273298a392eSIhor Antonov if (state.syslog_priority == -1) { 27453d49b37SJilles Tjoelker errx(4, "unrecognized syslog priority"); 2756b4ef4b1SIhor Antonov } 276298a392eSIhor Antonov state.syslog_enabled = true; 2778935a399SIhor Antonov state.mode = MODE_SUPERVISE; 27853d49b37SJilles Tjoelker break; 27953d49b37SJilles Tjoelker case 'S': 280298a392eSIhor Antonov state.syslog_enabled = true; 2818935a399SIhor Antonov state.mode = MODE_SUPERVISE; 28253d49b37SJilles Tjoelker break; 283112bfcf5SConrad Meyer case 't': 284298a392eSIhor Antonov state.title = optarg; 285112bfcf5SConrad Meyer break; 28653d49b37SJilles Tjoelker case 'T': 287298a392eSIhor Antonov state.syslog_tag = optarg; 288298a392eSIhor Antonov state.syslog_enabled = true; 2898935a399SIhor Antonov state.mode = MODE_SUPERVISE; 29053d49b37SJilles Tjoelker break; 291e6d4b388STom Rhodes case 'u': 292298a392eSIhor Antonov state.user = optarg; 293e6d4b388STom Rhodes break; 2940a402ad2SIhor Antonov case 'h': 2950a402ad2SIhor Antonov usage(0); 296f7a10a77SCollin Funk __unreachable(); 297bd06a3ecSMike Barcroft default: 2980a402ad2SIhor Antonov usage(1); 299bd06a3ecSMike Barcroft } 300bd06a3ecSMike Barcroft } 301bd06a3ecSMike Barcroft argc -= optind; 302bd06a3ecSMike Barcroft argv += optind; 3034c41f4a0SIhor Antonov state.argv = argv; 304bd06a3ecSMike Barcroft 3056b4ef4b1SIhor Antonov if (argc == 0) { 3060a402ad2SIhor Antonov usage(1); 3076b4ef4b1SIhor Antonov } 30812d7249eSTom Rhodes 309298a392eSIhor Antonov if (!state.title) { 310298a392eSIhor Antonov state.title = argv[0]; 3116b4ef4b1SIhor Antonov } 31253d49b37SJilles Tjoelker 313298a392eSIhor Antonov if (state.output_filename) { 314298a392eSIhor Antonov state.output_fd = open_log(state.output_filename); 315298a392eSIhor Antonov if (state.output_fd == -1) { 31653d49b37SJilles Tjoelker err(7, "open"); 31753d49b37SJilles Tjoelker } 3186b4ef4b1SIhor Antonov } 31953d49b37SJilles Tjoelker 320298a392eSIhor Antonov if (state.syslog_enabled) { 321298a392eSIhor Antonov openlog(state.syslog_tag, LOG_PID | LOG_NDELAY, 322298a392eSIhor Antonov state.syslog_facility); 3236b4ef4b1SIhor Antonov } 32453d49b37SJilles Tjoelker 325846be7bdSPoul-Henning Kamp /* 326846be7bdSPoul-Henning Kamp * Try to open the pidfile before calling daemon(3), 327846be7bdSPoul-Henning Kamp * to be able to report the error intelligently 328846be7bdSPoul-Henning Kamp */ 329298a392eSIhor Antonov open_pid_files(&state); 3308935a399SIhor Antonov 3318935a399SIhor Antonov /* 3328935a399SIhor Antonov * TODO: add feature to avoid backgrounding 3338935a399SIhor Antonov * i.e. --foreground, -f 3348935a399SIhor Antonov */ 335298a392eSIhor Antonov if (daemon(state.keep_cur_workdir, state.keep_fds_open) == -1) { 3369da0ef13SMikolaj Golub warn("daemon"); 337cf6356fdSIhor Antonov daemon_terminate(&state); 3389da0ef13SMikolaj Golub } 3398935a399SIhor Antonov 3408935a399SIhor Antonov if (state.mode == MODE_DAEMON) { 3418935a399SIhor Antonov daemon_exec(&state); 3428935a399SIhor Antonov } 3438935a399SIhor Antonov 3449da0ef13SMikolaj Golub /* Write out parent pidfile if needed. */ 345298a392eSIhor Antonov pidfile_write(state.parent_pidfh); 346203df05bSIhor Antonov 3474c41f4a0SIhor Antonov do { 3488935a399SIhor Antonov state.mode = MODE_SUPERVISE; 3494c41f4a0SIhor Antonov daemon_eventloop(&state); 3508935a399SIhor Antonov daemon_sleep(&state); 3517618c9e1SJuraj Lutter if (state.restart_enabled && state.restart_count > -1) { 3527618c9e1SJuraj Lutter if (state.restarted_count >= state.restart_count) { 3537618c9e1SJuraj Lutter state.restart_enabled = false; 3547618c9e1SJuraj Lutter } 3557618c9e1SJuraj Lutter state.restarted_count++; 3567618c9e1SJuraj Lutter } 3578935a399SIhor Antonov } while (state.restart_enabled); 3584c41f4a0SIhor Antonov 3594c41f4a0SIhor Antonov daemon_terminate(&state); 3604c41f4a0SIhor Antonov } 3614c41f4a0SIhor Antonov 3628935a399SIhor Antonov static void 3638935a399SIhor Antonov daemon_exec(struct daemon_state *state) 3648935a399SIhor Antonov { 3658935a399SIhor Antonov pidfile_write(state->child_pidfh); 3664c41f4a0SIhor Antonov 3678935a399SIhor Antonov if (state->user != NULL) { 3688935a399SIhor Antonov restrict_process(state->user); 3698935a399SIhor Antonov } 3708935a399SIhor Antonov 3718935a399SIhor Antonov /* Ignored signals remain ignored after execve, unignore them */ 3728935a399SIhor Antonov signal(SIGHUP, SIG_DFL); 3738935a399SIhor Antonov signal(SIGTERM, SIG_DFL); 3748935a399SIhor Antonov execvp(state->argv[0], state->argv); 3758935a399SIhor Antonov /* execvp() failed - report error and exit this process */ 3768935a399SIhor Antonov err(1, "%s", state->argv[0]); 3778935a399SIhor Antonov } 3788935a399SIhor Antonov 3798935a399SIhor Antonov /* Main event loop: fork the child and watch for events. 380a6f795ccSIhor Antonov * After SIGTERM is received and propagated to the child there are 3814c41f4a0SIhor Antonov * several options on what to do next: 3824c41f4a0SIhor Antonov * - read until EOF 3834c41f4a0SIhor Antonov * - read until EOF but only for a while 3844c41f4a0SIhor Antonov * - bail immediately 3854c41f4a0SIhor Antonov * Currently the third option is used, because otherwise there is no 3864c41f4a0SIhor Antonov * guarantee that read() won't block indefinitely if the child refuses 3874c41f4a0SIhor Antonov * to depart. To handle the second option, a different approach 3884c41f4a0SIhor Antonov * would be needed (procctl()?). 3894c41f4a0SIhor Antonov */ 3904c41f4a0SIhor Antonov static void 3914c41f4a0SIhor Antonov daemon_eventloop(struct daemon_state *state) 3924c41f4a0SIhor Antonov { 3938935a399SIhor Antonov struct kevent event; 3948935a399SIhor Antonov int kq; 3958935a399SIhor Antonov int ret; 396407e3790SIhor Antonov int pipe_fd[2]; 3978935a399SIhor Antonov 3988935a399SIhor Antonov /* 3998935a399SIhor Antonov * Try to protect against pageout kill. Ignore the 4008935a399SIhor Antonov * error, madvise(2) will fail only if a process does 4018935a399SIhor Antonov * not have superuser privileges. 4028935a399SIhor Antonov */ 4038935a399SIhor Antonov (void)madvise(NULL, 0, MADV_PROTECT); 4048935a399SIhor Antonov 405407e3790SIhor Antonov if (pipe(pipe_fd)) { 40653d49b37SJilles Tjoelker err(1, "pipe"); 4076b4ef4b1SIhor Antonov } 408407e3790SIhor Antonov state->pipe_rd = pipe_fd[0]; 409407e3790SIhor Antonov state->pipe_wr = pipe_fd[1]; 4108935a399SIhor Antonov 4118935a399SIhor Antonov kq = kqueuex(KQUEUE_CLOEXEC); 412407e3790SIhor Antonov EV_SET(&event, state->pipe_rd, EVFILT_READ, EV_ADD|EV_CLEAR, 0, 0, 4138935a399SIhor Antonov NULL); 4148935a399SIhor Antonov if (kevent(kq, &event, 1, NULL, 0, NULL) == -1) { 4158935a399SIhor Antonov err(EXIT_FAILURE, "failed to register kevent"); 41675f61ca9SIhor Antonov } 41775f61ca9SIhor Antonov 4188935a399SIhor Antonov EV_SET(&event, SIGHUP, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); 4198935a399SIhor Antonov if (kevent(kq, &event, 1, NULL, 0, NULL) == -1) { 4208935a399SIhor Antonov err(EXIT_FAILURE, "failed to register kevent"); 4218935a399SIhor Antonov } 4228935a399SIhor Antonov 4238935a399SIhor Antonov EV_SET(&event, SIGTERM, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); 4248935a399SIhor Antonov if (kevent(kq, &event, 1, NULL, 0, NULL) == -1) { 4258935a399SIhor Antonov err(EXIT_FAILURE, "failed to register kevent"); 4268935a399SIhor Antonov } 4278935a399SIhor Antonov 4288935a399SIhor Antonov EV_SET(&event, SIGCHLD, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); 4298935a399SIhor Antonov if (kevent(kq, &event, 1, NULL, 0, NULL) == -1) { 4308935a399SIhor Antonov err(EXIT_FAILURE, "failed to register kevent"); 4318935a399SIhor Antonov } 4328935a399SIhor Antonov memset(&event, 0, sizeof(struct kevent)); 4338935a399SIhor Antonov 4348935a399SIhor Antonov /* Spawn a child to exec the command. */ 4358935a399SIhor Antonov state->pid = fork(); 4368935a399SIhor Antonov 43775f61ca9SIhor Antonov /* fork failed, this can only happen when supervision is enabled */ 4388935a399SIhor Antonov switch (state->pid) { 4398935a399SIhor Antonov case -1: 4409da0ef13SMikolaj Golub warn("fork"); 4418935a399SIhor Antonov state->mode = MODE_NOCHILD; 4428935a399SIhor Antonov return; 4438935a399SIhor Antonov /* fork succeeded, this is child's branch */ 4448935a399SIhor Antonov case 0: 4458935a399SIhor Antonov close(kq); 4468935a399SIhor Antonov daemon_set_child_pipe(state); 4478935a399SIhor Antonov daemon_exec(state); 44853d49b37SJilles Tjoelker break; 449cd1e6e70SIhor Antonov } 450cd1e6e70SIhor Antonov 4518935a399SIhor Antonov /* case: pid > 0; fork succeeded */ 452407e3790SIhor Antonov close(state->pipe_wr); 453407e3790SIhor Antonov state->pipe_wr = -1; 4548935a399SIhor Antonov setproctitle("%s[%d]", state->title, (int)state->pid); 455cec8e6baSDag-Erling Smørgrav setbuf(stdout, NULL); 456cd1e6e70SIhor Antonov 4578935a399SIhor Antonov while (state->mode != MODE_NOCHILD) { 4588935a399SIhor Antonov ret = kevent(kq, NULL, 0, &event, 1, NULL); 4598935a399SIhor Antonov switch (ret) { 4608935a399SIhor Antonov case -1: 461494e7dfdSKyle Evans if (errno == EINTR) 462494e7dfdSKyle Evans continue; 4638935a399SIhor Antonov err(EXIT_FAILURE, "kevent wait"); 4648935a399SIhor Antonov case 0: 465cd1e6e70SIhor Antonov continue; 46653d49b37SJilles Tjoelker } 467cd1e6e70SIhor Antonov 4688935a399SIhor Antonov if (event.flags & EV_ERROR) { 4698935a399SIhor Antonov errx(EXIT_FAILURE, "Event error: %s", 470a6f795ccSIhor Antonov strerror((int)event.data)); 471cd1e6e70SIhor Antonov } 472cd1e6e70SIhor Antonov 4738935a399SIhor Antonov switch (event.filter) { 4748935a399SIhor Antonov case EVFILT_SIGNAL: 475cd1e6e70SIhor Antonov 4768935a399SIhor Antonov switch (event.ident) { 4778935a399SIhor Antonov case SIGCHLD: 4788935a399SIhor Antonov if (daemon_is_child_dead(state)) { 4798935a399SIhor Antonov /* child is dead, read all until EOF */ 4808935a399SIhor Antonov state->pid = -1; 4818935a399SIhor Antonov state->mode = MODE_NOCHILD; 482e0645579SKyle Evans while (listen_child(state)) { 483e0645579SKyle Evans continue; 484e0645579SKyle Evans } 4858935a399SIhor Antonov } 4868935a399SIhor Antonov continue; 4878935a399SIhor Antonov case SIGTERM: 4888935a399SIhor Antonov if (state->mode != MODE_SUPERVISE) { 4898935a399SIhor Antonov /* user is impatient */ 4908935a399SIhor Antonov /* TODO: warn about repeated SIGTERM? */ 4918935a399SIhor Antonov continue; 492cd1e6e70SIhor Antonov } 493cd1e6e70SIhor Antonov 4948935a399SIhor Antonov state->mode = MODE_TERMINATING; 4958935a399SIhor Antonov state->restart_enabled = false; 4968935a399SIhor Antonov if (state->pid > 0) { 4978935a399SIhor Antonov kill(state->pid, SIGTERM); 49853d49b37SJilles Tjoelker } 4994c41f4a0SIhor Antonov /* 5008935a399SIhor Antonov * TODO set kevent timer to exit 5018935a399SIhor Antonov * unconditionally after some time 5024c41f4a0SIhor Antonov */ 5038935a399SIhor Antonov continue; 5048935a399SIhor Antonov case SIGHUP: 5058935a399SIhor Antonov if (state->log_reopen && state->output_fd >= 0) { 5068935a399SIhor Antonov reopen_log(state); 5078935a399SIhor Antonov } 5088935a399SIhor Antonov continue; 5098935a399SIhor Antonov } 5108935a399SIhor Antonov break; 5118935a399SIhor Antonov 5128935a399SIhor Antonov case EVFILT_READ: 5138935a399SIhor Antonov /* 5148935a399SIhor Antonov * detecting EOF is no longer necessary 5158935a399SIhor Antonov * if child closes the pipe daemon will stop getting 5168935a399SIhor Antonov * EVFILT_READ events 5178935a399SIhor Antonov */ 5188935a399SIhor Antonov 5198935a399SIhor Antonov if (event.data > 0) { 5206ac7c9f0SIhor Antonov (void)listen_child(state); 5218935a399SIhor Antonov } 5228935a399SIhor Antonov continue; 5238935a399SIhor Antonov default: 5248935a399SIhor Antonov continue; 5258935a399SIhor Antonov } 52653d49b37SJilles Tjoelker } 5274c41f4a0SIhor Antonov 5288935a399SIhor Antonov close(kq); 529407e3790SIhor Antonov close(state->pipe_rd); 530407e3790SIhor Antonov state->pipe_rd = -1; 531*aa8722ccSKyle Evans 532*aa8722ccSKyle Evans /* 533*aa8722ccSKyle Evans * We don't have to truncate the pidfile, but it's easier to test 534*aa8722ccSKyle Evans * daemon(8) behavior in some respects if we do. We won't bother if 535*aa8722ccSKyle Evans * the child won't be restarted. 536*aa8722ccSKyle Evans */ 537*aa8722ccSKyle Evans if (state->child_pidfh != NULL && state->restart_enabled) { 538*aa8722ccSKyle Evans pidfile_truncate(state->child_pidfh); 539*aa8722ccSKyle Evans } 540bd06a3ecSMike Barcroft } 541bd06a3ecSMike Barcroft 542bd06a3ecSMike Barcroft static void 5438935a399SIhor Antonov daemon_sleep(struct daemon_state *state) 544195fc497SMikolaj Golub { 5458935a399SIhor Antonov struct timespec ts = { state->restart_delay, 0 }; 54609a3675dSConrad Meyer 5478935a399SIhor Antonov if (!state->restart_enabled) { 5488935a399SIhor Antonov return; 5498935a399SIhor Antonov } 5508935a399SIhor Antonov while (nanosleep(&ts, &ts) == -1) { 5516b4ef4b1SIhor Antonov if (errno != EINTR) { 55253d49b37SJilles Tjoelker err(1, "nanosleep"); 55353d49b37SJilles Tjoelker } 55453d49b37SJilles Tjoelker } 5556b4ef4b1SIhor Antonov } 55653d49b37SJilles Tjoelker 55753d49b37SJilles Tjoelker static void 558298a392eSIhor Antonov open_pid_files(struct daemon_state *state) 55953d49b37SJilles Tjoelker { 56053d49b37SJilles Tjoelker pid_t fpid; 56153d49b37SJilles Tjoelker int serrno; 56253d49b37SJilles Tjoelker 563298a392eSIhor Antonov if (state->child_pidfile) { 564298a392eSIhor Antonov state->child_pidfh = pidfile_open(state->child_pidfile, 0600, &fpid); 565298a392eSIhor Antonov if (state->child_pidfh == NULL) { 56653d49b37SJilles Tjoelker if (errno == EEXIST) { 56753d49b37SJilles Tjoelker errx(3, "process already running, pid: %d", 56853d49b37SJilles Tjoelker fpid); 56953d49b37SJilles Tjoelker } 570298a392eSIhor Antonov err(2, "pidfile ``%s''", state->child_pidfile); 57153d49b37SJilles Tjoelker } 57253d49b37SJilles Tjoelker } 57353d49b37SJilles Tjoelker /* Do the same for the actual daemon process. */ 574298a392eSIhor Antonov if (state->parent_pidfile) { 575298a392eSIhor Antonov state->parent_pidfh= pidfile_open(state->parent_pidfile, 0600, &fpid); 576298a392eSIhor Antonov if (state->parent_pidfh == NULL) { 57753d49b37SJilles Tjoelker serrno = errno; 578298a392eSIhor Antonov pidfile_remove(state->child_pidfh); 57953d49b37SJilles Tjoelker errno = serrno; 58053d49b37SJilles Tjoelker if (errno == EEXIST) { 58153d49b37SJilles Tjoelker errx(3, "process already running, pid: %d", 58253d49b37SJilles Tjoelker fpid); 58353d49b37SJilles Tjoelker } 584298a392eSIhor Antonov err(2, "ppidfile ``%s''", state->parent_pidfile); 58553d49b37SJilles Tjoelker } 58653d49b37SJilles Tjoelker } 58753d49b37SJilles Tjoelker } 58853d49b37SJilles Tjoelker 58953d49b37SJilles Tjoelker static int 59053d49b37SJilles Tjoelker get_log_mapping(const char *str, const CODE *c) 59153d49b37SJilles Tjoelker { 59253d49b37SJilles Tjoelker const CODE *cp; 59353d49b37SJilles Tjoelker for (cp = c; cp->c_name; cp++) 5946b4ef4b1SIhor Antonov if (strcmp(cp->c_name, str) == 0) { 59553d49b37SJilles Tjoelker return cp->c_val; 5966b4ef4b1SIhor Antonov } 59753d49b37SJilles Tjoelker return -1; 598195fc497SMikolaj Golub } 599195fc497SMikolaj Golub 600195fc497SMikolaj Golub static void 601e6d4b388STom Rhodes restrict_process(const char *user) 60212d7249eSTom Rhodes { 60312d7249eSTom Rhodes struct passwd *pw = NULL; 60412d7249eSTom Rhodes 605e6d4b388STom Rhodes pw = getpwnam(user); 6066b4ef4b1SIhor Antonov if (pw == NULL) { 607e6d4b388STom Rhodes errx(1, "unknown user: %s", user); 6086b4ef4b1SIhor Antonov } 60912d7249eSTom Rhodes 6106b4ef4b1SIhor Antonov if (setusercontext(NULL, pw, pw->pw_uid, LOGIN_SETALL) != 0) { 611e6d4b388STom Rhodes errx(1, "failed to set user environment"); 6126b4ef4b1SIhor Antonov } 6136b3ad1d7SMaxim Sobolev 6146b3ad1d7SMaxim Sobolev setenv("USER", pw->pw_name, 1); 6156b3ad1d7SMaxim Sobolev setenv("HOME", pw->pw_dir, 1); 6166b3ad1d7SMaxim Sobolev setenv("SHELL", *pw->pw_shell ? pw->pw_shell : _PATH_BSHELL, 1); 61712d7249eSTom Rhodes } 61812d7249eSTom Rhodes 61953d49b37SJilles Tjoelker /* 62053d49b37SJilles Tjoelker * We try to collect whole lines terminated by '\n'. Otherwise we collect a 62153d49b37SJilles Tjoelker * full buffer, and then output it. 62253d49b37SJilles Tjoelker * 623bc43a9a7SIhor Antonov * Return value of false is assumed to mean EOF or error, and true indicates to 62453d49b37SJilles Tjoelker * continue reading. 62553d49b37SJilles Tjoelker */ 626bc43a9a7SIhor Antonov static bool 6276ac7c9f0SIhor Antonov listen_child(struct daemon_state *state) 6282ad43027SMikolaj Golub { 629a6f795ccSIhor Antonov ssize_t rv; 6305745a584SIhor Antonov unsigned char *cp; 6312ad43027SMikolaj Golub 632cf6356fdSIhor Antonov assert(state != NULL); 63324fd3e96SIhor Antonov assert(state->pos < LBUF_SIZE - 1); 63453d49b37SJilles Tjoelker 635e0645579SKyle Evans rv = read(state->pipe_rd, state->buf + state->pos, 636e0645579SKyle Evans LBUF_SIZE - state->pos - 1); 63753d49b37SJilles Tjoelker if (rv > 0) { 63824fd3e96SIhor Antonov state->pos += rv; 63924fd3e96SIhor Antonov assert(state->pos <= LBUF_SIZE - 1); 64053d49b37SJilles Tjoelker /* Always NUL-terminate just in case. */ 64124fd3e96SIhor Antonov state->buf[LBUF_SIZE - 1] = '\0'; 6425745a584SIhor Antonov 64353d49b37SJilles Tjoelker /* 6445745a584SIhor Antonov * Find position of the last newline in the buffer. 6455745a584SIhor Antonov * The buffer is guaranteed to have one or more complete lines 6465745a584SIhor Antonov * if at least one newline was found when searching in reverse. 6475745a584SIhor Antonov * All complete lines are flushed. 64853d49b37SJilles Tjoelker * This does not take NUL characters into account. 64953d49b37SJilles Tjoelker */ 6505745a584SIhor Antonov cp = memrchr(state->buf, '\n', state->pos); 6515745a584SIhor Antonov if (cp != NULL) { 65224fd3e96SIhor Antonov size_t bytes_line = cp - state->buf + 1; 65324fd3e96SIhor Antonov assert(bytes_line <= state->pos); 65424fd3e96SIhor Antonov do_output(state->buf, bytes_line, state); 65524fd3e96SIhor Antonov state->pos -= bytes_line; 65624fd3e96SIhor Antonov memmove(state->buf, cp + 1, state->pos); 657195fc497SMikolaj Golub } 65853d49b37SJilles Tjoelker /* Wait until the buffer is full. */ 65924fd3e96SIhor Antonov if (state->pos < LBUF_SIZE - 1) { 660bc43a9a7SIhor Antonov return true; 6616b4ef4b1SIhor Antonov } 66224fd3e96SIhor Antonov do_output(state->buf, state->pos, state); 66324fd3e96SIhor Antonov state->pos = 0; 664bc43a9a7SIhor Antonov return true; 66553d49b37SJilles Tjoelker } else if (rv == -1) { 66653d49b37SJilles Tjoelker /* EINTR should trigger another read. */ 66753d49b37SJilles Tjoelker if (errno == EINTR) { 668bc43a9a7SIhor Antonov return true; 66953d49b37SJilles Tjoelker } else { 67053d49b37SJilles Tjoelker warn("read"); 671bc43a9a7SIhor Antonov return false; 672c60d51f9SMikolaj Golub } 67353d49b37SJilles Tjoelker } 67453d49b37SJilles Tjoelker /* Upon EOF, we have to flush what's left of the buffer. */ 67524fd3e96SIhor Antonov if (state->pos > 0) { 67624fd3e96SIhor Antonov do_output(state->buf, state->pos, state); 67724fd3e96SIhor Antonov state->pos = 0; 67853d49b37SJilles Tjoelker } 679bc43a9a7SIhor Antonov return false; 68053d49b37SJilles Tjoelker } 68153d49b37SJilles Tjoelker 68253d49b37SJilles Tjoelker /* 68353d49b37SJilles Tjoelker * The default behavior is to stay silent if the user wants to redirect 68453d49b37SJilles Tjoelker * output to a file and/or syslog. If neither are provided, then we bounce 68553d49b37SJilles Tjoelker * everything back to parent's stdout. 68653d49b37SJilles Tjoelker */ 68753d49b37SJilles Tjoelker static void 688298a392eSIhor Antonov do_output(const unsigned char *buf, size_t len, struct daemon_state *state) 68953d49b37SJilles Tjoelker { 69053d49b37SJilles Tjoelker assert(len <= LBUF_SIZE); 691cf6356fdSIhor Antonov assert(state != NULL); 69253d49b37SJilles Tjoelker 6936b4ef4b1SIhor Antonov if (len < 1) { 69453d49b37SJilles Tjoelker return; 6956b4ef4b1SIhor Antonov } 696298a392eSIhor Antonov if (state->syslog_enabled) { 697298a392eSIhor Antonov syslog(state->syslog_priority, "%.*s", (int)len, buf); 6986b4ef4b1SIhor Antonov } 699298a392eSIhor Antonov if (state->output_fd != -1) { 700298a392eSIhor Antonov if (write(state->output_fd, buf, len) == -1) 70153d49b37SJilles Tjoelker warn("write"); 70253d49b37SJilles Tjoelker } 703298a392eSIhor Antonov if (state->keep_fds_open && 704298a392eSIhor Antonov !state->syslog_enabled && 705298a392eSIhor Antonov state->output_fd == -1) { 70653d49b37SJilles Tjoelker printf("%.*s", (int)len, buf); 70753d49b37SJilles Tjoelker } 7086b4ef4b1SIhor Antonov } 70953d49b37SJilles Tjoelker 7104cd407ecSMaxim Sobolev static int 7114cd407ecSMaxim Sobolev open_log(const char *outfn) 7124cd407ecSMaxim Sobolev { 7134cd407ecSMaxim Sobolev 7144cd407ecSMaxim Sobolev return open(outfn, O_CREAT | O_WRONLY | O_APPEND | O_CLOEXEC, 0600); 7154cd407ecSMaxim Sobolev } 7164cd407ecSMaxim Sobolev 7174cd407ecSMaxim Sobolev static void 718298a392eSIhor Antonov reopen_log(struct daemon_state *state) 7194cd407ecSMaxim Sobolev { 7204cd407ecSMaxim Sobolev int outfd; 7214cd407ecSMaxim Sobolev 722298a392eSIhor Antonov outfd = open_log(state->output_filename); 723298a392eSIhor Antonov if (state->output_fd >= 0) { 724298a392eSIhor Antonov close(state->output_fd); 7256b4ef4b1SIhor Antonov } 726298a392eSIhor Antonov state->output_fd = outfd; 7274cd407ecSMaxim Sobolev } 7284cd407ecSMaxim Sobolev 729298a392eSIhor Antonov static void 730298a392eSIhor Antonov daemon_state_init(struct daemon_state *state) 731298a392eSIhor Antonov { 732298a392eSIhor Antonov *state = (struct daemon_state) { 73324fd3e96SIhor Antonov .buf = {0}, 73424fd3e96SIhor Antonov .pos = 0, 7354c41f4a0SIhor Antonov .argv = NULL, 736298a392eSIhor Antonov .parent_pidfh = NULL, 737298a392eSIhor Antonov .child_pidfh = NULL, 738298a392eSIhor Antonov .child_pidfile = NULL, 739298a392eSIhor Antonov .parent_pidfile = NULL, 740298a392eSIhor Antonov .title = NULL, 741298a392eSIhor Antonov .user = NULL, 7428935a399SIhor Antonov .mode = MODE_DAEMON, 743298a392eSIhor Antonov .restart_enabled = false, 7448935a399SIhor Antonov .pid = 0, 745407e3790SIhor Antonov .pipe_rd = -1, 746407e3790SIhor Antonov .pipe_wr = -1, 747298a392eSIhor Antonov .keep_cur_workdir = 1, 748298a392eSIhor Antonov .restart_delay = 1, 749298a392eSIhor Antonov .stdmask = STDOUT_FILENO | STDERR_FILENO, 750298a392eSIhor Antonov .syslog_enabled = false, 751298a392eSIhor Antonov .log_reopen = false, 752298a392eSIhor Antonov .syslog_priority = LOG_NOTICE, 753298a392eSIhor Antonov .syslog_tag = "daemon", 754298a392eSIhor Antonov .syslog_facility = LOG_DAEMON, 755298a392eSIhor Antonov .keep_fds_open = 1, 756298a392eSIhor Antonov .output_fd = -1, 757298a392eSIhor Antonov .output_filename = NULL, 7587618c9e1SJuraj Lutter .restart_count = -1, 7597618c9e1SJuraj Lutter .restarted_count = 0 760298a392eSIhor Antonov }; 761298a392eSIhor Antonov } 762cf6356fdSIhor Antonov 763cf6356fdSIhor Antonov static _Noreturn void 764cf6356fdSIhor Antonov daemon_terminate(struct daemon_state *state) 765cf6356fdSIhor Antonov { 766cf6356fdSIhor Antonov assert(state != NULL); 7678935a399SIhor Antonov 7688935a399SIhor Antonov if (state->output_fd >= 0) { 769cf6356fdSIhor Antonov close(state->output_fd); 7708935a399SIhor Antonov } 771407e3790SIhor Antonov if (state->pipe_rd >= 0) { 772407e3790SIhor Antonov close(state->pipe_rd); 7738935a399SIhor Antonov } 7748935a399SIhor Antonov 775407e3790SIhor Antonov if (state->pipe_wr >= 0) { 776407e3790SIhor Antonov close(state->pipe_wr); 7778935a399SIhor Antonov } 778cf6356fdSIhor Antonov if (state->syslog_enabled) { 779cf6356fdSIhor Antonov closelog(); 780cf6356fdSIhor Antonov } 781cf6356fdSIhor Antonov pidfile_remove(state->child_pidfh); 782cf6356fdSIhor Antonov pidfile_remove(state->parent_pidfh); 783cf6356fdSIhor Antonov 784cf6356fdSIhor Antonov /* 785cf6356fdSIhor Antonov * Note that the exit value here doesn't matter in the case of a clean 786cf6356fdSIhor Antonov * exit; daemon(3) already detached us from the caller, nothing is left 787cf6356fdSIhor Antonov * to care about this one. 788cf6356fdSIhor Antonov */ 789cf6356fdSIhor Antonov exit(1); 790cf6356fdSIhor Antonov } 7918935a399SIhor Antonov 7928935a399SIhor Antonov /* 7938eaa6be8SKonstantin Belousov * Returns true if SIGCHILD came from state->pid due to its exit. 7948935a399SIhor Antonov */ 7958935a399SIhor Antonov static bool 7968935a399SIhor Antonov daemon_is_child_dead(struct daemon_state *state) 7978935a399SIhor Antonov { 7988eaa6be8SKonstantin Belousov int status; 7998eaa6be8SKonstantin Belousov 8008935a399SIhor Antonov for (;;) { 8018eaa6be8SKonstantin Belousov int who = waitpid(-1, &status, WNOHANG); 8028eaa6be8SKonstantin Belousov if (state->pid == who && (WIFEXITED(status) || 8038eaa6be8SKonstantin Belousov WIFSIGNALED(status))) { 8048935a399SIhor Antonov return true; 8058935a399SIhor Antonov } 8068eaa6be8SKonstantin Belousov if (who == 0) { 8078eaa6be8SKonstantin Belousov return false; 8088eaa6be8SKonstantin Belousov } 8098935a399SIhor Antonov if (who == -1 && errno != EINTR) { 8108935a399SIhor Antonov warn("waitpid"); 8118935a399SIhor Antonov return false; 8128935a399SIhor Antonov } 8138935a399SIhor Antonov } 8148935a399SIhor Antonov } 8158935a399SIhor Antonov 8168935a399SIhor Antonov static void 8178935a399SIhor Antonov daemon_set_child_pipe(struct daemon_state *state) 8188935a399SIhor Antonov { 8198935a399SIhor Antonov if (state->stdmask & STDERR_FILENO) { 820407e3790SIhor Antonov if (dup2(state->pipe_wr, STDERR_FILENO) == -1) { 8218935a399SIhor Antonov err(1, "dup2"); 8228935a399SIhor Antonov } 8238935a399SIhor Antonov } 8248935a399SIhor Antonov if (state->stdmask & STDOUT_FILENO) { 825407e3790SIhor Antonov if (dup2(state->pipe_wr, STDOUT_FILENO) == -1) { 8268935a399SIhor Antonov err(1, "dup2"); 8278935a399SIhor Antonov } 8288935a399SIhor Antonov } 829407e3790SIhor Antonov if (state->pipe_wr != STDERR_FILENO && 830407e3790SIhor Antonov state->pipe_wr != STDOUT_FILENO) { 831407e3790SIhor Antonov close(state->pipe_wr); 8328935a399SIhor Antonov } 8338935a399SIhor Antonov 8348935a399SIhor Antonov /* The child gets dup'd pipes. */ 835407e3790SIhor Antonov close(state->pipe_rd); 8368935a399SIhor Antonov } 837*aa8722ccSKyle Evans 838*aa8722ccSKyle Evans static int 839*aa8722ccSKyle Evans pidfile_truncate(struct pidfh *pfh) 840*aa8722ccSKyle Evans { 841*aa8722ccSKyle Evans int pfd = pidfile_fileno(pfh); 842*aa8722ccSKyle Evans 843*aa8722ccSKyle Evans assert(pfd >= 0); 844*aa8722ccSKyle Evans 845*aa8722ccSKyle Evans if (ftruncate(pfd, 0) == -1) 846*aa8722ccSKyle Evans return (-1); 847*aa8722ccSKyle Evans 848*aa8722ccSKyle Evans /* 849*aa8722ccSKyle Evans * pidfile_write(3) will always pwrite(..., 0) today, but let's assume 850*aa8722ccSKyle Evans * it may not always and do a best-effort reset of the position just to 851*aa8722ccSKyle Evans * set a good example. 852*aa8722ccSKyle Evans */ 853*aa8722ccSKyle Evans (void)lseek(pfd, 0, SEEK_SET); 854*aa8722ccSKyle Evans return (0); 855*aa8722ccSKyle Evans } 856