]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
udevadm: settle - synchronise with the udev daemon
authorScott James Remnant <scott@ubuntu.com>
Tue, 10 Mar 2009 13:00:16 +0000 (13:00 +0000)
committerScott James Remnant <scott@ubuntu.com>
Thu, 12 Mar 2009 10:02:37 +0000 (10:02 +0000)
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>
udev/lib/libudev-ctrl.c
udev/lib/libudev-private.h
udev/udevadm-settle.c
udev/udevd.c

index 570e91c89e08fd51dcb8424982d03bc7aeddebba..ca8b845b94ff8367a478c5e8dcd179ec9f2e6f8f 100644 (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;
+}
index 0627aea071f9f79303b9dc6963eb5ea75ba187fe..91e2d5f81625faa1e4d9c26e1c7771c3e0f7a610 100644 (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);
 
index 54f905bf6ff1c2165b4421c5a87f7e4631aec0e4..1c3c28176b98fcea62f9e3a723ae9fb02f3e37e0 100644 (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))
index ee3d6f50338600db3608ce4d49a8408bc62b3c19..291655ef000132c677138109140d729892045343 100644 (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;