X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fudev%2Fudevd.c;h=d79891a7ca207322072be254690905a3e54c9a7b;hp=6c051049741b4c03b1f55690b887d44e76891bfa;hb=10fab50a3091e0b819c3ddab5a7c07acf750c050;hpb=e918a1b5a94f270186dca59156354acd2a596494 diff --git a/src/udev/udevd.c b/src/udev/udevd.c index 6c0510497..d79891a7c 100644 --- a/src/udev/udevd.c +++ b/src/udev/udevd.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -46,6 +47,7 @@ #include #include "udev.h" +#include "udev-util.h" #include "sd-daemon.h" #include "cgroup-util.h" #include "dev-setup.h" @@ -733,20 +735,95 @@ out: return udev_ctrl_connection_unref(ctrl_conn); } -/* read inotify messages */ +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("dm-", udev_device_get_sysname(dev))) { + 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 will send out a change event for the disk, and + * "remove/add" for all partitions. + */ + fd = open(udev_device_get_devnode(dev), O_RDONLY|O_EXCL|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK); + if (fd >= 0) { + r = ioctl(fd, BLKRRPART, 0); + close(fd); + if (r >= 0) + return 0; + } + + /* + * Re-reading the partition table did not work, synthesize "change" + * events 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"); + + 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); + 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; +} + static int handle_inotify(struct udev *udev) { int nbytes, pos; char *buf; struct inotify_event *ev; + int r; - if ((ioctl(fd_inotify, FIONREAD, &nbytes) < 0) || (nbytes <= 0)) - return 0; + r = ioctl(fd_inotify, FIONREAD, &nbytes); + if (r < 0 || nbytes <= 0) + return -errno; buf = malloc(nbytes); - if (buf == NULL) { + if (!buf) { log_error("error getting buffer for inotify"); - return -1; + return -ENOMEM; } nbytes = read(fd_inotify, buf, nbytes); @@ -756,27 +833,16 @@ static int handle_inotify(struct udev *udev) ev = (struct inotify_event *)(buf + pos); dev = udev_watch_lookup(udev, ev->wd); - if (dev != NULL) { - log_debug("inotify event: %x for %s", ev->mask, udev_device_get_devnode(dev)); - if (ev->mask & IN_CLOSE_WRITE) { - char filename[UTIL_PATH_SIZE]; - int fd; - - log_debug("device %s closed, synthesising 'change'", udev_device_get_devnode(dev)); - strscpyl(filename, sizeof(filename), udev_device_get_syspath(dev), "/uevent", NULL); - fd = open(filename, O_WRONLY|O_CLOEXEC); - if (fd >= 0) { - if (write(fd, "change", 6) < 0) - log_debug("error writing uevent: %m"); - close(fd); - } - } - if (ev->mask & IN_IGNORED) - udev_watch_end(udev, dev); + if (!dev) + continue; - udev_device_unref(dev); - } + log_debug("inotify event: %x for %s", ev->mask, udev_device_get_devnode(dev)); + if (ev->mask & IN_CLOSE_WRITE) + synthesize_change(dev); + else if (ev->mask & IN_IGNORED) + udev_watch_end(udev, dev); + udev_device_unref(dev); } free(buf);