chiark / gitweb /
resolved: properly free network monitor
[elogind.git] / src / udev / udevd.c
index 59b5568fb61753274d1d04c0b53adffb145535e0..a45d3240c6661165b35b92b4f6bcab62e2a8dda8 100644 (file)
@@ -38,6 +38,7 @@
 #include <sys/un.h>
 #include <sys/signalfd.h>
 #include <sys/epoll.h>
+#include <sys/mount.h>
 #include <sys/poll.h>
 #include <sys/wait.h>
 #include <sys/stat.h>
@@ -46,6 +47,7 @@
 #include <sys/utsname.h>
 
 #include "udev.h"
+#include "udev-util.h"
 #include "sd-daemon.h"
 #include "cgroup-util.h"
 #include "dev-setup.h"
@@ -302,7 +304,7 @@ static void worker_new(struct event *event)
                          * <kabi_> IMHO this sounds like a good plan for this moment
                          */
                         if (streq_ptr("block", udev_device_get_subsystem(dev)) &&
-                            !startswith("dm-", udev_device_get_sysname(dev))) {
+                            !startswith(udev_device_get_sysname(dev), "dm-")) {
                                 struct udev_device *d = dev;
 
                                 if (streq_ptr("partition", udev_device_get_devtype(d)))
@@ -733,15 +735,110 @@ out:
         return udev_ctrl_connection_unref(ctrl_conn);
 }
 
-static void synthesize_change(struct udev_device *dev) {
+static int synthesize_change(struct udev_device *dev) {
         char filename[UTIL_PATH_SIZE];
+        int r;
+
+        if (streq_ptr("block", udev_device_get_subsystem(dev)) &&
+            streq_ptr("disk", udev_device_get_devtype(dev)) &&
+            !startswith(udev_device_get_sysname(dev), "dm-")) {
+                bool part_table_read = false;
+                bool has_partitions = false;
+                int fd;
+                struct udev *udev = udev_device_get_udev(dev);
+                _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
+                struct udev_list_entry *item;
+
+                /*
+                 * Try to re-read the partition table. This only succeeds if
+                 * none of the devices is busy. The kernel returns 0 if no
+                 * partition table is found, and we will not get an event for
+                 * the disk.
+                 */
+                fd = open(udev_device_get_devnode(dev), O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK);
+                if (fd >= 0) {
+                        r = flock(fd, LOCK_EX|LOCK_NB);
+                        if (r >= 0)
+                                r = ioctl(fd, BLKRRPART, 0);
+
+                        close(fd);
+                        if (r >= 0)
+                                part_table_read = true;
+                }
+
+                /* search for partitions */
+                e = udev_enumerate_new(udev);
+                if (!e)
+                        return -ENOMEM;
+
+                r = udev_enumerate_add_match_parent(e, dev);
+                if (r < 0)
+                        return r;
+
+                r = udev_enumerate_add_match_subsystem(e, "block");
+                if (r < 0)
+                        return r;
+
+                r = udev_enumerate_scan_devices(e);
+                if (r < 0)
+                        return r;
+
+                udev_list_entry_foreach(item, udev_enumerate_get_list_entry(e)) {
+                        _cleanup_udev_device_unref_ struct udev_device *d = NULL;
+
+                        d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
+                        if (!d)
+                                continue;
+
+                        if (!streq_ptr("partition", udev_device_get_devtype(d)))
+                                continue;
+
+                        has_partitions = true;
+                        break;
+                }
+
+                /*
+                 * We have partitions and re-read the table, the kernel already sent
+                 * out a "change" event for the disk, and "remove/add" for all
+                 * partitions.
+                 */
+                if (part_table_read && has_partitions)
+                        return 0;
+
+                /*
+                 * We have partitions but re-reading the partition table did not
+                 * work, synthesize "change" for the disk and all partitions.
+                 */
+                log_debug("device %s closed, synthesising 'change'", udev_device_get_devnode(dev));
+                strscpyl(filename, sizeof(filename), udev_device_get_syspath(dev), "/uevent", NULL);
+                write_string_file(filename, "change");
+
+                udev_list_entry_foreach(item, udev_enumerate_get_list_entry(e)) {
+                        _cleanup_udev_device_unref_ struct udev_device *d = NULL;
+
+                        d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
+                        if (!d)
+                                continue;
+
+                        if (!streq_ptr("partition", udev_device_get_devtype(d)))
+                                continue;
+
+                        log_debug("device %s closed, synthesising partition '%s' 'change'",
+                                  udev_device_get_devnode(dev), udev_device_get_devnode(d));
+                        strscpyl(filename, sizeof(filename), udev_device_get_syspath(d), "/uevent", NULL);
+                        write_string_file(filename, "change");
+                }
+
+                return 0;
+        }
 
         log_debug("device %s closed, synthesising 'change'", udev_device_get_devnode(dev));
         strscpyl(filename, sizeof(filename), udev_device_get_syspath(dev), "/uevent", NULL);
         write_string_file(filename, "change");
+
+        return 0;
 }
 
-/* read inotify messages */
 static int handle_inotify(struct udev *udev)
 {
         int nbytes, pos;