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> 3753c49998SMikolaj Golub #include <sys/mman.h> 382ad43027SMikolaj Golub #include <sys/wait.h> 39bd06a3ecSMike Barcroft 4053d49b37SJilles Tjoelker #include <fcntl.h> 41bd06a3ecSMike Barcroft #include <err.h> 42846be7bdSPoul-Henning Kamp #include <errno.h> 430a402ad2SIhor Antonov #include <getopt.h> 44c6262cb6SPawel Jakub Dawidek #include <libutil.h> 45e6d4b388STom Rhodes #include <login_cap.h> 466b3ad1d7SMaxim Sobolev #include <paths.h> 47195fc497SMikolaj Golub #include <pwd.h> 48195fc497SMikolaj Golub #include <signal.h> 49bd06a3ecSMike Barcroft #include <stdio.h> 50203df05bSIhor Antonov #include <stdbool.h> 51bd06a3ecSMike Barcroft #include <stdlib.h> 52bd06a3ecSMike Barcroft #include <unistd.h> 5353d49b37SJilles Tjoelker #include <string.h> 5453d49b37SJilles Tjoelker #include <strings.h> 5553d49b37SJilles Tjoelker #define SYSLOG_NAMES 5653d49b37SJilles Tjoelker #include <syslog.h> 5753d49b37SJilles Tjoelker #include <time.h> 5853d49b37SJilles Tjoelker #include <assert.h> 59bd06a3ecSMike Barcroft 6053d49b37SJilles Tjoelker #define LBUF_SIZE 4096 6153d49b37SJilles Tjoelker 62298a392eSIhor Antonov struct daemon_state { 63298a392eSIhor Antonov int pipe_fd[2]; 64298a392eSIhor Antonov const char *child_pidfile; 65298a392eSIhor Antonov const char *parent_pidfile; 66e70444c6SIhor Antonov const char *output_filename; 676f063672SIhor Antonov const char *syslog_tag; 68298a392eSIhor Antonov const char *title; 69298a392eSIhor Antonov const char *user; 70298a392eSIhor Antonov struct pidfh *parent_pidfh; 71298a392eSIhor Antonov struct pidfh *child_pidfh; 72298a392eSIhor Antonov int keep_cur_workdir; 73298a392eSIhor Antonov int restart_delay; 74298a392eSIhor Antonov int stdmask; 75e70444c6SIhor Antonov int syslog_priority; 766f063672SIhor Antonov int syslog_facility; 77129ec8f4SIhor Antonov int keep_fds_open; 78e70444c6SIhor Antonov int output_fd; 79298a392eSIhor Antonov bool supervision_enabled; 80298a392eSIhor Antonov bool child_eof; 81298a392eSIhor Antonov bool restart_enabled; 82f2f9d31dSIhor Antonov bool syslog_enabled; 83298a392eSIhor Antonov bool log_reopen; 8453d49b37SJilles Tjoelker }; 8553d49b37SJilles Tjoelker 86*9ee1faeeSIhor Antonov static void setup_signals(struct daemon_state *); 87e6d4b388STom Rhodes static void restrict_process(const char *); 8853d49b37SJilles Tjoelker static void handle_term(int); 8953d49b37SJilles Tjoelker static void handle_chld(int); 904cd407ecSMaxim Sobolev static void handle_hup(int); 914cd407ecSMaxim Sobolev static int open_log(const char *); 92298a392eSIhor Antonov static void reopen_log(struct daemon_state *); 93298a392eSIhor Antonov static bool listen_child(int, struct daemon_state *); 9453d49b37SJilles Tjoelker static int get_log_mapping(const char *, const CODE *); 95298a392eSIhor Antonov static void open_pid_files(struct daemon_state *); 96298a392eSIhor Antonov static void do_output(const unsigned char *, size_t, struct daemon_state *); 9753d49b37SJilles Tjoelker static void daemon_sleep(time_t, long); 98298a392eSIhor Antonov static void daemon_state_init(struct daemon_state *); 99cf6356fdSIhor Antonov static void daemon_terminate(struct daemon_state *); 100cf6356fdSIhor Antonov 101e745dc22SIhor Antonov static volatile sig_atomic_t terminate = 0; 102e745dc22SIhor Antonov static volatile sig_atomic_t child_gone = 0; 10375f61ca9SIhor Antonov static volatile sig_atomic_t pid = 0; 104e745dc22SIhor Antonov static volatile sig_atomic_t do_log_reopen = 0; 10553d49b37SJilles Tjoelker 1060a402ad2SIhor Antonov static const char shortopts[] = "+cfHSp:P:ru:o:s:l:t:m:R:T:h"; 1070a402ad2SIhor Antonov 1080a402ad2SIhor Antonov static const struct option longopts[] = { 1090a402ad2SIhor Antonov { "change-dir", no_argument, NULL, 'c' }, 1100a402ad2SIhor Antonov { "close-fds", no_argument, NULL, 'f' }, 1110a402ad2SIhor Antonov { "sighup", no_argument, NULL, 'H' }, 1120a402ad2SIhor Antonov { "syslog", no_argument, NULL, 'S' }, 1130a402ad2SIhor Antonov { "output-file", required_argument, NULL, 'o' }, 1140a402ad2SIhor Antonov { "output-mask", required_argument, NULL, 'm' }, 1150a402ad2SIhor Antonov { "child-pidfile", required_argument, NULL, 'p' }, 1160a402ad2SIhor Antonov { "supervisor-pidfile", required_argument, NULL, 'P' }, 1170a402ad2SIhor Antonov { "restart", no_argument, NULL, 'r' }, 1180a402ad2SIhor Antonov { "restart-delay", required_argument, NULL, 'R' }, 1190a402ad2SIhor Antonov { "title", required_argument, NULL, 't' }, 1200a402ad2SIhor Antonov { "user", required_argument, NULL, 'u' }, 1210a402ad2SIhor Antonov { "syslog-priority", required_argument, NULL, 's' }, 1220a402ad2SIhor Antonov { "syslog-facility", required_argument, NULL, 'l' }, 1230a402ad2SIhor Antonov { "syslog-tag", required_argument, NULL, 'T' }, 1240a402ad2SIhor Antonov { "help", no_argument, NULL, 'h' }, 1250a402ad2SIhor Antonov { NULL, 0, NULL, 0 } 1260a402ad2SIhor Antonov }; 1270a402ad2SIhor Antonov 1280a402ad2SIhor Antonov static _Noreturn void 1290a402ad2SIhor Antonov usage(int exitcode) 1300a402ad2SIhor Antonov { 1310a402ad2SIhor Antonov (void)fprintf(stderr, 1320a402ad2SIhor Antonov "usage: daemon [-cfHrS] [-p child_pidfile] [-P supervisor_pidfile]\n" 1330a402ad2SIhor Antonov " [-u user] [-o output_file] [-t title]\n" 1340a402ad2SIhor Antonov " [-l syslog_facility] [-s syslog_priority]\n" 1350a402ad2SIhor Antonov " [-T syslog_tag] [-m output_mask] [-R restart_delay_secs]\n" 1360a402ad2SIhor Antonov "command arguments ...\n"); 1370a402ad2SIhor Antonov 1380a402ad2SIhor Antonov (void)fprintf(stderr, 1390a402ad2SIhor Antonov " --change-dir -c Change the current working directory to root\n" 1400a402ad2SIhor Antonov " --close-fds -f Set stdin, stdout, stderr to /dev/null\n" 1410a402ad2SIhor Antonov " --sighup -H Close and re-open output file on SIGHUP\n" 1420a402ad2SIhor Antonov " --syslog -S Send output to syslog\n" 1430a402ad2SIhor Antonov " --output-file -o <file> Append output of the child process to file\n" 1440a402ad2SIhor Antonov " --output-mask -m <mask> What to send to syslog/file\n" 1450a402ad2SIhor Antonov " 1=stdout, 2=stderr, 3=both\n" 1460a402ad2SIhor Antonov " --child-pidfile -p <file> Write PID of the child process to file\n" 1470a402ad2SIhor Antonov " --supervisor-pidfile -P <file> Write PID of the supervisor process to file\n" 1480a402ad2SIhor Antonov " --restart -r Restart child if it terminates (1 sec delay)\n" 1490a402ad2SIhor Antonov " --restart-delay -R <N> Restart child if it terminates after N sec\n" 1500a402ad2SIhor Antonov " --title -t <title> Set the title of the supervisor process\n" 1510a402ad2SIhor Antonov " --user -u <user> Drop privileges, run as given user\n" 1520a402ad2SIhor Antonov " --syslog-priority -s <prio> Set syslog priority\n" 1530a402ad2SIhor Antonov " --syslog-facility -l <flty> Set syslog facility\n" 1540a402ad2SIhor Antonov " --syslog-tag -T <tag> Set syslog tag\n" 1550a402ad2SIhor Antonov " --help -h Show this help\n"); 1560a402ad2SIhor Antonov 1570a402ad2SIhor Antonov exit(exitcode); 1580a402ad2SIhor Antonov } 1590a402ad2SIhor Antonov 160bd06a3ecSMike Barcroft int 161bd06a3ecSMike Barcroft main(int argc, char *argv[]) 162bd06a3ecSMike Barcroft { 163e745dc22SIhor Antonov char *p = NULL; 164e745dc22SIhor Antonov int ch = 0; 165298a392eSIhor Antonov struct daemon_state state; 166e745dc22SIhor Antonov sigset_t mask_orig; 167e745dc22SIhor Antonov sigset_t mask_read; 168e745dc22SIhor Antonov sigset_t mask_term; 169e745dc22SIhor Antonov sigset_t mask_susp; 170bd06a3ecSMike Barcroft 171298a392eSIhor Antonov daemon_state_init(&state); 172*9ee1faeeSIhor Antonov 173*9ee1faeeSIhor Antonov /* 174*9ee1faeeSIhor Antonov * Signal handling logic: 175*9ee1faeeSIhor Antonov * 176*9ee1faeeSIhor Antonov * - SIGTERM is masked while there is no child. 177*9ee1faeeSIhor Antonov * 178*9ee1faeeSIhor Antonov * - SIGCHLD is masked while reading from the pipe. SIGTERM has to be 179*9ee1faeeSIhor Antonov * caught, to avoid indefinite blocking on read(). 180*9ee1faeeSIhor Antonov * 181*9ee1faeeSIhor Antonov * - Both SIGCHLD and SIGTERM are masked before calling sigsuspend() 182*9ee1faeeSIhor Antonov * to avoid racing. 183*9ee1faeeSIhor Antonov * 184*9ee1faeeSIhor Antonov * - After SIGTERM is recieved and propagated to the child there are 185*9ee1faeeSIhor Antonov * several options on what to do next: 186*9ee1faeeSIhor Antonov * - read until EOF 187*9ee1faeeSIhor Antonov * - read until EOF but only for a while 188*9ee1faeeSIhor Antonov * - bail immediately 189*9ee1faeeSIhor Antonov * Currently the third option is used, because otherwise there is no 190*9ee1faeeSIhor Antonov * guarantee that read() won't block indefinitely if the child refuses 191*9ee1faeeSIhor Antonov * to depart. To handle the second option, a different approach 192*9ee1faeeSIhor Antonov * would be needed (procctl()?). 193*9ee1faeeSIhor Antonov * 194*9ee1faeeSIhor Antonov * - Child's exit might be detected by receiveing EOF from the pipe. 195*9ee1faeeSIhor Antonov * But the child might have closed its stdout and stderr, so deamon 196*9ee1faeeSIhor Antonov * must wait for the SIGCHLD to ensure that the child is actually gone. 197*9ee1faeeSIhor Antonov */ 198e745dc22SIhor Antonov sigemptyset(&mask_susp); 199e745dc22SIhor Antonov sigemptyset(&mask_read); 200e745dc22SIhor Antonov sigemptyset(&mask_term); 20184866cefSIhor Antonov sigemptyset(&mask_orig); 202*9ee1faeeSIhor Antonov sigaddset(&mask_susp, SIGTERM); 203*9ee1faeeSIhor Antonov sigaddset(&mask_susp, SIGCHLD); 204*9ee1faeeSIhor Antonov sigaddset(&mask_term, SIGTERM); 205*9ee1faeeSIhor Antonov sigaddset(&mask_read, SIGCHLD); 206e745dc22SIhor Antonov 207f907027bSIhor Antonov /* 208f907027bSIhor Antonov * Supervision mode is enabled if one of the following options are used: 209f907027bSIhor Antonov * --child-pidfile -p 210f907027bSIhor Antonov * --supervisor-pidfile -P 211f907027bSIhor Antonov * --restart -r / --restart-delay -R 212f907027bSIhor Antonov * --syslog -S 213f907027bSIhor Antonov * --syslog-facility -l 214f907027bSIhor Antonov * --syslog-priority -s 215f907027bSIhor Antonov * --syslog-tag -T 216f907027bSIhor Antonov * 217f907027bSIhor Antonov * In supervision mode daemon executes the command in a forked process 218f907027bSIhor Antonov * and observes the child by waiting for SIGCHILD. In supervision mode 219f907027bSIhor Antonov * daemon must never exit before the child, this is necessary to prevent 220f907027bSIhor Antonov * orphaning the child and leaving a stale pid file. 221f907027bSIhor Antonov * To achieve this daemon catches SIGTERM and 222f907027bSIhor Antonov * forwards it to the child, expecting to get SIGCHLD eventually. 223f907027bSIhor Antonov */ 2240a402ad2SIhor Antonov while ((ch = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) { 225bd06a3ecSMike Barcroft switch (ch) { 226bd06a3ecSMike Barcroft case 'c': 227298a392eSIhor Antonov state.keep_cur_workdir = 0; 228bd06a3ecSMike Barcroft break; 229bd06a3ecSMike Barcroft case 'f': 230298a392eSIhor Antonov state.keep_fds_open = 0; 231bd06a3ecSMike Barcroft break; 2324cd407ecSMaxim Sobolev case 'H': 233298a392eSIhor Antonov state.log_reopen = true; 2344cd407ecSMaxim Sobolev break; 23553d49b37SJilles Tjoelker case 'l': 236298a392eSIhor Antonov state.syslog_facility = get_log_mapping(optarg, 23739ea4280SIhor Antonov facilitynames); 238298a392eSIhor Antonov if (state.syslog_facility == -1) { 23953d49b37SJilles Tjoelker errx(5, "unrecognized syslog facility"); 2406b4ef4b1SIhor Antonov } 241298a392eSIhor Antonov state.syslog_enabled = true; 242298a392eSIhor Antonov state.supervision_enabled = true; 24353d49b37SJilles Tjoelker break; 24453d49b37SJilles Tjoelker case 'm': 245298a392eSIhor Antonov state.stdmask = strtol(optarg, &p, 10); 246298a392eSIhor Antonov if (p == optarg || state.stdmask < 0 || state.stdmask > 3) { 24753d49b37SJilles Tjoelker errx(6, "unrecognized listening mask"); 2486b4ef4b1SIhor Antonov } 24953d49b37SJilles Tjoelker break; 25053d49b37SJilles Tjoelker case 'o': 251298a392eSIhor Antonov state.output_filename = optarg; 252f907027bSIhor Antonov /* 253f907027bSIhor Antonov * TODO: setting output filename doesn't have to turn 254f907027bSIhor Antonov * the supervision mode on. For non-supervised mode 255f907027bSIhor Antonov * daemon could open the specified file and set it's 256f907027bSIhor Antonov * descriptor as both stderr and stout before execve() 257f907027bSIhor Antonov */ 258298a392eSIhor Antonov state.supervision_enabled = true; 25953d49b37SJilles Tjoelker break; 260846be7bdSPoul-Henning Kamp case 'p': 261298a392eSIhor Antonov state.child_pidfile = optarg; 262298a392eSIhor Antonov state.supervision_enabled = true; 263846be7bdSPoul-Henning Kamp break; 26432b17786SJohn-Mark Gurney case 'P': 265298a392eSIhor Antonov state.parent_pidfile = optarg; 266298a392eSIhor Antonov state.supervision_enabled = true; 26732b17786SJohn-Mark Gurney break; 268b6193c24SMikolaj Golub case 'r': 269298a392eSIhor Antonov state.restart_enabled = true; 270298a392eSIhor Antonov state.supervision_enabled = true; 271b6193c24SMikolaj Golub break; 27237820b87SIan Lepore case 'R': 273298a392eSIhor Antonov state.restart_enabled = true; 274298a392eSIhor Antonov state.restart_delay = strtol(optarg, &p, 0); 275298a392eSIhor Antonov if (p == optarg || state.restart_delay < 1) { 27637820b87SIan Lepore errx(6, "invalid restart delay"); 2776b4ef4b1SIhor Antonov } 27837820b87SIan Lepore break; 27953d49b37SJilles Tjoelker case 's': 280298a392eSIhor Antonov state.syslog_priority = get_log_mapping(optarg, 28139ea4280SIhor Antonov prioritynames); 282298a392eSIhor Antonov if (state.syslog_priority == -1) { 28353d49b37SJilles Tjoelker errx(4, "unrecognized syslog priority"); 2846b4ef4b1SIhor Antonov } 285298a392eSIhor Antonov state.syslog_enabled = true; 286298a392eSIhor Antonov state.supervision_enabled = true; 28753d49b37SJilles Tjoelker break; 28853d49b37SJilles Tjoelker case 'S': 289298a392eSIhor Antonov state.syslog_enabled = true; 290298a392eSIhor Antonov state.supervision_enabled = true; 29153d49b37SJilles Tjoelker break; 292112bfcf5SConrad Meyer case 't': 293298a392eSIhor Antonov state.title = optarg; 294112bfcf5SConrad Meyer break; 29553d49b37SJilles Tjoelker case 'T': 296298a392eSIhor Antonov state.syslog_tag = optarg; 297298a392eSIhor Antonov state.syslog_enabled = true; 298298a392eSIhor Antonov state.supervision_enabled = true; 29953d49b37SJilles Tjoelker break; 300e6d4b388STom Rhodes case 'u': 301298a392eSIhor Antonov state.user = optarg; 302e6d4b388STom Rhodes break; 3030a402ad2SIhor Antonov case 'h': 3040a402ad2SIhor Antonov usage(0); 3050a402ad2SIhor Antonov __builtin_unreachable(); 306bd06a3ecSMike Barcroft default: 3070a402ad2SIhor Antonov usage(1); 308bd06a3ecSMike Barcroft } 309bd06a3ecSMike Barcroft } 310bd06a3ecSMike Barcroft argc -= optind; 311bd06a3ecSMike Barcroft argv += optind; 312bd06a3ecSMike Barcroft 3136b4ef4b1SIhor Antonov if (argc == 0) { 3140a402ad2SIhor Antonov usage(1); 3156b4ef4b1SIhor Antonov } 31612d7249eSTom Rhodes 317298a392eSIhor Antonov if (!state.title) { 318298a392eSIhor Antonov state.title = argv[0]; 3196b4ef4b1SIhor Antonov } 32053d49b37SJilles Tjoelker 321298a392eSIhor Antonov if (state.output_filename) { 322298a392eSIhor Antonov state.output_fd = open_log(state.output_filename); 323298a392eSIhor Antonov if (state.output_fd == -1) { 32453d49b37SJilles Tjoelker err(7, "open"); 32553d49b37SJilles Tjoelker } 3266b4ef4b1SIhor Antonov } 32753d49b37SJilles Tjoelker 328298a392eSIhor Antonov if (state.syslog_enabled) { 329298a392eSIhor Antonov openlog(state.syslog_tag, LOG_PID | LOG_NDELAY, 330298a392eSIhor Antonov state.syslog_facility); 3316b4ef4b1SIhor Antonov } 33253d49b37SJilles Tjoelker 333846be7bdSPoul-Henning Kamp /* 334846be7bdSPoul-Henning Kamp * Try to open the pidfile before calling daemon(3), 335846be7bdSPoul-Henning Kamp * to be able to report the error intelligently 336846be7bdSPoul-Henning Kamp */ 337298a392eSIhor Antonov open_pid_files(&state); 338298a392eSIhor Antonov if (daemon(state.keep_cur_workdir, state.keep_fds_open) == -1) { 3399da0ef13SMikolaj Golub warn("daemon"); 340cf6356fdSIhor Antonov daemon_terminate(&state); 3419da0ef13SMikolaj Golub } 3429da0ef13SMikolaj Golub /* Write out parent pidfile if needed. */ 343298a392eSIhor Antonov pidfile_write(state.parent_pidfh); 344203df05bSIhor Antonov 345298a392eSIhor Antonov if (state.supervision_enabled) { 34653d49b37SJilles Tjoelker /* Block SIGTERM to avoid racing until we have forked. */ 34753d49b37SJilles Tjoelker if (sigprocmask(SIG_BLOCK, &mask_term, &mask_orig)) { 3489da0ef13SMikolaj Golub warn("sigprocmask"); 349cf6356fdSIhor Antonov daemon_terminate(&state); 3509da0ef13SMikolaj Golub } 351*9ee1faeeSIhor Antonov 352*9ee1faeeSIhor Antonov setup_signals(&state); 353*9ee1faeeSIhor Antonov 35453c49998SMikolaj Golub /* 35553c49998SMikolaj Golub * Try to protect against pageout kill. Ignore the 35653c49998SMikolaj Golub * error, madvise(2) will fail only if a process does 35753c49998SMikolaj Golub * not have superuser privileges. 35853c49998SMikolaj Golub */ 35953c49998SMikolaj Golub (void)madvise(NULL, 0, MADV_PROTECT); 360b6193c24SMikolaj Golub restart: 361298a392eSIhor Antonov if (pipe(state.pipe_fd)) { 36253d49b37SJilles Tjoelker err(1, "pipe"); 3636b4ef4b1SIhor Antonov } 364195fc497SMikolaj Golub /* 36553d49b37SJilles Tjoelker * Spawn a child to exec the command. 3662ad43027SMikolaj Golub */ 36753d49b37SJilles Tjoelker child_gone = 0; 3682ad43027SMikolaj Golub pid = fork(); 36975f61ca9SIhor Antonov } 37075f61ca9SIhor Antonov 37175f61ca9SIhor Antonov /* fork failed, this can only happen when supervision is enabled */ 3722ad43027SMikolaj Golub if (pid == -1) { 3739da0ef13SMikolaj Golub warn("fork"); 374cf6356fdSIhor Antonov daemon_terminate(&state); 37553d49b37SJilles Tjoelker } 37675f61ca9SIhor Antonov 37775f61ca9SIhor Antonov /* fork succeeded, this is child's branch or supervision is disabled */ 37875f61ca9SIhor Antonov if (pid == 0) { 379298a392eSIhor Antonov pidfile_write(state.child_pidfh); 380846be7bdSPoul-Henning Kamp 381298a392eSIhor Antonov if (state.user != NULL) { 382298a392eSIhor Antonov restrict_process(state.user); 3836b4ef4b1SIhor Antonov } 38453d49b37SJilles Tjoelker /* 38575f61ca9SIhor Antonov * In supervision mode, the child gets the original sigmask, 38653d49b37SJilles Tjoelker * and dup'd pipes. 38753d49b37SJilles Tjoelker */ 388298a392eSIhor Antonov if (state.supervision_enabled) { 389298a392eSIhor Antonov close(state.pipe_fd[0]); 3906b4ef4b1SIhor Antonov if (sigprocmask(SIG_SETMASK, &mask_orig, NULL)) { 39153d49b37SJilles Tjoelker err(1, "sigprogmask"); 3926b4ef4b1SIhor Antonov } 393298a392eSIhor Antonov if (state.stdmask & STDERR_FILENO) { 394298a392eSIhor Antonov if (dup2(state.pipe_fd[1], STDERR_FILENO) == -1) { 39553d49b37SJilles Tjoelker err(1, "dup2"); 39653d49b37SJilles Tjoelker } 3976b4ef4b1SIhor Antonov } 398298a392eSIhor Antonov if (state.stdmask & STDOUT_FILENO) { 399298a392eSIhor Antonov if (dup2(state.pipe_fd[1], STDOUT_FILENO) == -1) { 40053d49b37SJilles Tjoelker err(1, "dup2"); 40153d49b37SJilles Tjoelker } 4026b4ef4b1SIhor Antonov } 403298a392eSIhor Antonov if (state.pipe_fd[1] != STDERR_FILENO && 404298a392eSIhor Antonov state.pipe_fd[1] != STDOUT_FILENO) { 405298a392eSIhor Antonov close(state.pipe_fd[1]); 40653d49b37SJilles Tjoelker } 4076b4ef4b1SIhor Antonov } 408bd06a3ecSMike Barcroft execvp(argv[0], argv); 40975f61ca9SIhor Antonov /* execvp() failed - report error and exit this process */ 4102ad43027SMikolaj Golub err(1, "%s", argv[0]); 4112ad43027SMikolaj Golub } 41275f61ca9SIhor Antonov 41375f61ca9SIhor Antonov /* 41475f61ca9SIhor Antonov * else: pid > 0 41575f61ca9SIhor Antonov * fork succeeded, this is the parent branch, this can only happen when 416*9ee1faeeSIhor Antonov * supervision is enabled. 41775f61ca9SIhor Antonov * 418*9ee1faeeSIhor Antonov * Unblock SIGTERM - now there is a valid child PID to signal to. 41975f61ca9SIhor Antonov */ 42075f61ca9SIhor Antonov if (sigprocmask(SIG_UNBLOCK, &mask_term, NULL)) { 42175f61ca9SIhor Antonov warn("sigprocmask"); 422cf6356fdSIhor Antonov daemon_terminate(&state); 42375f61ca9SIhor Antonov } 424298a392eSIhor Antonov close(state.pipe_fd[1]); 425298a392eSIhor Antonov state.pipe_fd[1] = -1; 42675f61ca9SIhor Antonov 427298a392eSIhor Antonov setproctitle("%s[%d]", state.title, (int)pid); 42853d49b37SJilles Tjoelker for (;;) { 429298a392eSIhor Antonov if (child_gone && state.child_eof) { 43053d49b37SJilles Tjoelker break; 431cd1e6e70SIhor Antonov } 432cd1e6e70SIhor Antonov 433cd1e6e70SIhor Antonov if (terminate) { 434cf6356fdSIhor Antonov daemon_terminate(&state); 43553d49b37SJilles Tjoelker } 436cd1e6e70SIhor Antonov 437298a392eSIhor Antonov if (state.child_eof) { 43853d49b37SJilles Tjoelker if (sigprocmask(SIG_BLOCK, &mask_susp, NULL)) { 43953d49b37SJilles Tjoelker warn("sigprocmask"); 440cf6356fdSIhor Antonov daemon_terminate(&state); 44153d49b37SJilles Tjoelker } 442d6c398d8SIhor Antonov while (!terminate && !child_gone) { 44353d49b37SJilles Tjoelker sigsuspend(&mask_orig); 444d6c398d8SIhor Antonov } 44553d49b37SJilles Tjoelker if (sigprocmask(SIG_UNBLOCK, &mask_susp, NULL)) { 44653d49b37SJilles Tjoelker warn("sigprocmask"); 447cf6356fdSIhor Antonov daemon_terminate(&state); 44853d49b37SJilles Tjoelker } 449cd1e6e70SIhor Antonov continue; 45053d49b37SJilles Tjoelker } 451cd1e6e70SIhor Antonov 452cd1e6e70SIhor Antonov if (sigprocmask(SIG_BLOCK, &mask_read, NULL)) { 453cd1e6e70SIhor Antonov warn("sigprocmask"); 454cf6356fdSIhor Antonov daemon_terminate(&state); 455cd1e6e70SIhor Antonov } 456cd1e6e70SIhor Antonov 457298a392eSIhor Antonov state.child_eof = !listen_child(state.pipe_fd[0], &state); 458cd1e6e70SIhor Antonov 459cd1e6e70SIhor Antonov if (sigprocmask(SIG_UNBLOCK, &mask_read, NULL)) { 460cd1e6e70SIhor Antonov warn("sigprocmask"); 461cf6356fdSIhor Antonov daemon_terminate(&state); 462cd1e6e70SIhor Antonov } 463cd1e6e70SIhor Antonov 46453d49b37SJilles Tjoelker } 465298a392eSIhor Antonov if (state.restart_enabled && !terminate) { 466298a392eSIhor Antonov daemon_sleep(state.restart_delay, 0); 4676b4ef4b1SIhor Antonov } 46853d49b37SJilles Tjoelker if (sigprocmask(SIG_BLOCK, &mask_term, NULL)) { 46953d49b37SJilles Tjoelker warn("sigprocmask"); 470cf6356fdSIhor Antonov daemon_terminate(&state); 47153d49b37SJilles Tjoelker } 472298a392eSIhor Antonov if (state.restart_enabled && !terminate) { 473298a392eSIhor Antonov close(state.pipe_fd[0]); 474298a392eSIhor Antonov state.pipe_fd[0] = -1; 475b6193c24SMikolaj Golub goto restart; 476b6193c24SMikolaj Golub } 477cf6356fdSIhor Antonov daemon_terminate(&state); 478bd06a3ecSMike Barcroft } 479bd06a3ecSMike Barcroft 480bd06a3ecSMike Barcroft static void 48153d49b37SJilles Tjoelker daemon_sleep(time_t secs, long nsecs) 482195fc497SMikolaj Golub { 48353d49b37SJilles Tjoelker struct timespec ts = { secs, nsecs }; 48409a3675dSConrad Meyer 48509a3675dSConrad Meyer while (!terminate && nanosleep(&ts, &ts) == -1) { 4866b4ef4b1SIhor Antonov if (errno != EINTR) { 48753d49b37SJilles Tjoelker err(1, "nanosleep"); 48853d49b37SJilles Tjoelker } 48953d49b37SJilles Tjoelker } 4906b4ef4b1SIhor Antonov } 49153d49b37SJilles Tjoelker 492*9ee1faeeSIhor Antonov /* 493*9ee1faeeSIhor Antonov * Setup SIGTERM, SIGCHLD and SIGHUP handlers. 494*9ee1faeeSIhor Antonov * To avoid racing SIGCHLD with SIGTERM corresponding 495*9ee1faeeSIhor Antonov * signal handlers mask the other signal. 496*9ee1faeeSIhor Antonov */ 497*9ee1faeeSIhor Antonov static void 498*9ee1faeeSIhor Antonov setup_signals(struct daemon_state *state) 499*9ee1faeeSIhor Antonov { 500*9ee1faeeSIhor Antonov struct sigaction act_term = { 0 }; 501*9ee1faeeSIhor Antonov struct sigaction act_chld = { 0 }; 502*9ee1faeeSIhor Antonov struct sigaction act_hup = { 0 }; 503*9ee1faeeSIhor Antonov 504*9ee1faeeSIhor Antonov /* Setup SIGTERM */ 505*9ee1faeeSIhor Antonov act_term.sa_handler = handle_term; 506*9ee1faeeSIhor Antonov sigemptyset(&act_term.sa_mask); 507*9ee1faeeSIhor Antonov sigaddset(&act_term.sa_mask, SIGCHLD); 508*9ee1faeeSIhor Antonov if (sigaction(SIGTERM, &act_term, NULL) == -1) { 509*9ee1faeeSIhor Antonov warn("sigaction"); 510*9ee1faeeSIhor Antonov daemon_terminate(state); 511*9ee1faeeSIhor Antonov } 512*9ee1faeeSIhor Antonov 513*9ee1faeeSIhor Antonov /* Setup SIGCHLD */ 514*9ee1faeeSIhor Antonov act_chld.sa_handler = handle_chld; 515*9ee1faeeSIhor Antonov sigemptyset(&act_chld.sa_mask); 516*9ee1faeeSIhor Antonov sigaddset(&act_chld.sa_mask, SIGTERM); 517*9ee1faeeSIhor Antonov if (sigaction(SIGCHLD, &act_chld, NULL) == -1) { 518*9ee1faeeSIhor Antonov warn("sigaction"); 519*9ee1faeeSIhor Antonov daemon_terminate(state); 520*9ee1faeeSIhor Antonov } 521*9ee1faeeSIhor Antonov 522*9ee1faeeSIhor Antonov /* Setup SIGHUP if configured */ 523*9ee1faeeSIhor Antonov if (!state->log_reopen || state->output_fd < 0) { 524*9ee1faeeSIhor Antonov return; 525*9ee1faeeSIhor Antonov } 526*9ee1faeeSIhor Antonov 527*9ee1faeeSIhor Antonov act_hup.sa_handler = handle_hup; 528*9ee1faeeSIhor Antonov sigemptyset(&act_hup.sa_mask); 529*9ee1faeeSIhor Antonov if (sigaction(SIGHUP, &act_hup, NULL) == -1) { 530*9ee1faeeSIhor Antonov warn("sigaction"); 531*9ee1faeeSIhor Antonov daemon_terminate(state); 532*9ee1faeeSIhor Antonov } 533*9ee1faeeSIhor Antonov } 534*9ee1faeeSIhor Antonov 53553d49b37SJilles Tjoelker static void 536298a392eSIhor Antonov open_pid_files(struct daemon_state *state) 53753d49b37SJilles Tjoelker { 53853d49b37SJilles Tjoelker pid_t fpid; 53953d49b37SJilles Tjoelker int serrno; 54053d49b37SJilles Tjoelker 541298a392eSIhor Antonov if (state->child_pidfile) { 542298a392eSIhor Antonov state->child_pidfh = pidfile_open(state->child_pidfile, 0600, &fpid); 543298a392eSIhor Antonov if (state->child_pidfh == NULL) { 54453d49b37SJilles Tjoelker if (errno == EEXIST) { 54553d49b37SJilles Tjoelker errx(3, "process already running, pid: %d", 54653d49b37SJilles Tjoelker fpid); 54753d49b37SJilles Tjoelker } 548298a392eSIhor Antonov err(2, "pidfile ``%s''", state->child_pidfile); 54953d49b37SJilles Tjoelker } 55053d49b37SJilles Tjoelker } 55153d49b37SJilles Tjoelker /* Do the same for the actual daemon process. */ 552298a392eSIhor Antonov if (state->parent_pidfile) { 553298a392eSIhor Antonov state->parent_pidfh= pidfile_open(state->parent_pidfile, 0600, &fpid); 554298a392eSIhor Antonov if (state->parent_pidfh == NULL) { 55553d49b37SJilles Tjoelker serrno = errno; 556298a392eSIhor Antonov pidfile_remove(state->child_pidfh); 55753d49b37SJilles Tjoelker errno = serrno; 55853d49b37SJilles Tjoelker if (errno == EEXIST) { 55953d49b37SJilles Tjoelker errx(3, "process already running, pid: %d", 56053d49b37SJilles Tjoelker fpid); 56153d49b37SJilles Tjoelker } 562298a392eSIhor Antonov err(2, "ppidfile ``%s''", state->parent_pidfile); 56353d49b37SJilles Tjoelker } 56453d49b37SJilles Tjoelker } 56553d49b37SJilles Tjoelker } 56653d49b37SJilles Tjoelker 56753d49b37SJilles Tjoelker static int 56853d49b37SJilles Tjoelker get_log_mapping(const char *str, const CODE *c) 56953d49b37SJilles Tjoelker { 57053d49b37SJilles Tjoelker const CODE *cp; 57153d49b37SJilles Tjoelker for (cp = c; cp->c_name; cp++) 5726b4ef4b1SIhor Antonov if (strcmp(cp->c_name, str) == 0) { 57353d49b37SJilles Tjoelker return cp->c_val; 5746b4ef4b1SIhor Antonov } 57553d49b37SJilles Tjoelker return -1; 576195fc497SMikolaj Golub } 577195fc497SMikolaj Golub 578195fc497SMikolaj Golub static void 579e6d4b388STom Rhodes restrict_process(const char *user) 58012d7249eSTom Rhodes { 58112d7249eSTom Rhodes struct passwd *pw = NULL; 58212d7249eSTom Rhodes 583e6d4b388STom Rhodes pw = getpwnam(user); 5846b4ef4b1SIhor Antonov if (pw == NULL) { 585e6d4b388STom Rhodes errx(1, "unknown user: %s", user); 5866b4ef4b1SIhor Antonov } 58712d7249eSTom Rhodes 5886b4ef4b1SIhor Antonov if (setusercontext(NULL, pw, pw->pw_uid, LOGIN_SETALL) != 0) { 589e6d4b388STom Rhodes errx(1, "failed to set user environment"); 5906b4ef4b1SIhor Antonov } 5916b3ad1d7SMaxim Sobolev 5926b3ad1d7SMaxim Sobolev setenv("USER", pw->pw_name, 1); 5936b3ad1d7SMaxim Sobolev setenv("HOME", pw->pw_dir, 1); 5946b3ad1d7SMaxim Sobolev setenv("SHELL", *pw->pw_shell ? pw->pw_shell : _PATH_BSHELL, 1); 59512d7249eSTom Rhodes } 59612d7249eSTom Rhodes 59753d49b37SJilles Tjoelker /* 59853d49b37SJilles Tjoelker * We try to collect whole lines terminated by '\n'. Otherwise we collect a 59953d49b37SJilles Tjoelker * full buffer, and then output it. 60053d49b37SJilles Tjoelker * 601bc43a9a7SIhor Antonov * Return value of false is assumed to mean EOF or error, and true indicates to 60253d49b37SJilles Tjoelker * continue reading. 60353d49b37SJilles Tjoelker */ 604bc43a9a7SIhor Antonov static bool 605298a392eSIhor Antonov listen_child(int fd, struct daemon_state *state) 6062ad43027SMikolaj Golub { 60753d49b37SJilles Tjoelker static unsigned char buf[LBUF_SIZE]; 60853d49b37SJilles Tjoelker static size_t bytes_read = 0; 60953d49b37SJilles Tjoelker int rv; 6102ad43027SMikolaj Golub 611cf6356fdSIhor Antonov assert(state != NULL); 61253d49b37SJilles Tjoelker assert(bytes_read < LBUF_SIZE - 1); 61353d49b37SJilles Tjoelker 6146b4ef4b1SIhor Antonov if (do_log_reopen) { 615298a392eSIhor Antonov reopen_log(state); 6166b4ef4b1SIhor Antonov } 61753d49b37SJilles Tjoelker rv = read(fd, buf + bytes_read, LBUF_SIZE - bytes_read - 1); 61853d49b37SJilles Tjoelker if (rv > 0) { 61953d49b37SJilles Tjoelker unsigned char *cp; 62053d49b37SJilles Tjoelker 62153d49b37SJilles Tjoelker bytes_read += rv; 62253d49b37SJilles Tjoelker assert(bytes_read <= LBUF_SIZE - 1); 62353d49b37SJilles Tjoelker /* Always NUL-terminate just in case. */ 62453d49b37SJilles Tjoelker buf[LBUF_SIZE - 1] = '\0'; 62553d49b37SJilles Tjoelker /* 62653d49b37SJilles Tjoelker * Chomp line by line until we run out of buffer. 62753d49b37SJilles Tjoelker * This does not take NUL characters into account. 62853d49b37SJilles Tjoelker */ 62953d49b37SJilles Tjoelker while ((cp = memchr(buf, '\n', bytes_read)) != NULL) { 63053d49b37SJilles Tjoelker size_t bytes_line = cp - buf + 1; 63153d49b37SJilles Tjoelker assert(bytes_line <= bytes_read); 632298a392eSIhor Antonov do_output(buf, bytes_line, state); 63353d49b37SJilles Tjoelker bytes_read -= bytes_line; 63453d49b37SJilles Tjoelker memmove(buf, cp + 1, bytes_read); 635195fc497SMikolaj Golub } 63653d49b37SJilles Tjoelker /* Wait until the buffer is full. */ 6376b4ef4b1SIhor Antonov if (bytes_read < LBUF_SIZE - 1) { 638bc43a9a7SIhor Antonov return true; 6396b4ef4b1SIhor Antonov } 640298a392eSIhor Antonov do_output(buf, bytes_read, state); 64153d49b37SJilles Tjoelker bytes_read = 0; 642bc43a9a7SIhor Antonov return true; 64353d49b37SJilles Tjoelker } else if (rv == -1) { 64453d49b37SJilles Tjoelker /* EINTR should trigger another read. */ 64553d49b37SJilles Tjoelker if (errno == EINTR) { 646bc43a9a7SIhor Antonov return true; 64753d49b37SJilles Tjoelker } else { 64853d49b37SJilles Tjoelker warn("read"); 649bc43a9a7SIhor Antonov return false; 650c60d51f9SMikolaj Golub } 65153d49b37SJilles Tjoelker } 65253d49b37SJilles Tjoelker /* Upon EOF, we have to flush what's left of the buffer. */ 65353d49b37SJilles Tjoelker if (bytes_read > 0) { 654298a392eSIhor Antonov do_output(buf, bytes_read, state); 65553d49b37SJilles Tjoelker bytes_read = 0; 65653d49b37SJilles Tjoelker } 657bc43a9a7SIhor Antonov return false; 65853d49b37SJilles Tjoelker } 65953d49b37SJilles Tjoelker 66053d49b37SJilles Tjoelker /* 66153d49b37SJilles Tjoelker * The default behavior is to stay silent if the user wants to redirect 66253d49b37SJilles Tjoelker * output to a file and/or syslog. If neither are provided, then we bounce 66353d49b37SJilles Tjoelker * everything back to parent's stdout. 66453d49b37SJilles Tjoelker */ 66553d49b37SJilles Tjoelker static void 666298a392eSIhor Antonov do_output(const unsigned char *buf, size_t len, struct daemon_state *state) 66753d49b37SJilles Tjoelker { 66853d49b37SJilles Tjoelker assert(len <= LBUF_SIZE); 669cf6356fdSIhor Antonov assert(state != NULL); 67053d49b37SJilles Tjoelker 6716b4ef4b1SIhor Antonov if (len < 1) { 67253d49b37SJilles Tjoelker return; 6736b4ef4b1SIhor Antonov } 674298a392eSIhor Antonov if (state->syslog_enabled) { 675298a392eSIhor Antonov syslog(state->syslog_priority, "%.*s", (int)len, buf); 6766b4ef4b1SIhor Antonov } 677298a392eSIhor Antonov if (state->output_fd != -1) { 678298a392eSIhor Antonov if (write(state->output_fd, buf, len) == -1) 67953d49b37SJilles Tjoelker warn("write"); 68053d49b37SJilles Tjoelker } 681298a392eSIhor Antonov if (state->keep_fds_open && 682298a392eSIhor Antonov !state->syslog_enabled && 683298a392eSIhor Antonov state->output_fd == -1) { 68453d49b37SJilles Tjoelker printf("%.*s", (int)len, buf); 68553d49b37SJilles Tjoelker } 6866b4ef4b1SIhor Antonov } 68753d49b37SJilles Tjoelker 68853d49b37SJilles Tjoelker /* 68953d49b37SJilles Tjoelker * We use the global PID acquired directly from fork. If there is no valid 69053d49b37SJilles Tjoelker * child pid, the handler should be blocked and/or child_gone == 1. 69153d49b37SJilles Tjoelker */ 69253d49b37SJilles Tjoelker static void 69353d49b37SJilles Tjoelker handle_term(int signo) 69453d49b37SJilles Tjoelker { 6956b4ef4b1SIhor Antonov if (pid > 0 && !child_gone) { 69653d49b37SJilles Tjoelker kill(pid, signo); 6976b4ef4b1SIhor Antonov } 698b6193c24SMikolaj Golub terminate = 1; 699195fc497SMikolaj Golub } 70053d49b37SJilles Tjoelker 70153d49b37SJilles Tjoelker static void 7024cd407ecSMaxim Sobolev handle_chld(int signo __unused) 70353d49b37SJilles Tjoelker { 7044cd407ecSMaxim Sobolev 70553d49b37SJilles Tjoelker for (;;) { 70653d49b37SJilles Tjoelker int rv = waitpid(-1, NULL, WNOHANG); 70753d49b37SJilles Tjoelker if (pid == rv) { 70853d49b37SJilles Tjoelker child_gone = 1; 70953d49b37SJilles Tjoelker break; 71053d49b37SJilles Tjoelker } else if (rv == -1 && errno != EINTR) { 71153d49b37SJilles Tjoelker warn("waitpid"); 71253d49b37SJilles Tjoelker return; 7132ad43027SMikolaj Golub } 7142ad43027SMikolaj Golub } 7152ad43027SMikolaj Golub } 7162ad43027SMikolaj Golub 7172ad43027SMikolaj Golub static void 7184cd407ecSMaxim Sobolev handle_hup(int signo __unused) 7194cd407ecSMaxim Sobolev { 7204cd407ecSMaxim Sobolev 7214cd407ecSMaxim Sobolev do_log_reopen = 1; 7224cd407ecSMaxim Sobolev } 7234cd407ecSMaxim Sobolev 7244cd407ecSMaxim Sobolev static int 7254cd407ecSMaxim Sobolev open_log(const char *outfn) 7264cd407ecSMaxim Sobolev { 7274cd407ecSMaxim Sobolev 7284cd407ecSMaxim Sobolev return open(outfn, O_CREAT | O_WRONLY | O_APPEND | O_CLOEXEC, 0600); 7294cd407ecSMaxim Sobolev } 7304cd407ecSMaxim Sobolev 7314cd407ecSMaxim Sobolev static void 732298a392eSIhor Antonov reopen_log(struct daemon_state *state) 7334cd407ecSMaxim Sobolev { 7344cd407ecSMaxim Sobolev int outfd; 7354cd407ecSMaxim Sobolev 7364cd407ecSMaxim Sobolev do_log_reopen = 0; 737298a392eSIhor Antonov outfd = open_log(state->output_filename); 738298a392eSIhor Antonov if (state->output_fd >= 0) { 739298a392eSIhor Antonov close(state->output_fd); 7406b4ef4b1SIhor Antonov } 741298a392eSIhor Antonov state->output_fd = outfd; 7424cd407ecSMaxim Sobolev } 7434cd407ecSMaxim Sobolev 744298a392eSIhor Antonov static void 745298a392eSIhor Antonov daemon_state_init(struct daemon_state *state) 746298a392eSIhor Antonov { 747298a392eSIhor Antonov *state = (struct daemon_state) { 748298a392eSIhor Antonov .pipe_fd = { -1, -1 }, 749298a392eSIhor Antonov .parent_pidfh = NULL, 750298a392eSIhor Antonov .child_pidfh = NULL, 751298a392eSIhor Antonov .child_pidfile = NULL, 752298a392eSIhor Antonov .parent_pidfile = NULL, 753298a392eSIhor Antonov .title = NULL, 754298a392eSIhor Antonov .user = NULL, 755298a392eSIhor Antonov .supervision_enabled = false, 756298a392eSIhor Antonov .child_eof = false, 757298a392eSIhor Antonov .restart_enabled = false, 758298a392eSIhor Antonov .keep_cur_workdir = 1, 759298a392eSIhor Antonov .restart_delay = 1, 760298a392eSIhor Antonov .stdmask = STDOUT_FILENO | STDERR_FILENO, 761298a392eSIhor Antonov .syslog_enabled = false, 762298a392eSIhor Antonov .log_reopen = false, 763298a392eSIhor Antonov .syslog_priority = LOG_NOTICE, 764298a392eSIhor Antonov .syslog_tag = "daemon", 765298a392eSIhor Antonov .syslog_facility = LOG_DAEMON, 766298a392eSIhor Antonov .keep_fds_open = 1, 767298a392eSIhor Antonov .output_fd = -1, 768298a392eSIhor Antonov .output_filename = NULL, 769298a392eSIhor Antonov }; 770298a392eSIhor Antonov } 771cf6356fdSIhor Antonov 772cf6356fdSIhor Antonov static _Noreturn void 773cf6356fdSIhor Antonov daemon_terminate(struct daemon_state *state) 774cf6356fdSIhor Antonov { 775cf6356fdSIhor Antonov assert(state != NULL); 776cf6356fdSIhor Antonov close(state->output_fd); 777cf6356fdSIhor Antonov close(state->pipe_fd[0]); 778cf6356fdSIhor Antonov close(state->pipe_fd[1]); 779cf6356fdSIhor Antonov if (state->syslog_enabled) { 780cf6356fdSIhor Antonov closelog(); 781cf6356fdSIhor Antonov } 782cf6356fdSIhor Antonov pidfile_remove(state->child_pidfh); 783cf6356fdSIhor Antonov pidfile_remove(state->parent_pidfh); 784cf6356fdSIhor Antonov 785cf6356fdSIhor Antonov /* 786cf6356fdSIhor Antonov * Note that the exit value here doesn't matter in the case of a clean 787cf6356fdSIhor Antonov * exit; daemon(3) already detached us from the caller, nothing is left 788cf6356fdSIhor Antonov * to care about this one. 789cf6356fdSIhor Antonov */ 790cf6356fdSIhor Antonov exit(1); 791cf6356fdSIhor Antonov } 792