summaryrefslogtreecommitdiff
path: root/udev
diff options
context:
space:
mode:
authorScott James Remnant <scott@ubuntu.com>2009-03-10 13:00:16 +0000
committerScott James Remnant <scott@ubuntu.com>2009-03-12 10:02:37 +0000
commitbb38678e3ccc02bcd970ccde3d8166a40edf92d3 (patch)
tree77c38998c139205350553b38bc727d0790e6bb0d /udev
parentc2c24d4d3c8f83c5f4507bb13ba6c989f9d6ea45 (diff)
udevadm: settle - synchronise with the udev daemon
There's still a slight race condition when using udevadm settle, if the udev daemon has a pending inotify event but hasn't yet generated the "change" uevent for it, the kernel and udev sequence numbers will match and settle will exit. Now udevadm settle will send a control message to udevd, which will respond by sending SIGUSR1 back to the waiting udevadm settle once it has completed the main loop iteration in which it received the control message. If there were no pending inotify events, this will simply wake up the udev daemon and allow settle to continue. If there are pending inotify events, they are handled first in the main loop so when settle is continued they will have been turned into uevents and the kernel sequence number will have been incremented. Since the inotify event is pending for udevd when the close() system call returns (it's queued as part of the kernel handling for that system call), and since the kernel sequence number is incremented by writing to the uevent file (as udevd does), this solves the race. When the settle continues, if there were pending inotify events that udevd had not read, they are now pending uevents which settle can wait for. Signed-off-by: Scott James Remnant <scott@ubuntu.com>
Diffstat (limited to 'udev')
-rw-r--r--udev/lib/libudev-ctrl.c16
-rw-r--r--udev/lib/libudev-private.h2
-rw-r--r--udev/udevadm-settle.c23
-rw-r--r--udev/udevd.c13
4 files changed, 54 insertions, 0 deletions
diff --git a/udev/lib/libudev-ctrl.c b/udev/lib/libudev-ctrl.c
index 570e91c89e..ca8b845b94 100644
--- a/udev/lib/libudev-ctrl.c
+++ b/udev/lib/libudev-ctrl.c
@@ -42,6 +42,7 @@ enum udev_ctrl_msg_type {
UDEV_CTRL_SET_ENV,
UDEV_CTRL_SET_MAX_CHILDS,
UDEV_CTRL_SET_MAX_CHILDS_RUNNING,
+ UDEV_CTRL_SETTLE,
};
struct udev_ctrl_msg_wire {
@@ -58,6 +59,7 @@ struct udev_ctrl_msg {
int refcount;
struct udev_ctrl *uctrl;
struct udev_ctrl_msg_wire ctrl_msg_wire;
+ pid_t pid;
};
struct udev_ctrl {
@@ -196,6 +198,11 @@ int udev_ctrl_send_set_max_childs(struct udev_ctrl *uctrl, int count)
return ctrl_send(uctrl, UDEV_CTRL_SET_MAX_CHILDS, count, NULL);
}
+int udev_ctrl_send_settle(struct udev_ctrl *uctrl)
+{
+ return ctrl_send(uctrl, UDEV_CTRL_SETTLE, 0, NULL);
+}
+
struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl *uctrl)
{
struct udev_ctrl_msg *uctrl_msg;
@@ -239,6 +246,8 @@ struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl *uctrl)
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);
goto err;
@@ -311,3 +320,10 @@ int udev_ctrl_get_set_max_childs(struct udev_ctrl_msg *ctrl_msg)
return ctrl_msg->ctrl_msg_wire.intval;
return -1;
}
+
+pid_t udev_ctrl_get_settle(struct udev_ctrl_msg *ctrl_msg)
+{
+ if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_SETTLE)
+ return ctrl_msg->pid;
+ return -1;
+}
diff --git a/udev/lib/libudev-private.h b/udev/lib/libudev-private.h
index 0627aea071..91e2d5f816 100644
--- a/udev/lib/libudev-private.h
+++ b/udev/lib/libudev-private.h
@@ -109,6 +109,7 @@ extern int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority);
extern int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl);
extern int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl);
extern int udev_ctrl_send_reload_rules(struct udev_ctrl *uctrl);
+extern int udev_ctrl_send_settle(struct udev_ctrl *uctrl);
extern int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key);
extern int udev_ctrl_send_set_max_childs(struct udev_ctrl *uctrl, int count);
struct udev_ctrl_msg;
@@ -120,6 +121,7 @@ extern int udev_ctrl_get_set_log_level(struct udev_ctrl_msg *ctrl_msg);
extern int udev_ctrl_get_stop_exec_queue(struct udev_ctrl_msg *ctrl_msg);
extern int udev_ctrl_get_start_exec_queue(struct udev_ctrl_msg *ctrl_msg);
extern int udev_ctrl_get_reload_rules(struct udev_ctrl_msg *ctrl_msg);
+extern pid_t udev_ctrl_get_settle(struct udev_ctrl_msg *ctrl_msg);
extern const char *udev_ctrl_get_set_env(struct udev_ctrl_msg *ctrl_msg);
extern int udev_ctrl_get_set_max_childs(struct udev_ctrl_msg *ctrl_msg);
diff --git a/udev/udevadm-settle.c b/udev/udevadm-settle.c
index 54f905bf6f..1c3c28176b 100644
--- a/udev/udevadm-settle.c
+++ b/udev/udevadm-settle.c
@@ -1,5 +1,7 @@
/*
* Copyright (C) 2006-2008 Kay Sievers <kay@vrfy.org>
+ * Copyright (C) 2009 Canonical Ltd.
+ * Copyright (C) 2009 Scott James Remnant <scott@netsplit.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -41,6 +43,8 @@ static void asmlinkage sig_handler(int signum)
switch (signum) {
case SIGALRM:
is_timeout = 1;
+ case SIGUSR1:
+ ;
}
}
@@ -70,6 +74,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);
while (1) {
int option;
@@ -148,6 +153,24 @@ int udevadm_settle(struct udev *udev, int argc, char *argv[])
}
}
+ /* guarantee that the udev daemon isn't pre-processing */
+ if (getuid() == 0) {
+ struct udev_ctrl *uctrl;
+
+ uctrl = udev_ctrl_new_from_socket(udev, UDEV_CTRL_SOCK_PATH);
+ if (uctrl != NULL) {
+ sigset_t mask, oldmask;
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGUSR1);
+ sigaddset(&mask, SIGALRM);
+ sigprocmask(SIG_BLOCK, &mask, &oldmask);
+ if (udev_ctrl_send_settle(uctrl) > 0)
+ sigsuspend(&oldmask);
+ udev_ctrl_unref(uctrl);
+ }
+ }
+
while (!is_timeout) {
/* exit if queue is empty */
if (udev_queue_get_queue_is_empty(udev_queue))
diff --git a/udev/udevd.c b/udev/udevd.c
index ee3d6f5033..291655ef00 100644
--- a/udev/udevd.c
+++ b/udev/udevd.c
@@ -1,6 +1,8 @@
/*
* Copyright (C) 2004-2008 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>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -69,6 +71,7 @@ static volatile int sigchilds_waiting;
static volatile int udev_exit;
static volatile int reload_config;
static volatile int signal_received;
+static volatile pid_t settle_pid;
static int run_exec_q;
static int stop_exec_q;
static int max_childs;
@@ -513,6 +516,11 @@ static void handle_ctrl_msg(struct udev_ctrl *uctrl)
info(udev, "udevd message (SET_MAX_CHILDS) received, max_childs=%i\n", i);
max_childs = i;
}
+
+ settle_pid = udev_ctrl_get_settle(ctrl_msg);
+ if (settle_pid > 0) {
+ info(udev, "udevd message (SETTLE) received\n");
+ }
udev_ctrl_msg_unref(ctrl_msg);
}
@@ -1023,6 +1031,11 @@ handle_signals:
if (!stop_exec_q)
event_queue_manager(udev);
}
+
+ if (settle_pid > 0) {
+ kill(settle_pid, SIGUSR1);
+ settle_pid = 0;
+ }
}
cleanup_queue_dir(udev);
rc = 0;