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>
This commit is contained in:
Scott James Remnant 2009-03-10 13:00:16 +00:00
parent c2c24d4d3c
commit bb38678e3c
4 changed files with 54 additions and 0 deletions

View File

@ -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;
}

View File

@ -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);

View File

@ -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))

View File

@ -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;