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 6253d49b37SJilles Tjoelker struct log_params { 63e70444c6SIhor Antonov const char *output_filename; 646f063672SIhor Antonov const char *syslog_tag; 65e70444c6SIhor Antonov int syslog_priority; 666f063672SIhor Antonov int syslog_facility; 67129ec8f4SIhor Antonov int keep_fds_open; 68e70444c6SIhor Antonov int output_fd; 69f2f9d31dSIhor Antonov bool syslog_enabled; 7053d49b37SJilles Tjoelker }; 7153d49b37SJilles Tjoelker 72e6d4b388STom Rhodes static void restrict_process(const char *); 7353d49b37SJilles Tjoelker static void handle_term(int); 7453d49b37SJilles Tjoelker static void handle_chld(int); 754cd407ecSMaxim Sobolev static void handle_hup(int); 764cd407ecSMaxim Sobolev static int open_log(const char *); 774cd407ecSMaxim Sobolev static void reopen_log(struct log_params *); 78bc43a9a7SIhor Antonov static bool listen_child(int, struct log_params *); 7953d49b37SJilles Tjoelker static int get_log_mapping(const char *, const CODE *); 8053d49b37SJilles Tjoelker static void open_pid_files(const char *, const char *, struct pidfh **, 8153d49b37SJilles Tjoelker struct pidfh **); 8253d49b37SJilles Tjoelker static void do_output(const unsigned char *, size_t, struct log_params *); 8353d49b37SJilles Tjoelker static void daemon_sleep(time_t, long); 84bd06a3ecSMike Barcroft 85e745dc22SIhor Antonov static volatile sig_atomic_t terminate = 0; 86e745dc22SIhor Antonov static volatile sig_atomic_t child_gone = 0; 8775f61ca9SIhor Antonov static volatile sig_atomic_t pid = 0; 88e745dc22SIhor Antonov static volatile sig_atomic_t do_log_reopen = 0; 8953d49b37SJilles Tjoelker 900a402ad2SIhor Antonov static const char shortopts[] = "+cfHSp:P:ru:o:s:l:t:m:R:T:h"; 910a402ad2SIhor Antonov 920a402ad2SIhor Antonov static const struct option longopts[] = { 930a402ad2SIhor Antonov { "change-dir", no_argument, NULL, 'c' }, 940a402ad2SIhor Antonov { "close-fds", no_argument, NULL, 'f' }, 950a402ad2SIhor Antonov { "sighup", no_argument, NULL, 'H' }, 960a402ad2SIhor Antonov { "syslog", no_argument, NULL, 'S' }, 970a402ad2SIhor Antonov { "output-file", required_argument, NULL, 'o' }, 980a402ad2SIhor Antonov { "output-mask", required_argument, NULL, 'm' }, 990a402ad2SIhor Antonov { "child-pidfile", required_argument, NULL, 'p' }, 1000a402ad2SIhor Antonov { "supervisor-pidfile", required_argument, NULL, 'P' }, 1010a402ad2SIhor Antonov { "restart", no_argument, NULL, 'r' }, 1020a402ad2SIhor Antonov { "restart-delay", required_argument, NULL, 'R' }, 1030a402ad2SIhor Antonov { "title", required_argument, NULL, 't' }, 1040a402ad2SIhor Antonov { "user", required_argument, NULL, 'u' }, 1050a402ad2SIhor Antonov { "syslog-priority", required_argument, NULL, 's' }, 1060a402ad2SIhor Antonov { "syslog-facility", required_argument, NULL, 'l' }, 1070a402ad2SIhor Antonov { "syslog-tag", required_argument, NULL, 'T' }, 1080a402ad2SIhor Antonov { "help", no_argument, NULL, 'h' }, 1090a402ad2SIhor Antonov { NULL, 0, NULL, 0 } 1100a402ad2SIhor Antonov }; 1110a402ad2SIhor Antonov 1120a402ad2SIhor Antonov static _Noreturn void 1130a402ad2SIhor Antonov usage(int exitcode) 1140a402ad2SIhor Antonov { 1150a402ad2SIhor Antonov (void)fprintf(stderr, 1160a402ad2SIhor Antonov "usage: daemon [-cfHrS] [-p child_pidfile] [-P supervisor_pidfile]\n" 1170a402ad2SIhor Antonov " [-u user] [-o output_file] [-t title]\n" 1180a402ad2SIhor Antonov " [-l syslog_facility] [-s syslog_priority]\n" 1190a402ad2SIhor Antonov " [-T syslog_tag] [-m output_mask] [-R restart_delay_secs]\n" 1200a402ad2SIhor Antonov "command arguments ...\n"); 1210a402ad2SIhor Antonov 1220a402ad2SIhor Antonov (void)fprintf(stderr, 1230a402ad2SIhor Antonov " --change-dir -c Change the current working directory to root\n" 1240a402ad2SIhor Antonov " --close-fds -f Set stdin, stdout, stderr to /dev/null\n" 1250a402ad2SIhor Antonov " --sighup -H Close and re-open output file on SIGHUP\n" 1260a402ad2SIhor Antonov " --syslog -S Send output to syslog\n" 1270a402ad2SIhor Antonov " --output-file -o <file> Append output of the child process to file\n" 1280a402ad2SIhor Antonov " --output-mask -m <mask> What to send to syslog/file\n" 1290a402ad2SIhor Antonov " 1=stdout, 2=stderr, 3=both\n" 1300a402ad2SIhor Antonov " --child-pidfile -p <file> Write PID of the child process to file\n" 1310a402ad2SIhor Antonov " --supervisor-pidfile -P <file> Write PID of the supervisor process to file\n" 1320a402ad2SIhor Antonov " --restart -r Restart child if it terminates (1 sec delay)\n" 1330a402ad2SIhor Antonov " --restart-delay -R <N> Restart child if it terminates after N sec\n" 1340a402ad2SIhor Antonov " --title -t <title> Set the title of the supervisor process\n" 1350a402ad2SIhor Antonov " --user -u <user> Drop privileges, run as given user\n" 1360a402ad2SIhor Antonov " --syslog-priority -s <prio> Set syslog priority\n" 1370a402ad2SIhor Antonov " --syslog-facility -l <flty> Set syslog facility\n" 1380a402ad2SIhor Antonov " --syslog-tag -T <tag> Set syslog tag\n" 1390a402ad2SIhor Antonov " --help -h Show this help\n"); 1400a402ad2SIhor Antonov 1410a402ad2SIhor Antonov exit(exitcode); 1420a402ad2SIhor Antonov } 1430a402ad2SIhor Antonov 144bd06a3ecSMike Barcroft int 145bd06a3ecSMike Barcroft main(int argc, char *argv[]) 146bd06a3ecSMike Barcroft { 147203df05bSIhor Antonov bool supervision_enabled = false; 14897022e90SIhor Antonov bool log_reopen = false; 149bc43a9a7SIhor Antonov bool child_eof = false; 150e7817390SIhor Antonov bool restart_enabled = false; 151e745dc22SIhor Antonov char *p = NULL; 152129ec8f4SIhor Antonov const char *child_pidfile = NULL; 153129ec8f4SIhor Antonov const char *parent_pidfile = NULL; 154e745dc22SIhor Antonov const char *title = NULL; 155e745dc22SIhor Antonov const char *user = NULL; 156e745dc22SIhor Antonov int ch = 0; 157129ec8f4SIhor Antonov int keep_cur_workdir = 1; 158d6c398d8SIhor Antonov int pipe_fd[2] = { -1, -1 }; 159e7817390SIhor Antonov int restart_delay = 1; 160e745dc22SIhor Antonov int stdmask = STDOUT_FILENO | STDERR_FILENO; 161129ec8f4SIhor Antonov struct log_params logparams = { 162e70444c6SIhor Antonov .syslog_enabled = false, 163e70444c6SIhor Antonov .syslog_priority = LOG_NOTICE, 1646f063672SIhor Antonov .syslog_tag = "daemon", 1656f063672SIhor Antonov .syslog_facility = LOG_DAEMON, 166129ec8f4SIhor Antonov .keep_fds_open = 1, 167e70444c6SIhor Antonov .output_fd = -1, 168e70444c6SIhor Antonov .output_filename = NULL 169e70444c6SIhor Antonov }; 170129ec8f4SIhor Antonov struct pidfh *parent_pidfh = NULL; 171129ec8f4SIhor Antonov struct pidfh *child_pidfh = NULL; 172e745dc22SIhor Antonov sigset_t mask_orig; 173e745dc22SIhor Antonov sigset_t mask_read; 174e745dc22SIhor Antonov sigset_t mask_term; 175e745dc22SIhor Antonov sigset_t mask_susp; 176bd06a3ecSMike Barcroft 177e745dc22SIhor Antonov sigemptyset(&mask_susp); 178e745dc22SIhor Antonov sigemptyset(&mask_read); 179e745dc22SIhor Antonov sigemptyset(&mask_term); 18084866cefSIhor Antonov sigemptyset(&mask_orig); 181e745dc22SIhor Antonov 182*f907027bSIhor Antonov /* 183*f907027bSIhor Antonov * Supervision mode is enabled if one of the following options are used: 184*f907027bSIhor Antonov * --child-pidfile -p 185*f907027bSIhor Antonov * --supervisor-pidfile -P 186*f907027bSIhor Antonov * --restart -r / --restart-delay -R 187*f907027bSIhor Antonov * --syslog -S 188*f907027bSIhor Antonov * --syslog-facility -l 189*f907027bSIhor Antonov * --syslog-priority -s 190*f907027bSIhor Antonov * --syslog-tag -T 191*f907027bSIhor Antonov * 192*f907027bSIhor Antonov * In supervision mode daemon executes the command in a forked process 193*f907027bSIhor Antonov * and observes the child by waiting for SIGCHILD. In supervision mode 194*f907027bSIhor Antonov * daemon must never exit before the child, this is necessary to prevent 195*f907027bSIhor Antonov * orphaning the child and leaving a stale pid file. 196*f907027bSIhor Antonov * To achieve this daemon catches SIGTERM and 197*f907027bSIhor Antonov * forwards it to the child, expecting to get SIGCHLD eventually. 198*f907027bSIhor Antonov */ 1990a402ad2SIhor Antonov while ((ch = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) { 200bd06a3ecSMike Barcroft switch (ch) { 201bd06a3ecSMike Barcroft case 'c': 202129ec8f4SIhor Antonov keep_cur_workdir = 0; 203bd06a3ecSMike Barcroft break; 204bd06a3ecSMike Barcroft case 'f': 205129ec8f4SIhor Antonov logparams.keep_fds_open = 0; 206bd06a3ecSMike Barcroft break; 2074cd407ecSMaxim Sobolev case 'H': 20897022e90SIhor Antonov log_reopen = true; 2094cd407ecSMaxim Sobolev break; 21053d49b37SJilles Tjoelker case 'l': 21139ea4280SIhor Antonov logparams.syslog_facility = get_log_mapping(optarg, 21239ea4280SIhor Antonov facilitynames); 213129ec8f4SIhor Antonov if (logparams.syslog_facility == -1) { 21453d49b37SJilles Tjoelker errx(5, "unrecognized syslog facility"); 2156b4ef4b1SIhor Antonov } 216129ec8f4SIhor Antonov logparams.syslog_enabled = true; 217*f907027bSIhor Antonov supervision_enabled = true; 21853d49b37SJilles Tjoelker break; 21953d49b37SJilles Tjoelker case 'm': 22053d49b37SJilles Tjoelker stdmask = strtol(optarg, &p, 10); 2216b4ef4b1SIhor Antonov if (p == optarg || stdmask < 0 || stdmask > 3) { 22253d49b37SJilles Tjoelker errx(6, "unrecognized listening mask"); 2236b4ef4b1SIhor Antonov } 22453d49b37SJilles Tjoelker break; 22553d49b37SJilles Tjoelker case 'o': 226129ec8f4SIhor Antonov logparams.output_filename = optarg; 227*f907027bSIhor Antonov /* 228*f907027bSIhor Antonov * TODO: setting output filename doesn't have to turn 229*f907027bSIhor Antonov * the supervision mode on. For non-supervised mode 230*f907027bSIhor Antonov * daemon could open the specified file and set it's 231*f907027bSIhor Antonov * descriptor as both stderr and stout before execve() 232*f907027bSIhor Antonov */ 233*f907027bSIhor Antonov supervision_enabled = true; 23453d49b37SJilles Tjoelker break; 235846be7bdSPoul-Henning Kamp case 'p': 236129ec8f4SIhor Antonov child_pidfile = optarg; 237*f907027bSIhor Antonov supervision_enabled = true; 238846be7bdSPoul-Henning Kamp break; 23932b17786SJohn-Mark Gurney case 'P': 240129ec8f4SIhor Antonov parent_pidfile = optarg; 241*f907027bSIhor Antonov supervision_enabled = true; 24232b17786SJohn-Mark Gurney break; 243b6193c24SMikolaj Golub case 'r': 244e7817390SIhor Antonov restart_enabled = true; 245*f907027bSIhor Antonov supervision_enabled = true; 246b6193c24SMikolaj Golub break; 24737820b87SIan Lepore case 'R': 248e7817390SIhor Antonov restart_enabled = true; 249e7817390SIhor Antonov restart_delay = strtol(optarg, &p, 0); 250e7817390SIhor Antonov if (p == optarg || restart_delay < 1) { 25137820b87SIan Lepore errx(6, "invalid restart delay"); 2526b4ef4b1SIhor Antonov } 25337820b87SIan Lepore break; 25453d49b37SJilles Tjoelker case 's': 25539ea4280SIhor Antonov logparams.syslog_priority = get_log_mapping(optarg, 25639ea4280SIhor Antonov prioritynames); 257129ec8f4SIhor Antonov if (logparams.syslog_priority == -1) { 25853d49b37SJilles Tjoelker errx(4, "unrecognized syslog priority"); 2596b4ef4b1SIhor Antonov } 260129ec8f4SIhor Antonov logparams.syslog_enabled = true; 261*f907027bSIhor Antonov supervision_enabled = true; 26253d49b37SJilles Tjoelker break; 26353d49b37SJilles Tjoelker case 'S': 264129ec8f4SIhor Antonov logparams.syslog_enabled = true; 265*f907027bSIhor Antonov supervision_enabled = true; 26653d49b37SJilles Tjoelker break; 267112bfcf5SConrad Meyer case 't': 268112bfcf5SConrad Meyer title = optarg; 269112bfcf5SConrad Meyer break; 27053d49b37SJilles Tjoelker case 'T': 271129ec8f4SIhor Antonov logparams.syslog_tag = optarg; 272129ec8f4SIhor Antonov logparams.syslog_enabled = true; 273*f907027bSIhor Antonov supervision_enabled = true; 27453d49b37SJilles Tjoelker break; 275e6d4b388STom Rhodes case 'u': 276e6d4b388STom Rhodes user = optarg; 277e6d4b388STom Rhodes break; 2780a402ad2SIhor Antonov case 'h': 2790a402ad2SIhor Antonov usage(0); 2800a402ad2SIhor Antonov __builtin_unreachable(); 281bd06a3ecSMike Barcroft default: 2820a402ad2SIhor Antonov usage(1); 283bd06a3ecSMike Barcroft } 284bd06a3ecSMike Barcroft } 285bd06a3ecSMike Barcroft argc -= optind; 286bd06a3ecSMike Barcroft argv += optind; 287bd06a3ecSMike Barcroft 2886b4ef4b1SIhor Antonov if (argc == 0) { 2890a402ad2SIhor Antonov usage(1); 2906b4ef4b1SIhor Antonov } 29112d7249eSTom Rhodes 2926b4ef4b1SIhor Antonov if (!title) { 29353d49b37SJilles Tjoelker title = argv[0]; 2946b4ef4b1SIhor Antonov } 29553d49b37SJilles Tjoelker 296129ec8f4SIhor Antonov if (logparams.output_filename) { 297129ec8f4SIhor Antonov logparams.output_fd = open_log(logparams.output_filename); 298129ec8f4SIhor Antonov if (logparams.output_fd == -1) { 29953d49b37SJilles Tjoelker err(7, "open"); 30053d49b37SJilles Tjoelker } 3016b4ef4b1SIhor Antonov } 30253d49b37SJilles Tjoelker 303129ec8f4SIhor Antonov if (logparams.syslog_enabled) { 30439ea4280SIhor Antonov openlog(logparams.syslog_tag, LOG_PID | LOG_NDELAY, 30539ea4280SIhor Antonov logparams.syslog_facility); 3066b4ef4b1SIhor Antonov } 30753d49b37SJilles Tjoelker 308846be7bdSPoul-Henning Kamp /* 309846be7bdSPoul-Henning Kamp * Try to open the pidfile before calling daemon(3), 310846be7bdSPoul-Henning Kamp * to be able to report the error intelligently 311846be7bdSPoul-Henning Kamp */ 312129ec8f4SIhor Antonov open_pid_files(child_pidfile, parent_pidfile, &child_pidfh, &parent_pidfh); 313129ec8f4SIhor Antonov if (daemon(keep_cur_workdir, logparams.keep_fds_open) == -1) { 3149da0ef13SMikolaj Golub warn("daemon"); 3159da0ef13SMikolaj Golub goto exit; 3169da0ef13SMikolaj Golub } 3179da0ef13SMikolaj Golub /* Write out parent pidfile if needed. */ 318129ec8f4SIhor Antonov pidfile_write(parent_pidfh); 319203df05bSIhor Antonov 320203df05bSIhor Antonov if (supervision_enabled) { 321259ed21dSIhor Antonov struct sigaction act_term = { 0 }; 322259ed21dSIhor Antonov struct sigaction act_chld = { 0 }; 323259ed21dSIhor Antonov struct sigaction act_hup = { 0 }; 32453d49b37SJilles Tjoelker 32553d49b37SJilles Tjoelker /* Avoid PID racing with SIGCHLD and SIGTERM. */ 32653d49b37SJilles Tjoelker act_term.sa_handler = handle_term; 32753d49b37SJilles Tjoelker sigemptyset(&act_term.sa_mask); 32853d49b37SJilles Tjoelker sigaddset(&act_term.sa_mask, SIGCHLD); 32953d49b37SJilles Tjoelker 33053d49b37SJilles Tjoelker act_chld.sa_handler = handle_chld; 33153d49b37SJilles Tjoelker sigemptyset(&act_chld.sa_mask); 33253d49b37SJilles Tjoelker sigaddset(&act_chld.sa_mask, SIGTERM); 33353d49b37SJilles Tjoelker 3344cd407ecSMaxim Sobolev act_hup.sa_handler = handle_hup; 3354cd407ecSMaxim Sobolev sigemptyset(&act_hup.sa_mask); 3364cd407ecSMaxim Sobolev 33753d49b37SJilles Tjoelker /* Block these when avoiding racing before sigsuspend(). */ 33853d49b37SJilles Tjoelker sigaddset(&mask_susp, SIGTERM); 33953d49b37SJilles Tjoelker sigaddset(&mask_susp, SIGCHLD); 34053d49b37SJilles Tjoelker /* Block SIGTERM when we lack a valid child PID. */ 34153d49b37SJilles Tjoelker sigaddset(&mask_term, SIGTERM); 3422ad43027SMikolaj Golub /* 34353d49b37SJilles Tjoelker * When reading, we wish to avoid SIGCHLD. SIGTERM 34453d49b37SJilles Tjoelker * has to be caught, otherwise we'll be stuck until 34553d49b37SJilles Tjoelker * the read() returns - if it returns. 346195fc497SMikolaj Golub */ 34753d49b37SJilles Tjoelker sigaddset(&mask_read, SIGCHLD); 34853d49b37SJilles Tjoelker /* Block SIGTERM to avoid racing until we have forked. */ 34953d49b37SJilles Tjoelker if (sigprocmask(SIG_BLOCK, &mask_term, &mask_orig)) { 3509da0ef13SMikolaj Golub warn("sigprocmask"); 3519da0ef13SMikolaj Golub goto exit; 3529da0ef13SMikolaj Golub } 35353d49b37SJilles Tjoelker if (sigaction(SIGTERM, &act_term, NULL) == -1) { 35453d49b37SJilles Tjoelker warn("sigaction"); 35553d49b37SJilles Tjoelker goto exit; 35653d49b37SJilles Tjoelker } 35753d49b37SJilles Tjoelker if (sigaction(SIGCHLD, &act_chld, NULL) == -1) { 35853d49b37SJilles Tjoelker warn("sigaction"); 35953d49b37SJilles Tjoelker goto exit; 36053d49b37SJilles Tjoelker } 36153c49998SMikolaj Golub /* 36253c49998SMikolaj Golub * Try to protect against pageout kill. Ignore the 36353c49998SMikolaj Golub * error, madvise(2) will fail only if a process does 36453c49998SMikolaj Golub * not have superuser privileges. 36553c49998SMikolaj Golub */ 36653c49998SMikolaj Golub (void)madvise(NULL, 0, MADV_PROTECT); 367129ec8f4SIhor Antonov if (log_reopen && logparams.output_fd >= 0 && 3684cd407ecSMaxim Sobolev sigaction(SIGHUP, &act_hup, NULL) == -1) { 3694cd407ecSMaxim Sobolev warn("sigaction"); 3704cd407ecSMaxim Sobolev goto exit; 3714cd407ecSMaxim Sobolev } 372b6193c24SMikolaj Golub restart: 373d6c398d8SIhor Antonov if (pipe(pipe_fd)) { 37453d49b37SJilles Tjoelker err(1, "pipe"); 3756b4ef4b1SIhor Antonov } 376195fc497SMikolaj Golub /* 37753d49b37SJilles Tjoelker * Spawn a child to exec the command. 3782ad43027SMikolaj Golub */ 37953d49b37SJilles Tjoelker child_gone = 0; 3802ad43027SMikolaj Golub pid = fork(); 38175f61ca9SIhor Antonov } 38275f61ca9SIhor Antonov 38375f61ca9SIhor Antonov /* fork failed, this can only happen when supervision is enabled */ 3842ad43027SMikolaj Golub if (pid == -1) { 3859da0ef13SMikolaj Golub warn("fork"); 3869da0ef13SMikolaj Golub goto exit; 38753d49b37SJilles Tjoelker } 38875f61ca9SIhor Antonov 38975f61ca9SIhor Antonov 39075f61ca9SIhor Antonov /* fork succeeded, this is child's branch or supervision is disabled */ 39175f61ca9SIhor Antonov if (pid == 0) { 392129ec8f4SIhor Antonov pidfile_write(child_pidfh); 393846be7bdSPoul-Henning Kamp 3946b4ef4b1SIhor Antonov if (user != NULL) { 3952ad43027SMikolaj Golub restrict_process(user); 3966b4ef4b1SIhor Antonov } 39753d49b37SJilles Tjoelker /* 39875f61ca9SIhor Antonov * In supervision mode, the child gets the original sigmask, 39953d49b37SJilles Tjoelker * and dup'd pipes. 40053d49b37SJilles Tjoelker */ 40175f61ca9SIhor Antonov if (supervision_enabled) { 402d6c398d8SIhor Antonov close(pipe_fd[0]); 4036b4ef4b1SIhor Antonov if (sigprocmask(SIG_SETMASK, &mask_orig, NULL)) { 40453d49b37SJilles Tjoelker err(1, "sigprogmask"); 4056b4ef4b1SIhor Antonov } 40653d49b37SJilles Tjoelker if (stdmask & STDERR_FILENO) { 407d6c398d8SIhor Antonov if (dup2(pipe_fd[1], STDERR_FILENO) == -1) { 40853d49b37SJilles Tjoelker err(1, "dup2"); 40953d49b37SJilles Tjoelker } 4106b4ef4b1SIhor Antonov } 41153d49b37SJilles Tjoelker if (stdmask & STDOUT_FILENO) { 412d6c398d8SIhor Antonov if (dup2(pipe_fd[1], STDOUT_FILENO) == -1) { 41353d49b37SJilles Tjoelker err(1, "dup2"); 41453d49b37SJilles Tjoelker } 4156b4ef4b1SIhor Antonov } 416d6c398d8SIhor Antonov if (pipe_fd[1] != STDERR_FILENO && 417d6c398d8SIhor Antonov pipe_fd[1] != STDOUT_FILENO) { 418d6c398d8SIhor Antonov close(pipe_fd[1]); 41953d49b37SJilles Tjoelker } 4206b4ef4b1SIhor Antonov } 421bd06a3ecSMike Barcroft execvp(argv[0], argv); 42275f61ca9SIhor Antonov /* execvp() failed - report error and exit this process */ 4232ad43027SMikolaj Golub err(1, "%s", argv[0]); 4242ad43027SMikolaj Golub } 42575f61ca9SIhor Antonov 42675f61ca9SIhor Antonov /* 42775f61ca9SIhor Antonov * else: pid > 0 42875f61ca9SIhor Antonov * fork succeeded, this is the parent branch, this can only happen when 42975f61ca9SIhor Antonov * supervision is enabled 43075f61ca9SIhor Antonov * 43175f61ca9SIhor Antonov * Unblock SIGTERM after we know we have a valid child PID to signal. 43275f61ca9SIhor Antonov */ 43375f61ca9SIhor Antonov if (sigprocmask(SIG_UNBLOCK, &mask_term, NULL)) { 43475f61ca9SIhor Antonov warn("sigprocmask"); 43575f61ca9SIhor Antonov goto exit; 43675f61ca9SIhor Antonov } 437d6c398d8SIhor Antonov close(pipe_fd[1]); 438d6c398d8SIhor Antonov pipe_fd[1] = -1; 43975f61ca9SIhor Antonov 44053d49b37SJilles Tjoelker setproctitle("%s[%d]", title, (int)pid); 44153d49b37SJilles Tjoelker /* 44253d49b37SJilles Tjoelker * As we have closed the write end of pipe for parent process, 44353d49b37SJilles Tjoelker * we might detect the child's exit by reading EOF. The child 44453d49b37SJilles Tjoelker * might have closed its stdout and stderr, so we must wait for 44553d49b37SJilles Tjoelker * the SIGCHLD to ensure that the process is actually gone. 44653d49b37SJilles Tjoelker */ 44753d49b37SJilles Tjoelker for (;;) { 44853d49b37SJilles Tjoelker /* 44953d49b37SJilles Tjoelker * We block SIGCHLD when listening, but SIGTERM we accept 45053d49b37SJilles Tjoelker * so the read() won't block if we wish to depart. 45153d49b37SJilles Tjoelker * 45253d49b37SJilles Tjoelker * Upon receiving SIGTERM, we have several options after 45353d49b37SJilles Tjoelker * sending the SIGTERM to our child: 45453d49b37SJilles Tjoelker * - read until EOF 45553d49b37SJilles Tjoelker * - read until EOF but only for a while 45653d49b37SJilles Tjoelker * - bail immediately 45753d49b37SJilles Tjoelker * 45853d49b37SJilles Tjoelker * We go for the third, as otherwise we have no guarantee 45953d49b37SJilles Tjoelker * that we won't block indefinitely if the child refuses 46053d49b37SJilles Tjoelker * to depart. To handle the second option, a different 46153d49b37SJilles Tjoelker * approach would be needed (procctl()?) 46253d49b37SJilles Tjoelker */ 46353d49b37SJilles Tjoelker if (child_gone && child_eof) { 46453d49b37SJilles Tjoelker break; 465cd1e6e70SIhor Antonov } 466cd1e6e70SIhor Antonov 467cd1e6e70SIhor Antonov if (terminate) { 46853d49b37SJilles Tjoelker goto exit; 46953d49b37SJilles Tjoelker } 470cd1e6e70SIhor Antonov 471cd1e6e70SIhor Antonov if (child_eof) { 47253d49b37SJilles Tjoelker if (sigprocmask(SIG_BLOCK, &mask_susp, NULL)) { 47353d49b37SJilles Tjoelker warn("sigprocmask"); 47453d49b37SJilles Tjoelker goto exit; 47553d49b37SJilles Tjoelker } 476d6c398d8SIhor Antonov while (!terminate && !child_gone) { 47753d49b37SJilles Tjoelker sigsuspend(&mask_orig); 478d6c398d8SIhor Antonov } 47953d49b37SJilles Tjoelker if (sigprocmask(SIG_UNBLOCK, &mask_susp, NULL)) { 48053d49b37SJilles Tjoelker warn("sigprocmask"); 48153d49b37SJilles Tjoelker goto exit; 48253d49b37SJilles Tjoelker } 483cd1e6e70SIhor Antonov continue; 48453d49b37SJilles Tjoelker } 485cd1e6e70SIhor Antonov 486cd1e6e70SIhor Antonov if (sigprocmask(SIG_BLOCK, &mask_read, NULL)) { 487cd1e6e70SIhor Antonov warn("sigprocmask"); 488cd1e6e70SIhor Antonov goto exit; 489cd1e6e70SIhor Antonov } 490cd1e6e70SIhor Antonov 491d6c398d8SIhor Antonov child_eof = !listen_child(pipe_fd[0], &logparams); 492cd1e6e70SIhor Antonov 493cd1e6e70SIhor Antonov if (sigprocmask(SIG_UNBLOCK, &mask_read, NULL)) { 494cd1e6e70SIhor Antonov warn("sigprocmask"); 495cd1e6e70SIhor Antonov goto exit; 496cd1e6e70SIhor Antonov } 497cd1e6e70SIhor Antonov 49853d49b37SJilles Tjoelker } 499e7817390SIhor Antonov if (restart_enabled && !terminate) { 500e7817390SIhor Antonov daemon_sleep(restart_delay, 0); 5016b4ef4b1SIhor Antonov } 50253d49b37SJilles Tjoelker if (sigprocmask(SIG_BLOCK, &mask_term, NULL)) { 50353d49b37SJilles Tjoelker warn("sigprocmask"); 50453d49b37SJilles Tjoelker goto exit; 50553d49b37SJilles Tjoelker } 506e7817390SIhor Antonov if (restart_enabled && !terminate) { 507d6c398d8SIhor Antonov close(pipe_fd[0]); 508d6c398d8SIhor Antonov pipe_fd[0] = -1; 509b6193c24SMikolaj Golub goto restart; 510b6193c24SMikolaj Golub } 5119da0ef13SMikolaj Golub exit: 512129ec8f4SIhor Antonov close(logparams.output_fd); 513d6c398d8SIhor Antonov close(pipe_fd[0]); 514d6c398d8SIhor Antonov close(pipe_fd[1]); 515129ec8f4SIhor Antonov if (logparams.syslog_enabled) { 51653d49b37SJilles Tjoelker closelog(); 5176b4ef4b1SIhor Antonov } 518129ec8f4SIhor Antonov pidfile_remove(child_pidfh); 519129ec8f4SIhor Antonov pidfile_remove(parent_pidfh); 5209da0ef13SMikolaj Golub exit(1); /* If daemon(3) succeeded exit status does not matter. */ 521bd06a3ecSMike Barcroft } 522bd06a3ecSMike Barcroft 523bd06a3ecSMike Barcroft static void 52453d49b37SJilles Tjoelker daemon_sleep(time_t secs, long nsecs) 525195fc497SMikolaj Golub { 52653d49b37SJilles Tjoelker struct timespec ts = { secs, nsecs }; 52709a3675dSConrad Meyer 52809a3675dSConrad Meyer while (!terminate && nanosleep(&ts, &ts) == -1) { 5296b4ef4b1SIhor Antonov if (errno != EINTR) { 53053d49b37SJilles Tjoelker err(1, "nanosleep"); 53153d49b37SJilles Tjoelker } 53253d49b37SJilles Tjoelker } 5336b4ef4b1SIhor Antonov } 53453d49b37SJilles Tjoelker 53553d49b37SJilles Tjoelker static void 53653d49b37SJilles Tjoelker open_pid_files(const char *pidfile, const char *ppidfile, 53753d49b37SJilles Tjoelker struct pidfh **pfh, struct pidfh **ppfh) 53853d49b37SJilles Tjoelker { 53953d49b37SJilles Tjoelker pid_t fpid; 54053d49b37SJilles Tjoelker int serrno; 54153d49b37SJilles Tjoelker 54253d49b37SJilles Tjoelker if (pidfile) { 54353d49b37SJilles Tjoelker *pfh = pidfile_open(pidfile, 0600, &fpid); 54453d49b37SJilles Tjoelker if (*pfh == NULL) { 54553d49b37SJilles Tjoelker if (errno == EEXIST) { 54653d49b37SJilles Tjoelker errx(3, "process already running, pid: %d", 54753d49b37SJilles Tjoelker fpid); 54853d49b37SJilles Tjoelker } 54953d49b37SJilles Tjoelker err(2, "pidfile ``%s''", pidfile); 55053d49b37SJilles Tjoelker } 55153d49b37SJilles Tjoelker } 55253d49b37SJilles Tjoelker /* Do the same for the actual daemon process. */ 55353d49b37SJilles Tjoelker if (ppidfile) { 55453d49b37SJilles Tjoelker *ppfh = pidfile_open(ppidfile, 0600, &fpid); 55553d49b37SJilles Tjoelker if (*ppfh == NULL) { 55653d49b37SJilles Tjoelker serrno = errno; 55753d49b37SJilles Tjoelker pidfile_remove(*pfh); 55853d49b37SJilles Tjoelker errno = serrno; 55953d49b37SJilles Tjoelker if (errno == EEXIST) { 56053d49b37SJilles Tjoelker errx(3, "process already running, pid: %d", 56153d49b37SJilles Tjoelker fpid); 56253d49b37SJilles Tjoelker } 56353d49b37SJilles Tjoelker err(2, "ppidfile ``%s''", ppidfile); 56453d49b37SJilles Tjoelker } 56553d49b37SJilles Tjoelker } 56653d49b37SJilles Tjoelker } 56753d49b37SJilles Tjoelker 56853d49b37SJilles Tjoelker static int 56953d49b37SJilles Tjoelker get_log_mapping(const char *str, const CODE *c) 57053d49b37SJilles Tjoelker { 57153d49b37SJilles Tjoelker const CODE *cp; 57253d49b37SJilles Tjoelker for (cp = c; cp->c_name; cp++) 5736b4ef4b1SIhor Antonov if (strcmp(cp->c_name, str) == 0) { 57453d49b37SJilles Tjoelker return cp->c_val; 5756b4ef4b1SIhor Antonov } 57653d49b37SJilles Tjoelker return -1; 577195fc497SMikolaj Golub } 578195fc497SMikolaj Golub 579195fc497SMikolaj Golub static void 580e6d4b388STom Rhodes restrict_process(const char *user) 58112d7249eSTom Rhodes { 58212d7249eSTom Rhodes struct passwd *pw = NULL; 58312d7249eSTom Rhodes 584e6d4b388STom Rhodes pw = getpwnam(user); 5856b4ef4b1SIhor Antonov if (pw == NULL) { 586e6d4b388STom Rhodes errx(1, "unknown user: %s", user); 5876b4ef4b1SIhor Antonov } 58812d7249eSTom Rhodes 5896b4ef4b1SIhor Antonov if (setusercontext(NULL, pw, pw->pw_uid, LOGIN_SETALL) != 0) { 590e6d4b388STom Rhodes errx(1, "failed to set user environment"); 5916b4ef4b1SIhor Antonov } 5926b3ad1d7SMaxim Sobolev 5936b3ad1d7SMaxim Sobolev setenv("USER", pw->pw_name, 1); 5946b3ad1d7SMaxim Sobolev setenv("HOME", pw->pw_dir, 1); 5956b3ad1d7SMaxim Sobolev setenv("SHELL", *pw->pw_shell ? pw->pw_shell : _PATH_BSHELL, 1); 59612d7249eSTom Rhodes } 59712d7249eSTom Rhodes 59853d49b37SJilles Tjoelker /* 59953d49b37SJilles Tjoelker * We try to collect whole lines terminated by '\n'. Otherwise we collect a 60053d49b37SJilles Tjoelker * full buffer, and then output it. 60153d49b37SJilles Tjoelker * 602bc43a9a7SIhor Antonov * Return value of false is assumed to mean EOF or error, and true indicates to 60353d49b37SJilles Tjoelker * continue reading. 60453d49b37SJilles Tjoelker */ 605bc43a9a7SIhor Antonov static bool 60653d49b37SJilles Tjoelker listen_child(int fd, struct log_params *logpar) 6072ad43027SMikolaj Golub { 60853d49b37SJilles Tjoelker static unsigned char buf[LBUF_SIZE]; 60953d49b37SJilles Tjoelker static size_t bytes_read = 0; 61053d49b37SJilles Tjoelker int rv; 6112ad43027SMikolaj Golub 61253d49b37SJilles Tjoelker assert(logpar); 61353d49b37SJilles Tjoelker assert(bytes_read < LBUF_SIZE - 1); 61453d49b37SJilles Tjoelker 6156b4ef4b1SIhor Antonov if (do_log_reopen) { 6164cd407ecSMaxim Sobolev reopen_log(logpar); 6176b4ef4b1SIhor Antonov } 61853d49b37SJilles Tjoelker rv = read(fd, buf + bytes_read, LBUF_SIZE - bytes_read - 1); 61953d49b37SJilles Tjoelker if (rv > 0) { 62053d49b37SJilles Tjoelker unsigned char *cp; 62153d49b37SJilles Tjoelker 62253d49b37SJilles Tjoelker bytes_read += rv; 62353d49b37SJilles Tjoelker assert(bytes_read <= LBUF_SIZE - 1); 62453d49b37SJilles Tjoelker /* Always NUL-terminate just in case. */ 62553d49b37SJilles Tjoelker buf[LBUF_SIZE - 1] = '\0'; 62653d49b37SJilles Tjoelker /* 62753d49b37SJilles Tjoelker * Chomp line by line until we run out of buffer. 62853d49b37SJilles Tjoelker * This does not take NUL characters into account. 62953d49b37SJilles Tjoelker */ 63053d49b37SJilles Tjoelker while ((cp = memchr(buf, '\n', bytes_read)) != NULL) { 63153d49b37SJilles Tjoelker size_t bytes_line = cp - buf + 1; 63253d49b37SJilles Tjoelker assert(bytes_line <= bytes_read); 63353d49b37SJilles Tjoelker do_output(buf, bytes_line, logpar); 63453d49b37SJilles Tjoelker bytes_read -= bytes_line; 63553d49b37SJilles Tjoelker memmove(buf, cp + 1, bytes_read); 636195fc497SMikolaj Golub } 63753d49b37SJilles Tjoelker /* Wait until the buffer is full. */ 6386b4ef4b1SIhor Antonov if (bytes_read < LBUF_SIZE - 1) { 639bc43a9a7SIhor Antonov return true; 6406b4ef4b1SIhor Antonov } 64153d49b37SJilles Tjoelker do_output(buf, bytes_read, logpar); 64253d49b37SJilles Tjoelker bytes_read = 0; 643bc43a9a7SIhor Antonov return true; 64453d49b37SJilles Tjoelker } else if (rv == -1) { 64553d49b37SJilles Tjoelker /* EINTR should trigger another read. */ 64653d49b37SJilles Tjoelker if (errno == EINTR) { 647bc43a9a7SIhor Antonov return true; 64853d49b37SJilles Tjoelker } else { 64953d49b37SJilles Tjoelker warn("read"); 650bc43a9a7SIhor Antonov return false; 651c60d51f9SMikolaj Golub } 65253d49b37SJilles Tjoelker } 65353d49b37SJilles Tjoelker /* Upon EOF, we have to flush what's left of the buffer. */ 65453d49b37SJilles Tjoelker if (bytes_read > 0) { 65553d49b37SJilles Tjoelker do_output(buf, bytes_read, logpar); 65653d49b37SJilles Tjoelker bytes_read = 0; 65753d49b37SJilles Tjoelker } 658bc43a9a7SIhor Antonov return false; 65953d49b37SJilles Tjoelker } 66053d49b37SJilles Tjoelker 66153d49b37SJilles Tjoelker /* 66253d49b37SJilles Tjoelker * The default behavior is to stay silent if the user wants to redirect 66353d49b37SJilles Tjoelker * output to a file and/or syslog. If neither are provided, then we bounce 66453d49b37SJilles Tjoelker * everything back to parent's stdout. 66553d49b37SJilles Tjoelker */ 66653d49b37SJilles Tjoelker static void 66753d49b37SJilles Tjoelker do_output(const unsigned char *buf, size_t len, struct log_params *logpar) 66853d49b37SJilles Tjoelker { 66953d49b37SJilles Tjoelker assert(len <= LBUF_SIZE); 67053d49b37SJilles Tjoelker assert(logpar); 67153d49b37SJilles Tjoelker 6726b4ef4b1SIhor Antonov if (len < 1) { 67353d49b37SJilles Tjoelker return; 6746b4ef4b1SIhor Antonov } 675f2f9d31dSIhor Antonov if (logpar->syslog_enabled) { 676e70444c6SIhor Antonov syslog(logpar->syslog_priority, "%.*s", (int)len, buf); 6776b4ef4b1SIhor Antonov } 678e70444c6SIhor Antonov if (logpar->output_fd != -1) { 679e70444c6SIhor Antonov if (write(logpar->output_fd, buf, len) == -1) 68053d49b37SJilles Tjoelker warn("write"); 68153d49b37SJilles Tjoelker } 68239ea4280SIhor Antonov if (logpar->keep_fds_open && 68339ea4280SIhor Antonov !logpar->syslog_enabled && 68439ea4280SIhor Antonov logpar->output_fd == -1) { 68553d49b37SJilles Tjoelker printf("%.*s", (int)len, buf); 68653d49b37SJilles Tjoelker } 6876b4ef4b1SIhor Antonov } 68853d49b37SJilles Tjoelker 68953d49b37SJilles Tjoelker /* 69053d49b37SJilles Tjoelker * We use the global PID acquired directly from fork. If there is no valid 69153d49b37SJilles Tjoelker * child pid, the handler should be blocked and/or child_gone == 1. 69253d49b37SJilles Tjoelker */ 69353d49b37SJilles Tjoelker static void 69453d49b37SJilles Tjoelker handle_term(int signo) 69553d49b37SJilles Tjoelker { 6966b4ef4b1SIhor Antonov if (pid > 0 && !child_gone) { 69753d49b37SJilles Tjoelker kill(pid, signo); 6986b4ef4b1SIhor Antonov } 699b6193c24SMikolaj Golub terminate = 1; 700195fc497SMikolaj Golub } 70153d49b37SJilles Tjoelker 70253d49b37SJilles Tjoelker static void 7034cd407ecSMaxim Sobolev handle_chld(int signo __unused) 70453d49b37SJilles Tjoelker { 7054cd407ecSMaxim Sobolev 70653d49b37SJilles Tjoelker for (;;) { 70753d49b37SJilles Tjoelker int rv = waitpid(-1, NULL, WNOHANG); 70853d49b37SJilles Tjoelker if (pid == rv) { 70953d49b37SJilles Tjoelker child_gone = 1; 71053d49b37SJilles Tjoelker break; 71153d49b37SJilles Tjoelker } else if (rv == -1 && errno != EINTR) { 71253d49b37SJilles Tjoelker warn("waitpid"); 71353d49b37SJilles Tjoelker return; 7142ad43027SMikolaj Golub } 7152ad43027SMikolaj Golub } 7162ad43027SMikolaj Golub } 7172ad43027SMikolaj Golub 7182ad43027SMikolaj Golub static void 7194cd407ecSMaxim Sobolev handle_hup(int signo __unused) 7204cd407ecSMaxim Sobolev { 7214cd407ecSMaxim Sobolev 7224cd407ecSMaxim Sobolev do_log_reopen = 1; 7234cd407ecSMaxim Sobolev } 7244cd407ecSMaxim Sobolev 7254cd407ecSMaxim Sobolev static int 7264cd407ecSMaxim Sobolev open_log(const char *outfn) 7274cd407ecSMaxim Sobolev { 7284cd407ecSMaxim Sobolev 7294cd407ecSMaxim Sobolev return open(outfn, O_CREAT | O_WRONLY | O_APPEND | O_CLOEXEC, 0600); 7304cd407ecSMaxim Sobolev } 7314cd407ecSMaxim Sobolev 7324cd407ecSMaxim Sobolev static void 733129ec8f4SIhor Antonov reopen_log(struct log_params *logparams) 7344cd407ecSMaxim Sobolev { 7354cd407ecSMaxim Sobolev int outfd; 7364cd407ecSMaxim Sobolev 7374cd407ecSMaxim Sobolev do_log_reopen = 0; 738129ec8f4SIhor Antonov outfd = open_log(logparams->output_filename); 739129ec8f4SIhor Antonov if (logparams->output_fd >= 0) { 740129ec8f4SIhor Antonov close(logparams->output_fd); 7416b4ef4b1SIhor Antonov } 742129ec8f4SIhor Antonov logparams->output_fd = outfd; 7434cd407ecSMaxim Sobolev } 7444cd407ecSMaxim Sobolev 745