ryantimwilson / rpms / systemd

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