diff --git a/man/udevadm.xml b/man/udevadm.xml index 0107c57576..f25a1dfb48 100644 --- a/man/udevadm.xml +++ b/man/udevadm.xml @@ -372,8 +372,10 @@ - Signal and wait for systemd-udevd to exit. Note that systemd-udevd.service - contains and so as a result, this option restarts systemd-udevd. + Signal and wait for systemd-udevd to exit. No option except for + can be specified after this option. + Note that systemd-udevd.service contains + and so as a result, this option restarts systemd-udevd. If you want to stop systemd-udevd.service, please use the following: systemctl stop systemd-udevd-control.socket systemd-udevd-kernel.socket systemd-udevd.service diff --git a/src/udev/udev-ctrl.c b/src/udev/udev-ctrl.c index c217815ac6..59ce5c1f80 100644 --- a/src/udev/udev-ctrl.c +++ b/src/udev/udev-ctrl.c @@ -17,6 +17,8 @@ #include #include +#include "sd-event.h" + #include "alloc-util.h" #include "fd-util.h" #include "format-util.h" @@ -29,71 +31,53 @@ /* wire protocol magic must match */ #define UDEV_CTRL_MAGIC 0xdead1dea -enum udev_ctrl_msg_type { - UDEV_CTRL_UNKNOWN, - UDEV_CTRL_SET_LOG_LEVEL, - UDEV_CTRL_STOP_EXEC_QUEUE, - UDEV_CTRL_START_EXEC_QUEUE, - UDEV_CTRL_RELOAD, - UDEV_CTRL_SET_ENV, - UDEV_CTRL_SET_CHILDREN_MAX, - UDEV_CTRL_PING, - UDEV_CTRL_EXIT, -}; - struct udev_ctrl_msg_wire { char version[16]; unsigned magic; enum udev_ctrl_msg_type type; - union { - int intval; - char buf[256]; - }; -}; - -struct udev_ctrl_msg { - unsigned n_ref; - struct udev_ctrl_connection *conn; - struct udev_ctrl_msg_wire ctrl_msg_wire; + union udev_ctrl_msg_value value; }; struct udev_ctrl { unsigned n_ref; int sock; + int sock_connect; union sockaddr_union saddr; socklen_t addrlen; - bool bound; - bool cleanup_socket; - bool connected; + bool bound:1; + bool cleanup_socket:1; + bool connected:1; + bool maybe_disconnected:1; + sd_event *event; + sd_event_source *event_source; + sd_event_source *event_source_connect; + udev_ctrl_handler_t callback; + void *userdata; }; -struct udev_ctrl_connection { - unsigned n_ref; - struct udev_ctrl *uctrl; - int sock; -}; - -struct udev_ctrl *udev_ctrl_new_from_fd(int fd) { +int udev_ctrl_new_from_fd(struct udev_ctrl **ret, int fd) { + _cleanup_close_ int sock = -1; struct udev_ctrl *uctrl; int r; - uctrl = new0(struct udev_ctrl, 1); - if (!uctrl) - return NULL; - uctrl->n_ref = 1; + assert(ret); if (fd < 0) { - uctrl->sock = socket(AF_LOCAL, SOCK_SEQPACKET|SOCK_NONBLOCK|SOCK_CLOEXEC, 0); - if (uctrl->sock < 0) { - log_error_errno(errno, "Failed to create socket: %m"); - udev_ctrl_unref(uctrl); - return NULL; - } - } else { - uctrl->bound = true; - uctrl->sock = fd; + sock = socket(AF_LOCAL, SOCK_SEQPACKET|SOCK_NONBLOCK|SOCK_CLOEXEC, 0); + if (sock < 0) + return log_error_errno(errno, "Failed to create socket: %m"); } + uctrl = new(struct udev_ctrl, 1); + if (!uctrl) + return -ENOMEM; + + *uctrl = (struct udev_ctrl) { + .n_ref = 1, + .sock = fd >= 0 ? fd : TAKE_FD(sock), + .bound = fd >= 0, + }; + /* * FIXME: remove it as soon as we can depend on this: * http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=90c6bd34f884cd9cee21f1d152baf6c18bcac949 @@ -108,45 +92,58 @@ struct udev_ctrl *udev_ctrl_new_from_fd(int fd) { }; uctrl->addrlen = SOCKADDR_UN_LEN(uctrl->saddr.un); - return uctrl; -} -struct udev_ctrl *udev_ctrl_new(void) { - return udev_ctrl_new_from_fd(-1); + *ret = TAKE_PTR(uctrl); + return 0; } int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl) { - int err; + int r; - if (!uctrl->bound) { - err = bind(uctrl->sock, &uctrl->saddr.sa, uctrl->addrlen); - if (err < 0 && errno == EADDRINUSE) { - (void) sockaddr_un_unlink(&uctrl->saddr.un); - err = bind(uctrl->sock, &uctrl->saddr.sa, uctrl->addrlen); - } + assert(uctrl); - if (err < 0) - return log_error_errno(errno, "Failed to bind socket: %m"); + if (uctrl->bound) + return 0; - err = listen(uctrl->sock, 0); - if (err < 0) - return log_error_errno(errno, "Failed to listen: %m"); - - uctrl->bound = true; - uctrl->cleanup_socket = true; + r = bind(uctrl->sock, &uctrl->saddr.sa, uctrl->addrlen); + if (r < 0 && errno == EADDRINUSE) { + (void) sockaddr_un_unlink(&uctrl->saddr.un); + r = bind(uctrl->sock, &uctrl->saddr.sa, uctrl->addrlen); } + + if (r < 0) + return log_error_errno(errno, "Failed to bind udev control socket: %m"); + + if (listen(uctrl->sock, 0) < 0) + return log_error_errno(errno, "Failed to listen udev control socket: %m"); + + uctrl->bound = true; + uctrl->cleanup_socket = true; + return 0; } +static void udev_ctrl_disconnect(struct udev_ctrl *uctrl) { + if (!uctrl) + return; + + uctrl->event_source_connect = sd_event_source_unref(uctrl->event_source_connect); + uctrl->sock_connect = safe_close(uctrl->sock_connect); +} + static struct udev_ctrl *udev_ctrl_free(struct udev_ctrl *uctrl) { assert(uctrl); + udev_ctrl_disconnect(uctrl); + + sd_event_source_unref(uctrl->event_source); safe_close(uctrl->sock); + + sd_event_unref(uctrl->event); return mfree(uctrl); } -DEFINE_PRIVATE_TRIVIAL_REF_FUNC(struct udev_ctrl, udev_ctrl); -DEFINE_TRIVIAL_UNREF_FUNC(struct udev_ctrl, udev_ctrl, udev_ctrl_free); +DEFINE_TRIVIAL_REF_UNREF_FUNC(struct udev_ctrl, udev_ctrl, udev_ctrl_free); int udev_ctrl_cleanup(struct udev_ctrl *uctrl) { if (!uctrl) @@ -156,142 +153,41 @@ int udev_ctrl_cleanup(struct udev_ctrl *uctrl) { return 0; } -int udev_ctrl_get_fd(struct udev_ctrl *uctrl) { - if (!uctrl) - return -EINVAL; - return uctrl->sock; -} - -struct udev_ctrl_connection *udev_ctrl_get_connection(struct udev_ctrl *uctrl) { - struct udev_ctrl_connection *conn; - struct ucred ucred = {}; +int udev_ctrl_attach_event(struct udev_ctrl *uctrl, sd_event *event) { int r; - conn = new(struct udev_ctrl_connection, 1); - if (!conn) - return NULL; - conn->n_ref = 1; - conn->uctrl = uctrl; + assert_return(uctrl, -EINVAL); + assert_return(!uctrl->event, -EBUSY); - conn->sock = accept4(uctrl->sock, NULL, NULL, SOCK_CLOEXEC|SOCK_NONBLOCK); - if (conn->sock < 0) { - if (errno != EINTR) - log_error_errno(errno, "Failed to receive ctrl connection: %m"); - goto err; + if (event) + uctrl->event = sd_event_ref(event); + else { + r = sd_event_default(&uctrl->event); + if (r < 0) + return r; } - /* check peer credential of connection */ - r = getpeercred(conn->sock, &ucred); - if (r < 0) { - log_error_errno(r, "Failed to receive credentials of ctrl connection: %m"); - goto err; - } - if (ucred.uid > 0) { - log_error("Sender uid="UID_FMT", message ignored", ucred.uid); - goto err; - } - - /* enable receiving of the sender credentials in the messages */ - r = setsockopt_int(conn->sock, SOL_SOCKET, SO_PASSCRED, true); - if (r < 0) - log_warning_errno(r, "Failed to set SO_PASSCRED: %m"); - - udev_ctrl_ref(uctrl); - return conn; -err: - safe_close(conn->sock); - return mfree(conn); + return 0; } -static struct udev_ctrl_connection *udev_ctrl_connection_free(struct udev_ctrl_connection *conn) { - assert(conn); +sd_event_source *udev_ctrl_get_event_source(struct udev_ctrl *uctrl) { + assert(uctrl); - safe_close(conn->sock); - udev_ctrl_unref(conn->uctrl); - return mfree(conn); + return uctrl->event_source; } -DEFINE_TRIVIAL_REF_UNREF_FUNC(struct udev_ctrl_connection, udev_ctrl_connection, udev_ctrl_connection_free); - -static int ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int intval, const char *buf, usec_t timeout) { - struct udev_ctrl_msg_wire ctrl_msg_wire = { - .version = "udev-" STRINGIFY(PROJECT_VERSION), - .magic = UDEV_CTRL_MAGIC, - .type = type, - }; - - if (buf) - strscpy(ctrl_msg_wire.buf, sizeof(ctrl_msg_wire.buf), buf); - else - ctrl_msg_wire.intval = intval; - - if (!uctrl->connected) { - if (connect(uctrl->sock, &uctrl->saddr.sa, uctrl->addrlen) < 0) - return -errno; - uctrl->connected = true; - } - if (send(uctrl->sock, &ctrl_msg_wire, sizeof(ctrl_msg_wire), 0) < 0) - return -errno; - - /* wait for peer message handling or disconnect */ - for (;;) { - struct pollfd pfd = { - .fd = uctrl->sock, - .events = POLLIN, - }; - int r; - - r = poll(&pfd, 1, DIV_ROUND_UP(timeout, USEC_PER_MSEC)); - if (r < 0) { - if (errno == EINTR) - continue; - return -errno; - } - if (r == 0) - return -ETIMEDOUT; - if (pfd.revents & POLLERR) - return -EIO; - return 0; - } +static void udev_ctrl_disconnect_and_listen_again(struct udev_ctrl *uctrl) { + udev_ctrl_disconnect(uctrl); + udev_ctrl_unref(uctrl); + (void) sd_event_source_set_enabled(uctrl->event_source, SD_EVENT_ON); } -int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority, usec_t timeout) { - return ctrl_send(uctrl, UDEV_CTRL_SET_LOG_LEVEL, priority, NULL, timeout); -} +DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_ctrl *, udev_ctrl_disconnect_and_listen_again); -int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl, usec_t timeout) { - return ctrl_send(uctrl, UDEV_CTRL_STOP_EXEC_QUEUE, 0, NULL, timeout); -} - -int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl, usec_t timeout) { - return ctrl_send(uctrl, UDEV_CTRL_START_EXEC_QUEUE, 0, NULL, timeout); -} - -int udev_ctrl_send_reload(struct udev_ctrl *uctrl, usec_t timeout) { - return ctrl_send(uctrl, UDEV_CTRL_RELOAD, 0, NULL, timeout); -} - -int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key, usec_t timeout) { - return ctrl_send(uctrl, UDEV_CTRL_SET_ENV, 0, key, timeout); -} - -int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count, usec_t timeout) { - return ctrl_send(uctrl, UDEV_CTRL_SET_CHILDREN_MAX, count, NULL, timeout); -} - -int udev_ctrl_send_ping(struct udev_ctrl *uctrl, usec_t timeout) { - return ctrl_send(uctrl, UDEV_CTRL_PING, 0, NULL, timeout); -} - -int udev_ctrl_send_exit(struct udev_ctrl *uctrl, usec_t timeout) { - return ctrl_send(uctrl, UDEV_CTRL_EXIT, 0, NULL, timeout); -} - -struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn) { - struct udev_ctrl_msg *uctrl_msg; - ssize_t size; - struct cmsghdr *cmsg; - struct iovec iov; +static int udev_ctrl_connection_event_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + _cleanup_(udev_ctrl_disconnect_and_listen_againp) struct udev_ctrl *uctrl = NULL; + struct udev_ctrl_msg_wire msg_wire; + struct iovec iov = IOVEC_MAKE(&msg_wire, sizeof(struct udev_ctrl_msg_wire)); char cred_msg[CMSG_SPACE(sizeof(struct ucred))]; struct msghdr smsg = { .msg_iov = &iov, @@ -299,47 +195,28 @@ struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn) { .msg_control = cred_msg, .msg_controllen = sizeof(cred_msg), }; + struct cmsghdr *cmsg; struct ucred *cred; + ssize_t size; - uctrl_msg = new0(struct udev_ctrl_msg, 1); - if (!uctrl_msg) - return NULL; - uctrl_msg->n_ref = 1; - uctrl_msg->conn = conn; - udev_ctrl_connection_ref(conn); + assert(userdata); - /* wait for the incoming message */ - for (;;) { - struct pollfd pfd[1]; - int r; + /* When UDEV_CTRL_EXIT is received, manager unref udev_ctrl object. + * To avoid the object freed, let's increment the refcount. */ + uctrl = udev_ctrl_ref(userdata); - pfd[0].fd = conn->sock; - pfd[0].events = POLLIN; + size = next_datagram_size_fd(fd); + if (size < 0) + return log_error_errno(size, "Failed to get size of message: %m"); + if (size == 0) + return 0; /* Client disconnects? */ - r = poll(pfd, 1, 10000); - if (r < 0) { - if (errno == EINTR) - continue; - goto err; - } else if (r == 0) { - log_error("Timeout waiting for ctrl message"); - goto err; - } else { - if (!(pfd[0].revents & POLLIN)) { - log_error("Invalid ctrl connection: %m"); - goto err; - } - } - - break; - } - - iov = IOVEC_MAKE(&uctrl_msg->ctrl_msg_wire, sizeof(struct udev_ctrl_msg_wire)); - - size = recvmsg(conn->sock, &smsg, 0); + size = recvmsg(fd, &smsg, 0); if (size < 0) { - log_error_errno(errno, "Failed to receive ctrl message: %m"); - goto err; + if (errno != EINTR) + return log_error_errno(errno, "Failed to receive ctrl message: %m"); + + return 0; } cmsg_close_all(&smsg); @@ -348,80 +225,182 @@ struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn) { if (!cmsg || cmsg->cmsg_type != SCM_CREDENTIALS) { log_error("No sender credentials received, ignoring message"); - goto err; + return 0; } cred = (struct ucred *) CMSG_DATA(cmsg); if (cred->uid != 0) { - log_error("Sender uid="UID_FMT", ignoring message", cred->uid); - goto err; + log_error("Invalid sender uid "UID_FMT", ignoring message", cred->uid); + return 0; } - if (uctrl_msg->ctrl_msg_wire.magic != UDEV_CTRL_MAGIC) { - log_error("Message magic 0x%08x doesn't match, ignoring", uctrl_msg->ctrl_msg_wire.magic); - goto err; + if (msg_wire.magic != UDEV_CTRL_MAGIC) { + log_error("Message magic 0x%08x doesn't match, ignoring message", msg_wire.magic); + return 0; } - return uctrl_msg; -err: - udev_ctrl_msg_unref(uctrl_msg); - return NULL; + if (msg_wire.type == _UDEV_CTRL_END_MESSAGES) + return 0; + + if (uctrl->callback) + (void) uctrl->callback(uctrl, msg_wire.type, &msg_wire.value, uctrl->userdata); + + /* Do not disconnect and wait for next message. */ + uctrl = udev_ctrl_unref(uctrl); + return 0; } -static struct udev_ctrl_msg *udev_ctrl_msg_free(struct udev_ctrl_msg *ctrl_msg) { - assert(ctrl_msg); +static int udev_ctrl_event_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + struct udev_ctrl *uctrl = userdata; + _cleanup_close_ int sock = -1; + struct ucred ucred; + int r; - udev_ctrl_connection_unref(ctrl_msg->conn); - return mfree(ctrl_msg); + assert(uctrl); + + sock = accept4(fd, NULL, NULL, SOCK_CLOEXEC|SOCK_NONBLOCK); + if (sock < 0) { + if (errno != EINTR) + log_error_errno(errno, "Failed to accept ctrl connection: %m"); + return 0; + } + + /* check peer credential of connection */ + r = getpeercred(sock, &ucred); + if (r < 0) { + log_error_errno(r, "Failed to receive credentials of ctrl connection: %m"); + return 0; + } + + if (ucred.uid > 0) { + log_error("Invalid sender uid "UID_FMT", closing connection", ucred.uid); + return 0; + } + + /* enable receiving of the sender credentials in the messages */ + r = setsockopt_int(sock, SOL_SOCKET, SO_PASSCRED, true); + if (r < 0) + log_warning_errno(r, "Failed to set SO_PASSCRED, ignoring: %m"); + + r = sd_event_add_io(uctrl->event, &uctrl->event_source_connect, sock, EPOLLIN, udev_ctrl_connection_event_handler, uctrl); + if (r < 0) { + log_error_errno(r, "Failed to create event source for udev control connection: %m"); + return 0; + } + + (void) sd_event_source_set_description(uctrl->event_source_connect, "udev-ctrl-connection"); + + /* Do not accept multiple connection. */ + (void) sd_event_source_set_enabled(uctrl->event_source, SD_EVENT_OFF); + + uctrl->sock_connect = TAKE_FD(sock); + return 0; } -DEFINE_TRIVIAL_UNREF_FUNC(struct udev_ctrl_msg, udev_ctrl_msg, udev_ctrl_msg_free); +int udev_ctrl_start(struct udev_ctrl *uctrl, udev_ctrl_handler_t callback, void *userdata) { + int r; -int udev_ctrl_get_set_log_level(struct udev_ctrl_msg *ctrl_msg) { - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_SET_LOG_LEVEL) - return ctrl_msg->ctrl_msg_wire.intval; - return -1; + assert(uctrl); + + if (!uctrl->event) { + r = udev_ctrl_attach_event(uctrl, NULL); + if (r < 0) + return r; + } + + r = udev_ctrl_enable_receiving(uctrl); + if (r < 0) + return r; + + uctrl->callback = callback; + uctrl->userdata = userdata; + + r = sd_event_add_io(uctrl->event, &uctrl->event_source, uctrl->sock, EPOLLIN, udev_ctrl_event_handler, uctrl); + if (r < 0) + return r; + + (void) sd_event_source_set_description(uctrl->event_source, "udev-ctrl"); + + return 0; } -int udev_ctrl_get_stop_exec_queue(struct udev_ctrl_msg *ctrl_msg) { - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_STOP_EXEC_QUEUE) - return 1; - return -1; +int udev_ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int intval, const char *buf) { + struct udev_ctrl_msg_wire ctrl_msg_wire = { + .version = "udev-" STRINGIFY(PROJECT_VERSION), + .magic = UDEV_CTRL_MAGIC, + .type = type, + }; + + if (uctrl->maybe_disconnected) + return -ENOANO; /* to distinguish this from other errors. */ + + if (buf) + strscpy(ctrl_msg_wire.value.buf, sizeof(ctrl_msg_wire.value.buf), buf); + else + ctrl_msg_wire.value.intval = intval; + + if (!uctrl->connected) { + if (connect(uctrl->sock, &uctrl->saddr.sa, uctrl->addrlen) < 0) + return -errno; + uctrl->connected = true; + } + + if (send(uctrl->sock, &ctrl_msg_wire, sizeof(ctrl_msg_wire), 0) < 0) + return -errno; + + if (type == UDEV_CTRL_EXIT) + uctrl->maybe_disconnected = true; + + return 0; } -int udev_ctrl_get_start_exec_queue(struct udev_ctrl_msg *ctrl_msg) { - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_START_EXEC_QUEUE) - return 1; - return -1; +static int udev_ctrl_wait_io_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + return sd_event_exit(sd_event_source_get_event(s), 0); } -int udev_ctrl_get_reload(struct udev_ctrl_msg *ctrl_msg) { - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_RELOAD) - return 1; - return -1; -} +int udev_ctrl_wait(struct udev_ctrl *uctrl, usec_t timeout) { + _cleanup_(sd_event_source_unrefp) sd_event_source *source_io = NULL, *source_timeout = NULL; + int r; -const char *udev_ctrl_get_set_env(struct udev_ctrl_msg *ctrl_msg) { - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_SET_ENV) - return ctrl_msg->ctrl_msg_wire.buf; - return NULL; -} + assert(uctrl); -int udev_ctrl_get_set_children_max(struct udev_ctrl_msg *ctrl_msg) { - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_SET_CHILDREN_MAX) - return ctrl_msg->ctrl_msg_wire.intval; - return -1; -} + if (uctrl->sock < 0) + return 0; + if (!uctrl->connected) + return 0; -int udev_ctrl_get_ping(struct udev_ctrl_msg *ctrl_msg) { - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_PING) - return 1; - return -1; -} + if (!uctrl->maybe_disconnected) { + r = udev_ctrl_send(uctrl, _UDEV_CTRL_END_MESSAGES, 0, NULL); + if (r < 0) + return r; + } -int udev_ctrl_get_exit(struct udev_ctrl_msg *ctrl_msg) { - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_EXIT) - return 1; - return -1; + if (timeout == 0) + return 0; + + if (!uctrl->event) { + r = udev_ctrl_attach_event(uctrl, NULL); + if (r < 0) + return r; + } + + r = sd_event_add_io(uctrl->event, &source_io, uctrl->sock, EPOLLIN, udev_ctrl_wait_io_handler, NULL); + if (r < 0) + return r; + + (void) sd_event_source_set_description(uctrl->event_source, "udev-ctrl-wait-io"); + + if (timeout != USEC_INFINITY) { + usec_t usec; + + usec = now(clock_boottime_or_monotonic()) + timeout; + r = sd_event_add_time(uctrl->event, &source_timeout, clock_boottime_or_monotonic(), usec, 0, NULL, INT_TO_PTR(-ETIMEDOUT)); + if (r < 0) + return r; + + (void) sd_event_source_set_description(source_timeout, "udev-ctrl-wait-io"); + } + + return sd_event_loop(uctrl->event); } diff --git a/src/udev/udev-ctrl.h b/src/udev/udev-ctrl.h index 8e452a4249..2c84a8b16d 100644 --- a/src/udev/udev-ctrl.h +++ b/src/udev/udev-ctrl.h @@ -1,42 +1,79 @@ /* SPDX-License-Identifier: GPL-2.0+ */ #pragma once +#include "sd-event.h" + #include "macro.h" #include "time-util.h" struct udev_ctrl; -struct udev_ctrl *udev_ctrl_new(void); -struct udev_ctrl *udev_ctrl_new_from_fd(int fd); + +enum udev_ctrl_msg_type { + _UDEV_CTRL_END_MESSAGES, + UDEV_CTRL_SET_LOG_LEVEL, + UDEV_CTRL_STOP_EXEC_QUEUE, + UDEV_CTRL_START_EXEC_QUEUE, + UDEV_CTRL_RELOAD, + UDEV_CTRL_SET_ENV, + UDEV_CTRL_SET_CHILDREN_MAX, + UDEV_CTRL_PING, + UDEV_CTRL_EXIT, +}; + +union udev_ctrl_msg_value { + int intval; + char buf[256]; +}; + +typedef int (*udev_ctrl_handler_t)(struct udev_ctrl *udev_ctrl, enum udev_ctrl_msg_type type, + const union udev_ctrl_msg_value *value, void *userdata); + +int udev_ctrl_new_from_fd(struct udev_ctrl **ret, int fd); +static inline int udev_ctrl_new(struct udev_ctrl **ret) { + return udev_ctrl_new_from_fd(ret, -1); +} + int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl); +struct udev_ctrl *udev_ctrl_ref(struct udev_ctrl *uctrl); struct udev_ctrl *udev_ctrl_unref(struct udev_ctrl *uctrl); int udev_ctrl_cleanup(struct udev_ctrl *uctrl); -int udev_ctrl_get_fd(struct udev_ctrl *uctrl); -int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority, usec_t timeout); -int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl, usec_t timeout); -int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl, usec_t timeout); -int udev_ctrl_send_reload(struct udev_ctrl *uctrl, usec_t timeout); -int udev_ctrl_send_ping(struct udev_ctrl *uctrl, usec_t timeout); -int udev_ctrl_send_exit(struct udev_ctrl *uctrl, usec_t timeout); -int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key, usec_t timeout); -int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count, usec_t timeout); +int udev_ctrl_attach_event(struct udev_ctrl *uctrl, sd_event *event); +int udev_ctrl_start(struct udev_ctrl *uctrl, udev_ctrl_handler_t callback, void *userdata); +sd_event_source *udev_ctrl_get_event_source(struct udev_ctrl *uctrl); -struct udev_ctrl_connection; -struct udev_ctrl_connection *udev_ctrl_get_connection(struct udev_ctrl *uctrl); -struct udev_ctrl_connection *udev_ctrl_connection_ref(struct udev_ctrl_connection *conn); -struct udev_ctrl_connection *udev_ctrl_connection_unref(struct udev_ctrl_connection *conn); +int udev_ctrl_wait(struct udev_ctrl *uctrl, usec_t timeout); -struct udev_ctrl_msg; -struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn); -struct udev_ctrl_msg *udev_ctrl_msg_unref(struct udev_ctrl_msg *ctrl_msg); -int udev_ctrl_get_set_log_level(struct udev_ctrl_msg *ctrl_msg); -int udev_ctrl_get_stop_exec_queue(struct udev_ctrl_msg *ctrl_msg); -int udev_ctrl_get_start_exec_queue(struct udev_ctrl_msg *ctrl_msg); -int udev_ctrl_get_reload(struct udev_ctrl_msg *ctrl_msg); -int udev_ctrl_get_ping(struct udev_ctrl_msg *ctrl_msg); -int udev_ctrl_get_exit(struct udev_ctrl_msg *ctrl_msg); -const char *udev_ctrl_get_set_env(struct udev_ctrl_msg *ctrl_msg); -int udev_ctrl_get_set_children_max(struct udev_ctrl_msg *ctrl_msg); +int udev_ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int intval, const char *buf); +static inline int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority) { + return udev_ctrl_send(uctrl, UDEV_CTRL_SET_LOG_LEVEL, priority, NULL); +} + +static inline int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl) { + return udev_ctrl_send(uctrl, UDEV_CTRL_STOP_EXEC_QUEUE, 0, NULL); +} + +static inline int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl) { + return udev_ctrl_send(uctrl, UDEV_CTRL_START_EXEC_QUEUE, 0, NULL); +} + +static inline int udev_ctrl_send_reload(struct udev_ctrl *uctrl) { + return udev_ctrl_send(uctrl, UDEV_CTRL_RELOAD, 0, NULL); +} + +static inline int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key) { + return udev_ctrl_send(uctrl, UDEV_CTRL_SET_ENV, 0, key); +} + +static inline int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count) { + return udev_ctrl_send(uctrl, UDEV_CTRL_SET_CHILDREN_MAX, count, NULL); +} + +static inline int udev_ctrl_send_ping(struct udev_ctrl *uctrl) { + return udev_ctrl_send(uctrl, UDEV_CTRL_PING, 0, NULL); +} + +static inline int udev_ctrl_send_exit(struct udev_ctrl *uctrl) { + return udev_ctrl_send(uctrl, UDEV_CTRL_EXIT, 0, NULL); +} DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_ctrl*, udev_ctrl_unref); -DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_ctrl_connection*, udev_ctrl_connection_unref); -DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_ctrl_msg*, udev_ctrl_msg_unref); diff --git a/src/udev/udevadm-control.c b/src/udev/udevadm-control.c index 7cfc4c929d..f9b3e95794 100644 --- a/src/udev/udevadm-control.c +++ b/src/udev/udevadm-control.c @@ -86,48 +86,60 @@ int control_main(int argc, char *argv[], void *userdata) { return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This command expects one or more options."); - uctrl = udev_ctrl_new(); - if (!uctrl) - return log_oom(); + r = udev_ctrl_new(&uctrl); + if (r < 0) + return log_error_errno(r, "Failed to initialize udev control: %m"); while ((c = getopt_long(argc, argv, "el:sSRp:m:t:Vh", options, NULL)) >= 0) switch (c) { case 'e': - r = udev_ctrl_send_exit(uctrl, timeout); - if (r < 0) - return r; + r = udev_ctrl_send_exit(uctrl); + if (r == -ENOANO) + log_warning("Cannot specify --exit after --exit, ignoring."); + else if (r < 0) + return log_error_errno(r, "Failed to send exit request: %m"); break; case 'l': r = log_level_from_string(optarg); if (r < 0) return log_error_errno(r, "Failed to parse log priority '%s': %m", optarg); - r = udev_ctrl_send_set_log_level(uctrl, r, timeout); - if (r < 0) - return r; + r = udev_ctrl_send_set_log_level(uctrl, r); + if (r == -ENOANO) + log_warning("Cannot specify --log-priority after --exit, ignoring."); + else if (r < 0) + return log_error_errno(r, "Failed to send request to set log level: %m"); break; case 's': - r = udev_ctrl_send_stop_exec_queue(uctrl, timeout); - if (r < 0) - return r; + r = udev_ctrl_send_stop_exec_queue(uctrl); + if (r == -ENOANO) + log_warning("Cannot specify --stop-exec-queue after --exit, ignoring."); + else if (r < 0) + return log_error_errno(r, "Failed to send request to stop exec queue: %m"); break; case 'S': - r = udev_ctrl_send_start_exec_queue(uctrl, timeout); - if (r < 0) - return r; + r = udev_ctrl_send_start_exec_queue(uctrl); + if (r == -ENOANO) + log_warning("Cannot specify --start-exec-queue after --exit, ignoring."); + else if (r < 0) + return log_error_errno(r, "Failed to send request to start exec queue: %m"); break; case 'R': - r = udev_ctrl_send_reload(uctrl, timeout); - if (r < 0) - return r; + r = udev_ctrl_send_reload(uctrl); + if (r == -ENOANO) + log_warning("Cannot specify --reload after --exit, ignoring."); + else if (r < 0) + return log_error_errno(r, "Failed to send reload request: %m"); break; case 'p': if (!strchr(optarg, '=')) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "expect = instead of '%s'", optarg); - r = udev_ctrl_send_set_env(uctrl, optarg, timeout); - if (r < 0) - return r; + r = udev_ctrl_send_set_env(uctrl, optarg); + if (r == -ENOANO) + log_warning("Cannot specify --property after --exit, ignoring."); + else if (r < 0) + return log_error_errno(r, "Failed to send request to update environment: %m"); break; case 'm': { unsigned i; @@ -136,15 +148,19 @@ int control_main(int argc, char *argv[], void *userdata) { if (r < 0) return log_error_errno(r, "Failed to parse maximum number of events '%s': %m", optarg); - r = udev_ctrl_send_set_children_max(uctrl, i, timeout); - if (r < 0) - return r; + r = udev_ctrl_send_set_children_max(uctrl, i); + if (r == -ENOANO) + log_warning("Cannot specify --children-max after --exit, ignoring."); + else if (r < 0) + return log_error_errno(r, "Failed to send request to set number of children: %m"); break; } case ARG_PING: - r = udev_ctrl_send_ping(uctrl, timeout); - if (r < 0) - return log_error_errno(r, "Failed to connect to udev daemon: %m"); + r = udev_ctrl_send_ping(uctrl); + if (r == -ENOANO) + log_error("Cannot specify --ping after --exit, ignoring."); + else if (r < 0) + return log_error_errno(r, "Failed to send a ping message: %m"); break; case 't': r = parse_sec(optarg, &timeout); @@ -165,5 +181,9 @@ int control_main(int argc, char *argv[], void *userdata) { return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Extraneous argument: %s", argv[optind]); + r = udev_ctrl_wait(uctrl, timeout); + if (r < 0) + return log_error_errno(r, "Failed to wait for daemon to reply: %m"); + return 0; } diff --git a/src/udev/udevadm-settle.c b/src/udev/udevadm-settle.c index 8d9c4509ff..9cf88b245c 100644 --- a/src/udev/udevadm-settle.c +++ b/src/udev/udevadm-settle.c @@ -100,13 +100,16 @@ int settle_main(int argc, char *argv[], void *userdata) { if (getuid() == 0) { _cleanup_(udev_ctrl_unrefp) struct udev_ctrl *uctrl = NULL; - uctrl = udev_ctrl_new(); - if (uctrl) { - r = udev_ctrl_send_ping(uctrl, MAX(5 * USEC_PER_SEC, arg_timeout)); + if (udev_ctrl_new(&uctrl) >= 0) { + r = udev_ctrl_send_ping(uctrl); if (r < 0) { log_debug_errno(r, "Failed to connect to udev daemon: %m"); return 0; } + + r = udev_ctrl_wait(uctrl, MAX(5 * USEC_PER_SEC, arg_timeout)); + if (r < 0) + return log_error_errno(r, "Failed to wait for daemon to reply: %m"); } } diff --git a/src/udev/udevadm-trigger.c b/src/udev/udevadm-trigger.c index 2a658158e6..63ceaaf957 100644 --- a/src/udev/udevadm-trigger.c +++ b/src/udev/udevadm-trigger.c @@ -308,13 +308,17 @@ int trigger_main(int argc, char *argv[], void *userdata) { if (ping) { _cleanup_(udev_ctrl_unrefp) struct udev_ctrl *uctrl = NULL; - uctrl = udev_ctrl_new(); - if (!uctrl) - return log_oom(); + r = udev_ctrl_new(&uctrl); + if (r < 0) + return log_error_errno(r, "Failed to initialize udev control: %m"); - r = udev_ctrl_send_ping(uctrl, ping_timeout_usec); + r = udev_ctrl_send_ping(uctrl); if (r < 0) return log_error_errno(r, "Failed to connect to udev daemon: %m"); + + r = udev_ctrl_wait(uctrl, ping_timeout_usec); + if (r < 0) + return log_error_errno(r, "Failed to wait for daemon to reply: %m"); } for (; optind < argc; optind++) { diff --git a/src/udev/udevd.c b/src/udev/udevd.c index a5c24a70f4..2e31994b58 100644 --- a/src/udev/udevd.c +++ b/src/udev/udevd.c @@ -89,12 +89,9 @@ typedef struct Manager { sd_device_monitor *monitor; struct udev_ctrl *ctrl; - struct udev_ctrl_connection *ctrl_conn_blocking; int fd_inotify; int worker_watch[2]; - sd_event_source *ctrl_event; - sd_event_source *uevent_event; sd_event_source *inotify_event; sd_event_source *kill_workers_event; @@ -278,8 +275,6 @@ static void worker_attach_event(struct worker *worker, struct event *event) { static void manager_clear_for_worker(Manager *manager) { assert(manager); - manager->ctrl_event = sd_event_source_unref(manager->ctrl_event); - manager->uevent_event = sd_event_source_unref(manager->uevent_event); manager->inotify_event = sd_event_source_unref(manager->inotify_event); manager->kill_workers_event = sd_event_source_unref(manager->kill_workers_event); @@ -289,7 +284,6 @@ static void manager_clear_for_worker(Manager *manager) { event_queue_cleanup(manager, EVENT_UNDEF); manager->monitor = sd_device_monitor_unref(manager->monitor); - manager->ctrl_conn_blocking = udev_ctrl_connection_unref(manager->ctrl_conn_blocking); manager->ctrl = udev_ctrl_unref(manager->ctrl); manager->worker_watch[READ_END] = safe_close(manager->worker_watch[READ_END]); @@ -799,13 +793,11 @@ static void manager_exit(Manager *manager) { "STATUS=Starting shutdown..."); /* close sources of new events and discard buffered events */ - manager->ctrl_event = sd_event_source_unref(manager->ctrl_event); manager->ctrl = udev_ctrl_unref(manager->ctrl); manager->inotify_event = sd_event_source_unref(manager->inotify_event); manager->fd_inotify = safe_close(manager->fd_inotify); - manager->uevent_event = sd_event_source_unref(manager->uevent_event); manager->monitor = sd_device_monitor_unref(manager->monitor); /* discard queued events and kill workers */ @@ -1000,59 +992,44 @@ static int on_uevent(sd_device_monitor *monitor, sd_device *dev, void *userdata) } /* receive the udevd message from userspace */ -static int on_ctrl_msg(sd_event_source *s, int fd, uint32_t revents, void *userdata) { +static int on_ctrl_msg(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, const union udev_ctrl_msg_value *value, void *userdata) { Manager *manager = userdata; - _cleanup_(udev_ctrl_connection_unrefp) struct udev_ctrl_connection *ctrl_conn = NULL; - _cleanup_(udev_ctrl_msg_unrefp) struct udev_ctrl_msg *ctrl_msg = NULL; - const char *str; - int i, r; + int r; + assert(value); assert(manager); - ctrl_conn = udev_ctrl_get_connection(manager->ctrl); - if (!ctrl_conn) - return 1; - - ctrl_msg = udev_ctrl_receive_msg(ctrl_conn); - if (!ctrl_msg) - return 1; - - i = udev_ctrl_get_set_log_level(ctrl_msg); - if (i >= 0) { - log_debug("Received udev control message (SET_LOG_LEVEL), setting log_priority=%i", i); - log_set_max_level_realm(LOG_REALM_UDEV, i); - log_set_max_level_realm(LOG_REALM_SYSTEMD, i); + switch (type) { + case UDEV_CTRL_SET_LOG_LEVEL: + log_debug("Received udev control message (SET_LOG_LEVEL), setting log_priority=%i", value->intval); + log_set_max_level_realm(LOG_REALM_UDEV, value->intval); + log_set_max_level_realm(LOG_REALM_SYSTEMD, value->intval); manager_kill_workers(manager); - } - - if (udev_ctrl_get_stop_exec_queue(ctrl_msg) > 0) { + break; + case UDEV_CTRL_STOP_EXEC_QUEUE: log_debug("Received udev control message (STOP_EXEC_QUEUE)"); manager->stop_exec_queue = true; - } - - if (udev_ctrl_get_start_exec_queue(ctrl_msg) > 0) { + break; + case UDEV_CTRL_START_EXEC_QUEUE: log_debug("Received udev control message (START_EXEC_QUEUE)"); manager->stop_exec_queue = false; event_queue_start(manager); - } - - if (udev_ctrl_get_reload(ctrl_msg) > 0) { + break; + case UDEV_CTRL_RELOAD: log_debug("Received udev control message (RELOAD)"); manager_reload(manager); - } - - str = udev_ctrl_get_set_env(ctrl_msg); - if (str) { + break; + case UDEV_CTRL_SET_ENV: { _cleanup_free_ char *key = NULL, *val = NULL, *old_key = NULL, *old_val = NULL; - char *eq; + const char *eq; - eq = strchr(str, '='); + eq = strchr(value->buf, '='); if (!eq) { - log_error("Invalid key format '%s'", str); + log_error("Invalid key format '%s'", value->buf); return 1; } - key = strndup(str, eq - str); + key = strndup(value->buf, eq - value->buf); if (!key) { log_oom(); return 1; @@ -1093,27 +1070,30 @@ static int on_ctrl_msg(sd_event_source *s, int fd, uint32_t revents, void *userd key = val = NULL; manager_kill_workers(manager); + break; } + case UDEV_CTRL_SET_CHILDREN_MAX: + if (value->intval <= 0) { + log_debug("Received invalid udev control message (SET_MAX_CHILDREN, %i), ignoring.", value->intval); + return 0; + } - i = udev_ctrl_get_set_children_max(ctrl_msg); - if (i >= 0) { - log_debug("Receivd udev control message (SET_MAX_CHILDREN), setting children_max=%i", i); - arg_children_max = i; + log_debug("Received udev control message (SET_MAX_CHILDREN), setting children_max=%i", value->intval); + arg_children_max = value->intval; (void) sd_notifyf(false, "READY=1\n" "STATUS=Processing with %u children at max", arg_children_max); - } - - if (udev_ctrl_get_ping(ctrl_msg) > 0) + break; + case UDEV_CTRL_PING: log_debug("Received udev control message (PING)"); - - if (udev_ctrl_get_exit(ctrl_msg) > 0) { + break; + case UDEV_CTRL_EXIT: log_debug("Received udev control message (EXIT)"); manager_exit(manager); - /* keep reference to block the client until we exit - TODO: deal with several blocking exit requests */ - manager->ctrl_conn_blocking = udev_ctrl_connection_ref(ctrl_conn); + break; + default: + log_debug("Received unknown udev control message, ignoring"); } return 1; @@ -1596,9 +1576,9 @@ static int manager_new(Manager **ret, int fd_ctrl, int fd_uevent, const char *cg .cgroup = cgroup, }; - manager->ctrl = udev_ctrl_new_from_fd(fd_ctrl); - if (!manager->ctrl) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to initialize udev control socket"); + r = udev_ctrl_new_from_fd(&manager->ctrl, fd_ctrl); + if (r < 0) + return log_error_errno(r, "Failed to initialize udev control socket: %m"); r = udev_ctrl_enable_receiving(manager->ctrl); if (r < 0) @@ -1620,7 +1600,7 @@ static int manager_new(Manager **ret, int fd_ctrl, int fd_uevent, const char *cg } static int main_loop(Manager *manager) { - int fd_worker, fd_ctrl, r; + int fd_worker, r; /* unnamed socket from workers to the main daemon */ r = socketpair(AF_LOCAL, SOCK_DGRAM|SOCK_CLOEXEC, 0, manager->worker_watch); @@ -1667,19 +1647,19 @@ static int main_loop(Manager *manager) { if (r < 0) return log_error_errno(r, "Failed to create watchdog event source: %m"); - fd_ctrl = udev_ctrl_get_fd(manager->ctrl); - if (fd_ctrl < 0) - return log_error_errno(fd_ctrl, "Failed to get udev control socket fd: %m"); - - r = sd_event_add_io(manager->event, &manager->ctrl_event, fd_ctrl, EPOLLIN, on_ctrl_msg, manager); + r = udev_ctrl_attach_event(manager->ctrl, manager->event); if (r < 0) - return log_error_errno(r, "Failed to create udev control event source: %m"); + return log_error_errno(r, "Failed to attach event to udev control: %m"); + + r = udev_ctrl_start(manager->ctrl, on_ctrl_msg, manager); + if (r < 0) + return log_error_errno(r, "Failed to start device monitor: %m"); /* This needs to be after the inotify and uevent handling, to make sure * that the ping is send back after fully processing the pending uevents * (including the synthetic ones we may create due to inotify events). */ - r = sd_event_source_set_priority(manager->ctrl_event, SD_EVENT_PRIORITY_IDLE); + r = sd_event_source_set_priority(udev_ctrl_get_event_source(manager->ctrl), SD_EVENT_PRIORITY_IDLE); if (r < 0) return log_error_errno(r, "Failed to set IDLE event priority for udev control event source: %m");