diff options
-rw-r--r-- | src/udev/udevd.c | 52 |
1 files changed, 45 insertions, 7 deletions
diff --git a/src/udev/udevd.c b/src/udev/udevd.c index e93098d84a..5916b58165 100644 --- a/src/udev/udevd.c +++ b/src/udev/udevd.c @@ -127,7 +127,6 @@ struct worker { /* passed from worker to main process */ struct worker_message { - pid_t pid; int exitcode; }; @@ -327,7 +326,6 @@ skip: /* send udevd the result of the event execution */ memzero(&msg, sizeof(struct worker_message)); msg.exitcode = err; - msg.pid = getpid(); send(worker_watch[WRITE_END], &msg, sizeof(struct worker_message), 0); log_debug("seq %llu processed with %i", udev_device_get_seqnum(dev), err); @@ -594,18 +592,54 @@ static void event_queue_cleanup(struct udev *udev, enum event_state match_type) static void worker_returned(int fd_worker) { for (;;) { struct worker_message msg; + struct iovec iovec = { + .iov_base = &msg, + .iov_len = sizeof(msg), + }; + union { + struct cmsghdr cmsghdr; + uint8_t buf[CMSG_SPACE(sizeof(struct ucred))]; + } control = {}; + struct msghdr msghdr = { + .msg_iov = &iovec, + .msg_iovlen = 1, + .msg_control = &control, + .msg_controllen = sizeof(control), + }; + struct cmsghdr *cmsg; ssize_t size; + struct ucred *ucred = NULL; struct udev_list_node *loop; - size = recv(fd_worker, &msg, sizeof(struct worker_message), MSG_DONTWAIT); - if (size != sizeof(struct worker_message)) - break; + size = recvmsg(fd_worker, &msghdr, MSG_DONTWAIT); + if (size < 0) { + if (errno == EAGAIN || errno == EINTR) + return; + + log_error_errno(errno, "failed to receive message: %m"); + return; + } else if (size != sizeof(struct worker_message)) { + log_warning_errno(EIO, "ignoring worker message with invalid size %zi bytes", size); + return; + } + + for (cmsg = CMSG_FIRSTHDR(&msghdr); cmsg; cmsg = CMSG_NXTHDR(&msghdr, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_CREDENTIALS && + cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) + ucred = (struct ucred*) CMSG_DATA(cmsg); + } + + if (!ucred || ucred->pid <= 0) { + log_warning_errno(EIO, "ignoring worker message without valid PID"); + continue; + } /* lookup worker who sent the signal */ udev_list_node_foreach(loop, &worker_list) { struct worker *worker = node_to_worker(loop); - if (worker->pid != msg.pid) + if (worker->pid != ucred->pid) continue; /* worker returned */ @@ -1092,7 +1126,7 @@ int main(int argc, char *argv[]) { struct epoll_event ep_netlink = { .events = EPOLLIN }; struct epoll_event ep_worker = { .events = EPOLLIN }; struct udev_ctrl_connection *ctrl_conn = NULL; - int rc = 1, r; + int rc = 1, r, one = 1; udev = udev_new(); if (udev == NULL) @@ -1275,6 +1309,10 @@ int main(int argc, char *argv[]) { } fd_worker = worker_watch[READ_END]; + r = setsockopt(fd_worker, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)); + if (r < 0) + return log_error_errno(errno, "could not enable SO_PASSCRED: %m"); + ep_ctrl.data.fd = fd_ctrl; ep_inotify.data.fd = fd_inotify; ep_signal.data.fd = fd_signal; |