From: Roy Marples Date: Thu, 19 Oct 2023 10:11:05 +0000 (+0100) Subject: privsep: Notify processes when dhcpcd has daemonised X-Git-Tag: v10.0.4~3 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ac4a70dc89ffd545e7a8c8c07711497376ac85f5;p=thirdparty%2Fdhcpcd.git privsep: Notify processes when dhcpcd has daemonised This allows us to dup stdout and stderr onto stdin which is guaranteed to be dupped to /dev/null. This in turn avoids SIGPIPE when the privileged proccess launches the script and it wants to write to stdout/stderr or stupidly read from stdin. --- diff --git a/src/dhcpcd.c b/src/dhcpcd.c index 53d5de44..52ff1dbb 100644 --- a/src/dhcpcd.c +++ b/src/dhcpcd.c @@ -329,6 +329,36 @@ dhcpcd_ipwaited(struct dhcpcd_ctx *ctx) return 1; } +#ifndef THERE_IS_NO_FORK +void +dhcpcd_daemonised(struct dhcpcd_ctx *ctx) +{ + unsigned int logopts = loggetopts(); + + /* + * Stop writing to stderr. + * On the happy path, only the manager process writes to stderr, + * so this just stops wasting fprintf calls to nowhere. + * All other calls - ie errors in privsep processes or script output, + * will error when printing. + * If we *really* want to fix that, then we need to suck + * stderr/stdout in the manager process and either discard it or pass + * it to the launcher process and then to stderr. + */ + logopts &= ~LOGERR_ERR; + logsetopts(logopts); + + /* + * We need to do something with stdout/stderr to avoid SIGPIPE + * We know that stdin is already mapped to /dev/null + */ + dup2(STDIN_FILENO, STDOUT_FILENO); + dup2(STDIN_FILENO, STDERR_FILENO); + + ctx->options |= DHCPCD_DAEMONISED; +} +#endif + /* Returns the pid of the child, otherwise 0. */ void dhcpcd_daemonise(struct dhcpcd_ctx *ctx) @@ -363,6 +393,13 @@ dhcpcd_daemonise(struct dhcpcd_ctx *ctx) if (!(logopts & LOGERR_QUIET) && ctx->stderr_valid) (void)fprintf(stderr, "forked to background, child pid %d\n", getpid()); + +#ifdef PRIVSEP + ps_daemonised(ctx); +#else + dhcpcd_daemonised(ctx); +#endif + i = EXIT_SUCCESS; if (write(ctx->fork_fd, &i, sizeof(i)) == -1) logerr("write"); @@ -370,19 +407,6 @@ dhcpcd_daemonise(struct dhcpcd_ctx *ctx) eloop_event_delete(ctx->eloop, ctx->fork_fd); close(ctx->fork_fd); ctx->fork_fd = -1; - - /* - * Stop writing to stderr. - * On the happy path, only the manager process writes to stderr, - * so this just stops wasting fprintf calls to nowhere. - * All other calls - ie errors in privsep processes or script output, - * will error when printing. - * If we *really* want to fix that, then we need to suck - * stderr/stdout in the manager process and either disacrd it or pass - * it to the launcher process and then to stderr. - */ - logopts &= ~LOGERR_ERR; - logsetopts(logopts); #endif } @@ -1871,17 +1895,17 @@ dhcpcd_pidfile_timeout(void *arg) static int dup_null(int fd) { + int fd_null = open(_PATH_DEVNULL, O_WRONLY); int err; - int fd_null = open(_PATH_DEVNULL, O_RDONLY); if (fd_null == -1) { logwarn("open %s", _PATH_DEVNULL); return -1; } - if ((err = dup2(fd, fd_null)) == -1) + if ((err = dup2(fd_null, fd)) == -1) logwarn("dup2 %d", fd); - close(fd); + close(fd_null); return err; } @@ -1988,6 +2012,15 @@ main(int argc, char **argv, char **envp) ctx.stdout_valid = fcntl(STDOUT_FILENO, F_GETFD) != -1; ctx.stderr_valid = fcntl(STDERR_FILENO, F_GETFD) != -1; + /* Even we if we don't have input/outputs, we need to + * ensure they are setup for shells. */ + if (!ctx.stdin_valid) + dup_null(STDIN_FILENO); + if (!ctx.stdout_valid) + dup_null(STDOUT_FILENO); + if (!ctx.stderr_valid) + dup_null(STDERR_FILENO); + logopts = LOGERR_LOG | LOGERR_LOG_DATE | LOGERR_LOG_PID; if (ctx.stderr_valid) logopts |= LOGERR_ERR; diff --git a/src/dhcpcd.h b/src/dhcpcd.h index cdb0e135..918dc687 100644 --- a/src/dhcpcd.h +++ b/src/dhcpcd.h @@ -265,6 +265,7 @@ extern const char *dhcpcd_default_script; int dhcpcd_ifafwaiting(const struct interface *); int dhcpcd_afwaiting(const struct dhcpcd_ctx *); +void dhcpcd_daemonised(struct dhcpcd_ctx *); void dhcpcd_daemonise(struct dhcpcd_ctx *); void dhcpcd_signal_cb(int, void *); diff --git a/src/privsep-control.c b/src/privsep-control.c index 38f18c4e..40bfb164 100644 --- a/src/privsep-control.c +++ b/src/privsep-control.c @@ -69,8 +69,7 @@ ps_ctl_recvmsg(void *arg, unsigned short events) { struct ps_process *psp = arg; - if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events, - NULL, psp->psp_ctx) == -1) + if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events, NULL, NULL) == -1) logerr(__func__); } diff --git a/src/privsep.c b/src/privsep.c index ecc517ec..03f4be91 100644 --- a/src/privsep.c +++ b/src/privsep.c @@ -1099,6 +1099,26 @@ ps_recvmsg(int rfd, unsigned short events, uint16_t cmd, int wfd) return len; } +ssize_t +ps_daemonised(struct dhcpcd_ctx *ctx) +{ + struct ps_process *psp; + ssize_t err = 0; + + dhcpcd_daemonised(ctx); + + /* Echo the message to all processes */ + TAILQ_FOREACH(psp, &ctx->ps_processes, next) { + if (psp->psp_pid == getpid()) + continue; + if (ps_sendcmd(psp->psp_ctx, psp->psp_fd, PS_DAEMONISED, + 0, NULL, 0) == -1) + err = -1; + } + + return err; +} + ssize_t ps_recvpsmsg(struct dhcpcd_ctx *ctx, int fd, unsigned short events, ssize_t (*callback)(void *, struct ps_msghdr *, struct msghdr *), @@ -1131,6 +1151,9 @@ ps_recvpsmsg(struct dhcpcd_ctx *ctx, int fd, unsigned short events, if (psm.psm_hdr.ps_cmd == PS_STOP) { stop = true; len = 0; + } else if (psm.psm_hdr.ps_cmd == PS_DAEMONISED) { + ps_daemonised(ctx); + return 0; } } diff --git a/src/privsep.h b/src/privsep.h index 00e8fc4f..cab17550 100644 --- a/src/privsep.h +++ b/src/privsep.h @@ -55,6 +55,7 @@ #define PS_CTL_EOF 0x0019 #define PS_LOGREOPEN 0x0020 #define PS_STOPPROCS 0x0021 +#define PS_DAEMONISED 0x0022 /* Domains */ #define PS_ROOT 0x0101 @@ -203,6 +204,7 @@ int ps_stop(struct dhcpcd_ctx *); int ps_stopwait(struct dhcpcd_ctx *); int ps_entersandbox(const char *, const char **); int ps_managersandbox(struct dhcpcd_ctx *, const char *); +ssize_t ps_daemonised(struct dhcpcd_ctx *); int ps_unrollmsg(struct msghdr *, struct ps_msghdr *, const void *, size_t); ssize_t ps_sendpsmmsg(struct dhcpcd_ctx *, int,