summaryrefslogtreecommitdiff
path: root/libudev
diff options
context:
space:
mode:
Diffstat (limited to 'libudev')
-rw-r--r--libudev/libudev-ctrl.c233
-rw-r--r--libudev/libudev-device-private.c7
-rw-r--r--libudev/libudev-device.c11
-rw-r--r--libudev/libudev-monitor.c93
-rw-r--r--libudev/libudev-private.h37
-rw-r--r--libudev/libudev-util-private.c343
-rw-r--r--libudev/test-libudev.c82
7 files changed, 523 insertions, 283 deletions
diff --git a/libudev/libudev-ctrl.c b/libudev/libudev-ctrl.c
index 63bf539197..a2133fff1c 100644
--- a/libudev/libudev-ctrl.c
+++ b/libudev/libudev-ctrl.c
@@ -16,6 +16,7 @@
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
+#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/un.h>
@@ -33,7 +34,8 @@ enum udev_ctrl_msg_type {
UDEV_CTRL_RELOAD_RULES,
UDEV_CTRL_SET_ENV,
UDEV_CTRL_SET_CHILDREN_MAX,
- UDEV_CTRL_SETTLE,
+ UDEV_CTRL_PING,
+ UDEV_CTRL_EXIT,
};
struct udev_ctrl_msg_wire {
@@ -48,9 +50,8 @@ struct udev_ctrl_msg_wire {
struct udev_ctrl_msg {
int refcount;
- struct udev_ctrl *uctrl;
+ struct udev_ctrl_connection *conn;
struct udev_ctrl_msg_wire ctrl_msg_wire;
- pid_t pid;
};
struct udev_ctrl {
@@ -59,6 +60,14 @@ struct udev_ctrl {
int sock;
struct sockaddr_un saddr;
socklen_t addrlen;
+ bool bound;
+ bool connected;
+};
+
+struct udev_ctrl_connection {
+ int refcount;
+ struct udev_ctrl *uctrl;
+ int sock;
};
static struct udev_ctrl *udev_ctrl_new(struct udev *udev)
@@ -73,7 +82,7 @@ static struct udev_ctrl *udev_ctrl_new(struct udev *udev)
return uctrl;
}
-struct udev_ctrl *udev_ctrl_new_from_socket(struct udev *udev, const char *socket_path)
+struct udev_ctrl *udev_ctrl_new_from_socket_fd(struct udev *udev, const char *socket_path, int fd)
{
struct udev_ctrl *uctrl;
@@ -81,11 +90,16 @@ struct udev_ctrl *udev_ctrl_new_from_socket(struct udev *udev, const char *socke
if (uctrl == NULL)
return NULL;
- uctrl->sock = socket(AF_LOCAL, SOCK_DGRAM, 0);
- if (uctrl->sock < 0) {
- err(udev, "error getting socket: %m\n");
- udev_ctrl_unref(uctrl);
- return NULL;
+ if (fd < 0) {
+ uctrl->sock = socket(AF_LOCAL, SOCK_SEQPACKET|SOCK_NONBLOCK|SOCK_CLOEXEC, 0);
+ if (uctrl->sock < 0) {
+ err(udev, "error getting socket: %m\n");
+ udev_ctrl_unref(uctrl);
+ return NULL;
+ }
+ } else {
+ uctrl->bound = true;
+ uctrl->sock = fd;
}
uctrl->saddr.sun_family = AF_LOCAL;
@@ -97,33 +111,32 @@ struct udev_ctrl *udev_ctrl_new_from_socket(struct udev *udev, const char *socke
return uctrl;
}
-struct udev_ctrl *udev_ctrl_new_from_fd(struct udev *udev, int fd)
+struct udev_ctrl *udev_ctrl_new_from_socket(struct udev *udev, const char *socket_path)
{
- struct udev_ctrl *uctrl;
-
- uctrl = udev_ctrl_new(udev);
- if (uctrl == NULL)
- return NULL;
- uctrl->sock = fd;
-
- return uctrl;
+ return udev_ctrl_new_from_socket_fd(udev, socket_path, -1);
}
int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl)
{
int err;
- const int on = 1;
- if (uctrl->addrlen > 0) {
+ if (!uctrl->bound) {
err = bind(uctrl->sock, (struct sockaddr *)&uctrl->saddr, uctrl->addrlen);
if (err < 0) {
+ err = -errno;
err(uctrl->udev, "bind failed: %m\n");
return err;
}
- }
- /* enable receiving of the sender credentials */
- setsockopt(uctrl->sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
+ err = listen(uctrl->sock, 0);
+ if (err < 0) {
+ err = -errno;
+ err(uctrl->udev, "listen failed: %m\n");
+ return err;
+ }
+
+ uctrl->bound = true;
+ }
return 0;
}
@@ -140,16 +153,17 @@ struct udev_ctrl *udev_ctrl_ref(struct udev_ctrl *uctrl)
return uctrl;
}
-void udev_ctrl_unref(struct udev_ctrl *uctrl)
+struct udev_ctrl *udev_ctrl_unref(struct udev_ctrl *uctrl)
{
if (uctrl == NULL)
- return;
+ return NULL;
uctrl->refcount--;
if (uctrl->refcount > 0)
- return;
+ return uctrl;
if (uctrl->sock >= 0)
close(uctrl->sock);
free(uctrl);
+ return NULL;
}
int udev_ctrl_get_fd(struct udev_ctrl *uctrl)
@@ -159,10 +173,55 @@ int udev_ctrl_get_fd(struct udev_ctrl *uctrl)
return uctrl->sock;
}
-static int ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int intval, const char *buf)
+struct udev_ctrl_connection *udev_ctrl_get_connection(struct udev_ctrl *uctrl)
+{
+ struct udev_ctrl_connection *conn;
+ const int on = 1;
+
+ conn = calloc(1, sizeof(struct udev_ctrl_connection));
+ if (conn == NULL)
+ return NULL;
+ conn->refcount = 1;
+ conn->uctrl = uctrl;
+
+ conn->sock = accept4(uctrl->sock, NULL, NULL, SOCK_CLOEXEC);
+ if (conn->sock < 0) {
+ free(conn);
+ return NULL;
+ }
+
+ /* enable receiving of the sender credentials */
+ setsockopt(conn->sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
+ udev_ctrl_ref(uctrl);
+ return conn;
+}
+
+struct udev_ctrl_connection *udev_ctrl_connection_ref(struct udev_ctrl_connection *conn)
+{
+ if (conn == NULL)
+ return NULL;
+ conn->refcount++;
+ return conn;
+}
+
+struct udev_ctrl_connection *udev_ctrl_connection_unref(struct udev_ctrl_connection *conn)
+{
+ if (conn == NULL)
+ return NULL;
+ conn->refcount--;
+ if (conn->refcount > 0)
+ return conn;
+ if (conn->sock >= 0)
+ close(conn->sock);
+ udev_ctrl_unref(conn->uctrl);
+ free(conn);
+ return NULL;
+}
+
+static int ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int intval, const char *buf, int timeout)
{
struct udev_ctrl_msg_wire ctrl_msg_wire;
- int err;
+ int err = 0;
memset(&ctrl_msg_wire, 0x00, sizeof(struct udev_ctrl_msg_wire));
strcpy(ctrl_msg_wire.version, "udev-" VERSION);
@@ -174,51 +233,89 @@ static int ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int
else
ctrl_msg_wire.intval = intval;
- err = sendto(uctrl->sock, &ctrl_msg_wire, sizeof(ctrl_msg_wire), 0,
- (struct sockaddr *)&uctrl->saddr, uctrl->addrlen);
- if (err == -1) {
- err(uctrl->udev, "error sending message: %m\n");
+ if (!uctrl->connected) {
+ if (connect(uctrl->sock, (struct sockaddr *)&uctrl->saddr, uctrl->addrlen) < 0) {
+ err = -errno;
+ goto out;
+ }
+ uctrl->connected = true;
}
+ if (send(uctrl->sock, &ctrl_msg_wire, sizeof(ctrl_msg_wire), 0) < 0) {
+ err = -errno;
+ goto out;
+ }
+
+ /* wait for peer message handling or disconnect */
+ for (;;) {
+ struct pollfd pfd[1];
+ int r;
+
+ pfd[0].fd = uctrl->sock;
+ pfd[0].events = POLLIN;
+ r = poll(pfd, 1, timeout * 1000);
+ if (r < 0) {
+ if (errno == EINTR)
+ continue;
+ err = -errno;
+ break;
+ }
+
+ if (r > 0 && pfd[0].revents & POLLERR) {
+ err = -EIO;
+ break;
+ }
+
+ if (r == 0)
+ err = -ETIMEDOUT;
+ break;
+ }
+out:
return err;
}
-int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority)
+int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority, int timeout)
{
- return ctrl_send(uctrl, UDEV_CTRL_SET_LOG_LEVEL, priority, NULL);
+ return ctrl_send(uctrl, UDEV_CTRL_SET_LOG_LEVEL, priority, NULL, timeout);
}
-int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl)
+int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl, int timeout)
{
- return ctrl_send(uctrl, UDEV_CTRL_STOP_EXEC_QUEUE, 0, NULL);
+ return ctrl_send(uctrl, UDEV_CTRL_STOP_EXEC_QUEUE, 0, NULL, timeout);
}
-int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl)
+int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl, int timeout)
{
- return ctrl_send(uctrl, UDEV_CTRL_START_EXEC_QUEUE, 0, NULL);
+ return ctrl_send(uctrl, UDEV_CTRL_START_EXEC_QUEUE, 0, NULL, timeout);
}
-int udev_ctrl_send_reload_rules(struct udev_ctrl *uctrl)
+int udev_ctrl_send_reload_rules(struct udev_ctrl *uctrl, int timeout)
{
- return ctrl_send(uctrl, UDEV_CTRL_RELOAD_RULES, 0, NULL);
+ return ctrl_send(uctrl, UDEV_CTRL_RELOAD_RULES, 0, NULL, timeout);
}
-int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key)
+int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key, int timeout)
{
- return ctrl_send(uctrl, UDEV_CTRL_SET_ENV, 0, key);
+ return ctrl_send(uctrl, UDEV_CTRL_SET_ENV, 0, key, timeout);
}
-int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count)
+int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count, int timeout)
{
- return ctrl_send(uctrl, UDEV_CTRL_SET_CHILDREN_MAX, count, NULL);
+ return ctrl_send(uctrl, UDEV_CTRL_SET_CHILDREN_MAX, count, NULL, timeout);
}
-int udev_ctrl_send_settle(struct udev_ctrl *uctrl)
+int udev_ctrl_send_ping(struct udev_ctrl *uctrl, int timeout)
{
- return ctrl_send(uctrl, UDEV_CTRL_SETTLE, 0, NULL);
+ return ctrl_send(uctrl, UDEV_CTRL_PING, 0, NULL, timeout);
}
-struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl *uctrl)
+int udev_ctrl_send_exit(struct udev_ctrl *uctrl, int 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 *udev = conn->uctrl->udev;
struct udev_ctrl_msg *uctrl_msg;
ssize_t size;
struct msghdr smsg;
@@ -231,43 +328,40 @@ struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl *uctrl)
if (uctrl_msg == NULL)
return NULL;
uctrl_msg->refcount = 1;
- uctrl_msg->uctrl = uctrl;
+ uctrl_msg->conn = conn;
+ udev_ctrl_connection_ref(conn);
iov.iov_base = &uctrl_msg->ctrl_msg_wire;
iov.iov_len = sizeof(struct udev_ctrl_msg_wire);
-
memset(&smsg, 0x00, sizeof(struct msghdr));
smsg.msg_iov = &iov;
smsg.msg_iovlen = 1;
smsg.msg_control = cred_msg;
smsg.msg_controllen = sizeof(cred_msg);
-
- size = recvmsg(uctrl->sock, &smsg, 0);
+ size = recvmsg(conn->sock, &smsg, 0);
if (size < 0) {
- err(uctrl->udev, "unable to receive user udevd message: %m\n");
+ err(udev, "unable to receive user udevd message: %m\n");
goto err;
}
cmsg = CMSG_FIRSTHDR(&smsg);
cred = (struct ucred *) CMSG_DATA(cmsg);
if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {
- err(uctrl->udev, "no sender credentials received, message ignored\n");
+ err(udev, "no sender credentials received, message ignored\n");
goto err;
}
if (cred->uid != 0) {
- err(uctrl->udev, "sender uid=%i, message ignored\n", cred->uid);
+ err(udev, "sender uid=%i, message ignored\n", cred->uid);
goto err;
}
- uctrl_msg->pid = cred->pid;
-
if (uctrl_msg->ctrl_msg_wire.magic != UDEV_CTRL_MAGIC) {
- err(uctrl->udev, "message magic 0x%08x doesn't match, ignore it\n", uctrl_msg->ctrl_msg_wire.magic);
+ err(udev, "message magic 0x%08x doesn't match, ignore it\n", uctrl_msg->ctrl_msg_wire.magic);
goto err;
}
- dbg(uctrl->udev, "created ctrl_msg %p (%i)\n", uctrl_msg, uctrl_msg->ctrl_msg_wire.type);
+ dbg(udev, "created ctrl_msg %p (%i)\n", uctrl_msg, uctrl_msg->ctrl_msg_wire.type);
return uctrl_msg;
err:
udev_ctrl_msg_unref(uctrl_msg);
@@ -282,15 +376,17 @@ struct udev_ctrl_msg *udev_ctrl_msg_ref(struct udev_ctrl_msg *ctrl_msg)
return ctrl_msg;
}
-void udev_ctrl_msg_unref(struct udev_ctrl_msg *ctrl_msg)
+struct udev_ctrl_msg *udev_ctrl_msg_unref(struct udev_ctrl_msg *ctrl_msg)
{
if (ctrl_msg == NULL)
- return;
+ return NULL;
ctrl_msg->refcount--;
if (ctrl_msg->refcount > 0)
- return;
- dbg(ctrl_msg->uctrl->udev, "release ctrl_msg %p\n", ctrl_msg);
+ return ctrl_msg;
+ dbg(ctrl_msg->conn->uctrl->udev, "release ctrl_msg %p\n", ctrl_msg);
+ udev_ctrl_connection_unref(ctrl_msg->conn);
free(ctrl_msg);
+ return NULL;
}
int udev_ctrl_get_set_log_level(struct udev_ctrl_msg *ctrl_msg)
@@ -335,9 +431,16 @@ int udev_ctrl_get_set_children_max(struct udev_ctrl_msg *ctrl_msg)
return -1;
}
-pid_t udev_ctrl_get_settle(struct udev_ctrl_msg *ctrl_msg)
+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;
+}
+
+int udev_ctrl_get_exit(struct udev_ctrl_msg *ctrl_msg)
{
- if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_SETTLE)
- return ctrl_msg->pid;
+ if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_EXIT)
+ return 1;
return -1;
}
diff --git a/libudev/libudev-device-private.c b/libudev/libudev-device-private.c
index 6d72d328bd..ee9e297cea 100644
--- a/libudev/libudev-device-private.c
+++ b/libudev/libudev-device-private.c
@@ -134,6 +134,13 @@ int udev_device_update_db(struct udev_device *udev_device)
return -1;
}
+ /*
+ * set 'sticky' bit to indicate that we should not clean the
+ * database when we transition from initramfs to the real root
+ */
+ if (udev_device_get_db_persist(udev_device))
+ fchmod(fileno(f), 01644);
+
if (has_info) {
size_t devlen = strlen(udev_get_dev_path(udev))+1;
struct udev_list_entry *list_entry;
diff --git a/libudev/libudev-device.c b/libudev/libudev-device.c
index ccd4a70677..a141dadf0a 100644
--- a/libudev/libudev-device.c
+++ b/libudev/libudev-device.c
@@ -89,6 +89,7 @@ struct udev_device {
bool uevent_loaded;
bool is_initialized;
bool sysattr_list_read;
+ bool db_persist;
};
struct udev_list_entry *udev_device_add_property(struct udev_device *udev_device, const char *key, const char *value)
@@ -1774,3 +1775,13 @@ int udev_device_set_ifindex(struct udev_device *udev_device, int ifindex)
udev_device_add_property(udev_device, "IFINDEX", num);
return 0;
}
+
+bool udev_device_get_db_persist(struct udev_device *udev_device)
+{
+ return udev_device->db_persist;
+}
+
+void udev_device_set_db_persist(struct udev_device *udev_device)
+{
+ udev_device->db_persist = true;
+}
diff --git a/libudev/libudev-monitor.c b/libudev/libudev-monitor.c
index c97f6faa65..d890b4b142 100644
--- a/libudev/libudev-monitor.c
+++ b/libudev/libudev-monitor.c
@@ -50,6 +50,7 @@ struct udev_monitor {
socklen_t addrlen;
struct udev_list_node filter_subsystem_list;
struct udev_list_node filter_tag_list;
+ bool bound;
};
enum udev_monitor_netlink_group {
@@ -144,7 +145,7 @@ struct udev_monitor *udev_monitor_new_from_socket(struct udev *udev, const char
util_strscpy(&udev_monitor->sun.sun_path[1], sizeof(udev_monitor->sun.sun_path)-1, socket_path);
udev_monitor->addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(socket_path)+1;
}
- udev_monitor->sock = socket(AF_LOCAL, SOCK_DGRAM|SOCK_CLOEXEC, 0);
+ udev_monitor->sock = socket(AF_LOCAL, SOCK_DGRAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0);
if (udev_monitor->sock == -1) {
err(udev, "error getting socket: %m\n");
free(udev_monitor);
@@ -155,32 +156,7 @@ struct udev_monitor *udev_monitor_new_from_socket(struct udev *udev, const char
return udev_monitor;
}
-/**
- * udev_monitor_new_from_netlink:
- * @udev: udev library context
- * @name: name of event source
- *
- * Create new udev monitor and connect to a specified event
- * source. Valid sources identifiers are "udev" and "kernel".
- *
- * Applications should usually not connect directly to the
- * "kernel" events, because the devices might not be useable
- * at that time, before udev has configured them, and created
- * device nodes.
- *
- * Accessing devices at the same time as udev, might result
- * in unpredictable behavior.
- *
- * The "udev" events are sent out after udev has finished its
- * event processing, all rules have been processed, and needed
- * device nodes are created.
- *
- * The initial refcount is 1, and needs to be decremented to
- * release the resources of the udev monitor.
- *
- * Returns: a new udev monitor, or #NULL, in case of an error
- **/
-struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char *name)
+struct udev_monitor *udev_monitor_new_from_netlink_fd(struct udev *udev, const char *name, int fd)
{
struct udev_monitor *udev_monitor;
unsigned int group;
@@ -201,11 +177,16 @@ struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char
if (udev_monitor == NULL)
return NULL;
- udev_monitor->sock = socket(PF_NETLINK, SOCK_DGRAM|SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT);
- if (udev_monitor->sock == -1) {
- err(udev, "error getting socket: %m\n");
- free(udev_monitor);
- return NULL;
+ if (fd < 0) {
+ udev_monitor->sock = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT);
+ if (udev_monitor->sock == -1) {
+ err(udev, "error getting socket: %m\n");
+ free(udev_monitor);
+ return NULL;
+ }
+ } else {
+ udev_monitor->bound = true;
+ udev_monitor->sock = fd;
}
udev_monitor->snl.nl_family = AF_NETLINK;
@@ -219,6 +200,36 @@ struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char
return udev_monitor;
}
+/**
+ * udev_monitor_new_from_netlink:
+ * @udev: udev library context
+ * @name: name of event source
+ *
+ * Create new udev monitor and connect to a specified event
+ * source. Valid sources identifiers are "udev" and "kernel".
+ *
+ * Applications should usually not connect directly to the
+ * "kernel" events, because the devices might not be useable
+ * at that time, before udev has configured them, and created
+ * device nodes.
+ *
+ * Accessing devices at the same time as udev, might result
+ * in unpredictable behavior.
+ *
+ * The "udev" events are sent out after udev has finished its
+ * event processing, all rules have been processed, and needed
+ * device nodes are created.
+ *
+ * The initial refcount is 1, and needs to be decremented to
+ * release the resources of the udev monitor.
+ *
+ * Returns: a new udev monitor, or #NULL, in case of an error
+ **/
+struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char *name)
+{
+ return udev_monitor_new_from_netlink_fd(udev, name, -1);
+}
+
static inline void bpf_stmt(struct sock_filter *inss, unsigned int *i,
unsigned short code, unsigned int data)
{
@@ -364,16 +375,24 @@ int udev_monitor_allow_unicast_sender(struct udev_monitor *udev_monitor, struct
*/
int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor)
{
- int err;
+ int err = 0;
const int on = 1;
if (udev_monitor->sun.sun_family != 0) {
- err = bind(udev_monitor->sock,
- (struct sockaddr *)&udev_monitor->sun, udev_monitor->addrlen);
+ if (!udev_monitor->bound) {
+ err = bind(udev_monitor->sock,
+ (struct sockaddr *)&udev_monitor->sun, udev_monitor->addrlen);
+ if (err == 0)
+ udev_monitor->bound = true;
+ }
} else if (udev_monitor->snl.nl_family != 0) {
udev_monitor_filter_update(udev_monitor);
- err = bind(udev_monitor->sock,
- (struct sockaddr *)&udev_monitor->snl, sizeof(struct sockaddr_nl));
+ if (!udev_monitor->bound) {
+ err = bind(udev_monitor->sock,
+ (struct sockaddr *)&udev_monitor->snl, sizeof(struct sockaddr_nl));
+ if (err == 0)
+ udev_monitor->bound = true;
+ }
if (err == 0) {
struct sockaddr_nl snl;
socklen_t addrlen;
diff --git a/libudev/libudev-private.h b/libudev/libudev-private.h
index 8495f9aaee..63eb0704d6 100644
--- a/libudev/libudev-private.h
+++ b/libudev/libudev-private.h
@@ -109,6 +109,8 @@ int udev_device_set_watch_handle(struct udev_device *udev_device, int handle);
int udev_device_get_ifindex(struct udev_device *udev_device);
int udev_device_set_ifindex(struct udev_device *udev_device, int ifindex);
void udev_device_set_info_loaded(struct udev_device *device);
+bool udev_device_get_db_persist(struct udev_device *udev_device);
+void udev_device_set_db_persist(struct udev_device *udev_device);
/* libudev-device-private.c */
int udev_device_update_db(struct udev_device *udev_device);
@@ -120,33 +122,39 @@ int udev_monitor_disconnect(struct udev_monitor *udev_monitor);
int udev_monitor_allow_unicast_sender(struct udev_monitor *udev_monitor, struct udev_monitor *sender);
int udev_monitor_send_device(struct udev_monitor *udev_monitor,
struct udev_monitor *destination, struct udev_device *udev_device);
+struct udev_monitor *udev_monitor_new_from_netlink_fd(struct udev *udev, const char *name, int fd);
/* libudev-ctrl.c - daemon runtime setup */
struct udev_ctrl;
struct udev_ctrl *udev_ctrl_new_from_socket(struct udev *udev, const char *socket_path);
-struct udev_ctrl *udev_ctrl_new_from_fd(struct udev *udev, int fd);
+struct udev_ctrl *udev_ctrl_new_from_socket_fd(struct udev *udev, const char *socket_path, int fd);
int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl);
struct udev_ctrl *udev_ctrl_ref(struct udev_ctrl *uctrl);
-void udev_ctrl_unref(struct udev_ctrl *uctrl);
+struct udev_ctrl *udev_ctrl_unref(struct udev_ctrl *uctrl);
struct udev *udev_ctrl_get_udev(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);
-int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl);
-int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl);
-int udev_ctrl_send_reload_rules(struct udev_ctrl *uctrl);
-int udev_ctrl_send_settle(struct udev_ctrl *uctrl);
-int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key);
-int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count);
+int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority, int timeout);
+int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl, int timeout);
+int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl, int timeout);
+int udev_ctrl_send_reload_rules(struct udev_ctrl *uctrl, int timeout);
+int udev_ctrl_send_ping(struct udev_ctrl *uctrl, int timeout);
+int udev_ctrl_send_exit(struct udev_ctrl *uctrl, int timeout);
+int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key, int timeout);
+int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count, int timeout);
+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);
struct udev_ctrl_msg;
-struct udev_ctrl_msg *udev_ctrl_msg(struct udev_ctrl *uctrl);
-struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl *uctrl);
+struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn);
struct udev_ctrl_msg *udev_ctrl_msg_ref(struct udev_ctrl_msg *ctrl_msg);
-void udev_ctrl_msg_unref(struct udev_ctrl_msg *ctrl_msg);
+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_rules(struct udev_ctrl_msg *ctrl_msg);
-pid_t udev_ctrl_get_settle(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);
@@ -154,6 +162,7 @@ int udev_ctrl_get_set_children_max(struct udev_ctrl_msg *ctrl_msg);
struct udev_list_node {
struct udev_list_node *next, *prev;
};
+#define UDEV_LIST(list) struct udev_list_node list = { &(list), &(list) }
void udev_list_init(struct udev_list_node *list);
int udev_list_is_empty(struct udev_list_node *list);
void udev_list_node_append(struct udev_list_node *new, struct udev_list_node *list);
@@ -227,7 +236,7 @@ uid_t util_lookup_user(struct udev *udev, const char *user);
gid_t util_lookup_group(struct udev *udev, const char *group);
int util_run_program(struct udev *udev, const char *command, char **envp,
char *result, size_t ressize, size_t *reslen,
- const sigset_t *sigmask, bool reset_prio);
+ const sigset_t *sigmask);
int util_resolve_subsys_kernel(struct udev *udev, const char *string,
char *result, size_t maxsize, int read_value);
unsigned long long usec_monotonic(void);
diff --git a/libudev/libudev-util-private.c b/libudev/libudev-util-private.c
index 19f979eeab..87b9edbc71 100644
--- a/libudev/libudev-util-private.c
+++ b/libudev/libudev-util-private.c
@@ -19,6 +19,8 @@
#include <ctype.h>
#include <pwd.h>
#include <grp.h>
+#include <sys/prctl.h>
+#include <sys/epoll.h>
#include <sys/wait.h>
#include <sys/param.h>
@@ -134,7 +136,7 @@ int util_unlink_secure(struct udev *udev, const char *filename)
uid_t util_lookup_user(struct udev *udev, const char *user)
{
char *endptr;
- int buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
+ size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
char buf[buflen];
struct passwd pwbuf;
struct passwd *pw;
@@ -159,7 +161,7 @@ uid_t util_lookup_user(struct udev *udev, const char *user)
gid_t util_lookup_group(struct udev *udev, const char *group)
{
char *endptr;
- int buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
+ size_t buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
char *buf;
struct group grbuf;
struct group *gr;
@@ -259,9 +261,172 @@ int util_resolve_subsys_kernel(struct udev *udev, const char *string,
return 0;
}
+static int run_program_exec(struct udev *udev, int fd_stdout, int fd_stderr,
+ char *const argv[], char **envp, const sigset_t *sigmask)
+{
+ int err;
+ int fd;
+
+ /* discard child output or connect to pipe */
+ fd = open("/dev/null", O_RDWR);
+ if (fd >= 0) {
+ dup2(fd, STDIN_FILENO);
+ if (fd_stdout < 0)
+ dup2(fd, STDOUT_FILENO);
+ if (fd_stderr < 0)
+ dup2(fd, STDERR_FILENO);
+ close(fd);
+ } else {
+ err(udev, "open /dev/null failed: %m\n");
+ }
+
+ /* connect pipes to std{out,err} */
+ if (fd_stdout >= 0) {
+ dup2(fd_stdout, STDOUT_FILENO);
+ close(fd_stdout);
+ }
+ if (fd_stderr >= 0) {
+ dup2(fd_stderr, STDERR_FILENO);
+ close(fd_stderr);
+ }
+
+ /* terminate child in case parent goes away */
+ prctl(PR_SET_PDEATHSIG, SIGTERM);
+
+ /* restore original udev sigmask before exec */
+ if (sigmask)
+ sigprocmask(SIG_SETMASK, sigmask, NULL);
+
+ execve(argv[0], argv, envp);
+
+ /* exec failed */
+ err = -errno;
+ if (err == -ENOENT || err == -ENOTDIR) {
+ /* may be on a filesystem which is not mounted right now */
+ info(udev, "program '%s' not found\n", argv[0]);
+ } else {
+ /* other problems */
+ err(udev, "exec of program '%s' failed\n", argv[0]);
+ }
+ return err;
+}
+
+static int run_program_read(struct udev *udev, int fd_stdout, int fd_stderr,
+ char *const argv[], char *result, size_t ressize, size_t *reslen)
+{
+ size_t respos = 0;
+ int fd_ep = -1;
+ struct epoll_event ep_outpipe;
+ struct epoll_event ep_errpipe;
+ int err = 0;
+
+ /* read from child if requested */
+ if (fd_stdout < 0 && fd_stderr < 0)
+ return 0;
+
+ fd_ep = epoll_create1(EPOLL_CLOEXEC);
+ if (fd_ep < 0) {
+ err = -errno;
+ err(udev, "error creating epoll fd: %m\n");
+ goto out;
+ }
+
+ if (fd_stdout >= 0) {
+ memset(&ep_outpipe, 0, sizeof(struct epoll_event));
+ ep_outpipe.events = EPOLLIN;
+ ep_outpipe.data.ptr = &fd_stdout;
+ if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_stdout, &ep_outpipe) < 0) {
+ err(udev, "fail to add fd to epoll: %m\n");
+ goto out;
+ }
+ }
+
+ if (fd_stderr >= 0) {
+ memset(&ep_errpipe, 0, sizeof(struct epoll_event));
+ ep_errpipe.events = EPOLLIN;
+ ep_errpipe.data.ptr = &fd_stderr;
+ if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_stderr, &ep_errpipe) < 0) {
+ err(udev, "fail to add fd to epoll: %m\n");
+ goto out;
+ }
+ }
+
+ /* read child output */
+ while (fd_stdout >= 0 || fd_stderr >= 0) {
+ int fdcount;
+ struct epoll_event ev[4];
+ int i;
+
+ fdcount = epoll_wait(fd_ep, ev, ARRAY_SIZE(ev), -1);
+ if (fdcount < 0) {
+ if (errno == EINTR)
+ continue;
+ err(udev, "failed to poll: %m\n");
+ err = -errno;
+ goto out;
+ }
+
+ for (i = 0; i < fdcount; i++) {
+ int *fd = (int *)ev[i].data.ptr;
+
+ if (ev[i].events & EPOLLIN) {
+ ssize_t count;
+ char buf[4096];
+
+ count = read(*fd, buf, sizeof(buf)-1);
+ if (count <= 0)
+ continue;
+ buf[count] = '\0';
+
+ /* store stdout result */
+ if (result != NULL && *fd == fd_stdout) {
+ if (respos + count < ressize) {
+ memcpy(&result[respos], buf, count);
+ respos += count;
+ } else {
+ err(udev, "ressize %zd too short\n", ressize);
+ err = -ENOBUFS;
+ }
+ }
+
+ /* log debug output only if we watch stderr */
+ if (fd_stderr >= 0) {
+ char *pos;
+ char *line;
+
+ pos = buf;
+ while ((line = strsep(&pos, "\n"))) {
+ if (pos != NULL || line[0] != '\0')
+ info(udev, "'%s'(%s) '%s'\n", argv[0], *fd == fd_stdout ? "out" : "err" , line);
+ }
+ }
+ } else if (ev[i].events & EPOLLHUP) {
+ if (epoll_ctl(fd_ep, EPOLL_CTL_DEL, *fd, NULL) < 0) {
+ err = -errno;
+ err(udev, "failed to remove fd from epoll: %m\n");
+ goto out;
+ }
+ *fd = -1;
+ }
+ }
+ }
+
+ /* return the child's stdout string */
+ if (result != NULL) {
+ result[respos] = '\0';
+ dbg(udev, "result='%s'\n", result);
+ if (reslen != NULL)
+ *reslen = respos;
+ }
+out:
+ if (fd_ep >= 0)
+ close(fd_ep);
+ return err;
+}
+
int util_run_program(struct udev *udev, const char *command, char **envp,
char *result, size_t ressize, size_t *reslen,
- const sigset_t *sigmask, bool reset_prio)
+ const sigset_t *sigmask)
{
int status;
int outpipe[2] = {-1, -1};
@@ -270,7 +435,6 @@ int util_run_program(struct udev *udev, const char *command, char **envp,
char arg[UTIL_PATH_SIZE];
char program[UTIL_PATH_SIZE];
char *argv[((sizeof(arg) + 1) / 2) + 1];
- int devnull;
int i;
int err = 0;
@@ -305,17 +469,19 @@ int util_run_program(struct udev *udev, const char *command, char **envp,
argv[1] = NULL;
}
- /* prepare pipes from child to parent */
+ /* pipes from child to parent */
if (result != NULL || udev_get_log_priority(udev) >= LOG_INFO) {
- if (pipe(outpipe) != 0) {
+ if (pipe2(outpipe, O_NONBLOCK) != 0) {
+ err = -errno;
err(udev, "pipe failed: %m\n");
- return -1;
+ goto out;
}
}
if (udev_get_log_priority(udev) >= LOG_INFO) {
- if (pipe(errpipe) != 0) {
+ if (pipe2(errpipe, O_NONBLOCK) != 0) {
+ err = -errno;
err(udev, "pipe failed: %m\n");
- return -1;
+ goto out;
}
}
@@ -328,147 +494,36 @@ int util_run_program(struct udev *udev, const char *command, char **envp,
pid = fork();
switch(pid) {
case 0:
- /* child closes parent ends of pipes */
- if (outpipe[READ_END] > 0)
+ /* child closes parent's ends of pipes */
+ if (outpipe[READ_END] >= 0) {
close(outpipe[READ_END]);
- if (errpipe[READ_END] > 0)
- close(errpipe[READ_END]);
-
- /* discard child output or connect to pipe */
- devnull = open("/dev/null", O_RDWR);
- if (devnull > 0) {
- dup2(devnull, STDIN_FILENO);
- if (outpipe[WRITE_END] < 0)
- dup2(devnull, STDOUT_FILENO);
- if (errpipe[WRITE_END] < 0)
- dup2(devnull, STDERR_FILENO);
- close(devnull);
- } else
- err(udev, "open /dev/null failed: %m\n");
- if (outpipe[WRITE_END] > 0) {
- dup2(outpipe[WRITE_END], STDOUT_FILENO);
- close(outpipe[WRITE_END]);
+ outpipe[READ_END] = -1;
}
- if (errpipe[WRITE_END] > 0) {
- dup2(errpipe[WRITE_END], STDERR_FILENO);
- close(errpipe[WRITE_END]);
+ if (errpipe[READ_END] >= 0) {
+ close(errpipe[READ_END]);
+ errpipe[READ_END] = -1;
}
- if (sigmask)
- sigprocmask(SIG_SETMASK, sigmask, NULL);
- if (reset_prio)
- setpriority(PRIO_PROCESS, 0, 0);
+ err = run_program_exec(udev, outpipe[WRITE_END], errpipe[WRITE_END], argv, envp, sigmask);
- execve(argv[0], argv, envp);
- if (errno == ENOENT || errno == ENOTDIR) {
- /* may be on a filesystem which is not mounted right now */
- info(udev, "program '%s' not found\n", argv[0]);
- } else {
- /* other problems */
- err(udev, "exec of program '%s' failed\n", argv[0]);
- }
_exit(1);
case -1:
err(udev, "fork of '%s' failed: %m\n", argv[0]);
- return -1;
+ err = -1;
+ goto out;
default:
- /* read from child if requested */
- if (outpipe[READ_END] > 0 || errpipe[READ_END] > 0) {
- ssize_t count;
- size_t respos = 0;
-
- /* parent closes child ends of pipes */
- if (outpipe[WRITE_END] > 0)
- close(outpipe[WRITE_END]);
- if (errpipe[WRITE_END] > 0)
- close(errpipe[WRITE_END]);
-
- /* read child output */
- while (outpipe[READ_END] > 0 || errpipe[READ_END] > 0) {
- int fdcount;
- fd_set readfds;
-
- FD_ZERO(&readfds);
- if (outpipe[READ_END] > 0)
- FD_SET(outpipe[READ_END], &readfds);
- if (errpipe[READ_END] > 0)
- FD_SET(errpipe[READ_END], &readfds);
- fdcount = select(MAX(outpipe[READ_END], errpipe[READ_END])+1, &readfds, NULL, NULL, NULL);
- if (fdcount < 0) {
- if (errno == EINTR)
- continue;
- err = -1;
- break;
- }
-
- /* get stdout */
- if (outpipe[READ_END] > 0 && FD_ISSET(outpipe[READ_END], &readfds)) {
- char inbuf[1024];
- char *pos;
- char *line;
-
- count = read(outpipe[READ_END], inbuf, sizeof(inbuf)-1);
- if (count <= 0) {
- close(outpipe[READ_END]);
- outpipe[READ_END] = -1;
- if (count < 0) {
- err(udev, "stdin read failed: %m\n");
- err = -1;
- }
- continue;
- }
- inbuf[count] = '\0';
-
- /* store result for rule processing */
- if (result) {
- if (respos + count < ressize) {
- memcpy(&result[respos], inbuf, count);
- respos += count;
- } else {
- err(udev, "ressize %ld too short\n", (long)ressize);
- err = -1;
- }
- }
- pos = inbuf;
- while ((line = strsep(&pos, "\n")))
- if (pos || line[0] != '\0')
- info(udev, "'%s' (stdout) '%s'\n", argv[0], line);
- }
+ /* parent closed child's ends of pipes */
+ if (outpipe[WRITE_END] >= 0) {
+ close(outpipe[WRITE_END]);
+ outpipe[WRITE_END] = -1;
+ }
+ if (errpipe[WRITE_END] >= 0) {
+ close(errpipe[WRITE_END]);
+ errpipe[WRITE_END] = -1;
+ }
- /* get stderr */
- if (errpipe[READ_END] > 0 && FD_ISSET(errpipe[READ_END], &readfds)) {
- char errbuf[1024];
- char *pos;
- char *line;
+ err = run_program_read(udev, outpipe[READ_END], errpipe[READ_END], argv, result, ressize, reslen);
- count = read(errpipe[READ_END], errbuf, sizeof(errbuf)-1);
- if (count <= 0) {
- close(errpipe[READ_END]);
- errpipe[READ_END] = -1;
- if (count < 0)
- err(udev, "stderr read failed: %m\n");
- continue;
- }
- errbuf[count] = '\0';
- pos = errbuf;
- while ((line = strsep(&pos, "\n")))
- if (pos || line[0] != '\0')
- info(udev, "'%s' (stderr) '%s'\n", argv[0], line);
- }
- }
- if (outpipe[READ_END] > 0)
- close(outpipe[READ_END]);
- if (errpipe[READ_END] > 0)
- close(errpipe[READ_END]);
-
- /* return the child's stdout string */
- if (result) {
- result[respos] = '\0';
- dbg(udev, "result='%s'\n", result);
- if (reslen)
- *reslen = respos;
- }
- }
waitpid(pid, &status, 0);
if (WIFEXITED(status)) {
info(udev, "'%s' returned with exitcode %i\n", command, WEXITSTATUS(status));
@@ -479,5 +534,15 @@ int util_run_program(struct udev *udev, const char *command, char **envp,
err = -1;
}
}
+
+out:
+ if (outpipe[READ_END] >= 0)
+ close(outpipe[READ_END]);
+ if (outpipe[WRITE_END] >= 0)
+ close(outpipe[WRITE_END]);
+ if (errpipe[READ_END] >= 0)
+ close(errpipe[READ_END]);
+ if (errpipe[WRITE_END] >= 0)
+ close(errpipe[WRITE_END]);
return err;
}
diff --git a/libudev/test-libudev.c b/libudev/test-libudev.c
index 5ff8663b90..4192ba14b2 100644
--- a/libudev/test-libudev.c
+++ b/libudev/test-libudev.c
@@ -18,10 +18,12 @@
#include <getopt.h>
#include <syslog.h>
#include <fcntl.h>
-#include <sys/select.h>
+#include <sys/epoll.h>
#include "libudev.h"
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
static void log_fn(struct udev *udev,
int priority, const char *file, int line, const char *fn,
const char *format, va_list args)
@@ -220,55 +222,79 @@ static int test_enumerate_print_list(struct udev_enumerate *enumerate)
static int test_monitor(struct udev *udev)
{
struct udev_monitor *udev_monitor;
- fd_set readfds;
- int fd;
+ int fd_ep;
+ int fd_udev = -1;
+ struct epoll_event ep_udev, ep_stdin;
+
+ fd_ep = epoll_create1(EPOLL_CLOEXEC);
+ if (fd_ep < 0) {
+ printf("error creating epoll fd: %m\n");
+ goto out;
+ }
udev_monitor = udev_monitor_new_from_netlink(udev, "udev");
if (udev_monitor == NULL) {
printf("no socket\n");
- return -1;
+ goto out;
}
+ fd_udev = udev_monitor_get_fd(udev_monitor);
+
if (udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "block", NULL) < 0 ||
udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "tty", NULL) < 0 ||
udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "usb", "usb_device") < 0) {
printf("filter failed\n");
- return -1;
+ goto out;
}
+
if (udev_monitor_enable_receiving(udev_monitor) < 0) {
printf("bind failed\n");
- return -1;
+ goto out;
}
- fd = udev_monitor_get_fd(udev_monitor);
- FD_ZERO(&readfds);
+ memset(&ep_udev, 0, sizeof(struct epoll_event));
+ ep_udev.events = EPOLLIN;
+ ep_udev.data.fd = fd_udev;
+ if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_udev, &ep_udev) < 0) {
+ printf("fail to add fd to epoll: %m\n");
+ goto out;
+ }
+
+ memset(&ep_stdin, 0, sizeof(struct epoll_event));
+ ep_stdin.events = EPOLLIN;
+ ep_stdin.data.fd = STDIN_FILENO;
+ if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, STDIN_FILENO, &ep_stdin) < 0) {
+ printf("fail to add fd to epoll: %m\n");
+ goto out;
+ }
for (;;) {
- struct udev_device *device;
int fdcount;
-
- FD_SET(STDIN_FILENO, &readfds);
- FD_SET(fd, &readfds);
+ struct epoll_event ev[4];
+ struct udev_device *device;
+ int i;
printf("waiting for events from udev, press ENTER to exit\n");
- fdcount = select(fd+1, &readfds, NULL, NULL, NULL);
- printf("select fd count: %i\n", fdcount);
-
- if (FD_ISSET(fd, &readfds)) {
- device = udev_monitor_receive_device(udev_monitor);
- if (device == NULL) {
- printf("no device from socket\n");
- continue;
+ fdcount = epoll_wait(fd_ep, ev, ARRAY_SIZE(ev), -1);
+ printf("epoll fd count: %i\n", fdcount);
+
+ for (i = 0; i < fdcount; i++) {
+ if (ev[i].data.fd == fd_udev && ev[i].events & EPOLLIN) {
+ device = udev_monitor_receive_device(udev_monitor);
+ if (device == NULL) {
+ printf("no device from socket\n");
+ continue;
+ }
+ print_device(device);
+ udev_device_unref(device);
+ } else if (ev[i].data.fd == STDIN_FILENO && ev[i].events & EPOLLIN) {
+ printf("exiting loop\n");
+ goto out;
}
- print_device(device);
- udev_device_unref(device);
- }
-
- if (FD_ISSET(STDIN_FILENO, &readfds)) {
- printf("exiting loop\n");
- break;
}
}
-
+out:
+ if (fd_ep >= 0)
+ close(fd_ep);
udev_monitor_unref(udev_monitor);
return 0;
}