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");