Based on 628c89cc68ab96fce2de7ebba5933725d147aecc Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Fri, 27 Feb 2015 21:55:08 +0100
Subject: [PATCH] core: rework device state logic

This change introduces a new state "tentative" for device units. Device
units are considered "plugged" when udev announced them, "dead" when
they are not available in the kernel, and "tentative" when they are
referenced in /proc/self/mountinfo or /proc/swaps but not (yet)
announced via udev.

This should fix a race when device nodes (like loop devices) are created
and immediately mounted. Previously, systemd might end up seeing the
mount unit before the device, and would thus pull down the mount because
its BindTo dependency on the device would not be fulfilled.
---
 src/core/device.c |  430 +++++++++++++++++++++++++++++++++++-------------------
 src/core/device.h |   14 +
 src/core/mount.c  |   21 +-
 src/core/swap.c   |   32 ++--
 src/core/swap.h   |    4 
 src/core/unit.c   |    1 
 6 files changed, 328 insertions(+), 174 deletions(-)

--- src/core/device.c
+++ src/core/device.c	2015-03-13 11:13:06.273518170 +0000
@@ -23,11 +23,9 @@
 #include <sys/epoll.h>
 #include <libudev.h>
 
-#include "strv.h"
 #include "log.h"
 #include "unit-name.h"
 #include "dbus-device.h"
-#include "def.h"
 #include "path-util.h"
 #include "udev-util.h"
 #include "unit.h"
@@ -36,7 +34,8 @@
 
 static const UnitActiveState state_translation_table[_DEVICE_STATE_MAX] = {
         [DEVICE_DEAD] = UNIT_INACTIVE,
-        [DEVICE_PLUGGED] = UNIT_ACTIVE
+        [DEVICE_TENTATIVE] = UNIT_ACTIVATING,
+        [DEVICE_PLUGGED] = UNIT_ACTIVE,
 };
 
 static int device_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata);
@@ -65,6 +64,41 @@ static void device_unset_sysfs(Device *d
         d->sysfs = NULL;
 }
 
+static int device_set_sysfs(Device *d, const char *sysfs) {
+        Device *first;
+        char *copy;
+        int r;
+
+        assert(d);
+
+        if (streq_ptr(d->sysfs, sysfs))
+                return 0;
+
+        r = hashmap_ensure_allocated(&UNIT(d)->manager->devices_by_sysfs, string_hash_func, string_compare_func);
+        if (r < 0)
+                return r;
+
+        copy = strdup(sysfs);
+        if (!copy)
+                return -ENOMEM;
+
+        device_unset_sysfs(d);
+
+        first = hashmap_get(UNIT(d)->manager->devices_by_sysfs, sysfs);
+        LIST_PREPEND(same_sysfs, first, d);
+
+        r = hashmap_replace(UNIT(d)->manager->devices_by_sysfs, copy, first);
+        if (r < 0) {
+                LIST_REMOVE(same_sysfs, first, d);
+                free(copy);
+                return r;
+        }
+
+        d->sysfs = copy;
+
+        return 0;
+}
+
 static void device_init(Unit *u) {
         Device *d = DEVICE(u);
 
@@ -112,8 +146,13 @@ static int device_coldplug(Unit *u) {
         assert(d);
         assert(d->state == DEVICE_DEAD);
 
-        if (d->sysfs)
+        if (d->found & DEVICE_FOUND_UDEV)
+                /* If udev says the device is around, it's around */
                 device_set_state(d, DEVICE_PLUGGED);
+        else if (d->found != DEVICE_NOT_FOUND)
+                /* If a device is found in /proc/self/mountinfo or
+                 * /proc/swaps, it's "tentatively" around. */
+                device_set_state(d, DEVICE_TENTATIVE);
 
         return 0;
 }
@@ -142,65 +181,113 @@ _pure_ static const char *device_sub_sta
         return device_state_to_string(DEVICE(u)->state);
 }
 
-static int device_add_escaped_name(Unit *u, const char *dn) {
-        _cleanup_free_ char *e = NULL;
+static int device_update_description(Unit *u, struct udev_device *dev, const char *path) {
+        const char *model;
         int r;
 
         assert(u);
-        assert(dn);
-        assert(dn[0] == '/');
+        assert(dev);
+        assert(path);
 
-        e = unit_name_from_path(dn, ".device");
-        if (!e)
-                return -ENOMEM;
+        model = udev_device_get_property_value(dev, "ID_MODEL_FROM_DATABASE");
+        if (!model)
+                model = udev_device_get_property_value(dev, "ID_MODEL");
+
+        if (model) {
+                const char *label;
+
+                /* Try to concatenate the device model string with a label, if there is one */
+                label = udev_device_get_property_value(dev, "ID_FS_LABEL");
+                if (!label)
+                        label = udev_device_get_property_value(dev, "ID_PART_ENTRY_NAME");
+                if (!label)
+                        label = udev_device_get_property_value(dev, "ID_PART_ENTRY_NUMBER");
+
+                if (label) {
+                        _cleanup_free_ char *j;
+
+                        j = strjoin(model, " ", label, NULL);
+                        if (j)
+                                r = unit_set_description(u, j);
+                        else
+                                r = -ENOMEM;
+                } else
+                        r = unit_set_description(u, model);
+        } else
+                r = unit_set_description(u, path);
 
-        r = unit_add_name(u, e);
-        if (r < 0 && r != -EEXIST)
-                return r;
+        if (r < 0)
+                log_error_unit(u->id, "Failed to set device description: %s", strerror(-r));
 
-        return 0;
+        return r;
 }
 
-static int device_find_escape_name(Manager *m, const char *dn, Unit **_u) {
-        _cleanup_free_ char *e = NULL;
-        Unit *u;
+static int device_add_udev_wants(Unit *u, struct udev_device *dev) {
+        const char *wants;
+        const char *word;
+        char *state;
+        size_t l;
+        int r;
+        const char *property;
 
-        assert(m);
-        assert(dn);
-        assert(dn[0] == '/');
-        assert(_u);
+        assert(u);
+        assert(dev);
 
-        e = unit_name_from_path(dn, ".device");
-        if (!e)
-                return -ENOMEM;
+        property = u->manager->running_as == SYSTEMD_USER ? "SYSTEMD_USER_WANTS" : "SYSTEMD_WANTS";
+        wants = udev_device_get_property_value(dev, property);
+        if (!wants)
+                return 0;
 
-        u = manager_get_unit(m, e);
-        if (u) {
-                *_u = u;
-                return 1;
+        FOREACH_WORD_QUOTED(word, l, wants, state) {
+                _cleanup_free_ char *n = NULL;
+                char e[l+1];
+
+                memcpy(e, word, l);
+                e[l] = 0;
+
+                n = unit_name_mangle(e, MANGLE_NOGLOB);
+                if (!n)
+                        return log_oom();
+
+                r = unit_add_dependency_by_name(u, UNIT_WANTS, n, NULL, true);
+                if (r < 0) {
+                        log_error_unit(u->id, "Failed to add wants dependency: %s", strerror(-r));
+			return r;
+		}
         }
+        if (!isempty(state))
+                log_warning_unit(u->id, "Property %s on %s has trailing garbage, ignoring.", property, strna(udev_device_get_syspath(dev)));
 
         return 0;
 }
 
-static int device_update_unit(Manager *m, struct udev_device *dev, const char *path, bool main) {
-        const char *sysfs, *model;
+static int device_setup_unit(Manager *m, struct udev_device *dev, const char *path, bool main) {
+        _cleanup_free_ char *e = NULL;
+        const char *sysfs;
         Unit *u = NULL;
-        int r;
         bool delete;
+        int r;
 
         assert(m);
+        assert(dev);
+        assert(path);
 
         sysfs = udev_device_get_syspath(dev);
         if (!sysfs)
                 return 0;
 
-        r = device_find_escape_name(m, path, &u);
-        if (r < 0)
-                return r;
+        e = unit_name_from_path(path, ".device");
+        if (!e)
+                return log_oom();
+
+        u = manager_get_unit(m, e);
 
-        if (u && DEVICE(u)->sysfs && !path_equal(DEVICE(u)->sysfs, sysfs))
+        if (u &&
+            DEVICE(u)->sysfs &&
+            !path_equal(DEVICE(u)->sysfs, sysfs)) {
+                log_error_unit(u->id, "Device %s appeared twice with different sysfs paths %s and %s", e, DEVICE(u)->sysfs, sysfs);
                 return -EEXIST;
+        }
 
         if (!u) {
                 delete = true;
@@ -209,7 +296,7 @@ static int device_update_unit(Manager *m
                 if (!u)
                         return log_oom();
 
-                r = device_add_escaped_name(u, path);
+                r = unit_add_name(u, e);
                 if (r < 0)
                         goto fail;
 
@@ -221,65 +308,16 @@ static int device_update_unit(Manager *m
          * actually been seen yet ->sysfs will not be
          * initialized. Hence initialize it if necessary. */
 
-        if (!DEVICE(u)->sysfs) {
-                Device *first;
-
-                DEVICE(u)->sysfs = strdup(sysfs);
-                if (!DEVICE(u)->sysfs) {
-                        r = -ENOMEM;
-                        goto fail;
-                }
-
-                r = hashmap_ensure_allocated(&m->devices_by_sysfs, string_hash_func, string_compare_func);
-                if (r < 0)
-                        goto fail;
-
-                first = hashmap_get(m->devices_by_sysfs, sysfs);
-                LIST_PREPEND(same_sysfs, first, DEVICE(u));
-
-                r = hashmap_replace(m->devices_by_sysfs, DEVICE(u)->sysfs, first);
-                if (r < 0)
-                        goto fail;
-        }
-
-        if ((model = udev_device_get_property_value(dev, "ID_MODEL_FROM_DATABASE")) ||
-            (model = udev_device_get_property_value(dev, "ID_MODEL")))
-                r = unit_set_description(u, model);
-        else
-                r = unit_set_description(u, path);
+        r = device_set_sysfs(DEVICE(u), sysfs);
         if (r < 0)
                 goto fail;
 
-        if (main) {
-                const char *wants;
+        (void) device_update_description(u, dev, path);
 
-                /* The additional systemd udev properties we only
-                 * interpret for the main object */
-
-                wants = udev_device_get_property_value(dev, m->running_as == SYSTEMD_USER ? "SYSTEMD_USER_WANTS" : "SYSTEMD_WANTS");
-                if (wants) {
-                        char *state, *w;
-                        size_t l;
-
-                        FOREACH_WORD_QUOTED(w, l, wants, state) {
-                                _cleanup_free_ char *n = NULL;
-                                char e[l+1];
-
-                                memcpy(e, w, l);
-                                e[l] = 0;
-
-                                n = unit_name_mangle(e, MANGLE_NOGLOB);
-                                if (!n) {
-                                        r = -ENOMEM;
-                                        goto fail;
-                                }
-
-                                r = unit_add_dependency_by_name(u, UNIT_WANTS, n, NULL, true);
-                                if (r < 0)
-                                        goto fail;
-                        }
-                }
-        }
+        /* The additional systemd udev properties we only interpret
+         * for the main object */
+        if (main)
+                (void) device_add_udev_wants(u, dev);
 
         /* Note that this won't dispatch the load queue, the caller
          * has to do that if needed and appropriate */
@@ -288,7 +326,7 @@ static int device_update_unit(Manager *m
         return 0;
 
 fail:
-        log_warning("Failed to load device unit: %s", strerror(-r));
+        log_warning_unit(u->id, "Failed to set up device unit: %m");
 
         if (delete && u)
                 unit_free(u);
@@ -296,7 +334,7 @@ fail:
         return r;
 }
 
-static int device_process_new_device(Manager *m, struct udev_device *dev) {
+static int device_process_new(Manager *m, struct udev_device *dev) {
         const char *sysfs, *dn, *alias;
         struct udev_list_entry *item = NULL, *first = NULL;
         int r;
@@ -308,14 +346,14 @@ static int device_process_new_device(Man
                 return 0;
 
         /* Add the main unit named after the sysfs path */
-        r = device_update_unit(m, dev, sysfs, true);
+        r = device_setup_unit(m, dev, sysfs, true);
         if (r < 0)
                 return r;
 
         /* Add an additional unit for the device node */
         dn = udev_device_get_devnode(dev);
         if (dn)
-                device_update_unit(m, dev, dn, false);
+                (void) device_setup_unit(m, dev, dn, false);
 
         /* Add additional units for all symlinks */
         first = udev_device_get_devlinks_list_entry(dev);
@@ -336,13 +374,13 @@ static int device_process_new_device(Man
                  * same /dev/disk/by-label/xxx link because they have
                  * the same label. We want to make sure that the same
                  * device that won the symlink wins in systemd, so we
-                 * check the device node major/minor*/
+                 * check the device node major/minor */
                 if (stat(p, &st) >= 0)
                         if ((!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode)) ||
                             st.st_rdev != udev_device_get_devnum(dev))
                                 continue;
 
-                device_update_unit(m, dev, p, false);
+                (void) device_setup_unit(m, dev, p, false);
         }
 
         /* Add additional units for all explicitly configured
@@ -359,48 +397,73 @@ static int device_process_new_device(Man
                         e[l] = 0;
 
                         if (path_is_absolute(e))
-                                device_update_unit(m, dev, e, false);
+                                (void) device_setup_unit(m, dev, e, false);
                         else
                                 log_warning("SYSTEMD_ALIAS for %s is not an absolute path, ignoring: %s", sysfs, e);
                 }
+                if (!isempty(state))
+                        log_warning("SYSTEMD_ALIAS for %s has trailing garbage, ignoring.", sysfs);
         }
 
         return 0;
 }
 
-static void device_set_path_plugged(Manager *m, struct udev_device *dev) {
-        const char *sysfs;
+static void device_update_found_one(Device *d, bool add, DeviceFound found, bool now) {
+        DeviceFound n;
+
+        assert(d);
+
+        n = add ? (d->found | found) : (d->found & ~found);
+        if (n == d->found)
+                return;
+
+        d->found = n;
+
+        if (now) {
+                if (d->found & DEVICE_FOUND_UDEV)
+                        device_set_state(d, DEVICE_PLUGGED);
+                else if (d->found != DEVICE_NOT_FOUND)
+                        device_set_state(d, DEVICE_TENTATIVE);
+                else
+                        device_set_state(d, DEVICE_DEAD);
+        }
+}
+
+static int device_update_found_by_sysfs(Manager *m, const char *sysfs, bool add, DeviceFound found, bool now) {
         Device *d, *l;
 
         assert(m);
-        assert(dev);
+        assert(sysfs);
 
-        sysfs = udev_device_get_syspath(dev);
-        if (!sysfs)
-                return;
+        if (found == DEVICE_NOT_FOUND)
+                return 0;
 
         l = hashmap_get(m->devices_by_sysfs, sysfs);
         LIST_FOREACH(same_sysfs, d, l)
-                device_set_state(d, DEVICE_PLUGGED);
+                device_update_found_one(d, add, found, now);
+
+        return 0;
 }
 
-static int device_process_removed_device(Manager *m, struct udev_device *dev) {
-        const char *sysfs;
-        Device *d;
+static int device_update_found_by_name(Manager *m, const char *path, bool add, DeviceFound found, bool now) {
+        _cleanup_free_ char *e = NULL;
+        Unit *u;
 
         assert(m);
-        assert(dev);
+        assert(path);
 
-        sysfs = udev_device_get_syspath(dev);
-        if (!sysfs)
-                return -ENOMEM;
+        if (found == DEVICE_NOT_FOUND)
+                return 0;
 
-        /* Remove all units of this sysfs path */
-        while ((d = hashmap_get(m->devices_by_sysfs, sysfs))) {
-                device_unset_sysfs(d);
-                device_set_state(d, DEVICE_DEAD);
-        }
+        e = unit_name_from_path(path, ".device");
+        if (!e)
+                return log_oom();
+
+        u = manager_get_unit(m, e);
+        if (!u)
+                return 0;
 
+        device_update_found_one(DEVICE(u), add, found, now);
         return 0;
 }
 
@@ -416,22 +479,6 @@ static bool device_is_ready(struct udev_
         return parse_boolean(ready) != 0;
 }
 
-static int device_process_new_path(Manager *m, const char *path) {
-        _cleanup_udev_device_unref_ struct udev_device *dev = NULL;
-
-        assert(m);
-        assert(path);
-
-        dev = udev_device_new_from_syspath(m->udev, path);
-        if (!dev)
-                return log_oom();
-
-        if (!device_is_ready(dev))
-                return 0;
-
-        return device_process_new_device(m, dev);
-}
-
 static Unit *device_following(Unit *u) {
         Device *d = DEVICE(u);
         Device *other, *first = NULL;
@@ -558,12 +605,31 @@ static int device_enumerate(Manager *m)
                 goto fail;
 
         first = udev_enumerate_get_list_entry(e);
-        udev_list_entry_foreach(item, first)
-                device_process_new_path(m, udev_list_entry_get_name(item));
+        udev_list_entry_foreach(item, first) {
+                _cleanup_udev_device_unref_ struct udev_device *dev = NULL;
+                const char *sysfs;
+
+                sysfs = udev_list_entry_get_name(item);
+
+                dev = udev_device_new_from_syspath(m->udev, sysfs);
+                if (!dev) {
+                        log_oom();
+                        continue;
+                }
+
+                if (!device_is_ready(dev))
+                        continue;
+
+                (void) device_process_new(m, dev);
+
+                device_update_found_by_sysfs(m, sysfs, true, DEVICE_FOUND_UDEV, false);
+        }
 
         return 0;
 
 fail:
+        log_error("Failed to enumerate devices: %s", strerror(-r));
+
         device_shutdown(m);
         return r;
 }
@@ -571,7 +637,7 @@ fail:
 static int device_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
         _cleanup_udev_device_unref_ struct udev_device *dev = NULL;
         Manager *m = userdata;
-        const char *action;
+        const char *action, *sysfs;
         int r;
 
         assert(m);
@@ -593,41 +659,105 @@ static int device_dispatch_io(sd_event_s
         if (!dev)
                 return 0;
 
+        sysfs = udev_device_get_syspath(dev);
+        if (!sysfs) {
+                log_error("Failed to get udev sys path.");
+                return 0;
+        }
+
         action = udev_device_get_action(dev);
         if (!action) {
                 log_error("Failed to get udev action string.");
                 return 0;
         }
 
-        if (streq(action, "remove") || !device_is_ready(dev))  {
-                r = device_process_removed_device(m, dev);
-                if (r < 0)
-                        log_error("Failed to process device remove event: %s", strerror(-r));
-
-                r = swap_process_removed_device(m, dev);
+        if (streq(action, "remove"))  {
+                r = swap_process_device_remove(m, dev);
                 if (r < 0)
                         log_error("Failed to process swap device remove event: %s", strerror(-r));
 
-        } else {
-                r = device_process_new_device(m, dev);
-                if (r < 0)
-                        log_error("Failed to process device new event: %s", strerror(-r));
+                /* If we get notified that a device was removed by
+                 * udev, then it's completely gone, hence unset all
+                 * found bits */
+                device_update_found_by_sysfs(m, sysfs, false, DEVICE_FOUND_UDEV|DEVICE_FOUND_MOUNT|DEVICE_FOUND_SWAP, true);
 
-                r = swap_process_new_device(m, dev);
+        } else if (device_is_ready(dev)) {
+
+                (void) device_process_new(m, dev);
+
+                r = swap_process_device_new(m, dev);
                 if (r < 0)
                         log_error("Failed to process swap device new event: %s", strerror(-r));
 
                 manager_dispatch_load_queue(m);
 
-                device_set_path_plugged(m, dev);
+                /* The device is found now, set the udev found bit */
+                device_update_found_by_sysfs(m, sysfs, true, DEVICE_FOUND_UDEV, true);
+
+        } else {
+                /* The device is nominally around, but not ready for
+                 * us. Hence unset the udev bit, but leave the rest
+                 * around. */
+
+                device_update_found_by_sysfs(m, sysfs, false, DEVICE_FOUND_UDEV, true);
         }
 
         return 0;
 }
 
+int device_found_node(Manager *m, const char *node, bool add, DeviceFound found, bool now) {
+        _cleanup_udev_device_unref_ struct udev_device *dev = NULL;
+        struct stat st;
+
+        assert(m);
+        assert(node);
+
+        /* This is called whenever we find a device referenced in
+         * /proc/swaps or /proc/self/mounts. Such a device might be
+         * mounted/enabled at a time where udev has not finished
+         * probing it yet, and we thus haven't learned about it
+         * yet. In this case we will set the device unit to
+         * "tentative" state. */
+
+        if (add) {
+                if (!path_startswith(node, "/dev"))
+                        return 0;
+
+                if (stat(node, &st) < 0) {
+                        if (errno == ENOENT)
+                                return 0;
+
+                        log_error("Failed to stat device node file %s: %m", node);
+                        return -errno;
+                }
+
+                if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode))
+                        return 0;
+
+                dev = udev_device_new_from_devnum(m->udev, S_ISBLK(st.st_mode) ? 'b' : 'c', st.st_rdev);
+                if (!dev) {
+                        if (errno == ENOENT)
+                                return 0;
+
+                        return log_oom();
+                }
+
+                /* If the device is known in the kernel and newly
+                 * appeared, then we'll create a device unit for it,
+                 * under the name referenced in /proc/swaps or
+                 * /proc/self/mountinfo. */
+
+                (void) device_setup_unit(m, dev, node, false);
+        }
+
+        /* Update the device unit's state, should it exist */
+        return device_update_found_by_name(m, node, add, found, now);
+}
+
 static const char* const device_state_table[_DEVICE_STATE_MAX] = {
         [DEVICE_DEAD] = "dead",
-        [DEVICE_PLUGGED] = "plugged"
+        [DEVICE_TENTATIVE] = "tentative",
+        [DEVICE_PLUGGED] = "plugged",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(device_state, DeviceState);
--- src/core/device.h
+++ src/core/device.h	2015-03-12 00:00:00.000000000 +0000
@@ -29,20 +29,28 @@ typedef struct Device Device;
  * simplifies the state engine greatly */
 typedef enum DeviceState {
         DEVICE_DEAD,
-        DEVICE_PLUGGED,
+        DEVICE_TENTATIVE, /* mounted or swapped, but not (yet) announced by udev */
+        DEVICE_PLUGGED,   /* announced by udev */
         _DEVICE_STATE_MAX,
         _DEVICE_STATE_INVALID = -1
 } DeviceState;
 
+typedef enum DeviceFound {
+        DEVICE_NOT_FOUND = 0,
+        DEVICE_FOUND_UDEV = 1,
+        DEVICE_FOUND_MOUNT = 2,
+        DEVICE_FOUND_SWAP = 4,
+} DeviceFound;
+
 struct Device {
         Unit meta;
 
         char *sysfs;
+        DeviceFound found;
 
         /* In order to be able to distinguish dependencies on
         different device nodes we might end up creating multiple
         devices for the same sysfs path. We chain them up here. */
-
         LIST_FIELDS(struct Device, same_sysfs);
 
         DeviceState state;
@@ -52,3 +60,5 @@ extern const UnitVTable device_vtable;
 
 const char* device_state_to_string(DeviceState i) _const_;
 DeviceState device_state_from_string(const char *s) _pure_;
+
+int device_found_node(Manager *m, const char *node, bool add, DeviceFound found, bool now);
--- src/core/mount.c
+++ src/core/mount.c	2015-03-13 10:52:19.117518748 +0000
@@ -1381,7 +1381,7 @@ static int mount_dispatch_timer(sd_event
         return 0;
 }
 
-static int mount_add_one(
+static int mount_setup_unit(
                 Manager *m,
                 const char *what,
                 const char *where,
@@ -1427,7 +1427,7 @@ static int mount_add_one(
 
                 u = unit_new(m, sizeof(Mount));
                 if (!u)
-                        return -ENOMEM;
+                        return log_oom();
 
                 r = unit_add_name(u, e);
                 if (r < 0)
@@ -1558,6 +1558,8 @@ static int mount_add_one(
         return 0;
 
 fail:
+        log_warning("Failed to set up mount unit: %s", strerror(-r));
+
         if (delete && u)
                 unit_free(u);
 
@@ -1608,11 +1610,16 @@ static int mount_load_proc_self_mountinf
                         return log_oom();
 
                 d = cunescape(device);
+                if (!d)
+                        return log_oom();
+
                 p = cunescape(path);
-                if (!d || !p)
+                if (!p)
                         return log_oom();
 
-                k = mount_add_one(m, d, p, o, fstype, set_flags);
+		(void) device_found_node(m, d, true, DEVICE_FOUND_MOUNT, set_flags);
+
+                k = mount_setup_unit(m, d, p, o, fstype, set_flags);
                 if (k < 0)
                         r = k;
         }
@@ -1692,8 +1699,6 @@ static int mount_dispatch_io(sd_event_so
 
         r = mount_load_proc_self_mountinfo(m, true);
         if (r < 0) {
-                log_error("Failed to reread /proc/self/mountinfo: %s", strerror(-r));
-
                 /* Reset flags, just in case, for later calls */
                 LIST_FOREACH(units_by_type, u, m->units_by_type[UNIT_MOUNT]) {
                         Mount *mount = MOUNT(u);
@@ -1726,6 +1731,10 @@ static int mount_dispatch_io(sd_event_so
                                 break;
                         }
 
+                        if (mount->parameters_proc_self_mountinfo.what)
+                                (void) device_found_node(m, mount->parameters_proc_self_mountinfo.what, false, DEVICE_FOUND_MOUNT, true);
+
+
                 } else if (mount->just_mounted || mount->just_changed) {
 
                         /* New or changed mount entry */
--- src/core/swap.c
+++ src/core/swap.c	2015-03-12 14:01:14.402198277 +0000
@@ -362,7 +362,7 @@ static int swap_load(Unit *u) {
         return swap_verify(s);
 }
 
-static int swap_add_one(
+static int swap_setup_unit(
                 Manager *m,
                 const char *what,
                 const char *what_proc_swaps,
@@ -389,8 +389,10 @@ static int swap_add_one(
 
         if (u &&
             SWAP(u)->from_proc_swaps &&
-            !path_equal(SWAP(u)->parameters_proc_swaps.what, what_proc_swaps))
+            !path_equal(SWAP(u)->parameters_proc_swaps.what, what_proc_swaps)) {
+                log_error("Swap %s appeared twice with different device paths %s and %s", e, SWAP(u)->parameters_proc_swaps.what, what_proc_swaps);
                 return -EEXIST;
+        }
 
         if (!u) {
                 delete = true;
@@ -405,7 +407,7 @@ static int swap_add_one(
 
                 SWAP(u)->what = strdup(what);
                 if (!SWAP(u)->what) {
-                        r = log_oom();
+                        r = -ENOMEM;
                         goto fail;
                 }
 
@@ -435,7 +437,6 @@ static int swap_add_one(
         p->nofail = nofail;
 
         unit_add_to_dbus_queue(u);
-
         return 0;
 
 fail:
@@ -447,7 +448,7 @@ fail:
         return r;
 }
 
-static int swap_process_new_swap(Manager *m, const char *device, int prio, bool set_flags) {
+static int swap_process_new(Manager *m, const char *device, int prio, bool set_flags) {
         _cleanup_udev_device_unref_ struct udev_device *d = NULL;
         struct udev_list_entry *item = NULL, *first = NULL;
         const char *dn;
@@ -456,7 +457,7 @@ static int swap_process_new_swap(Manager
 
         assert(m);
 
-        r = swap_add_one(m, device, device, prio, false, false, set_flags);
+        r = swap_setup_unit(m, device, device, prio, false, false, set_flags);
         if (r < 0)
                 return r;
 
@@ -472,7 +473,7 @@ static int swap_process_new_swap(Manager
         /* Add the main device node */
         dn = udev_device_get_devnode(d);
         if (dn && !streq(dn, device))
-                swap_add_one(m, dn, device, prio, false, false, set_flags);
+                swap_setup_unit(m, dn, device, prio, false, false, set_flags);
 
         /* Add additional units for all symlinks */
         first = udev_device_get_devlinks_list_entry(d);
@@ -493,7 +494,7 @@ static int swap_process_new_swap(Manager
                             st.st_rdev != udev_device_get_devnum(d))
                                 continue;
 
-                swap_add_one(m, p, device, prio, false, false, set_flags);
+                swap_setup_unit(m, p, device, prio, false, false, set_flags);
         }
 
         return r;
@@ -1094,15 +1095,17 @@ static int swap_load_proc_swaps(Manager
                         if (k == EOF)
                                 break;
 
-                        log_warning("Failed to parse /proc/swaps:%u", i);
+                        log_warning("Failed to parse /proc/swaps:%u.", i);
                         continue;
                 }
 
                 d = cunescape(dev);
                 if (!d)
-                        return -ENOMEM;
+                        return log_oom();
+
+                device_found_node(m, d, true, DEVICE_FOUND_SWAP, set_flags);
 
-                k = swap_process_new_swap(m, d, prio, set_flags);
+                k = swap_process_new(m, d, prio, set_flags);
                 if (k < 0)
                         r = k;
         }
@@ -1154,6 +1157,9 @@ static int swap_dispatch_io(sd_event_sou
                                 break;
                         }
 
+                        if (swap->what)
+                                device_found_node(m, swap->what, false, DEVICE_FOUND_SWAP, true);
+
                 } else if (swap->just_activated) {
 
                         /* New swap entry */
@@ -1296,7 +1302,7 @@ fail:
         return r;
 }
 
-int swap_process_new_device(Manager *m, struct udev_device *dev) {
+int swap_process_device_new(Manager *m, struct udev_device *dev) {
         struct udev_list_entry *item = NULL, *first = NULL;
         _cleanup_free_ char *e = NULL;
         const char *dn;
@@ -1339,7 +1345,7 @@ int swap_process_new_device(Manager *m,
         return r;
 }
 
-int swap_process_removed_device(Manager *m, struct udev_device *dev) {
+int swap_process_device_remove(Manager *m, struct udev_device *dev) {
         const char *dn;
         int r = 0;
         Swap *s;
--- src/core/swap.h
+++ src/core/swap.h	2015-03-12 00:00:00.000000000 +0000
@@ -117,8 +117,8 @@ struct Swap {
 
 extern const UnitVTable swap_vtable;
 
-int swap_process_new_device(Manager *m, struct udev_device *dev);
-int swap_process_removed_device(Manager *m, struct udev_device *dev);
+int swap_process_device_new(Manager *m, struct udev_device *dev);
+int swap_process_device_remove(Manager *m, struct udev_device *dev);
 
 const char* swap_state_to_string(SwapState i) _const_;
 SwapState swap_state_from_string(const char *s) _pure_;
--- src/core/unit.c
+++ src/core/unit.c	2015-03-12 00:00:00.000000000 +0000
@@ -2511,7 +2511,6 @@ int unit_add_node_link(Unit *u, const ch
                 return -ENOMEM;
 
         r = manager_load_unit(u->manager, e, NULL, NULL, &device);
-
         if (r < 0)
                 return r;
 
