dhcpcd-discuss

Re: dhcpcd 9.3.1 timing out on Arch Linux ARM

Roy Marples

Fri Oct 30 14:39:09 2020

On 30/10/2020 13:30, Adolf Belka wrote:
Hi Roy,

Patches I can run against an existing install but I am afraid I don't know how to go about testing the current git master. I did some googling and it looks like I need to clone your git repository and then build dhcpcd but I am not sure then how to replace my distributions dhcpcd with the git one to test it, without messing everything up.

I've attached a patch to bring dhcpcd-9.3.1 up to the current master.
Let me know how it works - I'm pretty confident because another ARM user just reported success :)

Roy
diff --git a/src/dhcp.c b/src/dhcp.c
index cbfb2bc8..2b741385 100644
--- a/src/dhcp.c
+++ b/src/dhcp.c
@@ -2926,6 +2926,8 @@ dhcp_handledhcp(struct interface *ifp, struct bootp *bootp, size_t bootp_len,
 	unsigned int i;
 	char *msg;
 	bool bootp_copied;
+	uint32_t v6only_time = 0;
+	bool use_v6only = false;
 #ifdef AUTH
 	const uint8_t *auth;
 	size_t auth_len;
@@ -3143,6 +3145,23 @@ dhcp_handledhcp(struct interface *ifp, struct bootp *bootp, size_t bootp_len,
 		}
 	}
 
+	if (has_option_mask(ifo->requestmask, DHO_IPV6_PREFERRED_ONLY)) {
+		if (get_option_uint32(ifp->ctx, &v6only_time, bootp, bootp_len,
+		    DHO_IPV6_PREFERRED_ONLY) == 0 &&
+		    (state->state == DHS_DISCOVER || state->state == DHS_REBOOT))
+		{
+			char v6msg[128];
+
+			use_v6only = true;
+			if (v6only_time < MIN_V6ONLY_WAIT)
+				v6only_time = MIN_V6ONLY_WAIT;
+			snprintf(v6msg, sizeof(v6msg),
+			    "IPv6-Only Preferred received (%u seconds)",
+			    v6only_time);
+			LOGDHCP(LOG_INFO, v6msg);
+		}
+	}
+
 	/* DHCP Auto-Configure, RFC 2563 */
 	if (type == DHCP_OFFER && bootp->yiaddr == 0) {
 		LOGDHCP(LOG_WARNING, "no address given");
@@ -3177,12 +3196,22 @@ dhcp_handledhcp(struct interface *ifp, struct bootp *bootp, size_t bootp_len,
 			}
 			eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
 			eloop_timeout_add_sec(ifp->ctx->eloop,
-			    DHCP_MAX, dhcp_discover, ifp);
+			    use_v6only ? v6only_time : DHCP_MAX,
+			    dhcp_discover, ifp);
 		}
 #endif
 		return;
 	}
 
+	if (use_v6only) {
+		dhcp_drop(ifp, "EXPIRE");
+		dhcp_unlink(ifp->ctx, state->leasefile);
+		eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
+		eloop_timeout_add_sec(ifp->ctx->eloop, v6only_time,
+		    dhcp_discover, ifp);
+		return;
+	}
+
 	/* Ensure that the address offered is valid */
 	if ((type == 0 || type == DHCP_OFFER || type == DHCP_ACK) &&
 	    (bootp->ciaddr == INADDR_ANY || bootp->ciaddr == INADDR_BROADCAST)
diff --git a/src/dhcp.h b/src/dhcp.h
index 497e2888..d0f569d5 100644
--- a/src/dhcp.h
+++ b/src/dhcp.h
@@ -116,6 +116,7 @@ enum DHO {
 	DHO_RAPIDCOMMIT            = 80,  /* RFC 4039 */
 	DHO_FQDN                   = 81,
 	DHO_AUTHENTICATION         = 90,  /* RFC 3118 */
+	DHO_IPV6_PREFERRED_ONLY    = 108, /* RFC 8925 */
 	DHO_AUTOCONFIGURE          = 116, /* RFC 2563 */
 	DHO_DNSSEARCH              = 119, /* RFC 3397 */
 	DHO_CSR                    = 121, /* RFC 3442 */
@@ -139,6 +140,8 @@ enum FQDN {
 	FQDN_BOTH       = 0x31
 };
 
+#define	MIN_V6ONLY_WAIT		300 /* seconds, RFC 8925 */
+
 /* Sizes for BOOTP options */
 #define	BOOTP_CHADDR_LEN	 16
 #define	BOOTP_SNAME_LEN		 64
diff --git a/src/dhcpcd-definitions.conf b/src/dhcpcd-definitions.conf
index d0b64314..38870091 100644
--- a/src/dhcpcd-definitions.conf
+++ b/src/dhcpcd-definitions.conf
@@ -172,6 +172,9 @@ define 101	string			tzdb_timezone
 
 # Options 102-115 are unused, RFC3679
 
+# DHCP IPv6-Only Preferred, RFC8925
+define 108	uint32			ipv6_only_preferred
+
 # DHCP Auto-Configuration, RFC2563
 define 116	byte			auto_configure
 
diff --git a/src/dhcpcd.8.in b/src/dhcpcd.8.in
index 9309250a..50964565 100644
--- a/src/dhcpcd.8.in
+++ b/src/dhcpcd.8.in
@@ -24,7 +24,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd September 2, 2020
+.Dd October 30, 2020
 .Dt DHCPCD 8
 .Os
 .Sh NAME
@@ -846,13 +846,5 @@ RFC\ 6603, RFC\ 6704, RFC\ 7217, RFC\ 7550, RFC\ 7844.
 .Sh AUTHORS
 .An Roy Marples Aq Mt roy@xxxxxxxxxxxx
 .Sh BUGS
-If
-.Nm
-is running in a
-.Xr chroot 2
-then re-opening the
-.Fl Fl logfile
-from SIGUSR2 may not work.
-.Pp
 Please report them to
 .Lk http://roy.marples.name/projects/dhcpcd
diff --git a/src/dhcpcd.c b/src/dhcpcd.c
index 9b0a79a4..169aeec1 100644
--- a/src/dhcpcd.c
+++ b/src/dhcpcd.c
@@ -1422,10 +1422,14 @@ dhcpcd_signal_cb(int sig, void *arg)
 		return;
 	case SIGUSR2:
 		loginfox(sigmsg, "SIGUSR2", "reopening log");
-		/* XXX This may not work that well in a chroot */
-		logclose();
-		if (logopen(ctx->logfile) == -1)
-			logerr(__func__);
+		if (IN_PRIVSEP(ctx)) {
+			if (ps_root_logreopen(ctx) == -1)
+				logerr("ps_root_logreopen");
+		} else {
+			logclose();
+			if (logopen(ctx->logfile) == -1)
+				logerr("logopen");
+		}
 		return;
 	case SIGCHLD:
 		while (waitpid(-1, NULL, WNOHANG) > 0)
@@ -1860,7 +1864,7 @@ main(int argc, char **argv, char **envp)
 	ctx.dhcp6_wfd = -1;
 #endif
 #ifdef PRIVSEP
-	ctx.ps_root_fd = ctx.ps_data_fd = -1;
+	ctx.ps_root_fd = ctx.ps_log_fd = ctx.ps_data_fd = -1;
 	ctx.ps_inet_fd = ctx.ps_control_fd = -1;
 	TAILQ_INIT(&ctx.ps_processes);
 #endif
@@ -2328,6 +2332,7 @@ printpidfile:
 	/* We have now forked, setsid, forked once more.
 	 * From this point on, we are the controlling daemon. */
 	ctx.options |= DHCPCD_STARTED;
+	logdebugx("spawned master process on PID %d", getpid());
 	if ((pid = pidfile_lock(ctx.pidfile)) != 0) {
 		logerr("%s: pidfile_lock %d", __func__, pid);
 #ifdef PRIVSEP
diff --git a/src/dhcpcd.conf.5.in b/src/dhcpcd.conf.5.in
index 1169ae7a..1e7a8631 100644
--- a/src/dhcpcd.conf.5.in
+++ b/src/dhcpcd.conf.5.in
@@ -24,7 +24,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd September 28, 2020
+.Dd October 25, 2020
 .Dt DHCPCD.CONF 5
 .Os
 .Sh NAME
@@ -606,18 +606,24 @@ Suppress any dhcpcd output to the console, except for errors.
 .It Ic reboot Ar seconds
 Allow
 .Ar reboot
-seconds before moving to the DISCOVER phase if we have an old lease to use
-and moving from DISCOVER to IPv4LL if no reply.
+seconds before moving to the DISCOVER phase if we have an old lease to use.
+Allow
+.Ar reboot
+seconds before starting fallback states from the DISCOVER phase.
+IPv4LL is started when the first
+.Ar reboot
+timeout is reached.
 The default is 5 seconds.
 A setting of 0 seconds causes
-.Nm dhcpcd
-to skip the REBOOT phase and go straight into DISCOVER.
+.Nm
+to skip the reboot phase and go straight into DISCOVER.
 This is desirable for mobile users because if you change from network A to
 network B and they use the same subnet and the address from network A isn't
 in use on network B, then the DHCP server will remain silent even if
 authoritative which means
 .Nm dhcpcd
 will timeout before moving back to the DISCOVER phase.
+This has no effect on DHCPv6 other than skipping the reboot phase.
 .It Ic release
 .Nm dhcpcd
 will release the lease prior to stopping the interface.
diff --git a/src/dhcpcd.h b/src/dhcpcd.h
index 8cdd3c19..97d7a322 100644
--- a/src/dhcpcd.h
+++ b/src/dhcpcd.h
@@ -199,6 +199,7 @@ struct dhcpcd_ctx {
 	struct passwd *ps_user;	/* struct passwd for privsep user */
 	pid_t ps_root_pid;
 	int ps_root_fd;		/* Privileged Actioneer commands */
+	int ps_log_fd;		/* chroot logging */
 	int ps_data_fd;		/* Data from root spawned processes */
 	struct eloop *ps_eloop;	/* eloop for polling root data */
 	struct ps_process_head ps_processes;	/* List of spawned processes */
diff --git a/src/if.c b/src/if.c
index 5f4edb86..deb5280b 100644
--- a/src/if.c
+++ b/src/if.c
@@ -198,10 +198,8 @@ if_is_link_up(const struct interface *ifp)
 {
 
 	return ifp->flags & IFF_UP &&
-	    (ifp->carrier == LINK_UP ||
-	     (ifp->carrier == LINK_UNKNOWN &&
-	      !(ifp->options == NULL ||
-	        ifp->options->options & DHCPCD_LINK)));
+	    (ifp->carrier != LINK_DOWN ||
+	     (ifp->options != NULL && !(ifp->options->options & DHCPCD_LINK)));
 }
 
 int
diff --git a/src/logerr.c b/src/logerr.c
index ad337ba0..f407b2db 100644
--- a/src/logerr.c
+++ b/src/logerr.c
@@ -47,11 +47,16 @@
 #undef LOGERR_TAG
 #endif
 
+/* syslog protocol is 1k message max, RFC 3164 section 4.1 */
+#define LOGERR_SYSLOGBUF	1024 + sizeof(int) + sizeof(pid_t)
+
 #define UNUSED(a)		(void)(a)
 
 struct logctx {
 	char		 log_buf[BUFSIZ];
 	unsigned int	 log_opts;
+	int		 log_fd;
+	pid_t		 log_pid;
 #ifndef SMALL
 	FILE		*log_file;
 #ifdef LOGERR_TAG
@@ -63,9 +68,11 @@ struct logctx {
 static struct logctx _logctx = {
 	/* syslog style, but without the hostname or tag. */
 	.log_opts = LOGERR_LOG | LOGERR_LOG_DATE | LOGERR_LOG_PID,
+	.log_fd = -1,
+	.log_pid = 0,
 };
 
-#if defined(LOGERR_TAG) && defined(__linux__)
+#if defined(__linux__)
 /* Poor man's getprogname(3). */
 static char *_logprog;
 static const char *
@@ -79,9 +86,12 @@ getprogname(void)
 		 * so zero the buffer. */
 		if ((_logprog = calloc(1, PATH_MAX + 1)) == NULL)
 			return NULL;
+		if (readlink("/proc/self/exe", _logprog, PATH_MAX + 1) == -1) {
+			free(_logprog);
+			_logprog = NULL;
+			return NULL;
+		}
 	}
-	if (readlink("/proc/self/exe", _logprog, PATH_MAX + 1) == -1)
-		return NULL;
 	if (_logprog[0] == '[')
 		return NULL;
 	p = strrchr(_logprog, '/');
@@ -147,7 +157,13 @@ vlogprintf_r(struct logctx *ctx, FILE *stream, const char *fmt, va_list args)
 	log_pid = ((stream == stderr && ctx->log_opts & LOGERR_ERR_PID) ||
 	    (stream != stderr && ctx->log_opts & LOGERR_LOG_PID));
 	if (log_pid) {
-		if ((e = fprintf(stream, "[%d]", getpid())) == -1)
+		pid_t pid;
+
+		if (ctx->log_pid == 0)
+			pid = getpid();
+		else
+			pid = ctx->log_pid;
+		if ((e = fprintf(stream, "[%d]", pid)) == -1)
 			return -1;
 		len += e;
 	}
@@ -198,22 +214,37 @@ vlogmessage(int pri, const char *fmt, va_list args)
 	struct logctx *ctx = &_logctx;
 	int len = 0;
 
+	if (ctx->log_fd != -1) {
+		char buf[LOGERR_SYSLOGBUF];
+		pid_t pid;
+
+		memcpy(buf, &pri, sizeof(pri));
+		pid = getpid();
+		memcpy(buf + sizeof(pri), &pid, sizeof(pid));
+		len = vsnprintf(buf + sizeof(pri) + sizeof(pid),
+		    sizeof(buf) - sizeof(pri) - sizeof(pid),
+		    fmt, args);
+		if (len != -1)
+			len = (int)write(ctx->log_fd, buf,
+			    ((size_t)++len) + sizeof(pri) + sizeof(pid));
+		return len;
+	}
+
 	if (ctx->log_opts & LOGERR_ERR &&
 	    (pri <= LOG_ERR ||
 	    (!(ctx->log_opts & LOGERR_QUIET) && pri <= LOG_INFO) ||
 	    (ctx->log_opts & LOGERR_DEBUG && pri <= LOG_DEBUG)))
 		len = vlogprintf_r(ctx, stderr, fmt, args);
 
-	if (!(ctx->log_opts & LOGERR_LOG))
-		return len;
-
 #ifndef SMALL
 	if (ctx->log_file != NULL &&
 	    (pri != LOG_DEBUG || (ctx->log_opts & LOGERR_DEBUG)))
 		len = vlogprintf_r(ctx, ctx->log_file, fmt, args);
 #endif
 
-	vsyslog(pri, fmt, args);
+	if (ctx->log_opts & LOGERR_LOG)
+		vsyslog(pri, fmt, args);
+
 	return len;
 }
 #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 5))
@@ -331,6 +362,54 @@ log_errx(const char *fmt, ...)
 	va_end(args);
 }
 
+int
+loggetfd(void)
+{
+	struct logctx *ctx = &_logctx;
+
+	return ctx->log_fd;
+}
+
+void
+logsetfd(int fd)
+{
+	struct logctx *ctx = &_logctx;
+
+	ctx->log_fd = fd;
+#ifndef SMALL
+	if (fd != -1 && ctx->log_file != NULL) {
+		fclose(ctx->log_file);
+		ctx->log_file = NULL;
+	}
+#endif
+}
+
+int
+logreadfd(int fd)
+{
+	struct logctx *ctx = &_logctx;
+	char buf[LOGERR_SYSLOGBUF];
+	int len, pri;
+
+	len = (int)read(fd, buf, sizeof(buf));
+	if (len == -1)
+		return -1;
+
+	/* Ensure we have pri, pid and a terminator */
+	if (len < (int)(sizeof(pri) + sizeof(pid_t) + 1) ||
+	    buf[len - 1] != '\0')
+	{
+		errno = EINVAL;
+		return -1;
+	}
+
+	memcpy(&pri, buf, sizeof(pri));
+	memcpy(&ctx->log_pid, buf + sizeof(pri), sizeof(ctx->log_pid));
+	logmessage(pri, "%s", buf + sizeof(pri) + sizeof(ctx->log_pid));
+	ctx->log_pid = 0;
+	return len;
+}
+
 unsigned int
 loggetopts(void)
 {
@@ -373,15 +452,9 @@ logopen(const char *path)
 
 	(void)setvbuf(stderr, ctx->log_buf, _IOLBF, sizeof(ctx->log_buf));
 
-	if (!(ctx->log_opts & LOGERR_LOG))
-		return 1;
-
-#ifdef LOG_NDELAY
-	opts |= LOG_NDELAY;
-#endif
 	if (ctx->log_opts & LOGERR_LOG_PID)
 		opts |= LOG_PID;
-	openlog(NULL, opts, LOGERR_SYSLOG_FACILITY);
+	openlog(getprogname(), opts, LOGERR_SYSLOG_FACILITY);
 	if (path == NULL)
 		return 1;
 
@@ -410,7 +483,7 @@ logclose(void)
 	fclose(ctx->log_file);
 	ctx->log_file = NULL;
 #endif
-#if defined(LOGERR_TAG) && defined(__linux__)
+#if defined(__linux__)
 	free(_logprog);
 #endif
 }
diff --git a/src/logerr.h b/src/logerr.h
index 4b4d6dc4..baa43362 100644
--- a/src/logerr.h
+++ b/src/logerr.h
@@ -76,6 +76,11 @@ __printflike(2, 3) void logerrmessage(int pri, const char *fmt, ...);
 #define logerr(...)	log_err(__VA_ARGS__)
 #define logerrx(...)	log_errx(__VA_ARGS__)
 
+/* For logging in a chroot */
+int loggetfd(void);
+void logsetfd(int);
+int logreadfd(int);
+
 unsigned int loggetopts(void);
 void logsetopts(unsigned int);
 #define	LOGERR_DEBUG	(1U << 6)
diff --git a/src/privsep-bpf.c b/src/privsep-bpf.c
index ddd3cd82..23da9a07 100644
--- a/src/privsep-bpf.c
+++ b/src/privsep-bpf.c
@@ -70,9 +70,21 @@ ps_bpf_recvbpf(void *arg)
 	 * This mechanism allows us to read each packet from the buffer. */
 	while (!(bpf->bpf_flags & BPF_EOF)) {
 		len = bpf_read(bpf, buf, sizeof(buf));
-		if (len == -1)
-			logerr(__func__);
-		if (len == -1 || len == 0)
+		if (len == -1) {
+			int error = errno;
+
+			logerr("%s: %s", psp->psp_ifname, __func__);
+			if (error != ENXIO)
+				break;
+			/* If the interface has departed, close the BPF
+			 * socket. This stops log spam if RTM_IFANNOUNCE is
+			 * delayed in announcing the departing interface. */
+			eloop_event_delete(psp->psp_ctx->eloop, bpf->bpf_fd);
+			bpf_close(bpf);
+			psp->psp_bpf = NULL;
+			break;
+		}
+		if (len == 0)
 			break;
 		psm.ps_flags = bpf->bpf_flags;
 		len = ps_sendpsmdata(psp->psp_ctx, psp->psp_ctx->ps_data_fd,
@@ -107,6 +119,12 @@ ps_bpf_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
 		return -1;
 	}
 
+	/* We might have had an earlier ENXIO error. */
+	if (psp->psp_bpf == NULL) {
+		errno = ENXIO;
+		return -1;
+	}
+
 	return bpf_send(psp->psp_bpf, psp->psp_proto,
 	    iov->iov_base, iov->iov_len);
 }
diff --git a/src/privsep-linux.c b/src/privsep-linux.c
index 5d35ae2c..01f4975c 100644
--- a/src/privsep-linux.c
+++ b/src/privsep-linux.c
@@ -39,6 +39,7 @@
 #include <fcntl.h>
 #include <stddef.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
@@ -47,6 +48,14 @@
 #include "logerr.h"
 #include "privsep.h"
 
+/*
+ * Set this to debug SECCOMP.
+ * Then run dhcpcd with strace -f and strace will even translate
+ * the failing syscall into the __NR_name define we need to use below.
+ * DO NOT ENABLE THIS FOR PRODUCTION BUILDS!
+ */
+//#define SECCOMP_FILTER_DEBUG
+
 static ssize_t
 ps_root_dosendnetlink(int protocol, struct msghdr *msg)
 {
@@ -125,7 +134,11 @@ ps_root_sendnetlink(struct dhcpcd_ctx *ctx, int protocol, struct msghdr *msg)
 	BPF_STMT(BPF_LD + BPF_W + BPF_ABS,				\
 		offsetof(struct seccomp_data, nr))
 
+#ifdef SECCOMP_FILTER_DEBUG
+#define SECCOMP_FILTER_FAIL	SECCOMP_RET_TRAP
+#else
 #define SECCOMP_FILTER_FAIL	SECCOMP_RET_KILL
+#endif
 
 /* I personally find this quite nutty.
  * Why can a system header not define a default for this? */
@@ -247,6 +260,9 @@ static struct sock_filter ps_seccomp_filter[] = {
 #ifdef __NR_munmap
 	SECCOMP_ALLOW(__NR_munmap),
 #endif
+#ifdef __NR_nanosleep
+	SECCOMP_ALLOW(__NR_nanosleep),	/* XXX should use ppoll instead */
+#endif
 #ifdef __NR_ppoll
 	SECCOMP_ALLOW(__NR_ppoll),
 #endif
@@ -259,6 +275,9 @@ static struct sock_filter ps_seccomp_filter[] = {
 #ifdef __NR_readv
 	SECCOMP_ALLOW(__NR_readv),
 #endif
+#ifdef __NR_recv
+	SECCOMP_ALLOW(__NR_recv),
+#endif
 #ifdef __NR_recvfrom
 	SECCOMP_ALLOW(__NR_recvfrom),
 #endif
@@ -268,6 +287,9 @@ static struct sock_filter ps_seccomp_filter[] = {
 #ifdef __NR_rt_sigreturn
 	SECCOMP_ALLOW(__NR_rt_sigreturn),
 #endif
+#ifdef __NR_send
+	SECCOMP_ALLOW(__NR_send),
+#endif
 #ifdef __NR_sendmsg
 	SECCOMP_ALLOW(__NR_sendmsg),
 #endif
@@ -277,9 +299,15 @@ static struct sock_filter ps_seccomp_filter[] = {
 #ifdef __NR_shutdown
 	SECCOMP_ALLOW(__NR_shutdown),
 #endif
+#ifdef __NR_time
+	SECCOMP_ALLOW(__NR_time),
+#endif
 #ifdef __NR_wait4
 	SECCOMP_ALLOW(__NR_wait4),
 #endif
+#ifdef __NR_waitpid
+	SECCOMP_ALLOW(__NR_waitpid),
+#endif
 #ifdef __NR_write
 	SECCOMP_ALLOW(__NR_write),
 #endif
@@ -299,10 +327,44 @@ static struct sock_fprog ps_seccomp_prog = {
 	.filter = ps_seccomp_filter,
 };
 
+#ifdef SECCOMP_FILTER_DEBUG
+static void
+ps_seccomp_violation(__unused int signum, siginfo_t *si, __unused void *context)
+{
+
+	logerrx("%s: unexpected syscall %d (arch=0x%x)",
+	    __func__, si->si_syscall, si->si_arch);
+	_exit(EXIT_FAILURE);
+}
+
+static int
+ps_seccomp_debug(void)
+{
+	struct sigaction sa = {
+		.sa_flags = SA_SIGINFO,
+		.sa_sigaction = &ps_seccomp_violation,
+	};
+	sigset_t mask;
+
+	/* Install a signal handler to catch any issues with our filter. */
+	sigemptyset(&mask);
+	sigaddset(&mask, SIGSYS);
+	if (sigaction(SIGSYS, &sa, NULL) == -1 ||
+	    sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1)
+		return -1;
+
+	return 0;
+}
+#endif
+
 int
 ps_seccomp_enter(void)
 {
 
+#ifdef SECCOMP_FILTER_DEBUG
+	ps_seccomp_debug();
+#endif
+
 	if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1 ||
 	    prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &ps_seccomp_prog) == -1)
 	{
diff --git a/src/privsep-root.c b/src/privsep-root.c
index 770dd953..afe9c8f6 100644
--- a/src/privsep-root.c
+++ b/src/privsep-root.c
@@ -561,6 +561,10 @@ ps_root_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
 			rlen = sizeof(mtime);
 		}
 		break;
+	case PS_LOGREOPEN:
+		logclose();
+		err = logopen(ctx->logfile);
+		break;
 #ifdef AUTH
 	case PS_AUTH_MONORDM:
 		err = ps_root_monordm(data, len);
@@ -780,18 +784,34 @@ ps_root_dispatch(void *arg)
 		logerr(__func__);
 }
 
+static void
+ps_root_log(void *arg)
+{
+	struct dhcpcd_ctx *ctx = arg;
+
+	if (logreadfd(ctx->ps_log_fd) == -1)
+		logerr(__func__);
+}
+
 pid_t
 ps_root_start(struct dhcpcd_ctx *ctx)
 {
-	int fd[2];
+	int logfd[2], datafd[2];
 	pid_t pid;
 
-	if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CXNB, 0, fd) == -1)
+	if (xsocketpair(AF_UNIX, SOCK_DGRAM | SOCK_CXNB, 0, logfd) == -1)
 		return -1;
-	if (ps_setbuf_fdpair(fd) == -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)
+		return -1;
+	if (ps_setbuf_fdpair(datafd) == -1)
 		return -1;
 #ifdef PRIVSEP_RIGHTS
-	if (ps_rights_limit_fdpair(fd) == -1)
+	if (ps_rights_limit_fdpair(datafd) == -1)
 		return -1;
 #endif
 
@@ -800,14 +820,22 @@ ps_root_start(struct dhcpcd_ctx *ctx)
 	    ps_root_startcb, ps_root_signalcb, 0);
 
 	if (pid == 0) {
-		ctx->ps_data_fd = fd[1];
-		close(fd[0]);
+		ctx->ps_log_fd = logfd[1];
+		if (eloop_event_add(ctx->eloop, ctx->ps_log_fd,
+		    ps_root_log, ctx) == -1)
+			return -1;
+		close(logfd[0]);
+		ctx->ps_data_fd = datafd[1];
+		close(datafd[0]);
 		return 0;
 	} else if (pid == -1)
 		return -1;
 
-	ctx->ps_data_fd = fd[0];
-	close(fd[1]);
+	logsetfd(logfd[0]);
+	close(logfd[1]);
+
+	ctx->ps_data_fd = datafd[0];
+	close(datafd[1]);
 	if (eloop_event_add(ctx->eloop, ctx->ps_data_fd,
 	    ps_root_dispatch, ctx) == -1)
 		return -1;
@@ -906,6 +934,15 @@ ps_root_filemtime(struct dhcpcd_ctx *ctx, const char *file, time_t *time)
 	return ps_root_readerror(ctx, time, sizeof(*time));
 }
 
+ssize_t
+ps_root_logreopen(struct dhcpcd_ctx *ctx)
+{
+
+	if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_LOGREOPEN, 0, NULL, 0) == -1)
+		return -1;
+	return ps_root_readerror(ctx, NULL, 0);
+}
+
 #ifdef PRIVSEP_GETIFADDRS
 int
 ps_root_getifaddrs(struct dhcpcd_ctx *ctx, struct ifaddrs **ifahead)
diff --git a/src/privsep-root.h b/src/privsep-root.h
index 146622b1..9d8a1597 100644
--- a/src/privsep-root.h
+++ b/src/privsep-root.h
@@ -47,6 +47,7 @@ ssize_t ps_root_filemtime(struct dhcpcd_ctx *, const char *, time_t *);
 ssize_t ps_root_readfile(struct dhcpcd_ctx *, const char *, void *, size_t);
 ssize_t ps_root_writefile(struct dhcpcd_ctx *, const char *, mode_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);
 int ps_root_getauthrdm(struct dhcpcd_ctx *, uint64_t *);
 #ifdef PRIVSEP_GETIFADDRS
diff --git a/src/privsep-sun.c b/src/privsep-sun.c
index f406f4ee..2c5d586a 100644
--- a/src/privsep-sun.c
+++ b/src/privsep-sun.c
@@ -37,6 +37,8 @@
 
 #warning Solaris privsep should compile but wont work,
 #warning no DLPI support, ioctl support need rework
+/* We should implement privileges(5) as well.
+ * https://illumos.org/man/5/privileges */
 
 static ssize_t
 ps_root_doioctl6(unsigned long req, void *data, size_t len)
diff --git a/src/privsep.c b/src/privsep.c
index e9fa463f..6e76902a 100644
--- a/src/privsep.c
+++ b/src/privsep.c
@@ -542,6 +542,19 @@ ps_mastersandbox(struct dhcpcd_ctx *ctx, const char *_pledge)
 	dropped = ps_dropprivs(ctx);
 	if (forked)
 		ctx->options |= DHCPCD_FORKED;
+
+	/*
+	 * If we don't have a root process, we cannot use syslog.
+	 * 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) {
+		unsigned int logopts = loggetopts();
+
+		logopts &= ~LOGERR_LOG;
+		logsetopts(logopts);
+	}
+
 	if (dropped == -1) {
 		logerr("%s: ps_dropprivs", __func__);
 		return -1;
diff --git a/src/privsep.h b/src/privsep.h
index 4fd33e7f..87c84eb9 100644
--- a/src/privsep.h
+++ b/src/privsep.h
@@ -52,6 +52,7 @@
 #define	PS_AUTH_MONORDM		0x0017
 #define	PS_CTL			0x0018
 #define	PS_CTL_EOF		0x0019
+#define	PS_LOGREOPEN		0x0020
 
 /* BSD Commands */
 #define	PS_IOCTLLINK		0x0101

Follow-Ups:
Re: dhcpcd 9.3.1 timing out on Arch Linux ARMAdolf Belka
References:
dhcpcd 9.3.1 timing out on Arch Linux ARMAdolf Belka
Re: dhcpcd 9.3.1 timing out on Arch Linux ARMRoy Marples
Re: dhcpcd 9.3.1 timing out on Arch Linux ARMAdolf Belka
Archive administrator: postmaster@marples.name