macOS does not support SOCK_SEQPACKET.
All our messages use a fixed header which includes the
lengths of all parts sent.
We can use these limits with MSG_WAITALL on blocking sockets
in place of MSG_EOR to get the same effect.
Start of the work for #524.
eloop_event_delete(ctx->eloop, ctx->fork_fd);
exit_code = EXIT_SUCCESS;
- if (send(ctx->fork_fd, &exit_code, sizeof(exit_code), MSG_EOR) == -1)
+ if (send(ctx->fork_fd, &exit_code, sizeof(exit_code), 0) == -1)
logerr(__func__);
close(ctx->fork_fd);
ctx->fork_fd = -1;
if (sig != SIGCHLD && ctx->options & DHCPCD_FORKED) {
if (ctx->fork_fd != -1 && sig != SIGHUP &&
- send(ctx->fork_fd, &sig, sizeof(sig), MSG_EOR) == -1)
+ send(ctx->fork_fd, &sig, sizeof(sig), 0) == -1)
logerr("%s: send", __func__);
return;
}
if (!(events & ELE_READ))
logerrx("%s: unexpected event 0x%04x", __func__, events);
- len = read(ctx->fork_fd, &exit_code, sizeof(exit_code));
+ len = recv(ctx->fork_fd, &exit_code, sizeof(exit_code), MSG_WAITALL);
if (len == -1) {
logerr(__func__);
eloop_exit(ctx->eloop, EXIT_FAILURE);
if (!(ctx.options & DHCPCD_DAEMONISE))
goto start_manager;
- if (xsocketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CXNB, 0, fork_fd) ==
+ if (xsocketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, fork_fd) ==
-1) {
logerr("socketpair");
goto exit_failure;
#ifdef USE_SIGNALS
/* If still attached, detach from the launcher */
if (ctx.options & DHCPCD_STARTED && ctx.fork_fd != -1) {
- if (send(ctx.fork_fd, &i, sizeof(i), MSG_EOR) == -1)
+ if (send(ctx.fork_fd, &i, sizeof(i), 0) == -1)
logerr("%s: send", __func__);
}
#endif
if (ctx->log_fd != -1) {
pid_t pid = getpid();
char buf[LOGERR_SYSLOGBUF];
+ size_t mlen = 0;
struct iovec iov[] = {
{ .iov_base = &pri, .iov_len = sizeof(pri) },
{ .iov_base = &pid, .iov_len = sizeof(pid) },
+ { .iov_base = &mlen, .iov_len = sizeof(mlen) },
{ .iov_base = buf },
};
len = vsnprintf(buf, sizeof(buf), fmt, args);
if (len != -1) {
- if ((size_t)len >= sizeof(buf))
- len = (int)sizeof(buf) - 1;
- iov[2].iov_len = (size_t)(len + 1);
+ mlen = (size_t)len + 1;
+ if (mlen > sizeof(buf))
+ mlen = sizeof(buf);
+ iov[3].iov_len = mlen;
struct msghdr msg = {
.msg_iov = iov,
.msg_iovlen = sizeof(iov) / sizeof(iov[0]),
};
- len = (int)sendmsg(ctx->log_fd, &msg, MSG_EOR);
+ len = (int)sendmsg(ctx->log_fd, &msg, 0);
}
return len;
}
int pri;
pid_t pid;
char buf[LOGERR_SYSLOGBUF] = { '\0' };
+ size_t mlen;
struct iovec iov[] = {
{ .iov_base = &pri, .iov_len = sizeof(pri) },
{ .iov_base = &pid, .iov_len = sizeof(pid) },
- { .iov_base = buf, .iov_len = sizeof(buf) },
+ { .iov_base = &mlen, .iov_len = sizeof(mlen) },
};
struct msghdr msg = { .msg_iov = iov,
.msg_iovlen = sizeof(iov) / sizeof(iov[0]) };
len = recvmsg(fd, &msg, MSG_WAITALL);
if (len == -1 || len == 0)
return len;
- /* Ensure we received the minimum and at least one character to log */
- if ((size_t)len < sizeof(pri) + sizeof(pid) + 1 ||
- msg.msg_flags & MSG_TRUNC) {
+ if ((size_t)len != sizeof(pri) + sizeof(pid) + sizeof(mlen)) {
errno = EMSGSIZE;
return -1;
}
+ if (mlen > sizeof(buf)) {
+ errno = ENOBUFS;
+ return -1;
+ }
+
+ len = recv(fd, buf, mlen, MSG_WAITALL);
+ if (len == -1)
+ return -1;
+ if ((size_t)len != mlen) {
+ errno = EINVAL;
+ return -1;
+ }
+
/* Ensure what we receive is NUL terminated */
- buf[(size_t)len - (sizeof(pri) + sizeof(pid)) - 1] = '\0';
+ buf[mlen == sizeof(buf) ? mlen - 1 : mlen] = '\0';
ctx->log_pid = pid;
logmessage(pri, "%s", buf);
#define logerr(...) log_err(__VA_ARGS__)
#define logerrx(...) log_errx(__VA_ARGS__)
-/* For logging in a chroot using SOCK_SEQPACKET */
+/* For logging in a chroot using SOCK_STREAM */
int loggetfd(void);
void logsetfd(int);
ssize_t logreadfd(int);
#include "logerr.h"
#include "privsep.h"
-/* We expect to have open 3 SEQPACKET and one RAW fd */
+/* We expect to have open 3 SOCK_STREAM and one RAW fd */
static void
ps_bpf_recvbpf(void *arg, unsigned short events)
#include "logerr.h"
#include "privsep.h"
-/* We expect to have open 2 SEQPACKET, 2 STREAM and 2 file STREAM fds */
+/* We expect to have open 2 privsep STREAM, 2 STREAM and 2 file STREAM fds */
static int
ps_ctl_startcb(struct ps_process *psp)
#include "logerr.h"
#include "privsep.h"
-/* We expect to have open 2 SEQPACKET, 1 udp, 1 udp6 and 1 raw6 fds */
+/* We expect to have open 2 SOCK_STREAM, 1 udp, 1 udp6 and 1 raw6 fds */
#ifdef INET
static void
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
+#include <poll.h>
#include <pwd.h>
#include <signal.h>
#include <stddef.h>
struct dhcpcd_ctx *ctx = pc->psr_ctx;
int fd = PS_ROOT_FD(ctx);
struct psr_error *psr_error = &pc->psr_error;
- struct iovec iov[] = {
- { .iov_base = psr_error, .iov_len = sizeof(*psr_error) },
- { .iov_base = pc->psr_data, .iov_len = pc->psr_datalen },
- };
- struct msghdr msg = { .msg_iov = iov, .msg_iovlen = __arraycount(iov) };
ssize_t len;
#define PSR_ERROR(e) \
if (eloop_waitfd(fd) == -1)
PSR_ERROR(errno);
- if (!pc->psr_mallocdata)
- goto recv;
-
- /* We peek at the psr_error structure to tell us how much of a buffer
- * we need to read the whole packet. */
- msg.msg_iovlen--;
- len = recvmsg(fd, &msg, MSG_PEEK | MSG_WAITALL);
+ len = recv(fd, psr_error, sizeof(*psr_error), MSG_WAITALL);
if (len == -1)
PSR_ERROR(errno);
- /* After this point, we MUST do another recvmsg even on a failure
- * to remove the message after peeking. */
- if ((size_t)len < sizeof(*psr_error)) {
- /* We can't use the header to work out buffers, so
- * remove the message and bail. */
- (void)recvmsg(fd, &msg, MSG_WAITALL);
+ if ((size_t)len < sizeof(*psr_error))
PSR_ERROR(EINVAL);
- }
- /* No data to read? Unlikely but ... */
if (psr_error->psr_datalen == 0)
- goto recv;
+ return len;
- pc->psr_data = malloc(psr_error->psr_datalen);
- if (pc->psr_data != NULL) {
- iov[1].iov_base = pc->psr_data;
- iov[1].iov_len = psr_error->psr_datalen;
- msg.msg_iovlen++;
- }
+ if (pc->psr_mallocdata) {
+ pc->psr_data = malloc(psr_error->psr_datalen);
+ if (pc->psr_data == NULL)
+ PSR_ERROR(errno);
+ } else if (psr_error->psr_datalen > pc->psr_datalen)
+ PSR_ERROR(EMSGSIZE);
-recv:
- len = recvmsg(fd, &msg, MSG_WAITALL);
+ len = recv(fd, pc->psr_data, psr_error->psr_datalen, MSG_WAITALL);
if (len == -1)
PSR_ERROR(errno);
- else if ((size_t)len < sizeof(*psr_error))
- PSR_ERROR(EINVAL);
- else if (msg.msg_flags & MSG_TRUNC)
- PSR_ERROR(ENOBUFS);
- else if ((size_t)len != sizeof(*psr_error) + psr_error->psr_datalen) {
+ else if ((size_t)len != psr_error->psr_datalen) {
#ifdef PRIVSEP_DEBUG
logerrx("%s: recvmsg returned %zd, expecting %zu", __func__,
len, sizeof(*psr_error) + psr_error->psr_datalen);
if (len == 0)
msg.msg_iovlen = 1;
- err = sendmsg(fd, &msg, MSG_EOR);
+ err = sendmsg(fd, &msg, 0);
/* Error sending the message? Try sending the error of sending. */
if (err == -1 && errno != EPIPE) {
psr.psr_errno = errno;
psr.psr_datalen = 0;
msg.msg_iovlen = 1;
- err = sendmsg(fd, &msg, MSG_EOR);
+ err = sendmsg(fd, &msg, 0);
}
return err;
int logfd[2] = { -1, -1 }, datafd[2] = { -1, -1 };
pid_t pid;
- if (xsocketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CXNB, 0, logfd) == -1)
+ if (xsocketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, logfd) == -1)
return -1;
#ifdef PRIVSEP_RIGHTS
if (ps_rights_limit_fdpair(logfd) == -1)
return -1;
#endif
- if (xsocketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CXNB, 0, datafd) == -1)
+ if (xsocketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, datafd) == -1)
return -1;
if (ps_setbuf_fdpair(datafd) == -1)
/* drain the log */
if (ctx->ps_log_root_fd != -1) {
ssize_t loglen;
-
-#ifdef __linux__
- /* Seems to help to get the last parts,
- * sched_yield(2) does not. */
- sleep(0);
-#endif
- do {
+ struct pollfd pfd = {
+ .fd = ctx->ps_log_root_fd,
+ .events = POLLIN
+ };
+ int n;
+
+ /* the socket is blocking and we may not be able to
+ * change it to non blocking, so poll for data */
+ for (;;) {
+ n = poll(&pfd, 1, 1);
+ if (n == -1 || n == 0)
+ break;
loglen = logreadfd(ctx->ps_log_root_fd);
- } while (loglen != 0 && loglen != -1);
- close(ctx->ps_log_root_fd);
- ctx->ps_log_root_fd = -1;
+ if (loglen == -1 || loglen == 0)
+ break;
+ }
}
}
+ if (ctx->ps_log_root_fd != -1) {
+ close(ctx->ps_log_root_fd);
+ ctx->ps_log_root_fd = -1;
+ }
+
if (ctx->ps_data_fd != -1) {
eloop_event_delete(ctx->eloop, ctx->ps_data_fd);
close(ctx->ps_data_fd);
}
/* Only the manager process gets past this point. */
- if (ctx->options & DHCPCD_FORKED)
- return 0;
+ if (ctx->options & DHCPCD_FORKED) {
+ err = 0;
+ goto out;
+ }
/* We cannot log the root process exited before we
* log dhcpcd exits because the latter requires the former.
} /* else the root process has already exited :( */
err = ps_stopwait(ctx);
+out:
if (ctx->ps_root != NULL)
ps_freeprocess(ctx->ps_root);
return err;
int fd[2];
pid_t pid;
- if (xsocketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CXNB, 0, fd) == -1) {
+ if (xsocketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, fd) == -1) {
logerr("%s: socketpair", __func__);
return -1;
}
psm->ps_namelen = msg->msg_namelen;
psm->ps_controllen = (socklen_t)msg->msg_controllen;
+ psm->ps_datalen = 0;
iovp->iov_base = msg->msg_name;
iovp->iov_len = msg->msg_namelen;
m.msg_iovlen++;
for (i = 0; i < (int)msg->msg_iovlen; i++) {
- if ((size_t)(m.msg_iovlen++) > __arraycount(iov)) {
+ if ((size_t)m.msg_iovlen >= __arraycount(iov)) {
errno = ENOBUFS;
return -1;
}
+ m.msg_iovlen++;
iovp->iov_base = msg->msg_iov[i].iov_base;
iovp->iov_len = msg->msg_iov[i].iov_len;
iovp++;
+ psm->ps_datalen += msg->msg_iov[i].iov_len;
}
}
- len = sendmsg(fd, &m, MSG_EOR);
+ len = sendmsg(fd, &m, 0);
if (len == -1 && ctx != NULL) {
if (ctx->options & DHCPCD_FORKED &&
ssize_t (*callback)(void *, struct ps_msghdr *, struct msghdr *),
void *cbctx)
{
- struct ps_msg psm;
+ struct ps_msghdr psm;
+ uint8_t ps_data[PS_BUFLEN];
ssize_t len;
size_t dlen;
struct iovec iov[1];
struct msghdr msg = { .msg_iov = iov, .msg_iovlen = 1 };
bool stop = false;
+ socklen_t cmsg_padlen;
if (events & ELE_HANGUP) {
len = 0;
if (!(events & ELE_READ))
logerrx("%s: unexpected event 0x%04x", __func__, events);
- len = read(fd, &psm, sizeof(psm));
+ len = recv(fd, &psm, sizeof(psm), MSG_WAITALL);
#ifdef PRIVSEP_DEBUG
- logdebugx("%s: fd=%d %zd", __func__, fd, len);
+ logdebugx("%s: pid=%d fd=%d len=%zd", __func__, getpid(), fd, len);
#endif
if (len == -1 || len == 0)
stop = true;
else {
dlen = (size_t)len;
- if (dlen < sizeof(psm.psm_hdr)) {
+ if (dlen < sizeof(psm)) {
errno = EINVAL;
- return -1;
+ goto stop;
}
- if (psm.psm_hdr.ps_cmd == PS_STOP) {
+ if (psm.ps_cmd == PS_STOP) {
stop = true;
len = 0;
- } else if (psm.psm_hdr.ps_cmd == PS_DAEMONISED) {
+ } else if (psm.ps_cmd == PS_DAEMONISED) {
ps_daemonised(ctx);
return 0;
}
eloop_exit(ctx->eloop, len != -1 ? EXIT_SUCCESS : EXIT_FAILURE);
return len;
}
- dlen -= sizeof(psm.psm_hdr);
- if (ps_unrollmsg(&msg, &psm.psm_hdr, psm.psm_data, dlen) == -1)
+ cmsg_padlen = CALC_CMSG_PADLEN(psm.ps_controllen,
+ psm.ps_namelen);
+ dlen = psm.ps_namelen + psm.ps_controllen + cmsg_padlen + psm.ps_datalen;
+ if (dlen != 0) {
+ if (dlen > sizeof(ps_data)) {
+ errno = EMSGSIZE;
+ goto stop;
+ }
+ len = recv(fd, ps_data, dlen, MSG_WAITALL);
+ if ((size_t)len != dlen) {
+ errno = EINVAL;
+ goto stop;
+ }
+ }
+
+ if (ps_unrollmsg(&msg, &psm, ps_data, dlen) == -1)
return -1;
if (callback == NULL)
return 0;
errno = 0;
- return callback(cbctx, &psm.psm_hdr, &msg);
+ return callback(cbctx, &psm, &msg);
}
struct ps_process *