diff options
-rw-r--r-- | TODO | 4 | ||||
-rw-r--r-- | init/udev.socket | 2 | ||||
-rw-r--r-- | libudev/libudev-ctrl.c | 199 | ||||
-rw-r--r-- | libudev/libudev-private.h | 30 | ||||
-rw-r--r-- | udev/udevadm-control.c | 59 | ||||
-rw-r--r-- | udev/udevadm-settle.c | 21 | ||||
-rw-r--r-- | udev/udevadm.xml | 12 | ||||
-rw-r--r-- | udev/udevd.c | 371 |
8 files changed, 484 insertions, 214 deletions
@@ -1,4 +1,6 @@ - - bind control socket in systemd + - use inotify() in settle + + - do not write age/configured in initramfs - remove deprecated trigger --type=failed logic diff --git a/init/udev.socket b/init/udev.socket index 18a154fc82..81296306ac 100644 --- a/init/udev.socket +++ b/init/udev.socket @@ -3,4 +3,4 @@ Description=udev Kernel Device Manager Socket DefaultDependencies=no [Socket] -ListenDatagram=@/org/kernel/udev/udevd +ListenSequentialPacket=@/org/kernel/udev/udevd diff --git a/libudev/libudev-ctrl.c b/libudev/libudev-ctrl.c index 63bf539197..cea1b7f55b 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,13 @@ struct udev_ctrl { int sock; struct sockaddr_un saddr; socklen_t addrlen; + bool connected; +}; + +struct udev_ctrl_connection { + int refcount; + struct udev_ctrl *uctrl; + int sock; }; static struct udev_ctrl *udev_ctrl_new(struct udev *udev) @@ -81,7 +89,7 @@ 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); + uctrl->sock = socket(AF_LOCAL, SOCK_SEQPACKET, 0); if (uctrl->sock < 0) { err(udev, "error getting socket: %m\n"); udev_ctrl_unref(uctrl); @@ -112,18 +120,21 @@ struct udev_ctrl *udev_ctrl_new_from_fd(struct udev *udev, int fd) int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl) { int err; - const int on = 1; if (uctrl->addrlen > 0) { err = bind(uctrl->sock, (struct sockaddr *)&uctrl->saddr, uctrl->addrlen); if (err < 0) { + err = -errno; err(uctrl->udev, "bind failed: %m\n"); return err; } + err = listen(uctrl->sock, 0); + if (err < 0) { + err = -errno; + err(uctrl->udev, "listen failed: %m\n"); + return err; + } } - - /* enable receiving of the sender credentials */ - setsockopt(uctrl->sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); return 0; } @@ -140,16 +151,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 +171,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 +231,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, timeout); +} + +int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl, int timeout) { - return ctrl_send(uctrl, UDEV_CTRL_SET_LOG_LEVEL, priority, NULL); + return ctrl_send(uctrl, UDEV_CTRL_STOP_EXEC_QUEUE, 0, NULL, timeout); } -int udev_ctrl_send_stop_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_STOP_EXEC_QUEUE, 0, NULL); + return ctrl_send(uctrl, UDEV_CTRL_START_EXEC_QUEUE, 0, NULL, timeout); } -int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl) +int udev_ctrl_send_reload_rules(struct udev_ctrl *uctrl, int timeout) { - return ctrl_send(uctrl, UDEV_CTRL_START_EXEC_QUEUE, 0, NULL); + return ctrl_send(uctrl, UDEV_CTRL_RELOAD_RULES, 0, NULL, timeout); } -int udev_ctrl_send_reload_rules(struct udev_ctrl *uctrl) +int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key, int timeout) { - return ctrl_send(uctrl, UDEV_CTRL_RELOAD_RULES, 0, NULL); + return ctrl_send(uctrl, UDEV_CTRL_SET_ENV, 0, key, timeout); } -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 timeout) { - return ctrl_send(uctrl, UDEV_CTRL_SET_ENV, 0, key); + return ctrl_send(uctrl, UDEV_CTRL_SET_CHILDREN_MAX, count, NULL, timeout); } -int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count) +int udev_ctrl_send_ping(struct udev_ctrl *uctrl, int timeout) { - return ctrl_send(uctrl, UDEV_CTRL_SET_CHILDREN_MAX, count, NULL); + return ctrl_send(uctrl, UDEV_CTRL_PING, 0, NULL, timeout); } -int udev_ctrl_send_settle(struct udev_ctrl *uctrl) +int udev_ctrl_send_exit(struct udev_ctrl *uctrl, int timeout) { - return ctrl_send(uctrl, UDEV_CTRL_SETTLE, 0, NULL); + return ctrl_send(uctrl, UDEV_CTRL_EXIT, 0, NULL, timeout); } -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 *udev = conn->uctrl->udev; struct udev_ctrl_msg *uctrl_msg; ssize_t size; struct msghdr smsg; @@ -231,7 +326,7 @@ 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; iov.iov_base = &uctrl_msg->ctrl_msg_wire; iov.iov_len = sizeof(struct udev_ctrl_msg_wire); @@ -242,32 +337,31 @@ struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl *uctrl) 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); + udev_ctrl_connection_ref(conn); 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_SETTLE) - return ctrl_msg->pid; + 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_EXIT) + return 1; return -1; } diff --git a/libudev/libudev-private.h b/libudev/libudev-private.h index 8495f9aaee..f6137b5dc7 100644 --- a/libudev/libudev-private.h +++ b/libudev/libudev-private.h @@ -127,26 +127,31 @@ struct udev_ctrl *udev_ctrl_new_from_socket(struct udev *udev, const char *socke struct udev_ctrl *udev_ctrl_new_from_fd(struct udev *udev, 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 +159,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); diff --git a/udev/udevadm-control.c b/udev/udevadm-control.c index 0447804c95..69da86563d 100644 --- a/udev/udevadm-control.c +++ b/udev/udevadm-control.c @@ -30,21 +30,25 @@ static void print_help(void) { printf("Usage: udevadm control COMMAND\n" + " --exit instruct the daemon to cleanup and exit\n" " --log-priority=<level> set the udev log level for the daemon\n" " --stop-exec-queue keep udevd from executing events, queue only\n" " --start-exec-queue execute events, flush queue\n" " --reload-rules reloads the rules files\n" " --property=<KEY>=<value> set a global property for all events\n" " --children-max=<N> maximum number of children\n" + " --timeout=<seconds> maximum time to block for a reply\n" " --help print this help text\n\n"); } int udevadm_control(struct udev *udev, int argc, char *argv[]) { struct udev_ctrl *uctrl = NULL; + int timeout = 60; int rc = 1; static const struct option options[] = { + { "exit", no_argument, NULL, 'e' }, { "log-priority", required_argument, NULL, 'l' }, { "stop-exec-queue", no_argument, NULL, 's' }, { "start-exec-queue", no_argument, NULL, 'S' }, @@ -52,6 +56,7 @@ int udevadm_control(struct udev *udev, int argc, char *argv[]) { "property", required_argument, NULL, 'p' }, { "env", required_argument, NULL, 'p' }, { "children-max", required_argument, NULL, 'm' }, + { "timeout", required_argument, NULL, 't' }, { "help", no_argument, NULL, 'h' }, {} }; @@ -67,39 +72,46 @@ int udevadm_control(struct udev *udev, int argc, char *argv[]) for (;;) { int option; - int i; - char *endp; - option = getopt_long(argc, argv, "l:sSRp:m:h", options, NULL); + option = getopt_long(argc, argv, "el:sSRp:m:h", options, NULL); if (option == -1) break; switch (option) { - case 'l': + case 'e': + if (udev_ctrl_send_exit(uctrl, timeout) < 0) + rc = 2; + else + rc = 0; + break; + case 'l': { + int i; + i = util_log_priority(optarg); if (i < 0) { fprintf(stderr, "invalid number '%s'\n", optarg); - goto exit; + goto out; } - if (udev_ctrl_send_set_log_level(uctrl, util_log_priority(optarg)) < 0) + if (udev_ctrl_send_set_log_level(uctrl, util_log_priority(optarg), timeout) < 0) rc = 2; else rc = 0; break; + } case 's': - if (udev_ctrl_send_stop_exec_queue(uctrl) < 0) + if (udev_ctrl_send_stop_exec_queue(uctrl, timeout) < 0) rc = 2; else rc = 0; break; case 'S': - if (udev_ctrl_send_start_exec_queue(uctrl) < 0) + if (udev_ctrl_send_start_exec_queue(uctrl, timeout) < 0) rc = 2; else rc = 0; break; case 'R': - if (udev_ctrl_send_reload_rules(uctrl) < 0) + if (udev_ctrl_send_reload_rules(uctrl, timeout) < 0) rc = 2; else rc = 0; @@ -107,34 +119,45 @@ int udevadm_control(struct udev *udev, int argc, char *argv[]) case 'p': if (strchr(optarg, '=') == NULL) { fprintf(stderr, "expect <KEY>=<value> instead of '%s'\n", optarg); - goto exit; + goto out; } - if (udev_ctrl_send_set_env(uctrl, optarg) < 0) + if (udev_ctrl_send_set_env(uctrl, optarg, timeout) < 0) rc = 2; else rc = 0; break; - case 'm': + case 'm': { + char *endp; + int i; + i = strtoul(optarg, &endp, 0); if (endp[0] != '\0' || i < 1) { fprintf(stderr, "invalid number '%s'\n", optarg); - goto exit; + goto out; } - if (udev_ctrl_send_set_children_max(uctrl, i) < 0) + if (udev_ctrl_send_set_children_max(uctrl, i, timeout) < 0) rc = 2; else rc = 0; break; + } + case 't': { + int seconds; + + seconds = atoi(optarg); + if (seconds >= 0) + timeout = seconds; + else + fprintf(stderr, "invalid timeout value\n"); + break; + } case 'h': print_help(); rc = 0; break; } } - - if (rc == 1) - err(udev, "unrecognized command\n"); -exit: +out: udev_ctrl_unref(uctrl); return rc; } diff --git a/udev/udevadm-settle.c b/udev/udevadm-settle.c index 1423cec6dc..b7852ff60a 100644 --- a/udev/udevadm-settle.c +++ b/udev/udevadm-settle.c @@ -44,8 +44,6 @@ static void sig_handler(int signum) switch (signum) { case SIGALRM: is_timeout = 1; - case SIGUSR1: - ; } } @@ -78,9 +76,7 @@ int udevadm_settle(struct udev *udev, int argc, char *argv[]) sigemptyset (&act.sa_mask); act.sa_flags = 0; sigaction(SIGALRM, &act, NULL); - sigaction(SIGUSR1, &act, NULL); sigemptyset(&mask); - sigaddset(&mask, SIGUSR1); sigaddset(&mask, SIGALRM); sigprocmask(SIG_UNBLOCK, &mask, NULL); @@ -168,15 +164,12 @@ int udevadm_settle(struct udev *udev, int argc, char *argv[]) uctrl = udev_ctrl_new_from_socket(udev, UDEV_CTRL_SOCK_PATH); if (uctrl != NULL) { - sigset_t oldmask; - - sigemptyset(&mask); - sigaddset(&mask, SIGUSR1); - sigaddset(&mask, SIGALRM); - sigprocmask(SIG_BLOCK, &mask, &oldmask); - if (udev_ctrl_send_settle(uctrl) > 0) - sigsuspend(&oldmask); - sigprocmask(SIG_SETMASK, &oldmask, NULL); + if (udev_ctrl_send_ping(uctrl, timeout) < 0) { + info(udev, "no connection to daemon\n"); + udev_ctrl_unref(uctrl); + rc = 0; + goto out; + } udev_ctrl_unref(uctrl); } } @@ -223,7 +216,7 @@ int udevadm_settle(struct udev *udev, int argc, char *argv[]) udev_list_entry_get_value(list_entry)); } } - +out: udev_queue_unref(udev_queue); return rc; } diff --git a/udev/udevadm.xml b/udev/udevadm.xml index 7a682a68ef..04c3d0b963 100644 --- a/udev/udevadm.xml +++ b/udev/udevadm.xml @@ -297,6 +297,12 @@ <para>Modify the internal state of the running udev daemon.</para> <variablelist> <varlistentry> + <term><option>--exit</option></term> + <listitem> + <para>Signal and wait for udevd to exit.</para> + </listitem> + </varlistentry> + <varlistentry> <term><option>--log-priority=<replaceable>value</replaceable></option></term> <listitem> <para>Set the internal log level of udevd. Valid values are the numerical @@ -340,6 +346,12 @@ </listitem> </varlistentry> <varlistentry> + <term><option>--timeout=</option><replaceable>seconds</replaceable></term> + <listitem> + <para>The maximum number seonds to wait for a reply from udevd.</para> + </listitem> + </varlistentry> + <varlistentry> <term><option>--help</option></term> <listitem> <para>Print help text.</para> diff --git a/udev/udevd.c b/udev/udevd.c index df5c1995bc..c785b20757 100644 --- a/udev/udevd.c +++ b/udev/udevd.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2009 Kay Sievers <kay.sievers@vrfy.org> + * Copyright (C) 2004-2011 Kay Sievers <kay.sievers@vrfy.org> * Copyright (C) 2004 Chris Friesen <chris_friesen@sympatico.ca> * Copyright (C) 2009 Canonical Ltd. * Copyright (C) 2009 Scott James Remnant <scott@netsplit.com> @@ -36,7 +36,7 @@ #include <sys/socket.h> #include <sys/un.h> #include <sys/signalfd.h> -#include <sys/select.h> +#include <sys/epoll.h> #include <sys/poll.h> #include <sys/wait.h> #include <sys/stat.h> @@ -76,34 +76,20 @@ static struct udev_queue_export *udev_queue_export; static struct udev_ctrl *udev_ctrl; static struct udev_monitor *monitor; static int worker_watch[2] = { -1, -1 }; -static pid_t settle_pid; +static int fd_signal = -1; +static int fd_ep = -1; +static int fd_inotify = -1; static bool stop_exec_queue; static bool reload_config; static int children; static int children_max; static int exec_delay; static sigset_t orig_sigmask; -static struct udev_list_node event_list; -static struct udev_list_node worker_list; +static UDEV_LIST(event_list); +static UDEV_LIST(worker_list); static bool udev_exit; static volatile sig_atomic_t worker_exit; -enum poll_fd { - FD_CONTROL, - FD_NETLINK, - FD_INOTIFY, - FD_SIGNAL, - FD_WORKER, -}; - -static struct pollfd pfd[] = { - [FD_NETLINK] = { .events = POLLIN, .fd = -1 }, - [FD_WORKER] = { .events = POLLIN, .fd = -1 }, - [FD_SIGNAL] = { .events = POLLIN, .fd = -1 }, - [FD_INOTIFY] = { .events = POLLIN, .fd = -1 }, - [FD_CONTROL] = { .events = POLLIN, .fd = -1 }, -}; - enum event_state { EVENT_UNDEF, EVENT_QUEUED, @@ -135,6 +121,8 @@ static struct event *node_to_event(struct udev_list_node *node) return (struct event *)event; } +static void event_queue_cleanup(struct udev *udev, enum event_state type); + enum worker_state { WORKER_UNDEF, WORKER_RUNNING, @@ -167,17 +155,18 @@ static struct worker *node_to_worker(struct udev_list_node *node) return (struct worker *)worker; } -static void event_queue_delete(struct event *event) +static void event_queue_delete(struct event *event, bool export) { udev_list_node_remove(&event->node); - /* mark as failed, if "add" event returns non-zero */ - if (event->exitcode != 0 && strcmp(udev_device_get_action(event->dev), "remove") != 0) - udev_queue_export_device_failed(udev_queue_export, event->dev); - else - udev_queue_export_device_finished(udev_queue_export, event->dev); - - info(event->udev, "seq %llu done with %i\n", udev_device_get_seqnum(event->dev), event->exitcode); + if (export) { + /* mark as failed, if "add" event returns non-zero */ + if (event->exitcode != 0 && strcmp(udev_device_get_action(event->dev), "remove") != 0) + udev_queue_export_device_failed(udev_queue_export, event->dev); + else + udev_queue_export_device_finished(udev_queue_export, event->dev); + info(event->udev, "seq %llu done with %i\n", udev_device_get_seqnum(event->dev), event->exitcode); + } udev_device_unref(event->dev); free(event); } @@ -200,28 +189,44 @@ static struct worker *worker_ref(struct worker *worker) return worker; } +static void worker_cleanup(struct worker *worker) +{ + udev_list_node_remove(&worker->node); + udev_monitor_unref(worker->monitor); + children--; + free(worker); +} + static void worker_unref(struct worker *worker) { worker->refcount--; if (worker->refcount > 0) return; - - udev_list_node_remove(&worker->node); - udev_monitor_unref(worker->monitor); - children--; info(worker->udev, "worker [%u] cleaned up\n", worker->pid); - free(worker); + worker_cleanup(worker); +} + +static void worker_list_cleanup(struct udev *udev) +{ + struct udev_list_node *loop, *tmp; + + udev_list_node_foreach_safe(loop, tmp, &worker_list) { + struct worker *worker = node_to_worker(loop); + + worker_cleanup(worker); + } } static void worker_new(struct event *event) { + struct udev *udev = event->udev; struct worker *worker; struct udev_monitor *worker_monitor; pid_t pid; struct sigaction act; /* listen for new events */ - worker_monitor = udev_monitor_new_from_netlink(event->udev, NULL); + worker_monitor = udev_monitor_new_from_netlink(udev, NULL); if (worker_monitor == NULL) return; /* allow the main daemon netlink address to send devices to the worker */ @@ -235,7 +240,7 @@ static void worker_new(struct event *event) } /* worker + event reference */ worker->refcount = 2; - worker->udev = event->udev; + worker->udev = udev; pid = fork(); switch (pid) { @@ -247,10 +252,18 @@ static void worker_new(struct event *event) .events = POLLIN, }; + /* move initial device from queue */ + dev = event->dev; + event->dev = NULL; + + free(worker); + worker_list_cleanup(udev); + event_queue_cleanup(udev, EVENT_UNDEF); udev_queue_export_unref(udev_queue_export); udev_monitor_unref(monitor); udev_ctrl_unref(udev_ctrl); - close(pfd[FD_SIGNAL].fd); + close(fd_signal); + close(fd_ep); close(worker_watch[READ_END]); udev_log_close(); udev_log_init("udevd-work"); @@ -274,16 +287,13 @@ static void worker_new(struct event *event) /* request TERM signal if parent exits */ prctl(PR_SET_PDEATHSIG, SIGTERM); - /* initial device */ - dev = event->dev; - do { struct udev_event *udev_event; struct worker_message msg = {}; int err; int failed = 0; - info(event->udev, "seq %llu running\n", udev_device_get_seqnum(dev)); + info(udev, "seq %llu running\n", udev_device_get_seqnum(dev)); udev_event = udev_event_new(dev); if (udev_event == NULL) _exit(3); @@ -308,7 +318,7 @@ static void worker_new(struct event *event) /* apply/restore inotify watch */ if (err == 0 && udev_event->inotify_watch) { - udev_watch_begin(udev_event->udev, dev); + udev_watch_begin(udev, dev); udev_device_update_db(dev); } @@ -323,7 +333,7 @@ static void worker_new(struct event *event) msg.pid = getpid(); send(worker_watch[WRITE_END], &msg, sizeof(struct worker_message), 0); - info(event->udev, "seq %llu processed with %i\n", udev_device_get_seqnum(dev), err); + info(udev, "seq %llu processed with %i\n", udev_device_get_seqnum(dev), err); udev_event_unref(udev_event); udev_device_unref(dev); dev = NULL; @@ -344,15 +354,19 @@ static void worker_new(struct event *event) } } while (dev != NULL); + close(fd_inotify); + close(worker_watch[WRITE_END]); + udev_rules_unref(rules); udev_monitor_unref(worker_monitor); + udev_unref(udev); udev_log_close(); - exit(0); + exit(EXIT_SUCCESS); } case -1: udev_monitor_unref(worker_monitor); event->state = EVENT_QUEUED; free(worker); - err(event->udev, "fork of child failed: %m\n"); + err(udev, "fork of child failed: %m\n"); break; default: /* close monitor, but keep address around */ @@ -364,7 +378,7 @@ static void worker_new(struct event *event) event->state = EVENT_RUNNING; udev_list_node_append(&worker->node, &worker_list); children++; - info(event->udev, "seq %llu forked new worker [%u]\n", udev_device_get_seqnum(event->dev), pid); + info(udev, "seq %llu forked new worker [%u]\n", udev_device_get_seqnum(event->dev), pid); break; } } @@ -535,7 +549,7 @@ static bool is_devpath_busy(struct event *event) return false; } -static void events_start(struct udev *udev) +static void event_queue_start(struct udev *udev) { struct udev_list_node *loop; @@ -555,14 +569,28 @@ static void events_start(struct udev *udev) } } -static void worker_returned(void) +static void event_queue_cleanup(struct udev *udev, enum event_state match_type) +{ + struct udev_list_node *loop, *tmp; + + udev_list_node_foreach_safe(loop, tmp, &event_list) { + struct event *event = node_to_event(loop); + + if (match_type != EVENT_UNDEF && match_type != event->state) + continue; + + event_queue_delete(event, false); + } +} + +static void worker_returned(int fd_worker) { for (;;) { struct worker_message msg; ssize_t size; struct udev_list_node *loop; - size = recv(pfd[FD_WORKER].fd, &msg, sizeof(struct worker_message), MSG_DONTWAIT); + size = recv(fd_worker, &msg, sizeof(struct worker_message), MSG_DONTWAIT); if (size != sizeof(struct worker_message)) break; @@ -575,7 +603,7 @@ static void worker_returned(void) /* worker returned */ worker->event->exitcode = msg.exitcode; - event_queue_delete(worker->event); + event_queue_delete(worker->event, true); worker->event = NULL; if (worker->state != WORKER_KILLED) worker->state = WORKER_IDLE; @@ -586,16 +614,21 @@ static void worker_returned(void) } /* receive the udevd message from userspace */ -static void handle_ctrl_msg(struct udev_ctrl *uctrl) +static struct udev_ctrl_connection *handle_ctrl_msg(struct udev_ctrl *uctrl) { struct udev *udev = udev_ctrl_get_udev(uctrl); - struct udev_ctrl_msg *ctrl_msg; + struct udev_ctrl_connection *ctrl_conn; + struct udev_ctrl_msg *ctrl_msg = NULL; const char *str; int i; - ctrl_msg = udev_ctrl_receive_msg(uctrl); + ctrl_conn = udev_ctrl_get_connection(uctrl); + if (ctrl_conn == NULL) + goto out; + + ctrl_msg = udev_ctrl_receive_msg(ctrl_conn); if (ctrl_msg == NULL) - return; + goto out; i = udev_ctrl_get_set_log_level(ctrl_msg); if (i >= 0) { @@ -652,13 +685,18 @@ static void handle_ctrl_msg(struct udev_ctrl *uctrl) children_max = i; } - settle_pid = udev_ctrl_get_settle(ctrl_msg); - if (settle_pid > 0) { - info(udev, "udevd message (SETTLE) received\n"); - kill(settle_pid, SIGUSR1); - settle_pid = 0; + if (udev_ctrl_get_ping(ctrl_msg) > 0) + info(udev, "udevd message (SYNC) received\n"); + + if (udev_ctrl_get_exit(ctrl_msg) > 0) { + info(udev, "udevd message (EXIT) received\n"); + udev_exit = true; + /* keep reference to block the client until we exit */ + udev_ctrl_connection_ref(ctrl_conn); } +out: udev_ctrl_msg_unref(ctrl_msg); + return udev_ctrl_connection_unref(ctrl_conn); } /* read inotify messages */ @@ -668,7 +706,7 @@ static int handle_inotify(struct udev *udev) char *buf; struct inotify_event *ev; - if ((ioctl(pfd[FD_INOTIFY].fd, FIONREAD, &nbytes) < 0) || (nbytes <= 0)) + if ((ioctl(fd_inotify, FIONREAD, &nbytes) < 0) || (nbytes <= 0)) return 0; buf = malloc(nbytes); @@ -677,7 +715,7 @@ static int handle_inotify(struct udev *udev) return -1; } - nbytes = read(pfd[FD_INOTIFY].fd, buf, nbytes); + nbytes = read(fd_inotify, buf, nbytes); for (pos = 0; pos < nbytes; pos += sizeof(struct inotify_event) + ev->len) { struct udev_device *dev; @@ -751,7 +789,7 @@ static void handle_signal(struct udev *udev, int signo) if (worker->event != NULL) { err(udev, "worker [%u] failed while handling '%s'\n", pid, worker->event->devpath); worker->event->exitcode = -32; - event_queue_delete(worker->event); + event_queue_delete(worker->event, true); /* drop reference from running event */ worker_unref(worker); } @@ -1081,6 +1119,11 @@ int main(int argc, char *argv[]) { "version", no_argument, NULL, 'V' }, {} }; + int fd_ctrl = -1; + int fd_netlink = -1; + int fd_worker = -1; + struct epoll_event ep_ctrl, ep_inotify, ep_signal, ep_netlink, ep_worker; + struct udev_ctrl_connection *ctrl_conn = NULL; int rc = 1; udev = udev_new(); @@ -1229,7 +1272,7 @@ int main(int argc, char *argv[]) dup2(fd, STDERR_FILENO); /* udevadm control socket */ - if (sd_listen_fds(true) == 1 && sd_is_socket(SD_LISTEN_FDS_START, AF_LOCAL, SOCK_DGRAM, -1)) + if (sd_listen_fds(true) == 1 && sd_is_socket(SD_LISTEN_FDS_START, AF_LOCAL, SOCK_SEQPACKET, -1)) udev_ctrl = udev_ctrl_new_from_fd(udev, SD_LISTEN_FDS_START); else udev_ctrl = udev_ctrl_new_from_socket(udev, UDEV_CTRL_SOCK_PATH); @@ -1245,7 +1288,7 @@ int main(int argc, char *argv[]) rc = 1; goto exit; } - pfd[FD_CONTROL].fd = udev_ctrl_get_fd(udev_ctrl); + fd_ctrl = udev_ctrl_get_fd(udev_ctrl); monitor = udev_monitor_new_from_netlink(udev, "kernel"); if (monitor == NULL || udev_monitor_enable_receiving(monitor) < 0) { @@ -1255,10 +1298,29 @@ int main(int argc, char *argv[]) goto exit; } udev_monitor_set_receive_buffer_size(monitor, 128*1024*1024); - pfd[FD_NETLINK].fd = udev_monitor_get_fd(monitor); + fd_netlink = udev_monitor_get_fd(monitor); - pfd[FD_INOTIFY].fd = udev_watch_init(udev); - if (pfd[FD_INOTIFY].fd < 0) { + if (daemonize) { + pid_t pid; + + pid = fork(); + switch (pid) { + case 0: + break; + case -1: + err(udev, "fork of daemon failed: %m\n"); + rc = 4; + goto exit; + default: + rc = 0; + goto exit; + } + } else { + sd_notify(1, "READY=1"); + } + + fd_inotify = udev_watch_init(udev); + if (fd_inotify < 0) { fprintf(stderr, "error initializing inotify\n"); err(udev, "error initializing inotify\n"); rc = 4; @@ -1266,15 +1328,15 @@ int main(int argc, char *argv[]) } if (udev_get_rules_path(udev) != NULL) { - inotify_add_watch(pfd[FD_INOTIFY].fd, udev_get_rules_path(udev), + inotify_add_watch(fd_inotify, udev_get_rules_path(udev), IN_DELETE | IN_MOVE | IN_CLOSE_WRITE); } else { char filename[UTIL_PATH_SIZE]; struct stat statbuf; - inotify_add_watch(pfd[FD_INOTIFY].fd, LIBEXECDIR "/rules.d", + inotify_add_watch(fd_inotify, LIBEXECDIR "/rules.d", IN_DELETE | IN_MOVE | IN_CLOSE_WRITE); - inotify_add_watch(pfd[FD_INOTIFY].fd, SYSCONFDIR "/udev/rules.d", + inotify_add_watch(fd_inotify, SYSCONFDIR "/udev/rules.d", IN_DELETE | IN_MOVE | IN_CLOSE_WRITE); /* watch dynamic rules directory */ @@ -1283,7 +1345,7 @@ int main(int argc, char *argv[]) util_create_path(udev, filename); mkdir(filename, 0755); } - inotify_add_watch(pfd[FD_INOTIFY].fd, filename, + inotify_add_watch(fd_inotify, filename, IN_DELETE | IN_MOVE | IN_CLOSE_WRITE); } udev_watch_restore(udev); @@ -1291,8 +1353,8 @@ int main(int argc, char *argv[]) /* block and listen to all signals on signalfd */ sigfillset(&mask); sigprocmask(SIG_SETMASK, &mask, &orig_sigmask); - pfd[FD_SIGNAL].fd = signalfd(-1, &mask, 0); - if (pfd[FD_SIGNAL].fd < 0) { + fd_signal = signalfd(-1, &mask, SFD_CLOEXEC); + if (fd_signal < 0) { fprintf(stderr, "error getting signalfd\n"); err(udev, "error getting signalfd\n"); rc = 5; @@ -1306,7 +1368,7 @@ int main(int argc, char *argv[]) rc = 6; goto exit; } - pfd[FD_WORKER].fd = worker_watch[READ_END]; + fd_worker = worker_watch[READ_END]; rules = udev_rules_new(udev, resolve_names); if (rules == NULL) { @@ -1320,6 +1382,35 @@ int main(int argc, char *argv[]) goto exit; } + memset(&ep_ctrl, 0, sizeof(struct epoll_event)); + ep_ctrl.events = EPOLLIN; + ep_ctrl.data.fd = fd_ctrl; + memset(&ep_inotify, 0, sizeof(struct epoll_event)); + ep_inotify.events = EPOLLIN; + ep_inotify.data.fd = fd_inotify; + memset(&ep_signal, 0, sizeof(struct epoll_event)); + ep_signal.events = EPOLLIN; + ep_signal.data.fd = fd_signal; + memset(&ep_netlink, 0, sizeof(struct epoll_event)); + ep_netlink.events = EPOLLIN; + ep_netlink.data.fd = fd_netlink; + memset(&ep_worker, 0, sizeof(struct epoll_event)); + ep_worker.events = EPOLLIN; + ep_worker.data.fd = fd_worker; + fd_ep = epoll_create1(EPOLL_CLOEXEC); + if (fd_ep < 0) { + err(udev, "error creating epoll fd: %m\n"); + goto exit; + } + if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_ctrl, &ep_ctrl) < 0 || + epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_inotify, &ep_inotify) < 0 || + epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_signal, &ep_signal) < 0 || + epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_netlink, &ep_netlink) < 0 || + epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_worker, &ep_worker) < 0) { + err(udev, "fail to add fds to epoll: %m\n"); + goto exit; + } + /* if needed, convert old database from earlier udev version */ convert_db(udev); @@ -1331,25 +1422,6 @@ int main(int argc, char *argv[]) if (fd > STDERR_FILENO) close(fd); - if (daemonize) { - pid_t pid; - - pid = fork(); - switch (pid) { - case 0: - break; - case -1: - err(udev, "fork of daemon failed: %m\n"); - rc = 4; - goto exit; - default: - rc = 0; - goto exit; - } - } else { - sd_notify(1, "READY=1"); - } - /* set scheduling priority for the main daemon process */ setpriority(PRIO_PROCESS, 0, UDEVD_PRIORITY); @@ -1393,30 +1465,79 @@ int main(int argc, char *argv[]) udev_list_init(&event_list); udev_list_init(&worker_list); - while (!udev_exit) { + for (;;) { + struct epoll_event ev[8]; int fdcount; int timeout; + bool is_worker, is_signal, is_inotify, is_netlink, is_ctrl; + int i; + + if (udev_exit) { + /* close sources of new events and discard buffered events */ + if (fd_ctrl >= 0) { + epoll_ctl(fd_ep, EPOLL_CTL_DEL, fd_ctrl, NULL); + fd_ctrl = -1; + } + if (monitor != NULL) { + epoll_ctl(fd_ep, EPOLL_CTL_DEL, fd_netlink, NULL); + udev_monitor_unref(monitor); + monitor = NULL; + } + if (fd_inotify >= 0) { + epoll_ctl(fd_ep, EPOLL_CTL_DEL, fd_inotify, NULL); + close(fd_inotify); + fd_inotify = -1; + } - /* set timeout to kill idle workers */ - if (udev_list_is_empty(&event_list) && children > 2) + /* discard queued events and kill workers */ + event_queue_cleanup(udev, EVENT_QUEUED); + worker_kill(udev, 0); + + /* exit after all has cleaned up */ + if (udev_list_is_empty(&event_list) && udev_list_is_empty(&worker_list)) + break; + + /* timeout at exit for workers to finish */ + timeout = 60 * 1000; + } else if (udev_list_is_empty(&event_list) && children > 2) { + /* set timeout to kill idle workers */ timeout = 3 * 1000; - else + } else { timeout = -1; - /* wait for events */ - fdcount = poll(pfd, ARRAY_SIZE(pfd), timeout); + } + fdcount = epoll_wait(fd_ep, ev, ARRAY_SIZE(ev), timeout); if (fdcount < 0) continue; - /* timeout - kill idle workers */ - if (fdcount == 0) + if (fdcount == 0) { + if (udev_exit) { + info(udev, "timeout, giving up waiting for workers to finish\n"); + break; + } + + /* timeout - kill idle workers */ worker_kill(udev, 2); + } + + is_worker = is_signal = is_inotify = is_netlink = is_ctrl = false; + for (i = 0; i < fdcount; i++) { + if (ev[i].data.fd == fd_worker && ev[i].events & EPOLLIN) + is_worker = true; + else if (ev[i].data.fd == fd_netlink && ev[i].events & EPOLLIN) + is_netlink = true; + else if (ev[i].data.fd == fd_signal && ev[i].events & EPOLLIN) + is_signal = true; + else if (ev[i].data.fd == fd_inotify && ev[i].events & EPOLLIN) + is_inotify = true; + else if (ev[i].data.fd == fd_ctrl && ev[i].events & EPOLLIN) + is_ctrl = true; + } /* event has finished */ - if (pfd[FD_WORKER].revents & POLLIN) - worker_returned(); + if (is_worker) + worker_returned(fd_worker); - /* get kernel uevent */ - if (pfd[FD_NETLINK].revents & POLLIN) { + if (is_netlink) { struct udev_device *dev; dev = udev_monitor_receive_device(monitor); @@ -1426,32 +1547,37 @@ int main(int argc, char *argv[]) } /* start new events */ - if (!udev_list_is_empty(&event_list) && !stop_exec_queue) - events_start(udev); + if (!udev_list_is_empty(&event_list) && !udev_exit && !stop_exec_queue) + event_queue_start(udev); - /* get signal */ - if (pfd[FD_SIGNAL].revents & POLLIN) { + if (is_signal) { struct signalfd_siginfo fdsi; ssize_t size; - size = read(pfd[FD_SIGNAL].fd, &fdsi, sizeof(struct signalfd_siginfo)); + size = read(fd_signal, &fdsi, sizeof(struct signalfd_siginfo)); if (size == sizeof(struct signalfd_siginfo)) handle_signal(udev, fdsi.ssi_signo); } + /* we are shutting down, the events below are not handled anymore */ + if (udev_exit) + continue; + /* device node and rules directory inotify watch */ - if (pfd[FD_INOTIFY].revents & POLLIN) + if (is_inotify) handle_inotify(udev); /* - * get control message - * * This needs to be after the inotify handling, to make sure, - * that the settle signal is send back after the possibly generated + * that the ping is send back after the possibly generated * "change" events by the inotify device node watch. + * + * A single time we may receive a client connection which we need to + * keep open to block the client. It will be closed right before we + * exit. */ - if (pfd[FD_CONTROL].revents & POLLIN) - handle_ctrl_msg(udev_ctrl); + if (is_ctrl) + ctrl_conn = handle_ctrl_msg(udev_ctrl); /* rules changed, set by inotify or a HUP signal */ if (reload_config) { @@ -1470,16 +1596,21 @@ int main(int argc, char *argv[]) udev_queue_export_cleanup(udev_queue_export); rc = 0; exit: - udev_queue_export_unref(udev_queue_export); + if (fd_ep >= 0) + close(fd_ep); + worker_list_cleanup(udev); + event_queue_cleanup(udev, EVENT_UNDEF); udev_rules_unref(rules); - udev_ctrl_unref(udev_ctrl); - if (pfd[FD_SIGNAL].fd >= 0) - close(pfd[FD_SIGNAL].fd); + if (fd_signal >= 0) + close(fd_signal); if (worker_watch[READ_END] >= 0) close(worker_watch[READ_END]); if (worker_watch[WRITE_END] >= 0) close(worker_watch[WRITE_END]); udev_monitor_unref(monitor); + udev_queue_export_unref(udev_queue_export); + udev_ctrl_connection_unref(ctrl_conn); + udev_ctrl_unref(udev_ctrl); udev_selinux_exit(udev); udev_unref(udev); udev_log_close(); |