923a60
From 30ced6a8c742e1c798fff439b28a9800ca43f3e7 Mon Sep 17 00:00:00 2001
923a60
From: Lennart Poettering <lennart@poettering.net>
923a60
Date: Fri, 27 Feb 2015 21:55:08 +0100
923a60
Subject: [PATCH] core: rework device state logic
923a60
923a60
This change introduces a new state "tentative" for device units. Device
923a60
units are considered "plugged" when udev announced them, "dead" when
923a60
they are not available in the kernel, and "tentative" when they are
923a60
referenced in /proc/self/mountinfo or /proc/swaps but not (yet)
923a60
announced via udev.
923a60
923a60
This should fix a race when device nodes (like loop devices) are created
923a60
and immediately mounted. Previously, systemd might end up seeing the
923a60
mount unit before the device, and would thus pull down the mount because
923a60
its BindTo dependency on the device would not be fulfilled.
923a60
923a60
(cherry picked from commit 628c89cc68ab96fce2de7ebba5933725d147aecc)
923a60
---
923a60
 src/core/device.c | 368 ++++++++++++++++++++++++++++------------------
923a60
 src/core/device.h |  14 +-
923a60
 src/core/mount.c  |  46 +++---
923a60
 src/core/swap.c   |  32 ++--
923a60
 src/core/swap.h   |   4 +-
923a60
 src/core/unit.c   |   1 -
923a60
 6 files changed, 285 insertions(+), 180 deletions(-)
923a60
923a60
diff --git a/src/core/device.c b/src/core/device.c
923a60
index d3deac3936..75b9a46287 100644
923a60
--- a/src/core/device.c
923a60
+++ b/src/core/device.c
923a60
@@ -36,7 +36,8 @@
923a60
 
923a60
 static const UnitActiveState state_translation_table[_DEVICE_STATE_MAX] = {
923a60
         [DEVICE_DEAD] = UNIT_INACTIVE,
923a60
-        [DEVICE_PLUGGED] = UNIT_ACTIVE
923a60
+        [DEVICE_TENTATIVE] = UNIT_ACTIVATING,
923a60
+        [DEVICE_PLUGGED] = UNIT_ACTIVE,
923a60
 };
923a60
 
923a60
 static int device_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata);
923a60
@@ -65,6 +66,41 @@ static void device_unset_sysfs(Device *d) {
923a60
         d->sysfs = NULL;
923a60
 }
923a60
 
923a60
+static int device_set_sysfs(Device *d, const char *sysfs) {
923a60
+        Device *first;
923a60
+        char *copy;
923a60
+        int r;
923a60
+
923a60
+        assert(d);
923a60
+
923a60
+        if (streq_ptr(d->sysfs, sysfs))
923a60
+                return 0;
923a60
+
923a60
+        r = hashmap_ensure_allocated(&UNIT(d)->manager->devices_by_sysfs, &string_hash_ops);
923a60
+        if (r < 0)
923a60
+                return r;
923a60
+
923a60
+        copy = strdup(sysfs);
923a60
+        if (!copy)
923a60
+                return -ENOMEM;
923a60
+
923a60
+        device_unset_sysfs(d);
923a60
+
923a60
+        first = hashmap_get(UNIT(d)->manager->devices_by_sysfs, sysfs);
923a60
+        LIST_PREPEND(same_sysfs, first, d);
923a60
+
923a60
+        r = hashmap_replace(UNIT(d)->manager->devices_by_sysfs, copy, first);
923a60
+        if (r < 0) {
923a60
+                LIST_REMOVE(same_sysfs, first, d);
923a60
+                free(copy);
923a60
+                return r;
923a60
+        }
923a60
+
923a60
+        d->sysfs = copy;
923a60
+
923a60
+        return 0;
923a60
+}
923a60
+
923a60
 static void device_init(Unit *u) {
923a60
         Device *d = DEVICE(u);
923a60
 
923a60
@@ -112,8 +148,13 @@ static int device_coldplug(Unit *u) {
923a60
         assert(d);
923a60
         assert(d->state == DEVICE_DEAD);
923a60
 
923a60
-        if (d->sysfs)
923a60
+        if (d->found & DEVICE_FOUND_UDEV)
923a60
+                /* If udev says the device is around, it's around */
923a60
                 device_set_state(d, DEVICE_PLUGGED);
923a60
+        else if (d->found != DEVICE_NOT_FOUND)
923a60
+                /* If a device is found in /proc/self/mountinfo or
923a60
+                 * /proc/swaps, it's "tentatively" around. */
923a60
+                device_set_state(d, DEVICE_TENTATIVE);
923a60
 
923a60
         return 0;
923a60
 }
923a60
@@ -142,49 +183,9 @@ _pure_ static const char *device_sub_state_to_string(Unit *u) {
923a60
         return device_state_to_string(DEVICE(u)->state);
923a60
 }
923a60
 
923a60
-static int device_add_escaped_name(Unit *u, const char *dn) {
923a60
-        _cleanup_free_ char *e = NULL;
923a60
-        int r;
923a60
-
923a60
-        assert(u);
923a60
-        assert(dn);
923a60
-        assert(dn[0] == '/');
923a60
-
923a60
-        e = unit_name_from_path(dn, ".device");
923a60
-        if (!e)
923a60
-                return -ENOMEM;
923a60
-
923a60
-        r = unit_add_name(u, e);
923a60
-        if (r < 0 && r != -EEXIST)
923a60
-                return r;
923a60
-
923a60
-        return 0;
923a60
-}
923a60
-
923a60
-static int device_find_escape_name(Manager *m, const char *dn, Unit **_u) {
923a60
-        _cleanup_free_ char *e = NULL;
923a60
-        Unit *u;
923a60
-
923a60
-        assert(m);
923a60
-        assert(dn);
923a60
-        assert(dn[0] == '/');
923a60
-        assert(_u);
923a60
-
923a60
-        e = unit_name_from_path(dn, ".device");
923a60
-        if (!e)
923a60
-                return -ENOMEM;
923a60
-
923a60
-        u = manager_get_unit(m, e);
923a60
-        if (u) {
923a60
-                *_u = u;
923a60
-                return 1;
923a60
-        }
923a60
-
923a60
-        return 0;
923a60
-}
923a60
-
923a60
-static int device_make_description(Unit *u, struct udev_device *dev, const char *path) {
923a60
+static int device_update_description(Unit *u, struct udev_device *dev, const char *path) {
923a60
         const char *model;
923a60
+        int r;
923a60
 
923a60
         assert(u);
923a60
         assert(dev);
923a60
@@ -209,13 +210,16 @@ static int device_make_description(Unit *u, struct udev_device *dev, const char
923a60
 
923a60
                         j = strjoin(model, " ", label, NULL);
923a60
                         if (j)
923a60
-                                return unit_set_description(u, j);
923a60
-                }
923a60
+                                r = unit_set_description(u, j);
923a60
+                } else
923a60
+                        r = unit_set_description(u, model);
923a60
+        } else
923a60
+                r = unit_set_description(u, path);
923a60
 
923a60
-                return unit_set_description(u, model);
923a60
-        }
923a60
+        if (r < 0)
923a60
+                log_unit_error_errno(u->id, r, "Failed to set device description: %m");
923a60
 
923a60
-        return unit_set_description(u, path);
923a60
+        return r;
923a60
 }
923a60
 
923a60
 static int device_add_udev_wants(Unit *u, struct udev_device *dev) {
923a60
@@ -242,20 +246,20 @@ static int device_add_udev_wants(Unit *u, struct udev_device *dev) {
923a60
 
923a60
                 n = unit_name_mangle(e, MANGLE_NOGLOB);
923a60
                 if (!n)
923a60
-                        return -ENOMEM;
923a60
+                        return log_oom();
923a60
 
923a60
                 r = unit_add_dependency_by_name(u, UNIT_WANTS, n, NULL, true);
923a60
                 if (r < 0)
923a60
-                        return r;
923a60
+                        return log_unit_error_errno(u->id, r, "Failed to add wants dependency: %m");
923a60
         }
923a60
         if (!isempty(state))
923a60
-                log_unit_warning(u->id, "Property %s on %s has trailing garbage, ignoring.",
923a60
-                                 property, strna(udev_device_get_syspath(dev)));
923a60
+                log_unit_warning(u->id, "Property %s on %s has trailing garbage, ignoring.", property, strna(udev_device_get_syspath(dev)));
923a60
 
923a60
         return 0;
923a60
 }
923a60
 
923a60
-static int device_update_unit(Manager *m, struct udev_device *dev, const char *path, bool main) {
923a60
+static int device_setup_unit(Manager *m, struct udev_device *dev, const char *path, bool main) {
923a60
+        _cleanup_free_ char *e = NULL;
923a60
         const char *sysfs;
923a60
         Unit *u = NULL;
923a60
         bool delete;
923a60
@@ -269,12 +273,18 @@ static int device_update_unit(Manager *m, struct udev_device *dev, const char *p
923a60
         if (!sysfs)
923a60
                 return 0;
923a60
 
923a60
-        r = device_find_escape_name(m, path, &u);
923a60
-        if (r < 0)
923a60
-                return r;
923a60
+        e = unit_name_from_path(path, ".device");
923a60
+        if (!e)
923a60
+                return log_oom();
923a60
+
923a60
+        u = manager_get_unit(m, e);
923a60
 
923a60
-        if (u && DEVICE(u)->sysfs && !path_equal(DEVICE(u)->sysfs, sysfs))
923a60
+        if (u &&
923a60
+            DEVICE(u)->sysfs &&
923a60
+            !path_equal(DEVICE(u)->sysfs, sysfs)) {
923a60
+                log_unit_error(u->id, "Device %s appeared twice with different sysfs paths %s and %s", e, DEVICE(u)->sysfs, sysfs);
923a60
                 return -EEXIST;
923a60
+        }
923a60
 
923a60
         if (!u) {
923a60
                 delete = true;
923a60
@@ -283,7 +293,7 @@ static int device_update_unit(Manager *m, struct udev_device *dev, const char *p
923a60
                 if (!u)
923a60
                         return log_oom();
923a60
 
923a60
-                r = device_add_escaped_name(u, path);
923a60
+                r = unit_add_name(u, e);
923a60
                 if (r < 0)
923a60
                         goto fail;
923a60
 
923a60
@@ -295,37 +305,16 @@ static int device_update_unit(Manager *m, struct udev_device *dev, const char *p
923a60
          * actually been seen yet ->sysfs will not be
923a60
          * initialized. Hence initialize it if necessary. */
923a60
 
923a60
-        if (!DEVICE(u)->sysfs) {
923a60
-                Device *first;
923a60
-
923a60
-                DEVICE(u)->sysfs = strdup(sysfs);
923a60
-                if (!DEVICE(u)->sysfs) {
923a60
-                        r = -ENOMEM;
923a60
-                        goto fail;
923a60
-                }
923a60
-
923a60
-                r = hashmap_ensure_allocated(&m->devices_by_sysfs, &string_hash_ops);
923a60
-                if (r < 0)
923a60
-                        goto fail;
923a60
-
923a60
-                first = hashmap_get(m->devices_by_sysfs, sysfs);
923a60
-                LIST_PREPEND(same_sysfs, first, DEVICE(u));
923a60
-
923a60
-                r = hashmap_replace(m->devices_by_sysfs, DEVICE(u)->sysfs, first);
923a60
-                if (r < 0)
923a60
-                        goto fail;
923a60
-        }
923a60
-
923a60
-        device_make_description(u, dev, path);
923a60
+        r = device_set_sysfs(DEVICE(u), sysfs);
923a60
+        if (r < 0)
923a60
+                goto fail;
923a60
 
923a60
-        if (main) {
923a60
-                /* The additional systemd udev properties we only
923a60
-                 * interpret for the main object */
923a60
+        (void) device_update_description(u, dev, path);
923a60
 
923a60
-                r = device_add_udev_wants(u, dev);
923a60
-                if (r < 0)
923a60
-                        goto fail;
923a60
-        }
923a60
+        /* The additional systemd udev properties we only interpret
923a60
+         * for the main object */
923a60
+        if (main)
923a60
+                (void) device_add_udev_wants(u, dev);
923a60
 
923a60
         /* Note that this won't dispatch the load queue, the caller
923a60
          * has to do that if needed and appropriate */
923a60
@@ -334,7 +323,7 @@ static int device_update_unit(Manager *m, struct udev_device *dev, const char *p
923a60
         return 0;
923a60
 
923a60
 fail:
923a60
-        log_warning_errno(r, "Failed to load device unit: %m");
923a60
+        log_unit_warning_errno(u->id, r, "Failed to set up device unit: %m");
923a60
 
923a60
         if (delete && u)
923a60
                 unit_free(u);
923a60
@@ -342,7 +331,7 @@ fail:
923a60
         return r;
923a60
 }
923a60
 
923a60
-static int device_process_new_device(Manager *m, struct udev_device *dev) {
923a60
+static int device_process_new(Manager *m, struct udev_device *dev) {
923a60
         const char *sysfs, *dn, *alias;
923a60
         struct udev_list_entry *item = NULL, *first = NULL;
923a60
         int r;
923a60
@@ -354,14 +343,14 @@ static int device_process_new_device(Manager *m, struct udev_device *dev) {
923a60
                 return 0;
923a60
 
923a60
         /* Add the main unit named after the sysfs path */
923a60
-        r = device_update_unit(m, dev, sysfs, true);
923a60
+        r = device_setup_unit(m, dev, sysfs, true);
923a60
         if (r < 0)
923a60
                 return r;
923a60
 
923a60
         /* Add an additional unit for the device node */
923a60
         dn = udev_device_get_devnode(dev);
923a60
         if (dn)
923a60
-                device_update_unit(m, dev, dn, false);
923a60
+                (void) device_setup_unit(m, dev, dn, false);
923a60
 
923a60
         /* Add additional units for all symlinks */
923a60
         first = udev_device_get_devlinks_list_entry(dev);
923a60
@@ -388,7 +377,7 @@ static int device_process_new_device(Manager *m, struct udev_device *dev) {
923a60
                             st.st_rdev != udev_device_get_devnum(dev))
923a60
                                 continue;
923a60
 
923a60
-                device_update_unit(m, dev, p, false);
923a60
+                (void) device_setup_unit(m, dev, p, false);
923a60
         }
923a60
 
923a60
         /* Add additional units for all explicitly configured
923a60
@@ -405,7 +394,7 @@ static int device_process_new_device(Manager *m, struct udev_device *dev) {
923a60
                         e[l] = 0;
923a60
 
923a60
                         if (path_is_absolute(e))
923a60
-                                device_update_unit(m, dev, e, false);
923a60
+                                (void) device_setup_unit(m, dev, e, false);
923a60
                         else
923a60
                                 log_warning("SYSTEMD_ALIAS for %s is not an absolute path, ignoring: %s", sysfs, e);
923a60
                 }
923a60
@@ -416,39 +405,62 @@ static int device_process_new_device(Manager *m, struct udev_device *dev) {
923a60
         return 0;
923a60
 }
923a60
 
923a60
-static void device_set_path_plugged(Manager *m, struct udev_device *dev) {
923a60
-        const char *sysfs;
923a60
+static void device_update_found_one(Device *d, bool add, DeviceFound found, bool now) {
923a60
+        DeviceFound n;
923a60
+
923a60
+        assert(d);
923a60
+
923a60
+        n = add ? (d->found | found) : (d->found & ~found);
923a60
+        if (n == d->found)
923a60
+                return;
923a60
+
923a60
+        d->found = n;
923a60
+
923a60
+        if (now) {
923a60
+                if (d->found & DEVICE_FOUND_UDEV)
923a60
+                        device_set_state(d, DEVICE_PLUGGED);
923a60
+                else if (d->found != DEVICE_NOT_FOUND)
923a60
+                        device_set_state(d, DEVICE_TENTATIVE);
923a60
+                else
923a60
+                        device_set_state(d, DEVICE_DEAD);
923a60
+        }
923a60
+}
923a60
+
923a60
+static int device_update_found_by_sysfs(Manager *m, const char *sysfs, bool add, DeviceFound found, bool now) {
923a60
         Device *d, *l;
923a60
 
923a60
         assert(m);
923a60
-        assert(dev);
923a60
+        assert(sysfs);
923a60
 
923a60
-        sysfs = udev_device_get_syspath(dev);
923a60
-        if (!sysfs)
923a60
-                return;
923a60
+        if (found == DEVICE_NOT_FOUND)
923a60
+                return 0;
923a60
 
923a60
         l = hashmap_get(m->devices_by_sysfs, sysfs);
923a60
         LIST_FOREACH(same_sysfs, d, l)
923a60
-                device_set_state(d, DEVICE_PLUGGED);
923a60
+                device_update_found_one(d, add, found, now);
923a60
+
923a60
+        return 0;
923a60
 }
923a60
 
923a60
-static int device_process_removed_device(Manager *m, struct udev_device *dev) {
923a60
-        const char *sysfs;
923a60
-        Device *d;
923a60
+static int device_update_found_by_name(Manager *m, const char *path, bool add, DeviceFound found, bool now) {
923a60
+        _cleanup_free_ char *e = NULL;
923a60
+        Unit *u;
923a60
 
923a60
         assert(m);
923a60
-        assert(dev);
923a60
+        assert(path);
923a60
 
923a60
-        sysfs = udev_device_get_syspath(dev);
923a60
-        if (!sysfs)
923a60
-                return -ENOMEM;
923a60
+        if (found == DEVICE_NOT_FOUND)
923a60
+                return 0;
923a60
 
923a60
-        /* Remove all units of this sysfs path */
923a60
-        while ((d = hashmap_get(m->devices_by_sysfs, sysfs))) {
923a60
-                device_unset_sysfs(d);
923a60
-                device_set_state(d, DEVICE_DEAD);
923a60
-        }
923a60
+        e = unit_name_from_path(path, ".device");
923a60
+        if (!e)
923a60
+                return log_oom();
923a60
 
923a60
+        u = manager_get_unit(m, e);
923a60
+        if (!u)
923a60
+                return 0;
923a60
+
923a60
+        device_update_found_one(DEVICE(u), add, found, now);
923a60
         return 0;
923a60
 }
923a60
 
923a60
@@ -464,22 +476,6 @@ static bool device_is_ready(struct udev_device *dev) {
923a60
         return parse_boolean(ready) != 0;
923a60
 }
923a60
 
923a60
-static int device_process_new_path(Manager *m, const char *path) {
923a60
-        _cleanup_udev_device_unref_ struct udev_device *dev = NULL;
923a60
-
923a60
-        assert(m);
923a60
-        assert(path);
923a60
-
923a60
-        dev = udev_device_new_from_syspath(m->udev, path);
923a60
-        if (!dev)
923a60
-                return log_oom();
923a60
-
923a60
-        if (!device_is_ready(dev))
923a60
-                return 0;
923a60
-
923a60
-        return device_process_new_device(m, dev);
923a60
-}
923a60
-
923a60
 static Unit *device_following(Unit *u) {
923a60
         Device *d = DEVICE(u);
923a60
         Device *other, *first = NULL;
923a60
@@ -606,12 +602,31 @@ static int device_enumerate(Manager *m) {
923a60
                 goto fail;
923a60
 
923a60
         first = udev_enumerate_get_list_entry(e);
923a60
-        udev_list_entry_foreach(item, first)
923a60
-                device_process_new_path(m, udev_list_entry_get_name(item));
923a60
+        udev_list_entry_foreach(item, first) {
923a60
+                _cleanup_udev_device_unref_ struct udev_device *dev = NULL;
923a60
+                const char *sysfs;
923a60
+
923a60
+                sysfs = udev_list_entry_get_name(item);
923a60
+
923a60
+                dev = udev_device_new_from_syspath(m->udev, sysfs);
923a60
+                if (!dev) {
923a60
+                        log_oom();
923a60
+                        continue;
923a60
+                }
923a60
+
923a60
+                if (!device_is_ready(dev))
923a60
+                        continue;
923a60
+
923a60
+                (void) device_process_new(m, dev);
923a60
+
923a60
+                device_update_found_by_sysfs(m, sysfs, true, DEVICE_FOUND_UDEV, false);
923a60
+        }
923a60
 
923a60
         return 0;
923a60
 
923a60
 fail:
923a60
+        log_error_errno(r, "Failed to enumerate devices: %m");
923a60
+
923a60
         device_shutdown(m);
923a60
         return r;
923a60
 }
923a60
@@ -619,7 +634,7 @@ fail:
923a60
 static int device_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
923a60
         _cleanup_udev_device_unref_ struct udev_device *dev = NULL;
923a60
         Manager *m = userdata;
923a60
-        const char *action;
923a60
+        const char *action, *sysfs;
923a60
         int r;
923a60
 
923a60
         assert(m);
923a60
@@ -641,33 +656,47 @@ static int device_dispatch_io(sd_event_source *source, int fd, uint32_t revents,
923a60
         if (!dev)
923a60
                 return 0;
923a60
 
923a60
+        sysfs = udev_device_get_syspath(dev);
923a60
+        if (!sysfs) {
923a60
+                log_error("Failed to get udev sys path.");
923a60
+                return 0;
923a60
+        }
923a60
+
923a60
         action = udev_device_get_action(dev);
923a60
         if (!action) {
923a60
                 log_error("Failed to get udev action string.");
923a60
                 return 0;
923a60
         }
923a60
 
923a60
-        if (streq(action, "remove") || !device_is_ready(dev))  {
923a60
-                r = device_process_removed_device(m, dev);
923a60
-                if (r < 0)
923a60
-                        log_error_errno(r, "Failed to process device remove event: %m");
923a60
-
923a60
-                r = swap_process_removed_device(m, dev);
923a60
+        if (streq(action, "remove"))  {
923a60
+                r = swap_process_device_remove(m, dev);
923a60
                 if (r < 0)
923a60
                         log_error_errno(r, "Failed to process swap device remove event: %m");
923a60
 
923a60
-        } else {
923a60
-                r = device_process_new_device(m, dev);
923a60
-                if (r < 0)
923a60
-                        log_error_errno(r, "Failed to process device new event: %m");
923a60
+                /* If we get notified that a device was removed by
923a60
+                 * udev, then it's completely gone, hence unset all
923a60
+                 * found bits */
923a60
+                device_update_found_by_sysfs(m, sysfs, false, DEVICE_FOUND_UDEV|DEVICE_FOUND_MOUNT|DEVICE_FOUND_SWAP, true);
923a60
 
923a60
-                r = swap_process_new_device(m, dev);
923a60
+        } else if (device_is_ready(dev)) {
923a60
+
923a60
+                (void) device_process_new(m, dev);
923a60
+
923a60
+                r = swap_process_device_new(m, dev);
923a60
                 if (r < 0)
923a60
                         log_error_errno(r, "Failed to process swap device new event: %m");
923a60
 
923a60
                 manager_dispatch_load_queue(m);
923a60
 
923a60
-                device_set_path_plugged(m, dev);
923a60
+                /* The device is found now, set the udev found bit */
923a60
+                device_update_found_by_sysfs(m, sysfs, true, DEVICE_FOUND_UDEV, true);
923a60
+
923a60
+        } else {
923a60
+                /* The device is nominally around, but not ready for
923a60
+                 * us. Hence unset the udev bit, but leave the rest
923a60
+                 * around. */
923a60
+
923a60
+                device_update_found_by_sysfs(m, sysfs, false, DEVICE_FOUND_UDEV, true);
923a60
         }
923a60
 
923a60
         return 0;
923a60
@@ -686,9 +715,58 @@ static bool device_supported(Manager *m) {
923a60
         return read_only <= 0;
923a60
 }
923a60
 
923a60
+int device_found_node(Manager *m, const char *node, bool add, DeviceFound found, bool now) {
923a60
+        _cleanup_udev_device_unref_ struct udev_device *dev = NULL;
923a60
+        struct stat st;
923a60
+
923a60
+        assert(m);
923a60
+        assert(node);
923a60
+
923a60
+        /* This is called whenever we find a device referenced in
923a60
+         * /proc/swaps or /proc/self/mounts. Such a device might be
923a60
+         * mounted/enabled at a time where udev has not finished
923a60
+         * probing it yet, and we thus haven't learned about it
923a60
+         * yet. In this case we will set the device unit to
923a60
+         * "tentative" state. */
923a60
+
923a60
+        if (add) {
923a60
+                if (!path_startswith(node, "/dev"))
923a60
+                        return 0;
923a60
+
923a60
+                if (stat(node, &st) < 0) {
923a60
+                        if (errno == ENOENT)
923a60
+                                return 0;
923a60
+
923a60
+                        return log_error_errno(errno, "Failed to stat device node file %s: %m", node);
923a60
+                }
923a60
+
923a60
+                if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode))
923a60
+                        return 0;
923a60
+
923a60
+                dev = udev_device_new_from_devnum(m->udev, S_ISBLK(st.st_mode) ? 'b' : 'c', st.st_rdev);
923a60
+                if (!dev) {
923a60
+                        if (errno == ENOENT)
923a60
+                                return 0;
923a60
+
923a60
+                        return log_oom();
923a60
+                }
923a60
+
923a60
+                /* If the device is known in the kernel and newly
923a60
+                 * appeared, then we'll create a device unit for it,
923a60
+                 * under the name referenced in /proc/swaps or
923a60
+                 * /proc/self/mountinfo. */
923a60
+
923a60
+                (void) device_setup_unit(m, dev, node, false);
923a60
+        }
923a60
+
923a60
+        /* Update the device unit's state, should it exist */
923a60
+        return device_update_found_by_name(m, node, add, found, now);
923a60
+}
923a60
+
923a60
 static const char* const device_state_table[_DEVICE_STATE_MAX] = {
923a60
         [DEVICE_DEAD] = "dead",
923a60
-        [DEVICE_PLUGGED] = "plugged"
923a60
+        [DEVICE_TENTATIVE] = "tentative",
923a60
+        [DEVICE_PLUGGED] = "plugged",
923a60
 };
923a60
 
923a60
 DEFINE_STRING_TABLE_LOOKUP(device_state, DeviceState);
923a60
diff --git a/src/core/device.h b/src/core/device.h
923a60
index bb7ae07834..0609b20fdb 100644
923a60
--- a/src/core/device.h
923a60
+++ b/src/core/device.h
923a60
@@ -29,20 +29,28 @@ typedef struct Device Device;
923a60
  * simplifies the state engine greatly */
923a60
 typedef enum DeviceState {
923a60
         DEVICE_DEAD,
923a60
-        DEVICE_PLUGGED,
923a60
+        DEVICE_TENTATIVE, /* mounted or swapped, but not (yet) announced by udev */
923a60
+        DEVICE_PLUGGED,   /* announced by udev */
923a60
         _DEVICE_STATE_MAX,
923a60
         _DEVICE_STATE_INVALID = -1
923a60
 } DeviceState;
923a60
 
923a60
+typedef enum DeviceFound {
923a60
+        DEVICE_NOT_FOUND = 0,
923a60
+        DEVICE_FOUND_UDEV = 1,
923a60
+        DEVICE_FOUND_MOUNT = 2,
923a60
+        DEVICE_FOUND_SWAP = 4,
923a60
+} DeviceFound;
923a60
+
923a60
 struct Device {
923a60
         Unit meta;
923a60
 
923a60
         char *sysfs;
923a60
+        DeviceFound found;
923a60
 
923a60
         /* In order to be able to distinguish dependencies on
923a60
         different device nodes we might end up creating multiple
923a60
         devices for the same sysfs path. We chain them up here. */
923a60
-
923a60
         LIST_FIELDS(struct Device, same_sysfs);
923a60
 
923a60
         DeviceState state;
923a60
@@ -52,3 +60,5 @@ extern const UnitVTable device_vtable;
923a60
 
923a60
 const char* device_state_to_string(DeviceState i) _const_;
923a60
 DeviceState device_state_from_string(const char *s) _pure_;
923a60
+
923a60
+int device_found_node(Manager *m, const char *node, bool add, DeviceFound found, bool now);
923a60
diff --git a/src/core/mount.c b/src/core/mount.c
923a60
index f3977e62de..c971330af2 100644
923a60
--- a/src/core/mount.c
923a60
+++ b/src/core/mount.c
923a60
@@ -1391,7 +1391,7 @@ static int mount_dispatch_timer(sd_event_source *source, usec_t usec, void *user
923a60
         return 0;
923a60
 }
923a60
 
923a60
-static int mount_add_one(
923a60
+static int mount_setup_unit(
923a60
                 Manager *m,
923a60
                 const char *what,
923a60
                 const char *where,
923a60
@@ -1434,7 +1434,7 @@ static int mount_add_one(
923a60
 
923a60
                 u = unit_new(m, sizeof(Mount));
923a60
                 if (!u)
923a60
-                        return -ENOMEM;
923a60
+                        return log_oom();
923a60
 
923a60
                 r = unit_add_name(u, e);
923a60
                 if (r < 0)
923a60
@@ -1547,6 +1547,8 @@ static int mount_add_one(
923a60
         return 0;
923a60
 
923a60
 fail:
923a60
+        log_warning_errno(r, "Failed to set up mount unit: %m");
923a60
+
923a60
         if (delete && u)
923a60
                 unit_free(u);
923a60
 
923a60
@@ -1554,33 +1556,36 @@ fail:
923a60
 }
923a60
 
923a60
 static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) {
923a60
-        _cleanup_(mnt_free_tablep) struct libmnt_table *tb = NULL;
923a60
-        _cleanup_(mnt_free_iterp) struct libmnt_iter *itr = NULL;
923a60
-        struct libmnt_fs *fs;
923a60
+        _cleanup_(mnt_free_tablep) struct libmnt_table *t = NULL;
923a60
+        _cleanup_(mnt_free_iterp) struct libmnt_iter *i = NULL;
923a60
         int r = 0;
923a60
 
923a60
         assert(m);
923a60
 
923a60
-        tb = mnt_new_table();
923a60
-        itr = mnt_new_iter(MNT_ITER_FORWARD);
923a60
-        if (!tb || !itr)
923a60
+        t = mnt_new_table();
923a60
+        if (!t)
923a60
                 return log_oom();
923a60
 
923a60
-        r = mnt_table_parse_mtab(tb, NULL);
923a60
+        i = mnt_new_iter(MNT_ITER_FORWARD);
923a60
+        if (!i)
923a60
+                return log_oom();
923a60
+
923a60
+        r = mnt_table_parse_mtab(t, NULL);
923a60
         if (r < 0)
923a60
-                return r;
923a60
+                return log_error_errno(r, "Failed to parse /proc/self/mountinfo: %m");
923a60
 
923a60
         r = 0;
923a60
         for (;;) {
923a60
                 const char *device, *path, *options, *fstype;
923a60
                 _cleanup_free_ const char *d = NULL, *p = NULL;
923a60
+                struct libmnt_fs *fs;
923a60
                 int k;
923a60
 
923a60
-                k = mnt_table_next_fs(tb, itr, &fs);
923a60
+                k = mnt_table_next_fs(t, i, &fs);
923a60
                 if (k == 1)
923a60
                         break;
923a60
-                else if (k < 0)
923a60
-                        return log_error_errno(k, "Failed to get next entry from /etc/fstab: %m");
923a60
+                if (k < 0)
923a60
+                        return log_error_errno(k, "Failed to get next entry from /proc/self/mountinfo: %m");
923a60
 
923a60
                 device = mnt_fs_get_source(fs);
923a60
                 path = mnt_fs_get_target(fs);
923a60
@@ -1588,11 +1593,16 @@ static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) {
923a60
                 fstype = mnt_fs_get_fstype(fs);
923a60
 
923a60
                 d = cunescape(device);
923a60
+                if (!d)
923a60
+                        return log_oom();
923a60
+
923a60
                 p = cunescape(path);
923a60
-                if (!d || !p)
923a60
+                if (!p)
923a60
                         return log_oom();
923a60
 
923a60
-                k = mount_add_one(m, d, p, options, fstype, set_flags);
923a60
+                (void) device_found_node(m, d, true, DEVICE_FOUND_MOUNT, set_flags);
923a60
+
923a60
+                k = mount_setup_unit(m, d, p, options, fstype, set_flags);
923a60
                 if (r == 0 && k < 0)
923a60
                         r = k;
923a60
         }
923a60
@@ -1736,8 +1746,6 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents,
923a60
 
923a60
         r = mount_load_proc_self_mountinfo(m, true);
923a60
         if (r < 0) {
923a60
-                log_error_errno(r, "Failed to reread /proc/self/mountinfo: %m");
923a60
-
923a60
                 /* Reset flags, just in case, for later calls */
923a60
                 LIST_FOREACH(units_by_type, u, m->units_by_type[UNIT_MOUNT]) {
923a60
                         Mount *mount = MOUNT(u);
923a60
@@ -1770,6 +1778,10 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents,
923a60
                                 break;
923a60
                         }
923a60
 
923a60
+                        if (mount->parameters_proc_self_mountinfo.what)
923a60
+                                (void) device_found_node(m, mount->parameters_proc_self_mountinfo.what, false, DEVICE_FOUND_MOUNT, true);
923a60
+
923a60
+
923a60
                 } else if (mount->just_mounted || mount->just_changed) {
923a60
 
923a60
                         /* New or changed mount entry */
923a60
diff --git a/src/core/swap.c b/src/core/swap.c
923a60
index 6997921fde..5c19af5d91 100644
923a60
--- a/src/core/swap.c
923a60
+++ b/src/core/swap.c
923a60
@@ -338,7 +338,7 @@ static int swap_load(Unit *u) {
923a60
         return swap_verify(s);
923a60
 }
923a60
 
923a60
-static int swap_add_one(
923a60
+static int swap_setup_unit(
923a60
                 Manager *m,
923a60
                 const char *what,
923a60
                 const char *what_proc_swaps,
923a60
@@ -363,8 +363,10 @@ static int swap_add_one(
923a60
 
923a60
         if (u &&
923a60
             SWAP(u)->from_proc_swaps &&
923a60
-            !path_equal(SWAP(u)->parameters_proc_swaps.what, what_proc_swaps))
923a60
+            !path_equal(SWAP(u)->parameters_proc_swaps.what, what_proc_swaps)) {
923a60
+                log_error("Swap %s appeared twice with different device paths %s and %s", e, SWAP(u)->parameters_proc_swaps.what, what_proc_swaps);
923a60
                 return -EEXIST;
923a60
+        }
923a60
 
923a60
         if (!u) {
923a60
                 delete = true;
923a60
@@ -379,7 +381,7 @@ static int swap_add_one(
923a60
 
923a60
                 SWAP(u)->what = strdup(what);
923a60
                 if (!SWAP(u)->what) {
923a60
-                        r = log_oom();
923a60
+                        r = -ENOMEM;
923a60
                         goto fail;
923a60
                 }
923a60
 
923a60
@@ -407,7 +409,6 @@ static int swap_add_one(
923a60
         p->priority = priority;
923a60
 
923a60
         unit_add_to_dbus_queue(u);
923a60
-
923a60
         return 0;
923a60
 
923a60
 fail:
923a60
@@ -419,7 +420,7 @@ fail:
923a60
         return r;
923a60
 }
923a60
 
923a60
-static int swap_process_new_swap(Manager *m, const char *device, int prio, bool set_flags) {
923a60
+static int swap_process_new(Manager *m, const char *device, int prio, bool set_flags) {
923a60
         _cleanup_udev_device_unref_ struct udev_device *d = NULL;
923a60
         struct udev_list_entry *item = NULL, *first = NULL;
923a60
         const char *dn;
923a60
@@ -428,7 +429,7 @@ static int swap_process_new_swap(Manager *m, const char *device, int prio, bool
923a60
 
923a60
         assert(m);
923a60
 
923a60
-        r = swap_add_one(m, device, device, prio, set_flags);
923a60
+        r = swap_setup_unit(m, device, device, prio, set_flags);
923a60
         if (r < 0)
923a60
                 return r;
923a60
 
923a60
@@ -444,7 +445,7 @@ static int swap_process_new_swap(Manager *m, const char *device, int prio, bool
923a60
         /* Add the main device node */
923a60
         dn = udev_device_get_devnode(d);
923a60
         if (dn && !streq(dn, device))
923a60
-                swap_add_one(m, dn, device, prio, set_flags);
923a60
+                swap_setup_unit(m, dn, device, prio, set_flags);
923a60
 
923a60
         /* Add additional units for all symlinks */
923a60
         first = udev_device_get_devlinks_list_entry(d);
923a60
@@ -465,7 +466,7 @@ static int swap_process_new_swap(Manager *m, const char *device, int prio, bool
923a60
                             st.st_rdev != udev_device_get_devnum(d))
923a60
                                 continue;
923a60
 
923a60
-                swap_add_one(m, p, device, prio, set_flags);
923a60
+                swap_setup_unit(m, p, device, prio, set_flags);
923a60
         }
923a60
 
923a60
         return r;
923a60
@@ -1091,15 +1092,17 @@ static int swap_load_proc_swaps(Manager *m, bool set_flags) {
923a60
                         if (k == EOF)
923a60
                                 break;
923a60
 
923a60
-                        log_warning("Failed to parse /proc/swaps:%u", i);
923a60
+                        log_warning("Failed to parse /proc/swaps:%u.", i);
923a60
                         continue;
923a60
                 }
923a60
 
923a60
                 d = cunescape(dev);
923a60
                 if (!d)
923a60
-                        return -ENOMEM;
923a60
+                        return log_oom();
923a60
+
923a60
+                device_found_node(m, d, true, DEVICE_FOUND_SWAP, set_flags);
923a60
 
923a60
-                k = swap_process_new_swap(m, d, prio, set_flags);
923a60
+                k = swap_process_new(m, d, prio, set_flags);
923a60
                 if (k < 0)
923a60
                         r = k;
923a60
         }
923a60
@@ -1151,6 +1154,9 @@ static int swap_dispatch_io(sd_event_source *source, int fd, uint32_t revents, v
923a60
                                 break;
923a60
                         }
923a60
 
923a60
+                        if (swap->what)
923a60
+                                device_found_node(m, swap->what, false, DEVICE_FOUND_SWAP, true);
923a60
+
923a60
                 } else if (swap->just_activated) {
923a60
 
923a60
                         /* New swap entry */
923a60
@@ -1298,7 +1304,7 @@ fail:
923a60
         return r;
923a60
 }
923a60
 
923a60
-int swap_process_new_device(Manager *m, struct udev_device *dev) {
923a60
+int swap_process_device_new(Manager *m, struct udev_device *dev) {
923a60
         struct udev_list_entry *item = NULL, *first = NULL;
923a60
         _cleanup_free_ char *e = NULL;
923a60
         const char *dn;
923a60
@@ -1341,7 +1347,7 @@ int swap_process_new_device(Manager *m, struct udev_device *dev) {
923a60
         return r;
923a60
 }
923a60
 
923a60
-int swap_process_removed_device(Manager *m, struct udev_device *dev) {
923a60
+int swap_process_device_remove(Manager *m, struct udev_device *dev) {
923a60
         const char *dn;
923a60
         int r = 0;
923a60
         Swap *s;
923a60
diff --git a/src/core/swap.h b/src/core/swap.h
923a60
index 73e64d87a4..914a2dbccd 100644
923a60
--- a/src/core/swap.h
923a60
+++ b/src/core/swap.h
923a60
@@ -116,8 +116,8 @@ struct Swap {
923a60
 
923a60
 extern const UnitVTable swap_vtable;
923a60
 
923a60
-int swap_process_new_device(Manager *m, struct udev_device *dev);
923a60
-int swap_process_removed_device(Manager *m, struct udev_device *dev);
923a60
+int swap_process_device_new(Manager *m, struct udev_device *dev);
923a60
+int swap_process_device_remove(Manager *m, struct udev_device *dev);
923a60
 
923a60
 const char* swap_state_to_string(SwapState i) _const_;
923a60
 SwapState swap_state_from_string(const char *s) _pure_;
923a60
diff --git a/src/core/unit.c b/src/core/unit.c
923a60
index 563f6fe850..a6558ee23b 100644
923a60
--- a/src/core/unit.c
923a60
+++ b/src/core/unit.c
923a60
@@ -2843,7 +2843,6 @@ int unit_add_node_link(Unit *u, const char *what, bool wants) {
923a60
                 return -ENOMEM;
923a60
 
923a60
         r = manager_load_unit(u->manager, e, NULL, NULL, &device);
923a60
-
923a60
         if (r < 0)
923a60
                 return r;
923a60