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 59*7618c9e1SJuraj Lutter /* Maximum number of restarts */ 60*7618c9e1SJuraj Lutter #define MAX_RESTART_COUNT 128 61*7618c9e1SJuraj 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; 98*7618c9e1SJuraj Lutter int restart_count; 99*7618c9e1SJuraj 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*7618c9e1SJuraj Lutter static const char shortopts[] = "+cfHSp:P:ru:o:s:l:t:m:R:T:C:h"; 1180a402ad2SIhor Antonov 1190a402ad2SIhor Antonov static const struct option longopts[] = { 1200a402ad2SIhor Antonov { "change-dir", no_argument, NULL, 'c' }, 1210a402ad2SIhor Antonov { "close-fds", no_argument, NULL, 'f' }, 1220a402ad2SIhor Antonov { "sighup", no_argument, NULL, 'H' }, 1230a402ad2SIhor Antonov { "syslog", no_argument, NULL, 'S' }, 1240a402ad2SIhor Antonov { "output-file", required_argument, NULL, 'o' }, 1250a402ad2SIhor Antonov { "output-mask", required_argument, NULL, 'm' }, 1260a402ad2SIhor Antonov { "child-pidfile", required_argument, NULL, 'p' }, 1270a402ad2SIhor Antonov { "supervisor-pidfile", required_argument, NULL, 'P' }, 1280a402ad2SIhor Antonov { "restart", no_argument, NULL, 'r' }, 129*7618c9e1SJuraj Lutter { "restart-count", required_argument, NULL, 'C' }, 1300a402ad2SIhor Antonov { "restart-delay", required_argument, NULL, 'R' }, 1310a402ad2SIhor Antonov { "title", required_argument, NULL, 't' }, 1320a402ad2SIhor Antonov { "user", required_argument, NULL, 'u' }, 1330a402ad2SIhor Antonov { "syslog-priority", required_argument, NULL, 's' }, 1340a402ad2SIhor Antonov { "syslog-facility", required_argument, NULL, 'l' }, 1350a402ad2SIhor Antonov { "syslog-tag", required_argument, NULL, 'T' }, 1360a402ad2SIhor Antonov { "help", no_argument, NULL, 'h' }, 1370a402ad2SIhor Antonov { NULL, 0, NULL, 0 } 1380a402ad2SIhor Antonov }; 1390a402ad2SIhor Antonov 1400a402ad2SIhor Antonov static _Noreturn void 1410a402ad2SIhor Antonov usage(int exitcode) 1420a402ad2SIhor Antonov { 1430a402ad2SIhor Antonov (void)fprintf(stderr, 1440a402ad2SIhor Antonov "usage: daemon [-cfHrS] [-p child_pidfile] [-P supervisor_pidfile]\n" 1450a402ad2SIhor Antonov " [-u user] [-o output_file] [-t title]\n" 1460a402ad2SIhor Antonov " [-l syslog_facility] [-s syslog_priority]\n" 1470a402ad2SIhor Antonov " [-T syslog_tag] [-m output_mask] [-R restart_delay_secs]\n" 148*7618c9e1SJuraj Lutter " [-C restart_count]\n" 1490a402ad2SIhor Antonov "command arguments ...\n"); 1500a402ad2SIhor Antonov 1510a402ad2SIhor Antonov (void)fprintf(stderr, 1520a402ad2SIhor Antonov " --change-dir -c Change the current working directory to root\n" 1530a402ad2SIhor Antonov " --close-fds -f Set stdin, stdout, stderr to /dev/null\n" 1540a402ad2SIhor Antonov " --sighup -H Close and re-open output file on SIGHUP\n" 1550a402ad2SIhor Antonov " --syslog -S Send output to syslog\n" 1560a402ad2SIhor Antonov " --output-file -o <file> Append output of the child process to file\n" 1570a402ad2SIhor Antonov " --output-mask -m <mask> What to send to syslog/file\n" 1580a402ad2SIhor Antonov " 1=stdout, 2=stderr, 3=both\n" 1590a402ad2SIhor Antonov " --child-pidfile -p <file> Write PID of the child process to file\n" 1600a402ad2SIhor Antonov " --supervisor-pidfile -P <file> Write PID of the supervisor process to file\n" 1610a402ad2SIhor Antonov " --restart -r Restart child if it terminates (1 sec delay)\n" 162*7618c9e1SJuraj Lutter " --restart-count -C <N> Restart child at most N times, then exit\n" 1630a402ad2SIhor Antonov " --restart-delay -R <N> Restart child if it terminates after N sec\n" 1640a402ad2SIhor Antonov " --title -t <title> Set the title of the supervisor process\n" 1650a402ad2SIhor Antonov " --user -u <user> Drop privileges, run as given user\n" 1660a402ad2SIhor Antonov " --syslog-priority -s <prio> Set syslog priority\n" 1670a402ad2SIhor Antonov " --syslog-facility -l <flty> Set syslog facility\n" 1680a402ad2SIhor Antonov " --syslog-tag -T <tag> Set syslog tag\n" 1690a402ad2SIhor Antonov " --help -h Show this help\n"); 1700a402ad2SIhor Antonov 1710a402ad2SIhor Antonov exit(exitcode); 1720a402ad2SIhor Antonov } 1730a402ad2SIhor Antonov 174bd06a3ecSMike Barcroft int 175bd06a3ecSMike Barcroft main(int argc, char *argv[]) 176bd06a3ecSMike Barcroft { 177a6f795ccSIhor Antonov const char *e = NULL; 178e745dc22SIhor Antonov int ch = 0; 179298a392eSIhor Antonov struct daemon_state state; 180bd06a3ecSMike Barcroft 181298a392eSIhor Antonov daemon_state_init(&state); 1829ee1faeeSIhor Antonov 1838935a399SIhor Antonov /* Signals are processed via kqueue */ 1848935a399SIhor Antonov signal(SIGHUP, SIG_IGN); 1858935a399SIhor Antonov signal(SIGTERM, SIG_IGN); 1868935a399SIhor Antonov 1879ee1faeeSIhor Antonov /* 188f907027bSIhor Antonov * Supervision mode is enabled if one of the following options are used: 189f907027bSIhor Antonov * --child-pidfile -p 190f907027bSIhor Antonov * --supervisor-pidfile -P 191f907027bSIhor Antonov * --restart -r / --restart-delay -R 192f907027bSIhor Antonov * --syslog -S 193f907027bSIhor Antonov * --syslog-facility -l 194f907027bSIhor Antonov * --syslog-priority -s 195f907027bSIhor Antonov * --syslog-tag -T 196f907027bSIhor Antonov * 197f907027bSIhor Antonov * In supervision mode daemon executes the command in a forked process 198f907027bSIhor Antonov * and observes the child by waiting for SIGCHILD. In supervision mode 199f907027bSIhor Antonov * daemon must never exit before the child, this is necessary to prevent 200f907027bSIhor Antonov * orphaning the child and leaving a stale pid file. 201f907027bSIhor Antonov * To achieve this daemon catches SIGTERM and 202f907027bSIhor Antonov * forwards it to the child, expecting to get SIGCHLD eventually. 203f907027bSIhor Antonov */ 2040a402ad2SIhor Antonov while ((ch = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) { 205bd06a3ecSMike Barcroft switch (ch) { 206bd06a3ecSMike Barcroft case 'c': 207298a392eSIhor Antonov state.keep_cur_workdir = 0; 208bd06a3ecSMike Barcroft break; 209*7618c9e1SJuraj Lutter case 'C': 210*7618c9e1SJuraj Lutter state.restart_count = (int)strtonum(optarg, 0, 211*7618c9e1SJuraj Lutter MAX_RESTART_COUNT, &e); 212*7618c9e1SJuraj Lutter if (e != NULL) { 213*7618c9e1SJuraj Lutter errx(6, "invalid restart count: %s", e); 214*7618c9e1SJuraj Lutter } 215*7618c9e1SJuraj Lutter break; 216bd06a3ecSMike Barcroft case 'f': 217298a392eSIhor Antonov state.keep_fds_open = 0; 218bd06a3ecSMike Barcroft break; 2194cd407ecSMaxim Sobolev case 'H': 220298a392eSIhor Antonov state.log_reopen = true; 2214cd407ecSMaxim Sobolev break; 22253d49b37SJilles Tjoelker case 'l': 223298a392eSIhor Antonov state.syslog_facility = get_log_mapping(optarg, 22439ea4280SIhor Antonov facilitynames); 225298a392eSIhor Antonov if (state.syslog_facility == -1) { 22653d49b37SJilles Tjoelker errx(5, "unrecognized syslog facility"); 2276b4ef4b1SIhor Antonov } 228298a392eSIhor Antonov state.syslog_enabled = true; 2298935a399SIhor Antonov state.mode = MODE_SUPERVISE; 23053d49b37SJilles Tjoelker break; 23153d49b37SJilles Tjoelker case 'm': 232a6f795ccSIhor Antonov state.stdmask = (int)strtonum(optarg, 0, 3, &e); 233a6f795ccSIhor Antonov if (e != NULL) { 234a6f795ccSIhor Antonov errx(6, "unrecognized listening mask: %s", e); 2356b4ef4b1SIhor Antonov } 23653d49b37SJilles Tjoelker break; 23753d49b37SJilles Tjoelker case 'o': 238298a392eSIhor Antonov state.output_filename = optarg; 239f907027bSIhor Antonov /* 240f907027bSIhor Antonov * TODO: setting output filename doesn't have to turn 241f907027bSIhor Antonov * the supervision mode on. For non-supervised mode 242f907027bSIhor Antonov * daemon could open the specified file and set it's 243f907027bSIhor Antonov * descriptor as both stderr and stout before execve() 244f907027bSIhor Antonov */ 2458935a399SIhor Antonov state.mode = MODE_SUPERVISE; 24653d49b37SJilles Tjoelker break; 247846be7bdSPoul-Henning Kamp case 'p': 248298a392eSIhor Antonov state.child_pidfile = optarg; 2498935a399SIhor Antonov state.mode = MODE_SUPERVISE; 250846be7bdSPoul-Henning Kamp break; 25132b17786SJohn-Mark Gurney case 'P': 252298a392eSIhor Antonov state.parent_pidfile = optarg; 2538935a399SIhor Antonov state.mode = MODE_SUPERVISE; 25432b17786SJohn-Mark Gurney break; 255b6193c24SMikolaj Golub case 'r': 256298a392eSIhor Antonov state.restart_enabled = true; 2578935a399SIhor Antonov state.mode = MODE_SUPERVISE; 258b6193c24SMikolaj Golub break; 25937820b87SIan Lepore case 'R': 260298a392eSIhor Antonov state.restart_enabled = true; 261a6f795ccSIhor Antonov state.restart_delay = (int)strtonum(optarg, 1, 262a6f795ccSIhor Antonov MAX_RESTART_DELAY, &e); 263a6f795ccSIhor Antonov if (e != NULL) { 264a6f795ccSIhor Antonov errx(6, "invalid restart delay: %s", e); 2656b4ef4b1SIhor Antonov } 266bbc6e6c5SMathieu state.mode = MODE_SUPERVISE; 26737820b87SIan Lepore break; 26853d49b37SJilles Tjoelker case 's': 269298a392eSIhor Antonov state.syslog_priority = get_log_mapping(optarg, 27039ea4280SIhor Antonov prioritynames); 271298a392eSIhor Antonov if (state.syslog_priority == -1) { 27253d49b37SJilles Tjoelker errx(4, "unrecognized syslog priority"); 2736b4ef4b1SIhor Antonov } 274298a392eSIhor Antonov state.syslog_enabled = true; 2758935a399SIhor Antonov state.mode = MODE_SUPERVISE; 27653d49b37SJilles Tjoelker break; 27753d49b37SJilles Tjoelker case 'S': 278298a392eSIhor Antonov state.syslog_enabled = true; 2798935a399SIhor Antonov state.mode = MODE_SUPERVISE; 28053d49b37SJilles Tjoelker break; 281112bfcf5SConrad Meyer case 't': 282298a392eSIhor Antonov state.title = optarg; 283112bfcf5SConrad Meyer break; 28453d49b37SJilles Tjoelker case 'T': 285298a392eSIhor Antonov state.syslog_tag = optarg; 286298a392eSIhor Antonov state.syslog_enabled = true; 2878935a399SIhor Antonov state.mode = MODE_SUPERVISE; 28853d49b37SJilles Tjoelker break; 289e6d4b388STom Rhodes case 'u': 290298a392eSIhor Antonov state.user = optarg; 291e6d4b388STom Rhodes break; 2920a402ad2SIhor Antonov case 'h': 2930a402ad2SIhor Antonov usage(0); 294f7a10a77SCollin Funk __unreachable(); 295bd06a3ecSMike Barcroft default: 2960a402ad2SIhor Antonov usage(1); 297bd06a3ecSMike Barcroft } 298bd06a3ecSMike Barcroft } 299bd06a3ecSMike Barcroft argc -= optind; 300bd06a3ecSMike Barcroft argv += optind; 3014c41f4a0SIhor Antonov state.argv = argv; 302bd06a3ecSMike Barcroft 3036b4ef4b1SIhor Antonov if (argc == 0) { 3040a402ad2SIhor Antonov usage(1); 3056b4ef4b1SIhor Antonov } 30612d7249eSTom Rhodes 307298a392eSIhor Antonov if (!state.title) { 308298a392eSIhor Antonov state.title = argv[0]; 3096b4ef4b1SIhor Antonov } 31053d49b37SJilles Tjoelker 311298a392eSIhor Antonov if (state.output_filename) { 312298a392eSIhor Antonov state.output_fd = open_log(state.output_filename); 313298a392eSIhor Antonov if (state.output_fd == -1) { 31453d49b37SJilles Tjoelker err(7, "open"); 31553d49b37SJilles Tjoelker } 3166b4ef4b1SIhor Antonov } 31753d49b37SJilles Tjoelker 318298a392eSIhor Antonov if (state.syslog_enabled) { 319298a392eSIhor Antonov openlog(state.syslog_tag, LOG_PID | LOG_NDELAY, 320298a392eSIhor Antonov state.syslog_facility); 3216b4ef4b1SIhor Antonov } 32253d49b37SJilles Tjoelker 323846be7bdSPoul-Henning Kamp /* 324846be7bdSPoul-Henning Kamp * Try to open the pidfile before calling daemon(3), 325846be7bdSPoul-Henning Kamp * to be able to report the error intelligently 326846be7bdSPoul-Henning Kamp */ 327298a392eSIhor Antonov open_pid_files(&state); 3288935a399SIhor Antonov 3298935a399SIhor Antonov /* 3308935a399SIhor Antonov * TODO: add feature to avoid backgrounding 3318935a399SIhor Antonov * i.e. --foreground, -f 3328935a399SIhor Antonov */ 333298a392eSIhor Antonov if (daemon(state.keep_cur_workdir, state.keep_fds_open) == -1) { 3349da0ef13SMikolaj Golub warn("daemon"); 335cf6356fdSIhor Antonov daemon_terminate(&state); 3369da0ef13SMikolaj Golub } 3378935a399SIhor Antonov 3388935a399SIhor Antonov if (state.mode == MODE_DAEMON) { 3398935a399SIhor Antonov daemon_exec(&state); 3408935a399SIhor Antonov } 3418935a399SIhor Antonov 3429da0ef13SMikolaj Golub /* Write out parent pidfile if needed. */ 343298a392eSIhor Antonov pidfile_write(state.parent_pidfh); 344203df05bSIhor Antonov 3454c41f4a0SIhor Antonov do { 3468935a399SIhor Antonov state.mode = MODE_SUPERVISE; 3474c41f4a0SIhor Antonov daemon_eventloop(&state); 3488935a399SIhor Antonov daemon_sleep(&state); 349*7618c9e1SJuraj Lutter if (state.restart_enabled && state.restart_count > -1) { 350*7618c9e1SJuraj Lutter if (state.restarted_count >= state.restart_count) { 351*7618c9e1SJuraj Lutter state.restart_enabled = false; 352*7618c9e1SJuraj Lutter } 353*7618c9e1SJuraj Lutter state.restarted_count++; 354*7618c9e1SJuraj Lutter } 3558935a399SIhor Antonov } while (state.restart_enabled); 3564c41f4a0SIhor Antonov 3574c41f4a0SIhor Antonov daemon_terminate(&state); 3584c41f4a0SIhor Antonov } 3594c41f4a0SIhor Antonov 3608935a399SIhor Antonov static void 3618935a399SIhor Antonov daemon_exec(struct daemon_state *state) 3628935a399SIhor Antonov { 3638935a399SIhor Antonov pidfile_write(state->child_pidfh); 3644c41f4a0SIhor Antonov 3658935a399SIhor Antonov if (state->user != NULL) { 3668935a399SIhor Antonov restrict_process(state->user); 3678935a399SIhor Antonov } 3688935a399SIhor Antonov 3698935a399SIhor Antonov /* Ignored signals remain ignored after execve, unignore them */ 3708935a399SIhor Antonov signal(SIGHUP, SIG_DFL); 3718935a399SIhor Antonov signal(SIGTERM, SIG_DFL); 3728935a399SIhor Antonov execvp(state->argv[0], state->argv); 3738935a399SIhor Antonov /* execvp() failed - report error and exit this process */ 3748935a399SIhor Antonov err(1, "%s", state->argv[0]); 3758935a399SIhor Antonov } 3768935a399SIhor Antonov 3778935a399SIhor Antonov /* Main event loop: fork the child and watch for events. 378a6f795ccSIhor Antonov * After SIGTERM is received and propagated to the child there are 3794c41f4a0SIhor Antonov * several options on what to do next: 3804c41f4a0SIhor Antonov * - read until EOF 3814c41f4a0SIhor Antonov * - read until EOF but only for a while 3824c41f4a0SIhor Antonov * - bail immediately 3834c41f4a0SIhor Antonov * Currently the third option is used, because otherwise there is no 3844c41f4a0SIhor Antonov * guarantee that read() won't block indefinitely if the child refuses 3854c41f4a0SIhor Antonov * to depart. To handle the second option, a different approach 3864c41f4a0SIhor Antonov * would be needed (procctl()?). 3874c41f4a0SIhor Antonov */ 3884c41f4a0SIhor Antonov static void 3894c41f4a0SIhor Antonov daemon_eventloop(struct daemon_state *state) 3904c41f4a0SIhor Antonov { 3918935a399SIhor Antonov struct kevent event; 3928935a399SIhor Antonov int kq; 3938935a399SIhor Antonov int ret; 394407e3790SIhor Antonov int pipe_fd[2]; 3958935a399SIhor Antonov 3968935a399SIhor Antonov /* 3978935a399SIhor Antonov * Try to protect against pageout kill. Ignore the 3988935a399SIhor Antonov * error, madvise(2) will fail only if a process does 3998935a399SIhor Antonov * not have superuser privileges. 4008935a399SIhor Antonov */ 4018935a399SIhor Antonov (void)madvise(NULL, 0, MADV_PROTECT); 4028935a399SIhor Antonov 403407e3790SIhor Antonov if (pipe(pipe_fd)) { 40453d49b37SJilles Tjoelker err(1, "pipe"); 4056b4ef4b1SIhor Antonov } 406407e3790SIhor Antonov state->pipe_rd = pipe_fd[0]; 407407e3790SIhor Antonov state->pipe_wr = pipe_fd[1]; 4088935a399SIhor Antonov 4098935a399SIhor Antonov kq = kqueuex(KQUEUE_CLOEXEC); 410407e3790SIhor Antonov EV_SET(&event, state->pipe_rd, EVFILT_READ, EV_ADD|EV_CLEAR, 0, 0, 4118935a399SIhor Antonov NULL); 4128935a399SIhor Antonov if (kevent(kq, &event, 1, NULL, 0, NULL) == -1) { 4138935a399SIhor Antonov err(EXIT_FAILURE, "failed to register kevent"); 41475f61ca9SIhor Antonov } 41575f61ca9SIhor Antonov 4168935a399SIhor Antonov EV_SET(&event, SIGHUP, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); 4178935a399SIhor Antonov if (kevent(kq, &event, 1, NULL, 0, NULL) == -1) { 4188935a399SIhor Antonov err(EXIT_FAILURE, "failed to register kevent"); 4198935a399SIhor Antonov } 4208935a399SIhor Antonov 4218935a399SIhor Antonov EV_SET(&event, SIGTERM, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); 4228935a399SIhor Antonov if (kevent(kq, &event, 1, NULL, 0, NULL) == -1) { 4238935a399SIhor Antonov err(EXIT_FAILURE, "failed to register kevent"); 4248935a399SIhor Antonov } 4258935a399SIhor Antonov 4268935a399SIhor Antonov EV_SET(&event, SIGCHLD, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); 4278935a399SIhor Antonov if (kevent(kq, &event, 1, NULL, 0, NULL) == -1) { 4288935a399SIhor Antonov err(EXIT_FAILURE, "failed to register kevent"); 4298935a399SIhor Antonov } 4308935a399SIhor Antonov memset(&event, 0, sizeof(struct kevent)); 4318935a399SIhor Antonov 4328935a399SIhor Antonov /* Spawn a child to exec the command. */ 4338935a399SIhor Antonov state->pid = fork(); 4348935a399SIhor Antonov 43575f61ca9SIhor Antonov /* fork failed, this can only happen when supervision is enabled */ 4368935a399SIhor Antonov switch (state->pid) { 4378935a399SIhor Antonov case -1: 4389da0ef13SMikolaj Golub warn("fork"); 4398935a399SIhor Antonov state->mode = MODE_NOCHILD; 4408935a399SIhor Antonov return; 4418935a399SIhor Antonov /* fork succeeded, this is child's branch */ 4428935a399SIhor Antonov case 0: 4438935a399SIhor Antonov close(kq); 4448935a399SIhor Antonov daemon_set_child_pipe(state); 4458935a399SIhor Antonov daemon_exec(state); 44653d49b37SJilles Tjoelker break; 447cd1e6e70SIhor Antonov } 448cd1e6e70SIhor Antonov 4498935a399SIhor Antonov /* case: pid > 0; fork succeeded */ 450407e3790SIhor Antonov close(state->pipe_wr); 451407e3790SIhor Antonov state->pipe_wr = -1; 4528935a399SIhor Antonov setproctitle("%s[%d]", state->title, (int)state->pid); 453cec8e6baSDag-Erling Smørgrav setbuf(stdout, NULL); 454cd1e6e70SIhor Antonov 4558935a399SIhor Antonov while (state->mode != MODE_NOCHILD) { 4568935a399SIhor Antonov ret = kevent(kq, NULL, 0, &event, 1, NULL); 4578935a399SIhor Antonov switch (ret) { 4588935a399SIhor Antonov case -1: 459494e7dfdSKyle Evans if (errno == EINTR) 460494e7dfdSKyle Evans continue; 4618935a399SIhor Antonov err(EXIT_FAILURE, "kevent wait"); 4628935a399SIhor Antonov case 0: 463cd1e6e70SIhor Antonov continue; 46453d49b37SJilles Tjoelker } 465cd1e6e70SIhor Antonov 4668935a399SIhor Antonov if (event.flags & EV_ERROR) { 4678935a399SIhor Antonov errx(EXIT_FAILURE, "Event error: %s", 468a6f795ccSIhor Antonov strerror((int)event.data)); 469cd1e6e70SIhor Antonov } 470cd1e6e70SIhor Antonov 4718935a399SIhor Antonov switch (event.filter) { 4728935a399SIhor Antonov case EVFILT_SIGNAL: 473cd1e6e70SIhor Antonov 4748935a399SIhor Antonov switch (event.ident) { 4758935a399SIhor Antonov case SIGCHLD: 4768935a399SIhor Antonov if (daemon_is_child_dead(state)) { 4778935a399SIhor Antonov /* child is dead, read all until EOF */ 4788935a399SIhor Antonov state->pid = -1; 4798935a399SIhor Antonov state->mode = MODE_NOCHILD; 480e0645579SKyle Evans while (listen_child(state)) { 481e0645579SKyle Evans continue; 482e0645579SKyle Evans } 4838935a399SIhor Antonov } 4848935a399SIhor Antonov continue; 4858935a399SIhor Antonov case SIGTERM: 4868935a399SIhor Antonov if (state->mode != MODE_SUPERVISE) { 4878935a399SIhor Antonov /* user is impatient */ 4888935a399SIhor Antonov /* TODO: warn about repeated SIGTERM? */ 4898935a399SIhor Antonov continue; 490cd1e6e70SIhor Antonov } 491cd1e6e70SIhor Antonov 4928935a399SIhor Antonov state->mode = MODE_TERMINATING; 4938935a399SIhor Antonov state->restart_enabled = false; 4948935a399SIhor Antonov if (state->pid > 0) { 4958935a399SIhor Antonov kill(state->pid, SIGTERM); 49653d49b37SJilles Tjoelker } 4974c41f4a0SIhor Antonov /* 4988935a399SIhor Antonov * TODO set kevent timer to exit 4998935a399SIhor Antonov * unconditionally after some time 5004c41f4a0SIhor Antonov */ 5018935a399SIhor Antonov continue; 5028935a399SIhor Antonov case SIGHUP: 5038935a399SIhor Antonov if (state->log_reopen && state->output_fd >= 0) { 5048935a399SIhor Antonov reopen_log(state); 5058935a399SIhor Antonov } 5068935a399SIhor Antonov continue; 5078935a399SIhor Antonov } 5088935a399SIhor Antonov break; 5098935a399SIhor Antonov 5108935a399SIhor Antonov case EVFILT_READ: 5118935a399SIhor Antonov /* 5128935a399SIhor Antonov * detecting EOF is no longer necessary 5138935a399SIhor Antonov * if child closes the pipe daemon will stop getting 5148935a399SIhor Antonov * EVFILT_READ events 5158935a399SIhor Antonov */ 5168935a399SIhor Antonov 5178935a399SIhor Antonov if (event.data > 0) { 5186ac7c9f0SIhor Antonov (void)listen_child(state); 5198935a399SIhor Antonov } 5208935a399SIhor Antonov continue; 5218935a399SIhor Antonov default: 5228935a399SIhor Antonov continue; 5238935a399SIhor Antonov } 52453d49b37SJilles Tjoelker } 5254c41f4a0SIhor Antonov 5268935a399SIhor Antonov close(kq); 527407e3790SIhor Antonov close(state->pipe_rd); 528407e3790SIhor Antonov state->pipe_rd = -1; 529bd06a3ecSMike Barcroft } 530bd06a3ecSMike Barcroft 531bd06a3ecSMike Barcroft static void 5328935a399SIhor Antonov daemon_sleep(struct daemon_state *state) 533195fc497SMikolaj Golub { 5348935a399SIhor Antonov struct timespec ts = { state->restart_delay, 0 }; 53509a3675dSConrad Meyer 5368935a399SIhor Antonov if (!state->restart_enabled) { 5378935a399SIhor Antonov return; 5388935a399SIhor Antonov } 5398935a399SIhor Antonov while (nanosleep(&ts, &ts) == -1) { 5406b4ef4b1SIhor Antonov if (errno != EINTR) { 54153d49b37SJilles Tjoelker err(1, "nanosleep"); 54253d49b37SJilles Tjoelker } 54353d49b37SJilles Tjoelker } 5446b4ef4b1SIhor Antonov } 54553d49b37SJilles Tjoelker 54653d49b37SJilles Tjoelker static void 547298a392eSIhor Antonov open_pid_files(struct daemon_state *state) 54853d49b37SJilles Tjoelker { 54953d49b37SJilles Tjoelker pid_t fpid; 55053d49b37SJilles Tjoelker int serrno; 55153d49b37SJilles Tjoelker 552298a392eSIhor Antonov if (state->child_pidfile) { 553298a392eSIhor Antonov state->child_pidfh = pidfile_open(state->child_pidfile, 0600, &fpid); 554298a392eSIhor Antonov if (state->child_pidfh == NULL) { 55553d49b37SJilles Tjoelker if (errno == EEXIST) { 55653d49b37SJilles Tjoelker errx(3, "process already running, pid: %d", 55753d49b37SJilles Tjoelker fpid); 55853d49b37SJilles Tjoelker } 559298a392eSIhor Antonov err(2, "pidfile ``%s''", state->child_pidfile); 56053d49b37SJilles Tjoelker } 56153d49b37SJilles Tjoelker } 56253d49b37SJilles Tjoelker /* Do the same for the actual daemon process. */ 563298a392eSIhor Antonov if (state->parent_pidfile) { 564298a392eSIhor Antonov state->parent_pidfh= pidfile_open(state->parent_pidfile, 0600, &fpid); 565298a392eSIhor Antonov if (state->parent_pidfh == NULL) { 56653d49b37SJilles Tjoelker serrno = errno; 567298a392eSIhor Antonov pidfile_remove(state->child_pidfh); 56853d49b37SJilles Tjoelker errno = serrno; 56953d49b37SJilles Tjoelker if (errno == EEXIST) { 57053d49b37SJilles Tjoelker errx(3, "process already running, pid: %d", 57153d49b37SJilles Tjoelker fpid); 57253d49b37SJilles Tjoelker } 573298a392eSIhor Antonov err(2, "ppidfile ``%s''", state->parent_pidfile); 57453d49b37SJilles Tjoelker } 57553d49b37SJilles Tjoelker } 57653d49b37SJilles Tjoelker } 57753d49b37SJilles Tjoelker 57853d49b37SJilles Tjoelker static int 57953d49b37SJilles Tjoelker get_log_mapping(const char *str, const CODE *c) 58053d49b37SJilles Tjoelker { 58153d49b37SJilles Tjoelker const CODE *cp; 58253d49b37SJilles Tjoelker for (cp = c; cp->c_name; cp++) 5836b4ef4b1SIhor Antonov if (strcmp(cp->c_name, str) == 0) { 58453d49b37SJilles Tjoelker return cp->c_val; 5856b4ef4b1SIhor Antonov } 58653d49b37SJilles Tjoelker return -1; 587195fc497SMikolaj Golub } 588195fc497SMikolaj Golub 589195fc497SMikolaj Golub static void 590e6d4b388STom Rhodes restrict_process(const char *user) 59112d7249eSTom Rhodes { 59212d7249eSTom Rhodes struct passwd *pw = NULL; 59312d7249eSTom Rhodes 594e6d4b388STom Rhodes pw = getpwnam(user); 5956b4ef4b1SIhor Antonov if (pw == NULL) { 596e6d4b388STom Rhodes errx(1, "unknown user: %s", user); 5976b4ef4b1SIhor Antonov } 59812d7249eSTom Rhodes 5996b4ef4b1SIhor Antonov if (setusercontext(NULL, pw, pw->pw_uid, LOGIN_SETALL) != 0) { 600e6d4b388STom Rhodes errx(1, "failed to set user environment"); 6016b4ef4b1SIhor Antonov } 6026b3ad1d7SMaxim Sobolev 6036b3ad1d7SMaxim Sobolev setenv("USER", pw->pw_name, 1); 6046b3ad1d7SMaxim Sobolev setenv("HOME", pw->pw_dir, 1); 6056b3ad1d7SMaxim Sobolev setenv("SHELL", *pw->pw_shell ? pw->pw_shell : _PATH_BSHELL, 1); 60612d7249eSTom Rhodes } 60712d7249eSTom Rhodes 60853d49b37SJilles Tjoelker /* 60953d49b37SJilles Tjoelker * We try to collect whole lines terminated by '\n'. Otherwise we collect a 61053d49b37SJilles Tjoelker * full buffer, and then output it. 61153d49b37SJilles Tjoelker * 612bc43a9a7SIhor Antonov * Return value of false is assumed to mean EOF or error, and true indicates to 61353d49b37SJilles Tjoelker * continue reading. 61453d49b37SJilles Tjoelker */ 615bc43a9a7SIhor Antonov static bool 6166ac7c9f0SIhor Antonov listen_child(struct daemon_state *state) 6172ad43027SMikolaj Golub { 618a6f795ccSIhor Antonov ssize_t rv; 6195745a584SIhor Antonov unsigned char *cp; 6202ad43027SMikolaj Golub 621cf6356fdSIhor Antonov assert(state != NULL); 62224fd3e96SIhor Antonov assert(state->pos < LBUF_SIZE - 1); 62353d49b37SJilles Tjoelker 624e0645579SKyle Evans rv = read(state->pipe_rd, state->buf + state->pos, 625e0645579SKyle Evans LBUF_SIZE - state->pos - 1); 62653d49b37SJilles Tjoelker if (rv > 0) { 62724fd3e96SIhor Antonov state->pos += rv; 62824fd3e96SIhor Antonov assert(state->pos <= LBUF_SIZE - 1); 62953d49b37SJilles Tjoelker /* Always NUL-terminate just in case. */ 63024fd3e96SIhor Antonov state->buf[LBUF_SIZE - 1] = '\0'; 6315745a584SIhor Antonov 63253d49b37SJilles Tjoelker /* 6335745a584SIhor Antonov * Find position of the last newline in the buffer. 6345745a584SIhor Antonov * The buffer is guaranteed to have one or more complete lines 6355745a584SIhor Antonov * if at least one newline was found when searching in reverse. 6365745a584SIhor Antonov * All complete lines are flushed. 63753d49b37SJilles Tjoelker * This does not take NUL characters into account. 63853d49b37SJilles Tjoelker */ 6395745a584SIhor Antonov cp = memrchr(state->buf, '\n', state->pos); 6405745a584SIhor Antonov if (cp != NULL) { 64124fd3e96SIhor Antonov size_t bytes_line = cp - state->buf + 1; 64224fd3e96SIhor Antonov assert(bytes_line <= state->pos); 64324fd3e96SIhor Antonov do_output(state->buf, bytes_line, state); 64424fd3e96SIhor Antonov state->pos -= bytes_line; 64524fd3e96SIhor Antonov memmove(state->buf, cp + 1, state->pos); 646195fc497SMikolaj Golub } 64753d49b37SJilles Tjoelker /* Wait until the buffer is full. */ 64824fd3e96SIhor Antonov if (state->pos < LBUF_SIZE - 1) { 649bc43a9a7SIhor Antonov return true; 6506b4ef4b1SIhor Antonov } 65124fd3e96SIhor Antonov do_output(state->buf, state->pos, state); 65224fd3e96SIhor Antonov state->pos = 0; 653bc43a9a7SIhor Antonov return true; 65453d49b37SJilles Tjoelker } else if (rv == -1) { 65553d49b37SJilles Tjoelker /* EINTR should trigger another read. */ 65653d49b37SJilles Tjoelker if (errno == EINTR) { 657bc43a9a7SIhor Antonov return true; 65853d49b37SJilles Tjoelker } else { 65953d49b37SJilles Tjoelker warn("read"); 660bc43a9a7SIhor Antonov return false; 661c60d51f9SMikolaj Golub } 66253d49b37SJilles Tjoelker } 66353d49b37SJilles Tjoelker /* Upon EOF, we have to flush what's left of the buffer. */ 66424fd3e96SIhor Antonov if (state->pos > 0) { 66524fd3e96SIhor Antonov do_output(state->buf, state->pos, state); 66624fd3e96SIhor Antonov state->pos = 0; 66753d49b37SJilles Tjoelker } 668bc43a9a7SIhor Antonov return false; 66953d49b37SJilles Tjoelker } 67053d49b37SJilles Tjoelker 67153d49b37SJilles Tjoelker /* 67253d49b37SJilles Tjoelker * The default behavior is to stay silent if the user wants to redirect 67353d49b37SJilles Tjoelker * output to a file and/or syslog. If neither are provided, then we bounce 67453d49b37SJilles Tjoelker * everything back to parent's stdout. 67553d49b37SJilles Tjoelker */ 67653d49b37SJilles Tjoelker static void 677298a392eSIhor Antonov do_output(const unsigned char *buf, size_t len, struct daemon_state *state) 67853d49b37SJilles Tjoelker { 67953d49b37SJilles Tjoelker assert(len <= LBUF_SIZE); 680cf6356fdSIhor Antonov assert(state != NULL); 68153d49b37SJilles Tjoelker 6826b4ef4b1SIhor Antonov if (len < 1) { 68353d49b37SJilles Tjoelker return; 6846b4ef4b1SIhor Antonov } 685298a392eSIhor Antonov if (state->syslog_enabled) { 686298a392eSIhor Antonov syslog(state->syslog_priority, "%.*s", (int)len, buf); 6876b4ef4b1SIhor Antonov } 688298a392eSIhor Antonov if (state->output_fd != -1) { 689298a392eSIhor Antonov if (write(state->output_fd, buf, len) == -1) 69053d49b37SJilles Tjoelker warn("write"); 69153d49b37SJilles Tjoelker } 692298a392eSIhor Antonov if (state->keep_fds_open && 693298a392eSIhor Antonov !state->syslog_enabled && 694298a392eSIhor Antonov state->output_fd == -1) { 69553d49b37SJilles Tjoelker printf("%.*s", (int)len, buf); 69653d49b37SJilles Tjoelker } 6976b4ef4b1SIhor Antonov } 69853d49b37SJilles Tjoelker 6994cd407ecSMaxim Sobolev static int 7004cd407ecSMaxim Sobolev open_log(const char *outfn) 7014cd407ecSMaxim Sobolev { 7024cd407ecSMaxim Sobolev 7034cd407ecSMaxim Sobolev return open(outfn, O_CREAT | O_WRONLY | O_APPEND | O_CLOEXEC, 0600); 7044cd407ecSMaxim Sobolev } 7054cd407ecSMaxim Sobolev 7064cd407ecSMaxim Sobolev static void 707298a392eSIhor Antonov reopen_log(struct daemon_state *state) 7084cd407ecSMaxim Sobolev { 7094cd407ecSMaxim Sobolev int outfd; 7104cd407ecSMaxim Sobolev 711298a392eSIhor Antonov outfd = open_log(state->output_filename); 712298a392eSIhor Antonov if (state->output_fd >= 0) { 713298a392eSIhor Antonov close(state->output_fd); 7146b4ef4b1SIhor Antonov } 715298a392eSIhor Antonov state->output_fd = outfd; 7164cd407ecSMaxim Sobolev } 7174cd407ecSMaxim Sobolev 718298a392eSIhor Antonov static void 719298a392eSIhor Antonov daemon_state_init(struct daemon_state *state) 720298a392eSIhor Antonov { 721298a392eSIhor Antonov *state = (struct daemon_state) { 72224fd3e96SIhor Antonov .buf = {0}, 72324fd3e96SIhor Antonov .pos = 0, 7244c41f4a0SIhor Antonov .argv = NULL, 725298a392eSIhor Antonov .parent_pidfh = NULL, 726298a392eSIhor Antonov .child_pidfh = NULL, 727298a392eSIhor Antonov .child_pidfile = NULL, 728298a392eSIhor Antonov .parent_pidfile = NULL, 729298a392eSIhor Antonov .title = NULL, 730298a392eSIhor Antonov .user = NULL, 7318935a399SIhor Antonov .mode = MODE_DAEMON, 732298a392eSIhor Antonov .restart_enabled = false, 7338935a399SIhor Antonov .pid = 0, 734407e3790SIhor Antonov .pipe_rd = -1, 735407e3790SIhor Antonov .pipe_wr = -1, 736298a392eSIhor Antonov .keep_cur_workdir = 1, 737298a392eSIhor Antonov .restart_delay = 1, 738298a392eSIhor Antonov .stdmask = STDOUT_FILENO | STDERR_FILENO, 739298a392eSIhor Antonov .syslog_enabled = false, 740298a392eSIhor Antonov .log_reopen = false, 741298a392eSIhor Antonov .syslog_priority = LOG_NOTICE, 742298a392eSIhor Antonov .syslog_tag = "daemon", 743298a392eSIhor Antonov .syslog_facility = LOG_DAEMON, 744298a392eSIhor Antonov .keep_fds_open = 1, 745298a392eSIhor Antonov .output_fd = -1, 746298a392eSIhor Antonov .output_filename = NULL, 747*7618c9e1SJuraj Lutter .restart_count = -1, 748*7618c9e1SJuraj Lutter .restarted_count = 0 749298a392eSIhor Antonov }; 750298a392eSIhor Antonov } 751cf6356fdSIhor Antonov 752cf6356fdSIhor Antonov static _Noreturn void 753cf6356fdSIhor Antonov daemon_terminate(struct daemon_state *state) 754cf6356fdSIhor Antonov { 755cf6356fdSIhor Antonov assert(state != NULL); 7568935a399SIhor Antonov 7578935a399SIhor Antonov if (state->output_fd >= 0) { 758cf6356fdSIhor Antonov close(state->output_fd); 7598935a399SIhor Antonov } 760407e3790SIhor Antonov if (state->pipe_rd >= 0) { 761407e3790SIhor Antonov close(state->pipe_rd); 7628935a399SIhor Antonov } 7638935a399SIhor Antonov 764407e3790SIhor Antonov if (state->pipe_wr >= 0) { 765407e3790SIhor Antonov close(state->pipe_wr); 7668935a399SIhor Antonov } 767cf6356fdSIhor Antonov if (state->syslog_enabled) { 768cf6356fdSIhor Antonov closelog(); 769cf6356fdSIhor Antonov } 770cf6356fdSIhor Antonov pidfile_remove(state->child_pidfh); 771cf6356fdSIhor Antonov pidfile_remove(state->parent_pidfh); 772cf6356fdSIhor Antonov 773cf6356fdSIhor Antonov /* 774cf6356fdSIhor Antonov * Note that the exit value here doesn't matter in the case of a clean 775cf6356fdSIhor Antonov * exit; daemon(3) already detached us from the caller, nothing is left 776cf6356fdSIhor Antonov * to care about this one. 777cf6356fdSIhor Antonov */ 778cf6356fdSIhor Antonov exit(1); 779cf6356fdSIhor Antonov } 7808935a399SIhor Antonov 7818935a399SIhor Antonov /* 7828eaa6be8SKonstantin Belousov * Returns true if SIGCHILD came from state->pid due to its exit. 7838935a399SIhor Antonov */ 7848935a399SIhor Antonov static bool 7858935a399SIhor Antonov daemon_is_child_dead(struct daemon_state *state) 7868935a399SIhor Antonov { 7878eaa6be8SKonstantin Belousov int status; 7888eaa6be8SKonstantin Belousov 7898935a399SIhor Antonov for (;;) { 7908eaa6be8SKonstantin Belousov int who = waitpid(-1, &status, WNOHANG); 7918eaa6be8SKonstantin Belousov if (state->pid == who && (WIFEXITED(status) || 7928eaa6be8SKonstantin Belousov WIFSIGNALED(status))) { 7938935a399SIhor Antonov return true; 7948935a399SIhor Antonov } 7958eaa6be8SKonstantin Belousov if (who == 0) { 7968eaa6be8SKonstantin Belousov return false; 7978eaa6be8SKonstantin Belousov } 7988935a399SIhor Antonov if (who == -1 && errno != EINTR) { 7998935a399SIhor Antonov warn("waitpid"); 8008935a399SIhor Antonov return false; 8018935a399SIhor Antonov } 8028935a399SIhor Antonov } 8038935a399SIhor Antonov } 8048935a399SIhor Antonov 8058935a399SIhor Antonov static void 8068935a399SIhor Antonov daemon_set_child_pipe(struct daemon_state *state) 8078935a399SIhor Antonov { 8088935a399SIhor Antonov if (state->stdmask & STDERR_FILENO) { 809407e3790SIhor Antonov if (dup2(state->pipe_wr, STDERR_FILENO) == -1) { 8108935a399SIhor Antonov err(1, "dup2"); 8118935a399SIhor Antonov } 8128935a399SIhor Antonov } 8138935a399SIhor Antonov if (state->stdmask & STDOUT_FILENO) { 814407e3790SIhor Antonov if (dup2(state->pipe_wr, STDOUT_FILENO) == -1) { 8158935a399SIhor Antonov err(1, "dup2"); 8168935a399SIhor Antonov } 8178935a399SIhor Antonov } 818407e3790SIhor Antonov if (state->pipe_wr != STDERR_FILENO && 819407e3790SIhor Antonov state->pipe_wr != STDOUT_FILENO) { 820407e3790SIhor Antonov close(state->pipe_wr); 8218935a399SIhor Antonov } 8228935a399SIhor Antonov 8238935a399SIhor Antonov /* The child gets dup'd pipes. */ 824407e3790SIhor Antonov close(state->pipe_rd); 8258935a399SIhor Antonov } 826