summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO4
-rw-r--r--init/udev.socket2
-rw-r--r--libudev/libudev-ctrl.c199
-rw-r--r--libudev/libudev-private.h30
-rw-r--r--udev/udevadm-control.c59
-rw-r--r--udev/udevadm-settle.c21
-rw-r--r--udev/udevadm.xml12
-rw-r--r--udev/udevd.c371
8 files changed, 484 insertions, 214 deletions
diff --git a/TODO b/TODO
index 964ffe9cbb..65f7058ae5 100644
--- a/TODO
+++ b/TODO
@@ -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();