#ifdef USE_SIGNALS
#define sigmsg "received %s, %s"
-static void
+void
dhcpcd_signal_cb(int sig, void *arg)
{
struct dhcpcd_ctx *ctx = arg;
logerr("logopen");
return;
case SIGCHLD:
+#ifdef PRIVSEP
+ ps_root_signalcb(sig, ctx);
+#else
while (waitpid(-1, NULL, WNOHANG) > 0)
;
+#endif
return;
default:
logerrx("received signal %d but don't know what to do with it",
ctx.dhcp6_wfd = -1;
#endif
#ifdef PRIVSEP
- ctx.ps_root_fd = ctx.ps_log_fd = ctx.ps_data_fd = -1;
- ctx.ps_inet_fd = ctx.ps_control_fd = -1;
+ ctx.ps_log_fd = -1;
TAILQ_INIT(&ctx.ps_processes);
#endif
if (!(ctx.options & DHCPCD_DAEMONISE))
goto start_manager;
- if (xsocketpair(AF_UNIX, SOCK_DGRAM | SOCK_CXNB, 0, fork_fd) == -1 ||
+ if (xsocketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CXNB, 0, fork_fd) == -1 ||
(ctx.stderr_valid &&
- xsocketpair(AF_UNIX, SOCK_DGRAM | SOCK_CXNB, 0, stderr_fd) == -1))
+ xsocketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CXNB, 0, stderr_fd) == -1))
{
logerr("socketpair");
goto exit_failure;
#endif
if (ctx.script != dhcpcd_default_script)
free(ctx.script);
- if (ctx.options & DHCPCD_STARTED && !(ctx.options & DHCPCD_FORKED))
+#ifdef PRIVSEP
+ if (ps_stopwait(&ctx) != EXIT_SUCCESS)
+ i = EXIT_FAILURE;
+#endif
+ if (ctx.options & DHCPCD_STARTED && !(ctx.options & DHCPCD_FORKED)) {
loginfox(PACKAGE " exited");
+
+#ifdef PRIVSEP
+ /* Sleep some for the exited log entry to be written. */
+ struct timespec ts = { .tv_nsec = 10 };
+ nanosleep(&ts, NULL);
+#endif
+ }
#ifdef PRIVSEP
- ps_root_stop(&ctx);
+ if (ps_root_stop(&ctx) == -1)
+ i = EXIT_FAILURE;
eloop_free(ctx.ps_eloop);
#endif
eloop_free(ctx.eloop);
#ifdef PRIVSEP
struct passwd *ps_user; /* struct passwd for privsep user */
- pid_t ps_root_pid;
- int ps_root_fd; /* Privileged Proxy commands */
+ struct ps_process_head ps_processes; /* List of spawned processes */
+ struct ps_process *ps_root;
+ struct ps_process *ps_inet;
+ struct ps_process *ps_ctl;
+ int ps_data_fd; /* data returned from processes */
int ps_log_fd; /* chroot logging */
- int ps_data_fd; /* Data from root spawned processes */
+ int ps_log_root_fd; /* outside chroot log reader */
struct eloop *ps_eloop; /* eloop for polling root data */
- struct ps_process_head ps_processes; /* List of spawned processes */
- pid_t ps_inet_pid;
- int ps_inet_fd; /* Network Proxy commands and data */
- pid_t ps_control_pid;
- int ps_control_fd; /* Control Proxy - generic listener */
- int ps_control_data_fd; /* Control Proxy - data query */
struct fd_list *ps_control; /* Queue for the above */
struct fd_list *ps_control_client; /* Queue for the above */
#endif
int dhcpcd_afwaiting(const struct dhcpcd_ctx *);
void dhcpcd_daemonise(struct dhcpcd_ctx *);
+void dhcpcd_signal_cb(int, void *);
+
void dhcpcd_linkoverflow(struct dhcpcd_ctx *);
int dhcpcd_handleargs(struct dhcpcd_ctx *, struct fd_list *, int, char **);
void dhcpcd_handlecarrier(struct interface *, int, unsigned int);
/* Ignore messages from ourself. */
#ifdef PRIVSEP
- if (ctx->ps_root_pid != 0) {
- if (rtm->rtm_pid == ctx->ps_root_pid)
+ if (ctx->ps_root != NULL) {
+ if (rtm->rtm_pid == ctx->ps_root->psp_pid)
return 0;
}
#endif
* We need to process address flag changes though. */
if (ifam->ifam_type == RTM_DELADDR) {
#ifdef PRIVSEP
- if (ctx->ps_root_pid != 0) {
- if (ifam->ifam_pid == ctx->ps_root_pid)
+ if (ctx->ps_root != NULL) {
+ if (ifam->ifam_pid == ctx->ps_root->psp_pid)
return 0;
} else
#endif
}
static int
-ps_bpf_start_bpf(void *arg)
+ps_bpf_start_bpf(struct ps_process *psp)
{
- struct ps_process *psp = arg;
struct dhcpcd_ctx *ctx = psp->psp_ctx;
char *addr;
struct in_addr *ia = &psp->psp_id.psi_addr.psa_in_addr;
pid_t start;
struct iovec *iov = msg->msg_iov;
struct interface *ifp;
+ struct in_addr *ia = &psm->ps_id.psi_addr.psa_in_addr;
+ const char *addr;
cmd = (uint16_t)(psm->ps_cmd & ~(PS_START | PS_STOP));
psp = ps_findprocess(ctx, &psm->ps_id);
break;
}
- start = ps_dostart(ctx,
- &psp->psp_pid, &psp->psp_fd,
- ps_bpf_recvmsg, NULL, psp,
- ps_bpf_start_bpf, NULL,
- PSF_DROPPRIVS);
+ if (ia->s_addr == INADDR_ANY)
+ addr = NULL;
+ else
+ addr = inet_ntoa(*ia);
+ snprintf(psp->psp_name, sizeof(psp->psp_name), "BPF %s%s%s",
+ psp->psp_protostr,
+ addr != NULL ? " " : "", addr != NULL ? addr : "");
+
+ start = ps_startprocess(psp, ps_bpf_recvmsg, NULL,
+ ps_bpf_start_bpf, NULL, PSF_DROPPRIVS);
switch (start) {
case -1:
ps_freeprocess(psp);
ps_entersandbox("stdio", NULL);
break;
default:
- logdebugx("%s: spawned BPF %s on PID %d",
- psp->psp_ifname, psp->psp_protostr, start);
+ logdebugx("%s: spawned %s on PID %d",
+ psp->psp_ifname, psp->psp_name, psp->psp_pid);
break;
}
return start;
if (ia != NULL)
psm.ps_id.psi_addr.psa_in_addr = *ia;
- return ps_sendpsmdata(ctx, ctx->ps_root_fd, &psm, data, len);
+ return ps_sendpsmdata(ctx, ctx->ps_root->psp_fd, &psm, data, len);
}
#ifdef ARP
void *data, size_t len)
{
- if (ps_sendcmd(ctx, ctx->ps_root_fd, domain,
+ if (ps_sendcmd(ctx, ctx->ps_root->psp_fd, domain,
request, data, len) == -1)
return -1;
return ps_root_readerror(ctx, data, len);
ps_root_route(struct dhcpcd_ctx *ctx, void *data, size_t len)
{
- if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_ROUTE, 0, data, len) == -1)
+ if (ps_sendcmd(ctx, ctx->ps_root->psp_fd, PS_ROUTE, 0, data, len) == -1)
return -1;
return ps_root_readerror(ctx, data, len);
}
#include "privsep.h"
static int
-ps_ctl_startcb(void *arg)
+ps_ctl_startcb(struct ps_process *psp)
{
- struct dhcpcd_ctx *ctx = arg;
+ struct dhcpcd_ctx *ctx = psp->psp_ctx;
sa_family_t af;
if (ctx->options & DHCPCD_MANAGER) {
af = AF_UNSPEC;
}
- ctx->ps_control_pid = getpid();
-
return control_start(ctx,
ctx->options & DHCPCD_MANAGER ? NULL : *ctx->ifv, af);
}
return -1;
}
- if (ctx->ps_control_client != NULL)
- ctx->ps_control_client = NULL;
+ ctx->ps_control_client = NULL;
return 0;
}
static void
ps_ctl_recvmsg(void *arg, unsigned short events)
{
- struct dhcpcd_ctx *ctx = arg;
+ struct ps_process *psp = arg;
- if (ps_recvpsmsg(ctx, ctx->ps_control_fd, events,
- ps_ctl_recvmsgcb, ctx) == -1)
+ if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events,
+ ps_ctl_recvmsgcb, psp->psp_ctx) == -1)
logerr(__func__);
}
logerrx("%s: cannot handle another client", __func__);
return 0;
}
- fd = control_new(ctx, ctx->ps_control_data_fd, fd_flags);
+ fd = control_new(ctx, ctx->ps_ctl->psp_work_fd, fd_flags);
if (fd == NULL)
return -1;
ctx->ps_control_client = fd;
control_recvdata(fd, iov->iov_base, iov->iov_len);
break;
case PS_CTL_EOF:
- control_free(ctx->ps_control_client);
+ ctx->ps_control_client = NULL;
break;
default:
errno = ENOTSUP;
static void
ps_ctl_dodispatch(void *arg, unsigned short events)
{
- struct dhcpcd_ctx *ctx = arg;
+ struct ps_process *psp = arg;
- if (ps_recvpsmsg(ctx, ctx->ps_control_fd, events,
- ps_ctl_dispatch, ctx) == -1)
+ if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events,
+ ps_ctl_dispatch, psp->psp_ctx) == -1)
logerr(__func__);
}
if (!(events & ELE_READ))
logerrx("%s: unexpected event 0x%04x", __func__, events);
- errno = 0;
- len = read(ctx->ps_control_data_fd, buf, sizeof(buf));
+ len = read(ctx->ps_ctl->psp_work_fd, buf, sizeof(buf));
+ if (len == 0)
+ return;
if (len == -1) {
logerr("%s: read", __func__);
eloop_exit(ctx->eloop, EXIT_FAILURE);
pid_t
ps_ctl_start(struct dhcpcd_ctx *ctx)
{
+ struct ps_id id = {
+ .psi_ifindex = 0,
+ .psi_cmd = PS_CTL,
+ };
+ struct ps_process *psp;
int data_fd[2], listen_fd[2];
pid_t pid;
return -1;
#endif
- pid = ps_dostart(ctx, &ctx->ps_control_pid, &ctx->ps_control_fd,
- ps_ctl_recvmsg, ps_ctl_dodispatch, ctx,
- ps_ctl_startcb, NULL,
- PSF_DROPPRIVS);
+ psp = ctx->ps_ctl = ps_newprocess(ctx, &id);
+ strlcpy(psp->psp_name, "control proxy", sizeof(psp->psp_name));
+ pid = ps_startprocess(psp, ps_ctl_recvmsg, ps_ctl_dodispatch,
+ ps_ctl_startcb, NULL, PSF_DROPPRIVS);
if (pid == -1)
return -1;
else if (pid != 0) {
- ctx->ps_control_data_fd = data_fd[1];
+ psp->psp_work_fd = data_fd[1];
close(data_fd[0]);
ctx->ps_control = control_new(ctx,
listen_fd[1], FD_SENDLEN | FD_LISTEN);
return pid;
}
- ctx->ps_control_data_fd = data_fd[0];
+ psp->psp_work_fd = data_fd[0];
close(data_fd[1]);
- if (eloop_event_add(ctx->eloop, ctx->ps_control_data_fd, ELE_READ,
+ if (eloop_event_add(ctx->eloop, psp->psp_work_fd, ELE_READ,
ps_ctl_recv, ctx) == -1)
return -1;
- ctx->ps_control = control_new(ctx,
- listen_fd[0], 0);
+ ctx->ps_control = control_new(ctx, listen_fd[0], 0);
close(listen_fd[1]);
if (ctx->ps_control == NULL)
return -1;
ps_ctl_stop(struct dhcpcd_ctx *ctx)
{
- return ps_dostop(ctx, &ctx->ps_control_pid, &ctx->ps_control_fd);
+ return ps_stopprocess(ctx->ps_ctl);
}
ssize_t
if (ctx->ps_control_client != NULL && ctx->ps_control_client != fd)
logerrx("%s: cannot deal with another client", __func__);
ctx->ps_control_client = fd;
- return ps_sendcmd(ctx, ctx->ps_control_fd, PS_CTL,
+ return ps_sendcmd(ctx, ctx->ps_ctl->psp_fd, PS_CTL,
fd->flags & FD_UNPRIV ? PS_CTL_UNPRIV : PS_CTL_PRIV,
data, len);
}
{
struct dhcpcd_ctx *ctx = fd->ctx;
- return ps_sendcmd(ctx, ctx->ps_control_fd, PS_CTL_EOF, 0, NULL, 0);
+ return ps_sendcmd(ctx, ctx->ps_ctl->psp_fd, PS_CTL_EOF, 0, NULL, 0);
}
struct dhcpcd_ctx *ctx = arg;
if (ps_recvmsg(ctx, ctx->udp_rfd, events,
- PS_BOOTP, ctx->ps_inet_fd) == -1)
+ PS_BOOTP, ctx->ps_inet->psp_fd) == -1)
logerr(__func__);
}
#endif
struct dhcpcd_ctx *ctx = ifp->ctx;
if (ps_recvmsg(ctx, state->nd_fd, events,
- PS_ND, ctx->ps_inet_fd) == -1)
+ PS_ND, ctx->ps_inet->psp_fd) == -1)
logerr(__func__);
#else
struct dhcpcd_ctx *ctx = arg;
if (ps_recvmsg(ctx, ctx->nd_fd, events,
- PS_ND, ctx->ps_inet_fd) == -1)
+ PS_ND, ctx->ps_inet->psp_fd) == -1)
logerr(__func__);
#endif
}
struct dhcpcd_ctx *ctx = arg;
if (ps_recvmsg(ctx, ctx->dhcp6_rfd, events,
- PS_DHCP6, ctx->ps_inet_fd) == -1)
+ PS_DHCP6, ctx->ps_inet->psp_fd) == -1)
logerr(__func__);
}
#endif
}
static int
-ps_inet_startcb(void *arg)
+ps_inet_startcb(struct ps_process *psp)
{
- struct dhcpcd_ctx *ctx = arg;
+ struct dhcpcd_ctx *ctx = psp->psp_ctx;
int ret = 0;
if (ctx->options & DHCPCD_MANAGER)
setproctitle("[network proxy]");
else
setproctitle("[network proxy] %s%s%s",
- ctx->ifv[0],
+ ctx->ifc != 0 ? ctx->ifv[0] : "",
ctx->options & DHCPCD_IPV4 ? " [ip4]" : "",
ctx->options & DHCPCD_IPV6 ? " [ip6]" : "");
static void
ps_inet_recvmsg(void *arg, unsigned short events)
{
- struct dhcpcd_ctx *ctx = arg;
+ struct ps_process *psp = arg;
/* Receive shutdown */
- if (ps_recvpsmsg(ctx, ctx->ps_inet_fd, events, NULL, NULL) == -1)
+ if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events, NULL, NULL) == -1)
logerr(__func__);
}
static void
ps_inet_dodispatch(void *arg, unsigned short events)
{
- struct dhcpcd_ctx *ctx = arg;
+ struct ps_process *psp = arg;
- if (ps_recvpsmsg(ctx, ctx->ps_inet_fd, events,
- ps_inet_dispatch, ctx) == -1)
+ if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events,
+ ps_inet_dispatch, psp->psp_ctx) == -1)
logerr(__func__);
}
pid_t
ps_inet_start(struct dhcpcd_ctx *ctx)
{
+ struct ps_id id = {
+ .psi_ifindex = 0,
+ .psi_cmd = PS_INET,
+ };
+ struct ps_process *psp;
pid_t pid;
- pid = ps_dostart(ctx, &ctx->ps_inet_pid, &ctx->ps_inet_fd,
- ps_inet_recvmsg, ps_inet_dodispatch, ctx,
- ps_inet_startcb, NULL,
- PSF_DROPPRIVS);
+ psp = ctx->ps_inet = ps_newprocess(ctx, &id);
+ if (psp == NULL)
+ return -1;
+
+ strlcpy(psp->psp_name, "network proxy", sizeof(psp->psp_name));
+ pid = ps_startprocess(psp, ps_inet_recvmsg, ps_inet_dodispatch,
+ ps_inet_startcb, NULL, PSF_DROPPRIVS);
if (pid == 0)
ps_entersandbox("stdio", NULL);
ps_inet_stop(struct dhcpcd_ctx *ctx)
{
- return ps_dostop(ctx, &ctx->ps_inet_pid, &ctx->ps_inet_fd);
+ return ps_stopprocess(ctx->ps_inet);
}
#ifdef INET
}
static int
-ps_inet_listenin(void *arg)
+ps_inet_listenin(struct ps_process *psp)
{
- struct ps_process *psp = arg;
struct in_addr *ia = &psp->psp_id.psi_addr.psa_in_addr;
char buf[INET_ADDRSTRLEN];
inet_ntop(AF_INET, ia, buf, sizeof(buf));
- setproctitle("[network proxy] %s", buf);
+ setproctitle("[%s proxy] %s", psp->psp_protostr, buf);
psp->psp_work_fd = dhcp_openudp(ia);
if (psp->psp_work_fd == -1) {
logerr("%s: eloop_event_add DHCP", __func__);
return -1;
}
-
- logdebugx("spawned listener %s on PID %d", buf, getpid());
return 0;
}
#endif
}
static int
-ps_inet_listennd(void *arg)
+ps_inet_listennd(struct ps_process *psp)
{
- struct ps_process *psp = arg;
setproctitle("[ND network proxy]");
logerr(__func__);
return -1;
}
-
- logdebugx("spawned ND listener on PID %d", getpid());
return 0;
}
#endif
}
static int
-ps_inet_listenin6(void *arg)
+ps_inet_listenin6(struct ps_process *psp)
{
- struct ps_process *psp = arg;
struct in6_addr *ia = &psp->psp_id.psi_addr.psa_in6_addr;
char buf[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, ia, buf, sizeof(buf));
- setproctitle("[network proxy] %s", buf);
+ setproctitle("[%s proxy] %s", psp->psp_protostr, buf);
psp->psp_work_fd = dhcp6_openudp(psp->psp_id.psi_ifindex, ia);
if (psp->psp_work_fd == -1) {
logerr("%s: eloop_event_add DHCP", __func__);
return -1;
}
-
- logdebugx("spawned listener %s on PID %d", buf, getpid());
return 0;
}
#endif
{
uint16_t cmd;
struct ps_process *psp;
- int (*start_func)(void *);
+ int (*start_func)(struct ps_process *);
pid_t start;
+ struct ps_addr *psa = &psm->ps_id.psi_addr;
+ void *ia;
+ char buf[INET_MAX_ADDRSTRLEN];
cmd = (uint16_t)(psm->ps_cmd & ~(PS_START | PS_STOP));
if (cmd == psm->ps_cmd)
return 0;
}
+ if (!(psm->ps_cmd & PS_START)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (psp != NULL)
+ return 1;
+
+ psp = ps_newprocess(ctx, &psm->ps_id);
+ if (psp == NULL)
+ return -1;
+
+
switch (cmd) {
#ifdef INET
case PS_BOOTP:
start_func = ps_inet_listenin;
+ psp->psp_protostr = "BOOTP";
+ ia = &psa->psa_in_addr;
break;
#endif
#ifdef INET6
#ifdef __sun
case PS_ND:
start_func = ps_inet_listennd;
+ psp->psp_protostr = "ND";
+ ia = &psa->psa_in6_addr;
break;
#endif
#ifdef DHCP6
case PS_DHCP6:
start_func = ps_inet_listenin6;
+ psp->psp_protostr = "DHCP6";
+ ia = &psa->psa_in6_addr;
break;
#endif
#endif
return -1;
}
- if (!(psm->ps_cmd & PS_START)) {
- errno = EINVAL;
- return -1;
- }
-
- if (psp != NULL)
- return 1;
-
- psp = ps_newprocess(ctx, &psm->ps_id);
- if (psp == NULL)
- return -1;
-
- start = ps_dostart(ctx,
- &psp->psp_pid, &psp->psp_fd,
- ps_inet_recvmsgpsp, NULL, psp,
- start_func, NULL,
- PSF_DROPPRIVS);
+ snprintf(psp->psp_name, sizeof(psp->psp_name),
+ "%s proxy %s", psp->psp_protostr,
+ inet_ntop(psa->psa_family, ia, buf, sizeof(buf)));
+ start = ps_startprocess(psp, ps_inet_recvmsgpsp, NULL,
+ start_func, NULL, PSF_DROPPRIVS);
switch (start) {
case -1:
ps_freeprocess(psp);
ps_entersandbox("stdio", NULL);
break;
default:
+ logdebugx("%s: spawned %s on PID %d",
+ psp->psp_ifname, psp->psp_name, psp->psp_pid);
break;
}
return start;
.ps_id = {
.psi_cmd = (uint8_t)(cmd & ~(PS_START | PS_STOP)),
.psi_ifindex = ia->iface->index,
+ .psi_addr.psa_family = AF_INET,
.psi_addr.psa_in_addr = ia->addr,
},
};
- return ps_sendpsmmsg(ctx, ctx->ps_root_fd, &psm, msg);
+ return ps_sendpsmmsg(ctx, ctx->ps_root->psp_fd, &psm, msg);
}
ssize_t
ssize_t
ps_inet_sendbootp(struct interface *ifp, const struct msghdr *msg)
{
+ struct dhcpcd_ctx *ctx = ifp->ctx;
- return ps_sendmsg(ifp->ctx, ifp->ctx->ps_root_fd, PS_BOOTP, 0, msg);
+ return ps_sendmsg(ctx, ctx->ps_root->psp_fd, PS_BOOTP, 0, msg);
}
#endif /* INET */
.ps_id = {
.psi_cmd = (uint8_t)(cmd & ~(PS_START | PS_STOP)),
.psi_ifindex = ifp->index,
+ .psi_addr.psa_family = AF_INET6,
},
};
- return ps_sendpsmmsg(ctx, ctx->ps_root_fd, &psm, msg);
+ return ps_sendpsmmsg(ctx, ctx->ps_root->psp_fd, &psm, msg);
}
ssize_t
ssize_t
ps_inet_sendnd(struct interface *ifp, const struct msghdr *msg)
{
+ struct dhcpcd_ctx *ctx = ifp->ctx;
- return ps_sendmsg(ifp->ctx, ifp->ctx->ps_root_fd, PS_ND, 0, msg);
+ return ps_sendmsg(ctx, ctx->ps_root->psp_fd, PS_ND, 0, msg);
}
#endif
.ps_id = {
.psi_cmd = (uint8_t)(cmd & ~(PS_START | PS_STOP)),
.psi_ifindex = ia->iface->index,
+ .psi_addr.psa_family = AF_INET6,
.psi_addr.psa_in6_addr = ia->addr,
},
};
- return ps_sendpsmmsg(ctx, ctx->ps_root_fd, &psm, msg);
+ return ps_sendpsmmsg(ctx, ctx->ps_root->psp_fd, &psm, msg);
}
ssize_t
ssize_t
ps_inet_senddhcp6(struct interface *ifp, const struct msghdr *msg)
{
+ struct dhcpcd_ctx *ctx = ifp->ctx;
- return ps_sendmsg(ifp->ctx, ifp->ctx->ps_root_fd, PS_DHCP6, 0, msg);
+ return ps_sendmsg(ctx, ctx->ps_root->psp_fd, PS_DHCP6, 0, msg);
}
#endif /* DHCP6 */
#endif /* INET6 */
goto out; \
} while (0 /* CONSTCOND */)
- len = readv(ctx->ps_root_fd, iov, __arraycount(iov));
+ len = readv(ctx->ps_root->psp_fd, iov, __arraycount(iov));
if (len == -1)
PSR_ERROR(errno);
else if ((size_t)len < sizeof(*psr_error))
.psr_data = data, .psr_datalen = len,
};
- if (eloop_event_add(ctx->ps_eloop, ctx->ps_root_fd, ELE_READ,
+ if (eloop_event_add(ctx->ps_eloop, ctx->ps_root->psp_fd, ELE_READ,
ps_root_readerrorcb, &psr_ctx) == -1)
return -1;
eloop_enter(ctx->ps_eloop);
eloop_start(ctx->ps_eloop, &ctx->sigset);
+ eloop_event_delete(ctx->ps_eloop, ctx->ps_root->psp_fd);
errno = psr_ctx.psr_error.psr_errno;
return psr_ctx.psr_error.psr_result;
if (events != ELE_READ)
logerrx("%s: unexpected event 0x%04x", __func__, events);
- len = recv(ctx->ps_root_fd, psr_error, sizeof(*psr_error), MSG_PEEK);
+ len = recv(ctx->ps_root->psp_fd,
+ psr_error, sizeof(*psr_error), MSG_PEEK);
if (len == -1)
PSR_ERROR(errno);
else if ((size_t)len < sizeof(*psr_error))
iov[1].iov_len = psr_ctx->psr_datalen;
}
- len = readv(ctx->ps_root_fd, iov, __arraycount(iov));
+ len = readv(ctx->ps_root->psp_fd, iov, __arraycount(iov));
if (len == -1)
PSR_ERROR(errno);
else if ((size_t)len != sizeof(*psr_error) + psr_ctx->psr_datalen)
eloop_enter(ctx->ps_eloop);
eloop_start(ctx->ps_eloop, &ctx->sigset);
+ eloop_event_delete(ctx->ps_eloop, ctx->ps_root->psp_fd);
errno = psr_ctx.psr_error.psr_errno;
*data = psr_ctx.psr_data;
logdebugx("%s: result %zd errno %d", __func__, result, errno);
#endif
- return writev(ctx->ps_root_fd, iov, __arraycount(iov));
+ return writev(ctx->ps_root->psp_fd, iov, __arraycount(iov));
}
static ssize_t
pid = script_exec(argv, ctx->script_env);
if (pid == -1)
return -1;
+
/* Wait for the script to finish */
while (waitpid(pid, &status, 0) == -1) {
if (errno != EINTR) {
if (psp != NULL) {
if (psm->ps_cmd & PS_STOP) {
- int ret = ps_dostop(ctx, &psp->psp_pid, &psp->psp_fd);
-
- ps_freeprocess(psp);
- return ret;
+ return ps_stopprocess(psp);
} else if (psm->ps_cmd & PS_START) {
/* Process has already started .... */
+ logdebugx("%s%sprocess %s already started on pid %d",
+ psp->psp_ifname,
+ psp->psp_ifname[0] != '\0' ? ": " : "",
+ psp->psp_name, psp->psp_pid);
return 0;
}
if (err == -1) {
logerr("%s: failed to send message to pid %d",
__func__, psp->psp_pid);
- shutdown(psp->psp_fd, SHUT_RDWR);
- close(psp->psp_fd);
- psp->psp_fd = -1;
ps_freeprocess(psp);
}
return 0;
case PS_SCRIPT:
err = ps_root_run_script(ctx, data, len);
break;
+ case PS_STOPPROCS:
+ ctx->options |= DHCPCD_EXITING;
+ TAILQ_FOREACH(psp, &ctx->ps_processes, next) {
+ if (psp != ctx->ps_root)
+ ps_stopprocess(psp);
+ }
+ err = ps_stopwait(ctx);
+ break;
case PS_UNLINK:
if (!ps_root_validpath(ctx, psm->ps_cmd, data)) {
err = -1;
static void
ps_root_recvmsg(void *arg, unsigned short events)
{
- struct dhcpcd_ctx *ctx = arg;
+ struct ps_process *psp = arg;
- if (ps_recvpsmsg(ctx, ctx->ps_root_fd, events,
- ps_root_recvmsgcb, ctx) == -1)
+ if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events,
+ ps_root_recvmsgcb, psp->psp_ctx) == -1)
logerr(__func__);
}
return -1;
}
- return (int)ps_sendcmd(ctx, ctx->ps_data_fd, PS_DEV_IFCMD, flag,
- ifname, strlen(ifname) + 1);
+ return (int)ps_sendcmd(ctx, ctx->ps_data_fd, PS_DEV_IFCMD,
+ flag, ifname, strlen(ifname) + 1);
}
#endif
static int
-ps_root_startcb(void *arg)
+ps_root_startcb(struct ps_process *psp)
{
- struct dhcpcd_ctx *ctx = arg;
+ struct dhcpcd_ctx *ctx = psp->psp_ctx;
if (ctx->options & DHCPCD_MANAGER)
setproctitle("[privileged proxy]");
ctx->ifv[0],
ctx->options & DHCPCD_IPV4 ? " [ip4]" : "",
ctx->options & DHCPCD_IPV6 ? " [ip6]" : "");
- ctx->ps_root_pid = getpid();
ctx->options |= DHCPCD_PRIVSEPROOT;
/* Open network sockets for sending.
* but makes life very easy for unicasting DHCPv6 in non manager
* mode as we no longer care about address selection.
* We can't call shutdown SHUT_RD on the socket because it's
- * not connectd. All we can do is try and set a zero sized
+ * not connected. All we can do is try and set a zero sized
* receive buffer and just let it overflow.
* Reading from it just to drain it is a waste of CPU time. */
#ifdef INET
(DHCPCD_MANAGER | DHCPCD_DEV))
dev_start(ctx, ps_root_handleinterface);
#endif
-
return 0;
}
-static void
-ps_root_signalcb(int sig, __unused void *arg)
+void
+ps_root_signalcb(int sig, void *arg)
{
+ struct dhcpcd_ctx *ctx = arg;
+ int status;
+ pid_t pid;
+ const char *ifname, *name;
+ struct ps_process *psp;
- if (sig == SIGCHLD) {
- while (waitpid(-1, NULL, WNOHANG) > 0)
- ;
+ if (sig != SIGCHLD)
return;
+
+ while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
+ psp = ps_findprocesspid(ctx, pid);
+ if (psp != NULL) {
+ ifname = psp->psp_ifname;
+ name = psp->psp_name;
+ } else {
+ /* Ignore logging the double fork */
+ if (ctx->options & DHCPCD_LAUNCHER)
+ continue;
+ ifname = "";
+ name = "unknown process";
+ }
+
+ if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
+ logerrx("%s%s%s exited unexpectedly from PID %d,"
+ " code=%d",
+ ifname, ifname[0] != '\0' ? ": " : "",
+ name, pid, WEXITSTATUS(status));
+ else if (WIFSIGNALED(status))
+ logerrx("%s%s%s exited unexpectedly from PID %d,"
+ " signal=%s",
+ ifname, ifname[0] != '\0' ? ": " : "",
+ name, pid, strsignal(WTERMSIG(status)));
+ else
+ logdebugx("%s%s%s exited from PID %d",
+ ifname, ifname[0] != '\0' ? ": " : "",
+ name, pid);
+
+ if (psp != NULL)
+ ps_freeprocess(psp);
}
+
+ if (!(ctx->options & DHCPCD_EXITING))
+ return;
+ if (!(PS_WAITING_FOR_PROCESSES(ctx)))
+ eloop_exit(ctx->ps_eloop, EXIT_SUCCESS);
}
int (*handle_interface)(void *, int, const char *);
if (events != ELE_READ)
logerrx("%s: unexpected event 0x%04x", __func__, events);
- /* OpenBSD reports connection reset when dhcpcd exits ... */
- if (logreadfd(ctx->ps_log_fd) == -1 && errno != ECONNRESET)
+ if (logreadfd(ctx->ps_log_root_fd) == -1)
logerr(__func__);
}
pid_t
ps_root_start(struct dhcpcd_ctx *ctx)
{
+ struct ps_id id = {
+ .psi_ifindex = 0,
+ .psi_cmd = PS_ROOT,
+ };
+ struct ps_process *psp;
int logfd[2], datafd[2];
pid_t pid;
- if (xsocketpair(AF_UNIX, SOCK_DGRAM | SOCK_CXNB, 0, logfd) == -1)
+ if (xsocketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CXNB, 0, logfd) == -1)
return -1;
#ifdef PRIVSEP_RIGHTS
if (ps_rights_limit_fdpair(logfd) == -1)
return -1;
#endif
- if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CXNB, 0, datafd) == -1)
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CXNB, 0, datafd) == -1)
return -1;
if (ps_setbuf_fdpair(datafd) == -1)
return -1;
return -1;
#endif
- pid = ps_dostart(ctx, &ctx->ps_root_pid, &ctx->ps_root_fd,
- ps_root_recvmsg, NULL, ctx,
- ps_root_startcb, ps_root_signalcb, 0);
+ psp = ctx->ps_root = ps_newprocess(ctx, &id);
+ strlcpy(psp->psp_name, "privileged proxy", sizeof(psp->psp_name));
+ pid = ps_startprocess(psp, ps_root_recvmsg, NULL,
+ ps_root_startcb, ps_root_signalcb, PSF_ELOOP);
if (pid == 0) {
- ctx->ps_log_fd = logfd[1];
- if (eloop_event_add(ctx->eloop, ctx->ps_log_fd, ELE_READ,
+ ctx->ps_log_fd = logfd[0]; /* Keep open to pass to processes */
+ ctx->ps_log_root_fd = logfd[1];
+ if (eloop_event_add(ctx->eloop, ctx->ps_log_root_fd, ELE_READ,
ps_root_log, ctx) == -1)
return -1;
- close(logfd[0]);
ctx->ps_data_fd = datafd[1];
close(datafd[0]);
return 0;
close(datafd[1]);
if (eloop_event_add(ctx->eloop, ctx->ps_data_fd, ELE_READ,
ps_root_dispatch, ctx) == -1)
- return -1;
-
- if ((ctx->ps_eloop = eloop_new()) == NULL)
- return -1;
-
- eloop_signal_set_cb(ctx->ps_eloop,
- dhcpcd_signals, dhcpcd_signals_len,
- ps_root_signalcb, ctx);
+ return 1;
return pid;
}
ps_root_stop(struct dhcpcd_ctx *ctx)
{
+ /* If we are the root process, ensure the log fd is fully drained. */
+ if (ctx->options & DHCPCD_PRIVSEPROOT && ctx->ps_log_fd != -1) {
+ do {
+ ;
+ } while (logreadfd(ctx->ps_log_fd) != -1);
+ }
+
if (!(ctx->options & DHCPCD_PRIVSEP) ||
ctx->options & DHCPCD_FORKED ||
ctx->eloop == NULL)
return 0;
- return ps_dostop(ctx, &ctx->ps_root_pid, &ctx->ps_root_fd);
+ if (ps_stopprocess(ctx->ps_root) == -1)
+ return -1;
+ ctx->ps_root = NULL;
+ return ps_stopwait(ctx);
+}
+
+ssize_t
+ps_root_stopprocesses(struct dhcpcd_ctx *ctx)
+{
+
+ if (!(IN_PRIVSEP_SE(ctx)))
+ return 0;
+
+ if (ps_sendcmd(ctx, ctx->ps_root->psp_fd, PS_STOPPROCS, 0,
+ NULL, 0) == -1)
+ return -1;
+ return ps_root_readerror(ctx, NULL, 0);
}
ssize_t
ps_root_script(struct dhcpcd_ctx *ctx, const void *data, size_t len)
{
- if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_SCRIPT, 0, data, len) == -1)
+ if (ps_sendcmd(ctx, ctx->ps_root->psp_fd, PS_SCRIPT,
+ 0, data, len) == -1)
return -1;
return ps_root_readerror(ctx, NULL, 0);
}
ps_root_ioctl(struct dhcpcd_ctx *ctx, ioctl_request_t req, void *data,
size_t len)
{
+ int fd = ctx->ps_root->psp_fd;
#ifdef IOCTL_REQUEST_TYPE
unsigned long ulreq = 0;
memcpy(&ulreq, &req, sizeof(req));
- if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_IOCTL, ulreq, data, len) == -1)
+ if (ps_sendcmd(ctx, fd, PS_IOCTL, ulreq, data, len) == -1)
return -1;
#else
- if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_IOCTL, req, data, len) == -1)
+ if (ps_sendcmd(ctx, fd, PS_IOCTL, req, data, len) == -1)
return -1;
#endif
return ps_root_readerror(ctx, data, len);
ps_root_unlink(struct dhcpcd_ctx *ctx, const char *file)
{
- if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_UNLINK, 0,
+ if (ps_sendcmd(ctx, ctx->ps_root->psp_fd, PS_UNLINK, 0,
file, strlen(file) + 1) == -1)
return -1;
return ps_root_readerror(ctx, NULL, 0);
ps_root_readfile(struct dhcpcd_ctx *ctx, const char *file,
void *data, size_t len)
{
- if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_READFILE, 0,
+ if (ps_sendcmd(ctx, ctx->ps_root->psp_fd, PS_READFILE, 0,
file, strlen(file) + 1) == -1)
return -1;
return ps_root_readerror(ctx, data, len);
}
memcpy(buf + flen, data, len);
- if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_WRITEFILE, mode,
+ if (ps_sendcmd(ctx, ctx->ps_root->psp_fd, PS_WRITEFILE, mode,
buf, flen + len) == -1)
return -1;
return ps_root_readerror(ctx, NULL, 0);
ps_root_filemtime(struct dhcpcd_ctx *ctx, const char *file, time_t *time)
{
- if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_FILEMTIME, 0,
+ if (ps_sendcmd(ctx, ctx->ps_root->psp_fd, PS_FILEMTIME, 0,
file, strlen(file) + 1) == -1)
return -1;
return ps_root_readerror(ctx, time, sizeof(*time));
ps_root_logreopen(struct dhcpcd_ctx *ctx)
{
- if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_LOGREOPEN, 0, NULL, 0) == -1)
+ if (ps_sendcmd(ctx, ctx->ps_root->psp_fd, PS_LOGREOPEN, 0,
+ NULL, 0) == -1)
return -1;
return ps_root_readerror(ctx, NULL, 0);
}
size_t len;
ssize_t err;
- if (ps_sendcmd(ctx, ctx->ps_root_fd,
+ if (ps_sendcmd(ctx, ctx->ps_root->psp_fd,
PS_GETIFADDRS, 0, NULL, 0) == -1)
return -1;
err = ps_root_mreaderror(ctx, &buf, &len);
ps_root_ip6forwarding(struct dhcpcd_ctx *ctx, const char *ifname)
{
- if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_IP6FORWARDING, 0,
+ if (ps_sendcmd(ctx, ctx->ps_root->psp_fd, PS_IP6FORWARDING, 0,
ifname, ifname != NULL ? strlen(ifname) + 1 : 0) == -1)
return -1;
return ps_root_readerror(ctx, NULL, 0);
ps_root_getauthrdm(struct dhcpcd_ctx *ctx, uint64_t *rdm)
{
- if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_AUTH_MONORDM, 0,
+ if (ps_sendcmd(ctx, ctx->ps_root->psp_fd, PS_AUTH_MONORDM, 0,
rdm, sizeof(*rdm))== -1)
return -1;
return (int)ps_root_readerror(ctx, rdm, sizeof(*rdm));
ps_root_dev_initialised(struct dhcpcd_ctx *ctx, const char *ifname)
{
- if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_DEV_INITTED, 0,
+ if (ps_sendcmd(ctx, ctx->ps_root->psp_fd, PS_DEV_INITTED, 0,
ifname, strlen(ifname) + 1)== -1)
return -1;
return (int)ps_root_readerror(ctx, NULL, 0);
ps_root_dev_listening(struct dhcpcd_ctx * ctx)
{
- if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_DEV_LISTENING, 0, NULL, 0)== -1)
+ if (ps_sendcmd(ctx, ctx->ps_root->psp_fd, PS_DEV_LISTENING,
+ 0, NULL, 0) == -1)
return -1;
return (int)ps_root_readerror(ctx, NULL, 0);
}
pid_t ps_root_start(struct dhcpcd_ctx *ctx);
int ps_root_stop(struct dhcpcd_ctx *ctx);
+void ps_root_signalcb(int, void *);
ssize_t ps_root_readerror(struct dhcpcd_ctx *, void *, size_t);
ssize_t ps_root_mreaderror(struct dhcpcd_ctx *, void **, size_t *);
const void *, size_t);
ssize_t ps_root_logreopen(struct dhcpcd_ctx *);
ssize_t ps_root_script(struct dhcpcd_ctx *, const void *, size_t);
+ssize_t ps_root_stopprocesses(struct dhcpcd_ctx *);
int ps_root_getauthrdm(struct dhcpcd_ctx *, uint64_t *);
#ifdef PRIVSEP_GETIFADDRS
int ps_root_getifaddrs(struct dhcpcd_ctx *, struct ifaddrs **);
static int
ps_dropprivs(struct dhcpcd_ctx *ctx)
{
+ return 0;
struct passwd *pw = ctx->ps_user;
if (ctx->options & DHCPCD_LAUNCHER)
struct rlimit rzero = { .rlim_cur = 0, .rlim_max = 0 };
- if (ctx->ps_control_pid != getpid()) {
+ if (ctx->ps_ctl == NULL || ctx->ps_ctl->psp_pid != getpid()) {
/* Prohibit new files, sockets, etc */
#if (defined(__linux__) || defined(__sun) || defined(__OpenBSD__)) && \
!defined(HAVE_KQUEUE) && !defined(HAVE_EPOLL)
#endif
pid_t
-ps_dostart(struct dhcpcd_ctx *ctx,
- pid_t *priv_pid, int *priv_fd,
+ps_startprocess(struct ps_process *psp,
void (*recv_msg)(void *, unsigned short),
void (*recv_unpriv_msg)(void *, unsigned short),
- void *recv_ctx, int (*callback)(void *), void (*signal_cb)(int, void *),
+ int (*callback)(struct ps_process *), void (*signal_cb)(int, void *),
unsigned int flags)
{
+ struct dhcpcd_ctx *ctx = psp->psp_ctx;
int fd[2];
pid_t pid;
- if (xsocketpair(AF_UNIX, SOCK_DGRAM | SOCK_CXNB, 0, fd) == -1) {
+ if (xsocketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CXNB, 0, fd) == -1) {
logerr("%s: socketpair", __func__);
return -1;
}
logerr("fork");
return -1;
case 0:
- *priv_fd = fd[1];
+ psp->psp_pid = getpid();
+ psp->psp_fd = fd[1];
close(fd[0]);
break;
default:
- *priv_pid = pid;
- *priv_fd = fd[0];
+ psp->psp_pid = pid;
+ psp->psp_fd = fd[0];
close(fd[1]);
if (recv_unpriv_msg == NULL)
;
- else if (eloop_event_add(ctx->eloop, *priv_fd, ELE_READ,
- recv_unpriv_msg, recv_ctx) == -1)
+ else if (eloop_event_add(ctx->eloop, psp->psp_fd, ELE_READ,
+ recv_unpriv_msg, psp) == -1)
{
- logerr("%s: eloop_event_add", __func__);
+ logerr("%s: eloop_event_add fd %d",
+ __func__, psp->psp_fd);
return -1;
}
return pid;
}
ctx->options |= DHCPCD_FORKED;
- if (ctx->fork_fd != -1) {
- close(ctx->fork_fd);
- ctx->fork_fd = -1;
- }
- pidfile_clean();
-
- eloop_clear(ctx->eloop, loggetfd(), -1);
+ if (ctx->ps_log_fd != -1)
+ logsetfd(ctx->ps_log_fd);
+ eloop_clear(ctx->eloop, -1);
eloop_forked(ctx->eloop);
eloop_signal_set_cb(ctx->eloop,
dhcpcd_signals, dhcpcd_signals_len, signal_cb, ctx);
goto errexit;
}
- /* We are not root */
- if (priv_fd != &ctx->ps_root_fd) {
- ps_freeprocesses(ctx, recv_ctx);
- if (ctx->ps_root_fd != -1) {
- close(ctx->ps_root_fd);
- ctx->ps_root_fd = -1;
- }
+ if (ctx->fork_fd != -1) {
+ /* Already removed from eloop thanks to above clear. */
+ close(ctx->fork_fd);
+ ctx->fork_fd = -1;
+ }
+ /* This process has no need of the blocking inner eloop. */
+ if (!(flags & PSF_ELOOP)) {
+ eloop_free(ctx->ps_eloop);
+ ctx->ps_eloop = NULL;
+ } else
+ eloop_forked(ctx->ps_eloop);
+
+ pidfile_clean();
+ ps_freeprocesses(ctx, psp);
+
+ if (ctx->ps_root != psp) {
+ ctx->options &= ~DHCPCD_PRIVSEPROOT;
+ ctx->ps_root = NULL;
+ if (ctx->ps_log_root_fd != -1) {
+ /* Already removed from eloop thanks to above clear. */
+ close(ctx->ps_log_root_fd);
+ ctx->ps_log_root_fd = -1;
+ }
#ifdef PRIVSEP_RIGHTS
- /* We cannot limit the root process in any way. */
if (ps_rights_limit_stdio(ctx) == -1) {
logerr("ps_rights_limit_stdio");
goto errexit;
#endif
}
- if (priv_fd != &ctx->ps_inet_fd && ctx->ps_inet_fd != -1) {
- close(ctx->ps_inet_fd);
- ctx->ps_inet_fd = -1;
- }
+ if (ctx->ps_inet != psp)
+ ctx->ps_inet = NULL;
+ if (ctx->ps_ctl != psp)
+ ctx->ps_ctl = NULL;
+
+#if 0
+ char buf[1024];
+ errno = 0;
+ ssize_t xx = recv(psp->psp_fd, buf, sizeof(buf), MSG_PEEK);
+ logerr("pid %d test fd %d recv peek %zd", getpid(), psp->psp_fd, xx);
+#endif
- if (eloop_event_add(ctx->eloop, *priv_fd, ELE_READ,
- recv_msg, recv_ctx) == -1)
+ if (eloop_event_add(ctx->eloop, psp->psp_fd, ELE_READ,
+ recv_msg, psp) == -1)
{
- logerr("%s: eloop_event_add", __func__);
+ logerr("%d %s: eloop_event_add XX fd %d", getpid(), __func__, psp->psp_fd);
goto errexit;
}
- if (callback(recv_ctx) == -1)
+ if (callback(psp) == -1)
goto errexit;
if (flags & PSF_DROPPRIVS)
return 0;
errexit:
- /* Failure to start root or inet processes is fatal. */
- if (priv_fd == &ctx->ps_root_fd || priv_fd == &ctx->ps_inet_fd)
- (void)ps_sendcmd(ctx, *priv_fd, PS_STOP, 0, NULL, 0);
- shutdown(*priv_fd, SHUT_RDWR);
- *priv_fd = -1;
+ if (psp->psp_fd != -1) {
+ close(psp->psp_fd);
+ psp->psp_fd = -1;
+ }
eloop_exit(ctx->eloop, EXIT_FAILURE);
return -1;
}
+void
+ps_process_timeout(void *arg)
+{
+ struct dhcpcd_ctx *ctx = arg;
+
+ logerrx("%s: timed out", __func__);
+ eloop_exit(ctx->eloop, EXIT_FAILURE);
+}
+
int
-ps_dostop(struct dhcpcd_ctx *ctx, pid_t *pid, int *fd)
+ps_stopprocess(struct ps_process *psp)
{
int err = 0;
+ if (psp == NULL)
+ return 0;
+
#ifdef PRIVSEP_DEBUG
- logdebugx("%s: pid=%d fd=%d", __func__, *pid, *fd);
+ logdebugx("%s: me=%d pid=%d fd=%d %s", __func__,
+ getpid(), psp->psp_pid, psp->psp_fd, psp->psp_name);
#endif
- if (*fd != -1) {
- eloop_event_delete(ctx->eloop, *fd);
- if (ps_sendcmd(ctx, *fd, PS_STOP, 0, NULL, 0) == -1) {
+ if (psp->psp_fd != -1) {
+ eloop_event_delete(psp->psp_ctx->eloop, psp->psp_fd);
+#if 0
+ if (ps_sendcmd(psp->psp_ctx, psp->psp_fd, PS_STOP, 0,
+ NULL, 0) == -1)
+ {
+ logerr("%d %d %s %s", getpid(), psp->psp_pid, psp->psp_name, __func__);
+ err = -1;
+ }
+ shutdown(psp->psp_fd, SHUT_WR);
+#else
+ if (shutdown(psp->psp_fd, SHUT_WR) == -1) {
logerr(__func__);
err = -1;
}
- (void)shutdown(*fd, SHUT_RDWR);
- close(*fd);
- *fd = -1;
+#endif
+ psp->psp_fd = -1;
}
/* Don't wait for the process as it may not respond to the shutdown
* request. We'll reap the process on receipt of SIGCHLD. */
- *pid = 0;
return err;
}
TAILQ_INIT(&ctx->ps_processes);
+ /* We need an inner eloop to block with. */
+ if ((ctx->ps_eloop = eloop_new()) == NULL)
+ return -1;
+ eloop_signal_set_cb(ctx->ps_eloop,
+ dhcpcd_signals, dhcpcd_signals_len,
+ dhcpcd_signal_cb, ctx);
+
switch (pid = ps_root_start(ctx)) {
case -1:
logerr("ps_root_start");
* If it cannot be opened before chrooting then syslog(3) will fail.
* openlog(3) does not return an error which doubly sucks.
*/
- if (ctx->ps_root_fd == -1) {
+ if (ctx->ps_root == NULL) {
unsigned int logopts = loggetopts();
logopts &= ~LOGERR_LOG;
ctx->eloop == NULL)
return 0;
- r = ps_ctl_stop(ctx);
- if (r != 0)
- ret = r;
+ if (ctx->ps_ctl != NULL) {
+ r = ps_ctl_stop(ctx);
+ if (r != 0)
+ ret = r;
+ }
- r = ps_inet_stop(ctx);
- if (r != 0)
- ret = r;
+ if (ctx->ps_inet != NULL) {
+ r = ps_inet_stop(ctx);
+ if (r != 0)
+ ret = r;
+ }
- /* We've been chrooted, so we need to tell the
- * privileged proxy to remove the pidfile. */
- if (ps_root_unlink(ctx, ctx->pidfile) == -1)
- ret = -1;
+ if (ctx->ps_root != NULL) {
+ if (ps_root_stopprocesses(ctx) == -1)
+ ret = -1;
+ }
return ret;
}
+int
+ps_stopwait(struct dhcpcd_ctx *ctx)
+{
+ int error = EXIT_SUCCESS;
+
+ if (ctx->ps_eloop != NULL && PS_WAITING_FOR_PROCESSES(ctx)) {
+ int waited;
+
+ ctx->options |= DHCPCD_EXITING;
+ if (eloop_timeout_add_sec(ctx->ps_eloop, PS_PROCESS_TIMEOUT,
+ ps_process_timeout, ctx) == -1)
+ logerr("%s: eloop_timeout_add_sec", __func__);
+ eloop_enter(ctx->ps_eloop);
+ waited = eloop_start(ctx->ps_eloop, &ctx->sigset);
+ if (waited != EXIT_SUCCESS) {
+ logerr("%s: eloop_start", __func__);
+ error = waited;
+ }
+ eloop_timeout_delete(ctx->ps_eloop, ps_process_timeout, ctx);
+ }
+
+ if (IN_PRIVSEP_SE(ctx) && ctx->ps_root != NULL) {
+ struct ps_process *psp = ctx->ps_root;
+
+ if (ps_root_unlink(ctx, ctx->pidfile) == -1)
+ logerr("%s: ps_root_unlink", __func__);
+
+ /* We cannot log the root process exited before we
+ * log dhcpcd exits because the latter requires the former.
+ * So we just log the intent to exit. */
+ logdebugx("%s%s%s will exit from PID %d",
+ psp->psp_ifname,
+ psp->psp_ifname[0] != '\0' ? ": " : "",
+ psp->psp_name, psp->psp_pid);
+ }
+
+ return error;
+}
+
void
ps_freeprocess(struct ps_process *psp)
{
+ struct dhcpcd_ctx *ctx = psp->psp_ctx;
- TAILQ_REMOVE(&psp->psp_ctx->ps_processes, psp, next);
+ TAILQ_REMOVE(&ctx->ps_processes, psp, next);
if (psp->psp_fd != -1) {
- eloop_event_delete(psp->psp_ctx->eloop, psp->psp_fd);
+ eloop_event_delete(ctx->eloop, psp->psp_fd);
close(psp->psp_fd);
}
if (psp->psp_work_fd != -1) {
- eloop_event_delete(psp->psp_ctx->eloop, psp->psp_work_fd);
+ eloop_event_delete(ctx->eloop, psp->psp_work_fd);
close(psp->psp_work_fd);
}
+ if (ctx->ps_root == psp)
+ ctx->ps_root = NULL;
+ if (ctx->ps_inet == psp)
+ ctx->ps_inet = NULL;
+ if (ctx->ps_ctl == psp)
+ ctx->ps_ctl = NULL;
#ifdef INET
if (psp->psp_bpf != NULL)
bpf_close(psp->psp_bpf);
static void
ps_free(struct dhcpcd_ctx *ctx)
{
- struct ps_process *psp;
- bool stop = ctx->ps_root_pid == getpid();
+ struct ps_process *ppsp, *psp;
+ bool stop;
+
+ if (ctx->ps_root != NULL)
+ ppsp = ctx->ps_root;
+ else if (ctx->ps_ctl != NULL)
+ ppsp = ctx->ps_ctl;
+ else
+ ppsp = NULL;
+ if (ppsp != NULL)
+ stop = ppsp->psp_pid == getpid();
+ else
+ stop = false;
while ((psp = TAILQ_FIRST(&ctx->ps_processes)) != NULL) {
- if (stop)
- ps_dostop(ctx, &psp->psp_pid, &psp->psp_fd);
+ if (stop && psp != ppsp)
+ ps_stopprocess(psp);
ps_freeprocess(psp);
}
}
len = writev(fd, iov, iovlen);
if (len == -1) {
- logerr(__func__);
if (ctx->options & DHCPCD_FORKED &&
!(ctx->options & DHCPCD_PRIVSEPROOT))
eloop_exit(ctx->eloop, EXIT_FAILURE);
};
ssize_t len;
- if (events != ELE_READ)
+ if (!(events & ELE_READ))
logerrx("%s: unexpected event 0x%04x", __func__, events);
len = recvmsg(rfd, &msg, 0);
-
if (len == -1)
logerr("%s: recvmsg", __func__);
if (len == -1 || len == 0) {
- if (ctx->options & DHCPCD_FORKED &&
- !(ctx->options & DHCPCD_PRIVSEPROOT))
+ if (ctx->options & DHCPCD_FORKED)
eloop_exit(ctx->eloop,
- len == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
+ len != -1 ? EXIT_SUCCESS : EXIT_FAILURE);
return len;
}
iov[0].iov_len = (size_t)len;
len = ps_sendcmdmsg(wfd, cmd, &msg);
if (len == -1) {
- logerr("ps_sendcmdmsg");
- if (ctx->options & DHCPCD_FORKED &&
- !(ctx->options & DHCPCD_PRIVSEPROOT))
+ logerr("%s: ps_sendcmdmsg", __func__);
+ if (ctx->options & DHCPCD_FORKED)
eloop_exit(ctx->eloop, EXIT_FAILURE);
}
return len;
struct msghdr msg = { .msg_iov = iov, .msg_iovlen = 1 };
bool stop = false;
- if (events != ELE_READ)
+ if (!(events & ELE_READ))
logerrx("%s: unexpected event 0x%04x", __func__, events);
len = read(fd, &psm, sizeof(psm));
}
if (stop) {
+ ctx->options |= DHCPCD_EXITING;
#ifdef PRIVSEP_DEBUG
logdebugx("process %d stopping", getpid());
#endif
return NULL;
}
+struct ps_process *
+ps_findprocesspid(struct dhcpcd_ctx *ctx, pid_t pid)
+{
+ struct ps_process *psp;
+
+ TAILQ_FOREACH(psp, &ctx->ps_processes, next) {
+ if (psp->psp_pid == pid)
+ return psp;
+ }
+ errno = ESRCH;
+ return NULL;
+}
+
struct ps_process *
ps_newprocess(struct dhcpcd_ctx *ctx, struct ps_id *psid)
{
psp->psp_ctx = ctx;
memcpy(&psp->psp_id, psid, sizeof(psp->psp_id));
psp->psp_work_fd = -1;
+ if (!(ctx->options & DHCPCD_MANAGER))
+ strlcpy(psp->psp_ifname, ctx->ifv[0], sizeof(psp->psp_name));
TAILQ_INSERT_TAIL(&ctx->ps_processes, psp, next);
return psp;
}
/* Start flags */
#define PSF_DROPPRIVS 0x01
+#define PSF_ELOOP 0x02
/* Protocols */
#define PS_BOOTP 0x0001
#define PS_CTL 0x0018
#define PS_CTL_EOF 0x0019
#define PS_LOGREOPEN 0x0020
+#define PS_STOPPROCS 0x0021
+
+/* Domains */
+#define PS_ROOT 0x0101
+#define PS_INET 0x0102
+#define PS_CONTROL 0x0103
/* BSD Commands */
-#define PS_IOCTLLINK 0x0101
-#define PS_IOCTL6 0x0102
-#define PS_IOCTLINDIRECT 0x0103
-#define PS_IP6FORWARDING 0x0104
-#define PS_GETIFADDRS 0x0105
-#define PS_IFIGNOREGRP 0x0106
+#define PS_IOCTLLINK 0x0201
+#define PS_IOCTL6 0x0202
+#define PS_IOCTLINDIRECT 0x0203
+#define PS_IP6FORWARDING 0x0204
+#define PS_GETIFADDRS 0x0205
+#define PS_IFIGNOREGRP 0x0206
/* Dev Commands */
#define PS_DEV_LISTENING 0x1001
CMSG_SPACE(sizeof(struct in6_pktinfo) + \
sizeof(int)))
+#define PSP_NAMESIZE 16 + INET_MAX_ADDRSTRLEN
+
/* Handy macro to work out if in the privsep engine or not. */
#define IN_PRIVSEP(ctx) \
((ctx)->options & DHCPCD_PRIVSEP)
#define IN_PRIVSEP_SE(ctx) \
(((ctx)->options & (DHCPCD_PRIVSEP | DHCPCD_FORKED)) == DHCPCD_PRIVSEP)
+#define PS_PROCESS_TIMEOUT 5 /* seconds to stop all processes */
+
+/* We always have ourself as a process */
+#define PS_WAITING_FOR_PROCESSES(_ctx) \
+ ((IN_PRIVSEP_SE((_ctx)) && \
+ TAILQ_LAST(&(_ctx)->ps_processes, ps_process_head) != (_ctx)->ps_root) || \
+ (!IN_PRIVSEP_SE((_ctx)) && \
+ TAILQ_FIRST(&(_ctx)->ps_processes) != \
+ TAILQ_LAST(&(_ctx)->ps_processes, ps_process_head)))
+
#if defined(PRIVSEP) && defined(HAVE_CAPSICUM)
#define PRIVSEP_RIGHTS
#endif
int psp_work_fd;
unsigned int psp_ifindex;
char psp_ifname[IF_NAMESIZE];
+ char psp_name[PSP_NAMESIZE];
uint16_t psp_proto;
const char *psp_protostr;
int ps_init(struct dhcpcd_ctx *);
int ps_start(struct dhcpcd_ctx *);
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 *);
int ps_seccomp_enter(void);
#endif
-pid_t ps_dostart(struct dhcpcd_ctx * ctx,
- pid_t *priv_pid, int *priv_fd,
+pid_t ps_startprocess(struct ps_process *,
void (*recv_msg)(void *, unsigned short),
void (*recv_unpriv_msg)(void *, unsigned short),
- void *recv_ctx, int (*callback)(void *), void (*)(int, void *),
+ int (*callback)(struct ps_process *), void (*)(int, void *),
unsigned int);
-int ps_dostop(struct dhcpcd_ctx *ctx, pid_t *pid, int *fd);
-
+int ps_stopprocess(struct ps_process *);
struct ps_process *ps_findprocess(struct dhcpcd_ctx *, struct ps_id *);
+struct ps_process *ps_findprocesspid(struct dhcpcd_ctx *, pid_t);
struct ps_process *ps_newprocess(struct dhcpcd_ctx *, struct ps_id *);
+void ps_process_timeout(void *);
void ps_freeprocess(struct ps_process *);
void ps_freeprocesses(struct dhcpcd_ctx *, struct ps_process *);
#endif