diff options
Diffstat (limited to 'libudev')
-rw-r--r-- | libudev/libudev-ctrl.c | 233 | ||||
-rw-r--r-- | libudev/libudev-device-private.c | 7 | ||||
-rw-r--r-- | libudev/libudev-device.c | 11 | ||||
-rw-r--r-- | libudev/libudev-monitor.c | 93 | ||||
-rw-r--r-- | libudev/libudev-private.h | 37 | ||||
-rw-r--r-- | libudev/libudev-util-private.c | 343 | ||||
-rw-r--r-- | libudev/test-libudev.c | 82 |
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; } |