Skip to content

Commit

Permalink
privsep: Notify processes when dhcpcd has daemonised
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
rsmarples committed Oct 19, 2023
1 parent 5707073 commit ac4a70d
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 18 deletions.
65 changes: 49 additions & 16 deletions src/dhcpcd.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -363,26 +393,20 @@ 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");
ctx->options |= DHCPCD_DAEMONISED;
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
}

Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions src/dhcpcd.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 *);
Expand Down
3 changes: 1 addition & 2 deletions src/privsep-control.c
Original file line number Diff line number Diff line change
Expand Up @@ -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__);
}

Expand Down
23 changes: 23 additions & 0 deletions src/privsep.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 *),
Expand Down Expand Up @@ -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;
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/privsep.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit ac4a70d

Please sign in to comment.