summaryrefslogtreecommitdiff
path: root/udev
diff options
context:
space:
mode:
authorKay Sievers <kay.sievers@vrfy.org>2011-04-13 01:17:09 +0200
committerKay Sievers <kay.sievers@vrfy.org>2011-04-13 01:17:09 +0200
commitff2c503df091e6e4e9ab48cdb6df6ec8b7b525d0 (patch)
tree983bf55615a6bfeadc50f10355b286262e3ad188 /udev
parent960250952193db522b8ced336bf998bd7e8e4a31 (diff)
udevadm: control - add --exit
Diffstat (limited to 'udev')
-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
4 files changed, 311 insertions, 152 deletions
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();