From bb38678e3ccc02bcd970ccde3d8166a40edf92d3 Mon Sep 17 00:00:00 2001 From: Scott James Remnant Date: Tue, 10 Mar 2009 13:00:16 +0000 Subject: [PATCH] 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 --- udev/lib/libudev-ctrl.c | 16 ++++++++++++++++ udev/lib/libudev-private.h | 2 ++ udev/udevadm-settle.c | 23 +++++++++++++++++++++++ udev/udevd.c | 13 +++++++++++++ 4 files changed, 54 insertions(+) diff --git a/udev/lib/libudev-ctrl.c b/udev/lib/libudev-ctrl.c index 570e91c8..ca8b845b 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 0627aea0..91e2d5f8 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 54f905bf..1c3c2817 100644 --- a/udev/udevadm-settle.c +++ b/udev/udevadm-settle.c @@ -1,5 +1,7 @@ /* * Copyright (C) 2006-2008 Kay Sievers + * Copyright (C) 2009 Canonical Ltd. + * Copyright (C) 2009 Scott James Remnant * * 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 ee3d6f50..291655ef 100644 --- a/udev/udevd.c +++ b/udev/udevd.c @@ -1,6 +1,8 @@ /* * Copyright (C) 2004-2008 Kay Sievers * Copyright (C) 2004 Chris Friesen + * Copyright (C) 2009 Canonical Ltd. + * Copyright (C) 2009 Scott James Remnant * * 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; -- 2.39.5