]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Fix the control socket for dhcpcd-ui
authorRoy Marples <roy@marples.name>
Tue, 14 Jul 2026 14:01:47 +0000 (15:01 +0100)
committerRoy Marples <roy@marples.name>
Tue, 14 Jul 2026 14:01:47 +0000 (15:01 +0100)
Add --isprivileged to return either true or false so the
caller can work this out as we now just have the one socket.
Bump version for this.

src/control.c
src/control.h
src/defs.h
src/dhcpcd.c
src/privsep-control.c

index 8afc4f6253014130609d57173c8a842364d8d568..473d6f37f9b0760f99fa9a42406b78614f18f5d2 100644 (file)
@@ -133,7 +133,7 @@ getpeereid(int fd, uid_t *uid, gid_t *gid)
 }
 #endif
 
-static int
+static ssize_t
 control_handle_read(struct fd_list *fd)
 {
        uid_t uid;
@@ -153,7 +153,7 @@ control_handle_read(struct fd_list *fd)
        if (bytes == -1)
                logerr(__func__);
        if (bytes == -1 || bytes == 0)
-               return (int)bytes;
+               return bytes;
 
        if (getpeereid(fd->fd, &uid, &gid) == -1) {
                logerr("%s: getpeereid", __func__);
@@ -177,12 +177,14 @@ control_handle_read(struct fd_list *fd)
                fd->flags |= FD_SENDLEN;
                perr = ps_ctl_handleargs(fd, buf, (size_t)bytes);
                fd->flags &= ~FD_SENDLEN;
+               if (!(fd->flags & FD_LISTEN))
+                       fd->flags |= FD_COMMAND;
                if (perr == -1) {
                        logerr(__func__);
                        return 0;
                }
                iov[0].iov_len = (size_t)bytes;
-               if (perr == 1 && ps_ctl_sendmsg(fd, &msg) == -1) {
+               if (perr == 0 && ps_ctl_sendmsg(fd, &msg) == -1) {
                        logerr(__func__);
                        return -1;
                }
@@ -202,28 +204,30 @@ control_handle_read(struct fd_list *fd)
        return control_recvmsg(fd, &msg, (size_t)bytes);
 }
 
-static int
+static ssize_t
 control_handle_write(struct fd_list *fd)
 {
        struct iovec iov[2];
-       int iov_len;
+       struct msghdr msg = { .msg_iov = iov };
        struct fd_data *data;
+       ssize_t len;
 
        data = TAILQ_FIRST(&fd->queue);
 
        if (data->data_flags & FD_SENDLEN) {
                iov[0].iov_base = &data->data_len;
-               iov[0].iov_len = sizeof(size_t);
+               iov[0].iov_len = sizeof(data->data_len);
                iov[1].iov_base = data->data;
                iov[1].iov_len = data->data_len;
-               iov_len = 2;
+               msg.msg_iovlen = 2;
        } else {
                iov[0].iov_base = data->data;
                iov[0].iov_len = data->data_len;
-               iov_len = 1;
+               msg.msg_iovlen = 1;
        }
 
-       if (writev(fd->fd, iov, iov_len) == -1) {
+       len = sendmsg(fd->fd, &msg, 0);
+       if (len == -1) {
                if (errno != EPIPE && errno != ENOTCONN) {
                        // We don't get ELE_HANGUP for some reason
                        logerr("%s: write", __func__);
@@ -242,20 +246,23 @@ control_handle_write(struct fd_list *fd)
 #endif
 
        if (TAILQ_FIRST(&fd->queue) != NULL)
-               return 0;
+               return len;
 
        /* Done sending data, stop watching write to fd */
        if (eloop_event_add(fd->ctx->eloop, fd->fd, ELE_READ,
                control_handle_data, fd) == -1)
                logerr("%s: eloop_event_add", __func__);
-       return 0;
+       return len;
 }
 
 static void
 control_handle_data(void *arg, unsigned short events)
 {
        struct fd_list *fd = arg;
-       int err;
+       ssize_t err;
+
+       if (events & ELE_HANGUP)
+               goto hangup;
 
        if (!(events & (ELE_READ | ELE_WRITE | ELE_HANGUP)))
                logerrx("%s: unexpected event 0x%04x", __func__, events);
@@ -270,8 +277,6 @@ control_handle_data(void *arg, unsigned short events)
                if ((err == -1 && errno != EPERM) || err == 0)
                        goto hangup;
        }
-       if (events & ELE_HANGUP)
-               goto hangup;
 
        return;
 
@@ -339,6 +344,8 @@ control_recvmsg(struct fd_list *fd, struct msghdr *msg, size_t len)
                        if (errno != EINTR && errno != EAGAIN && errno != EPERM)
                                return -1;
                }
+               if (!(fd->flags & FD_LISTEN))
+                       fd->flags |= FD_COMMAND;
        }
 
        return 1;
@@ -412,7 +419,6 @@ control_handle1(struct dhcpcd_ctx *ctx, int lfd, unsigned int fd_flags,
        else
 #endif
                fd_flags |= FD_SENDLEN;
-
        l = control_new(ctx, fd, fd_flags);
        if (l == NULL)
                goto error;
@@ -618,7 +624,7 @@ control_send(struct dhcpcd_ctx *ctx, int argc, char *const *argv)
 }
 
 int
-control_queue(struct fd_list *fd, void *data, size_t data_len)
+control_queue(struct fd_list *fd, const void *data, size_t data_len)
 {
        struct fd_data *d;
        unsigned short events;
index 850860256b87a7d0b3c72b1cb69fde76b7178bee..fd8d64b78b6fe28dab2d31d65d0bd6c7ba767b25 100644 (file)
@@ -66,8 +66,9 @@ struct fd_list {
 TAILQ_HEAD(fd_list_head, fd_list);
 
 #define FD_LISTEN  0x01U
-#define FD_PRIV           0x02U
-#define FD_SENDLEN 0x04U
+#define FD_COMMAND 0x02U
+#define FD_PRIV           0x04U
+#define FD_SENDLEN 0x08U
 
 int control_start(struct dhcpcd_ctx *, const char *, sa_family_t);
 int control_stop(struct dhcpcd_ctx *);
@@ -77,7 +78,7 @@ struct fd_list *control_find(struct dhcpcd_ctx *, int);
 struct fd_list *control_new(struct dhcpcd_ctx *, int, unsigned int);
 void control_free(struct fd_list *);
 void control_delete(struct fd_list *);
-int control_queue(struct fd_list *, void *, size_t);
+int control_queue(struct fd_list *, const void *, size_t);
 int control_recvmsg(struct fd_list *, struct msghdr *, size_t);
 int control_user_ispriv(struct dhcpcd_ctx *ctx, uid_t uid, gid_t gid);
 #endif
index d21a8faeb5b0ec09d57040b2d95a810f054b6810..61f8057986dc36c1375c4b557b39d34a0f4061b0 100644 (file)
@@ -29,7 +29,7 @@
 #define DEFS_H
 
 #define PACKAGE "dhcpcd"
-#define VERSION "10.3.2"
+#define VERSION "10.5.0"
 
 #ifndef PRIVSEP_USER
 #define PRIVSEP_USER "_" PACKAGE
index f45c4f38d5da691cf07293a50049247209fde2c2..d3b2bbeaaf53f1b20773773aa621d77929933a01 100644 (file)
@@ -1587,24 +1587,6 @@ dhcpcd_handleargs(struct dhcpcd_ctx *ctx, struct fd_list *fd, int argc,
        size_t len, l, nifaces;
        char *tmp, *p;
 
-       /* Special commands for our control socket
-        * as the other end should be blocking until it gets the
-        * expected reply we should be safely able just to change the
-        * write callback on the fd */
-       /* Make any change here in privsep-control.c as well. */
-       if (strcmp(*argv, "--version") == 0) {
-               return control_queue(fd, UNCONST(VERSION), strlen(VERSION) + 1);
-       } else if (strcmp(*argv, "--getconfigfile") == 0) {
-               return control_queue(fd, UNCONST(fd->ctx->cffile),
-                   strlen(fd->ctx->cffile) + 1);
-       } else if (strcmp(*argv, "--getinterfaces") == 0) {
-               oifind = argc = 0;
-               goto dumplease;
-       } else if (strcmp(*argv, "--listen") == 0) {
-               fd->flags |= FD_LISTEN;
-               return 0;
-       }
-
        /* Log the command */
        len = 1;
        for (opt = 0; opt < argc; opt++)
@@ -1624,6 +1606,27 @@ dhcpcd_handleargs(struct dhcpcd_ctx *ctx, struct fd_list *fd, int argc,
        loginfox("control command: %s", tmp);
        free(tmp);
 
+       /* Special commands for our control socket
+        * as the other end should be blocking until it gets the
+        * expected reply we should be safely able just to change the
+        * write callback on the fd */
+       /* Make any change here in privsep-control.c as well. */
+       if (strcmp(*argv, "--version") == 0) {
+               return control_queue(fd, VERSION, strlen(VERSION) + 1);
+       } else if (strcmp(*argv, "--getconfigfile") == 0) {
+               return control_queue(fd, fd->ctx->cffile,
+                   strlen(fd->ctx->cffile) + 1);
+       } else if (strcmp(*argv, "--getinterfaces") == 0) {
+               oifind = argc = 0;
+               goto dumplease;
+       } else if (strcmp(*argv, "--isprivileged") == 0) {
+               const char *ret = fd->flags & FD_PRIV ? "true" : "false";
+               return control_queue(fd, ret, strlen(ret) + 1);
+       } else if (strcmp(*argv, "--listen") == 0) {
+               fd->flags |= FD_LISTEN;
+               return 0;
+       }
+
        optind = 0;
        oi = 0;
        opts = 0;
@@ -1682,8 +1685,10 @@ dhcpcd_handleargs(struct dhcpcd_ctx *ctx, struct fd_list *fd, int argc,
                                nifaces += (size_t)opt;
                        }
                }
-               if (write(fd->fd, &nifaces, sizeof(nifaces)) != sizeof(nifaces))
+               fd->flags &= ~FD_SENDLEN;
+               if (control_queue(fd, &nifaces, sizeof(nifaces)) == -1)
                        goto dumperr;
+               fd->flags |= FD_SENDLEN;
                TAILQ_FOREACH(ifp, ctx->ifaces, next) {
                        if (!ifp->active)
                                continue;
index cb27de1bdf56003e718fb3bd8b6032bf2ace784e..468270b84963baf9452f80aac7ea22a92bcf0397 100644 (file)
@@ -83,19 +83,26 @@ ps_ctl_recvmsg(void *arg, unsigned short events)
 ssize_t
 ps_ctl_handleargs(struct fd_list *fd, char *data, size_t len)
 {
+       int err;
+
        /* Make any change here in dhcpcd.c as well. */
        if (strncmp(data, "--version", MIN(strlen("--version"), len)) == 0) {
-               return control_queue(fd, UNCONST(VERSION), strlen(VERSION) + 1);
+               err = control_queue(fd, UNCONST(VERSION), strlen(VERSION) + 1);
+               return err == -1 ? -1 : 1;
        } else if (strncmp(data, "--getconfigfile",
                       MIN(strlen("--getconfigfile"), len)) == 0) {
-               return control_queue(fd, UNCONST(fd->ctx->cffile),
+               err = control_queue(fd, UNCONST(fd->ctx->cffile),
                    strlen(fd->ctx->cffile) + 1);
+               return err == -1 ? -1 : 1;
+       } else if (strcmp(data, "--isprivileged") == 0) {
+               const char *ret = fd->flags & FD_PRIV ? "true" : "false";
+               return control_queue(fd, ret, strlen(ret) + 1);
        } else if (strncmp(data, "--listen", MIN(strlen("--listen"), len)) ==
            0) {
                fd->flags |= FD_LISTEN;
-               return 0;
+               return 1;
        }
-       return 1;
+       return 0;
 }
 
 static ssize_t
@@ -141,10 +148,19 @@ ps_ctl_dodispatch(void *arg, unsigned short events)
 static void
 ps_ctl_recv(void *arg, unsigned short events)
 {
-       char buf[BUFSIZ];
        struct dhcpcd_ctx *ctx = arg;
-       ssize_t len;
-       struct fd_list *fd;
+       char buf[BUFSIZ];
+       struct iovec iov[] = { {
+           .iov_base = buf,
+           .iov_len = sizeof(buf),
+       } };
+       struct msghdr msg = {
+               .msg_iov = iov,
+               .msg_iovlen = __arraycount(iov),
+       };
+       ssize_t msglen;
+       int fd;
+       struct fd_list *fdl;
 
        if (events & ELE_HANGUP) {
        hangup:
@@ -152,22 +168,23 @@ ps_ctl_recv(void *arg, unsigned short events)
                return;
        }
 
-       if (!(events & ELE_READ)) {
+       if (!(events & ELE_READ))
                logerrx("%s: unexpected event 0x%04x", __func__, events);
-               return;
-       }
 
-       len = read(ctx->ps_ctl->psp_work_fd, buf, sizeof(buf));
-       if (len == -1) {
-               logerr("%s: read", __func__);
-               return;
-       } else if (len == 0)
+       fd = ctx->ps_ctl->psp_work_fd;
+       msglen = recvmsg(fd, &msg, 0);
+       if (msglen == 0)
                goto hangup;
+       if (msglen == -1) {
+               logerr("%s: read", __func__);
+               eloop_exit(ctx->eloop, EXIT_FAILURE);
+       }
 
-       TAILQ_FOREACH(fd, &ctx->control_fds, next) {
-               if (fd->flags & FD_LISTEN)
+       /* Send to our command controls */
+       TAILQ_FOREACH(fdl, &ctx->control_fds, next) {
+               if (!(fdl->flags & FD_COMMAND))
                        continue;
-               if (control_queue(fd, buf, (size_t)len) == -1)
+               if (control_queue(fdl, buf, (size_t)msglen) == -1)
                        logerr("%s: control_queue", __func__);
        }
 }
@@ -176,9 +193,10 @@ static void
 ps_ctl_listen(void *arg, unsigned short events)
 {
        struct dhcpcd_ctx *ctx = arg;
-       char buf[BUFSIZ];
        ssize_t len;
-       struct fd_list *fd;
+       size_t msglen;
+       int fd;
+       struct fd_list *fdl;
 
        if (events & ELE_HANGUP) {
        hangup:
@@ -189,22 +207,40 @@ ps_ctl_listen(void *arg, unsigned short events)
        if (!(events & ELE_READ))
                logerrx("%s: unexpected event 0x%04x", __func__, events);
 
-       len = read(ctx->ps_control->fd, buf, sizeof(buf));
-       if (len == -1) {
-               logerr("%s: read", __func__);
-               eloop_exit(ctx->eloop, EXIT_FAILURE);
-               return;
+       fd = ctx->ps_control->fd;
+       len = read(fd, &msglen, sizeof(msglen));
+       if (len == 0)
+               goto hangup;
+       if (len != sizeof(msglen)) {
+               logerr("%s: read len", __func__);
+               goto err;
        }
+
+       if (ps_bufalloc(ctx, msglen) == -1) {
+               logerr("%s: realloc", __func__);
+               goto err;
+       }
+
+       len = read(fd, ctx->ps_buf, msglen);
        if (len == 0)
                goto hangup;
+       if ((size_t)len != msglen) {
+               logerr("%s: read", __func__);
+               goto err;
+       }
 
        /* Send to our listeners */
-       TAILQ_FOREACH(fd, &ctx->control_fds, next) {
-               if (fd == ctx->ps_control || !(fd->flags & FD_LISTEN))
+       TAILQ_FOREACH(fdl, &ctx->control_fds, next) {
+               if (!(fdl->flags & FD_LISTEN))
                        continue;
-               if (control_queue(fd, buf, (size_t)len) == -1)
+               if (control_queue(fdl, ctx->ps_buf, msglen) == -1)
                        logerr("%s: control_queue", __func__);
        }
+
+       return;
+
+err:
+       eloop_exit(ctx->eloop, EXIT_FAILURE);
 }
 
 pid_t
@@ -220,8 +256,10 @@ ps_ctl_start(struct dhcpcd_ctx *ctx)
 
        if_closesockets(ctx);
 
-       if (xsocketpair(AF_UNIX, SOCK_STREAM | SOCK_CXNB, 0, work_fd) == -1 ||
-           xsocketpair(AF_UNIX, SOCK_STREAM | SOCK_CXNB, 0, listen_fd) == -1)
+       if (xsocketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, work_fd) ==
+               -1 ||
+           xsocketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, listen_fd) ==
+               -1)
                return -1;
 #ifdef PRIVSEP_RIGHTS
        if (ps_rights_limit_fdpair(work_fd) == -1 ||
@@ -255,7 +293,7 @@ ps_ctl_start(struct dhcpcd_ctx *ctx)
                ctx) == -1)
                return -1;
 
-       ctx->ps_control = control_new(ctx, listen_fd[1], FD_LISTEN);
+       ctx->ps_control = control_new(ctx, listen_fd[1], 0);
        if (ctx->ps_control == NULL)
                return -1;
        if (eloop_event_add(ctx->eloop, ctx->ps_control->fd, ELE_READ,