summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2018-06-07 17:04:57 +0200
committerGitHub <noreply@github.com>2018-06-07 17:04:57 +0200
commit3ceca73a596ede7af516f6bbd65a10c413be1fd5 (patch)
treea19a51d33bc4668e928458e737fd49df18bf7d5d
parentMerge pull request #9165 from ssahani/networkd-netdevsim (diff)
parentupdate TODO (diff)
downloadsystemd-3ceca73a596ede7af516f6bbd65a10c413be1fd5.tar.xz
systemd-3ceca73a596ede7af516f6bbd65a10c413be1fd5.zip
Merge pull request #9200 from poettering/device-state-fix
core: rework device state serialization/enumeration
-rw-r--r--TODO6
-rw-r--r--src/basic/conf-files.c2
-rw-r--r--src/basic/fs-util.c6
-rw-r--r--src/basic/selinux-util.c14
-rw-r--r--src/core/automount.c2
-rw-r--r--src/core/device.c391
-rw-r--r--src/core/device.h23
-rw-r--r--src/core/manager.c127
-rw-r--r--src/core/mount.c19
-rw-r--r--src/core/scope.c4
-rw-r--r--src/core/service.c2
-rw-r--r--src/core/slice.c4
-rw-r--r--src/core/swap.c25
-rw-r--r--src/core/unit.c11
-rw-r--r--src/core/unit.h23
-rw-r--r--src/journal/journalctl.c13
-rw-r--r--src/journal/journald-stream.c4
-rw-r--r--src/network/netdev/netdev.c2
-rw-r--r--src/nspawn/nspawn.c2
-rw-r--r--src/shared/bus-unit-util.c6
-rw-r--r--src/shared/conf-parser.c4
-rw-r--r--src/shared/dropin.c2
-rw-r--r--src/shared/udev-util.c23
-rw-r--r--src/shared/udev-util.h2
24 files changed, 437 insertions, 280 deletions
diff --git a/TODO b/TODO
index 533f9d87bd..4c4de2a1c8 100644
--- a/TODO
+++ b/TODO
@@ -24,6 +24,12 @@ Janitorial Clean-ups:
Features:
+* rework mount.c and swap.c to follow proper state enumeration/deserialization
+ semantics, like we do for device.c now
+
+* When reloading configuration PID 1 should reset all its properties to the
+ original defaults before calling parse_config()
+
* Add OnTimezoneChange= and OnTimeChange= stanzas to .timer units in order to
schedule events based on time and timezone changes.
diff --git a/src/basic/conf-files.c b/src/basic/conf-files.c
index 2e65a6e74f..15a066da7f 100644
--- a/src/basic/conf-files.c
+++ b/src/basic/conf-files.c
@@ -349,7 +349,7 @@ int conf_files_cat(const char *root, const char *name) {
assert(endswith(dir, "/"));
r = strv_extendf(&dirs, "%s%s.d", dir, name);
if (r < 0)
- return log_error("Failed to build directory list: %m");
+ return log_error_errno(r, "Failed to build directory list: %m");
}
r = conf_files_list_strv(&files, ".conf", root, 0, (const char* const*) dirs);
diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c
index 6146c66782..ab6ccf7c86 100644
--- a/src/basic/fs-util.c
+++ b/src/basic/fs-util.c
@@ -1155,9 +1155,9 @@ int fsync_directory_of_file(int fd) {
r = fd_get_path(fd, &path);
if (r < 0) {
- log_debug("Failed to query /proc/self/fd/%d%s: %m",
- fd,
- r == -EOPNOTSUPP ? ", ignoring" : "");
+ log_debug_errno(r, "Failed to query /proc/self/fd/%d%s: %m",
+ fd,
+ r == -EOPNOTSUPP ? ", ignoring" : "");
if (r == -EOPNOTSUPP)
/* If /proc is not available, we're most likely running in some
diff --git a/src/basic/selinux-util.c b/src/basic/selinux-util.c
index aba3e4f15d..61504a673a 100644
--- a/src/basic/selinux-util.c
+++ b/src/basic/selinux-util.c
@@ -79,7 +79,7 @@ int mac_selinux_init(void) {
label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0);
if (!label_hnd) {
- log_enforcing("Failed to initialize SELinux context: %m");
+ log_enforcing_errno(errno, "Failed to initialize SELinux context: %m");
r = security_getenforce() == 1 ? -errno : 0;
} else {
char timespan[FORMAT_TIMESPAN_MAX];
@@ -189,7 +189,7 @@ int mac_selinux_apply(const char *path, const char *label) {
assert(label);
if (setfilecon(path, label) < 0) {
- log_enforcing("Failed to set SELinux security context %s on path %s: %m", label, path);
+ log_enforcing_errno(errno, "Failed to set SELinux security context %s on path %s: %m", label, path);
if (security_getenforce() > 0)
return -errno;
}
@@ -349,12 +349,12 @@ int mac_selinux_create_file_prepare(const char *path, mode_t mode) {
if (errno == ENOENT)
return 0;
- log_enforcing("Failed to determine SELinux security context for %s: %m", path);
+ log_enforcing_errno(errno, "Failed to determine SELinux security context for %s: %m", path);
} else {
if (setfscreatecon_raw(filecon) >= 0)
return 0; /* Success! */
- log_enforcing("Failed to set SELinux security context %s for %s: %m", filecon, path);
+ log_enforcing_errno(errno, "Failed to set SELinux security context %s for %s: %m", filecon, path);
}
if (security_getenforce() > 0)
@@ -385,7 +385,7 @@ int mac_selinux_create_socket_prepare(const char *label) {
assert(label);
if (setsockcreatecon(label) < 0) {
- log_enforcing("Failed to set SELinux security context %s for sockets: %m", label);
+ log_enforcing_errno(errno, "Failed to set SELinux security context %s for sockets: %m", label);
if (security_getenforce() == 1)
return -errno;
@@ -457,13 +457,13 @@ int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
if (errno == ENOENT)
goto skipped;
- log_enforcing("Failed to determine SELinux security context for %s: %m", path);
+ log_enforcing_errno(errno, "Failed to determine SELinux security context for %s: %m", path);
if (security_getenforce() > 0)
return -errno;
} else {
if (setfscreatecon_raw(fcon) < 0) {
- log_enforcing("Failed to set SELinux security context %s for %s: %m", fcon, path);
+ log_enforcing_errno(errno, "Failed to set SELinux security context %s for %s: %m", fcon, path);
if (security_getenforce() > 0)
return -errno;
} else
diff --git a/src/core/automount.c b/src/core/automount.c
index 3e848c8085..0aba87b778 100644
--- a/src/core/automount.c
+++ b/src/core/automount.c
@@ -177,7 +177,7 @@ static int automount_verify(Automount *a) {
r = unit_name_from_path(a->where, ".automount", &e);
if (r < 0)
- return log_unit_error(UNIT(a), "Failed to generate unit name from path: %m");
+ return log_unit_error_errno(UNIT(a), r, "Failed to generate unit name from path: %m");
if (!unit_has_name(UNIT(a), e)) {
log_unit_error(UNIT(a), "Where= setting doesn't match unit name. Refusing.");
diff --git a/src/core/device.c b/src/core/device.c
index b3f8eb4e02..915991bf90 100644
--- a/src/core/device.c
+++ b/src/core/device.c
@@ -30,6 +30,7 @@ static const UnitActiveState state_translation_table[_DEVICE_STATE_MAX] = {
};
static int device_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata);
+static void device_update_found_one(Device *d, DeviceFound found, DeviceFound mask);
static void device_unset_sysfs(Device *d) {
Hashmap *devices;
@@ -55,8 +56,8 @@ static void device_unset_sysfs(Device *d) {
}
static int device_set_sysfs(Device *d, const char *sysfs) {
+ _cleanup_free_ char *copy = NULL;
Device *first;
- char *copy;
int r;
assert(d);
@@ -80,12 +81,10 @@ static int device_set_sysfs(Device *d, const char *sysfs) {
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;
-
+ d->sysfs = TAKE_PTR(copy);
return 0;
}
@@ -103,6 +102,8 @@ static void device_init(Unit *u) {
u->job_running_timeout = u->manager->default_timeout_start_usec;
u->ignore_on_isolate = true;
+
+ d->deserialized_state = _DEVICE_STATE_INVALID;
}
static void device_done(Unit *u) {
@@ -120,6 +121,9 @@ static void device_set_state(Device *d, DeviceState state) {
old_state = d->state;
d->state = state;
+ if (state == DEVICE_DEAD)
+ device_unset_sysfs(d);
+
if (state != old_state)
log_unit_debug(UNIT(d), "Changed %s -> %s", device_state_to_string(old_state), device_state_to_string(state));
@@ -132,34 +136,37 @@ static int device_coldplug(Unit *u) {
assert(d);
assert(d->state == DEVICE_DEAD);
- /* This should happen only when we reexecute PID1 from an old version
- * which didn't serialize d->found. In this case simply assume that the
- * device was in plugged state right before we started reexecuting which
- * might be a wrong assumption. */
- if (d->found == DEVICE_FOUND_UDEV_DB)
- d->found = DEVICE_FOUND_UDEV;
+ /* First, let's put the deserialized state and found mask into effect, if we have it. */
- 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 && d->deserialized_state != DEVICE_PLUGGED)
- /* If a device is found in /proc/self/mountinfo or
- * /proc/swaps, and was not yet announced via udev,
- * it's "tentatively" around. */
- device_set_state(d, DEVICE_TENTATIVE);
+ if (d->deserialized_state < 0 ||
+ (d->deserialized_state == d->state &&
+ d->deserialized_found == d->found))
+ return 0;
+ d->found = d->deserialized_found;
+ device_set_state(d, d->deserialized_state);
return 0;
}
+static void device_catchup(Unit *u) {
+ Device *d = DEVICE(u);
+
+ assert(d);
+
+ /* Second, let's update the state with the enumerated state if it's different */
+ if (d->enumerated_found == d->found)
+ return;
+
+ device_update_found_one(d, d->enumerated_found, DEVICE_FOUND_MASK);
+}
+
static const struct {
DeviceFound flag;
const char *name;
} device_found_map[] = {
- { DEVICE_FOUND_UDEV, "found-udev" },
- { DEVICE_FOUND_UDEV_DB, "found-udev-db" },
- { DEVICE_FOUND_MOUNT, "found-mount" },
- { DEVICE_FOUND_SWAP, "found-swap" },
- {}
+ { DEVICE_FOUND_UDEV, "found-udev" },
+ { DEVICE_FOUND_MOUNT, "found-mount" },
+ { DEVICE_FOUND_SWAP, "found-swap" },
};
static int device_found_to_string_many(DeviceFound flags, char **ret) {
@@ -168,8 +175,8 @@ static int device_found_to_string_many(DeviceFound flags, char **ret) {
assert(ret);
- for (i = 0; device_found_map[i].name; i++) {
- if ((flags & device_found_map[i].flag) != device_found_map[i].flag)
+ for (i = 0; i < ELEMENTSOF(device_found_map); i++) {
+ if (!FLAGS_SET(flags, device_found_map[i].flag))
continue;
if (!strextend_with_separator(&s, ",", device_found_map[i].name, NULL))
@@ -198,7 +205,7 @@ static int device_found_from_string_many(const char *name, DeviceFound *ret) {
if (r == 0)
break;
- for (i = 0; device_found_map[i].name; i++)
+ for (i = 0; i < ELEMENTSOF(device_found_map); i++)
if (streq(word, device_found_map[i].name)) {
f = device_found_map[i].flag;
break;
@@ -224,8 +231,8 @@ static int device_serialize(Unit *u, FILE *f, FDSet *fds) {
unit_serialize_item(u, f, "state", device_state_to_string(d->state));
- (void) device_found_to_string_many(d->found, &s);
- unit_serialize_item(u, f, "found", s);
+ if (device_found_to_string_many(d->found, &s) >= 0)
+ unit_serialize_item(u, f, "found", s);
return 0;
}
@@ -239,31 +246,19 @@ static int device_deserialize_item(Unit *u, const char *key, const char *value,
assert(value);
assert(fds);
- /* The device was known at the time units were serialized but it's not
- * anymore at the time units are deserialized. This happens when PID1 is
- * re-executed after having switched to the new rootfs: devices were
- * enumerated but udevd wasn't running yet thus the list of devices
- * (handled by systemd) to initialize was empty. In such case we wait
- * for the device events to be re-triggered by udev so device units are
- * properly re-initialized. */
- if (d->found == DEVICE_NOT_FOUND) {
- assert(d->sysfs == NULL);
- return 0;
- }
-
if (streq(key, "state")) {
DeviceState state;
state = device_state_from_string(value);
if (state < 0)
- log_unit_debug(u, "Failed to parse state value: %s", value);
+ log_unit_debug(u, "Failed to parse state value, ignoring: %s", value);
else
d->deserialized_state = state;
} else if (streq(key, "found")) {
- r = device_found_from_string_many(value, &d->found);
+ r = device_found_from_string_many(value, &d->deserialized_found);
if (r < 0)
- log_unit_debug(u, "Failed to parse found value: %s", value);
+ log_unit_debug_errno(u, r, "Failed to parse found value, ignoring: %s", value);
} else
log_unit_debug(u, "Unknown serialization key: %s", key);
@@ -326,19 +321,18 @@ static int device_update_description(Unit *u, struct udev_device *dev, const cha
_cleanup_free_ char *j;
j = strjoin(model, " ", label);
- if (j)
- r = unit_set_description(u, j);
- else
- r = -ENOMEM;
+ if (!j)
+ return log_oom();
+
+ r = unit_set_description(u, j);
} else
r = unit_set_description(u, model);
} else
r = unit_set_description(u, path);
-
if (r < 0)
- log_unit_error_errno(u, r, "Failed to set device description: %m");
+ return log_unit_error_errno(u, r, "Failed to set device description: %m");
- return r;
+ return 0;
}
static int device_add_udev_wants(Unit *u, struct udev_device *dev) {
@@ -412,7 +406,7 @@ static bool device_is_bound_by_mounts(Device *d, struct udev_device *dev) {
return d->bind_mounts;
}
-static int device_upgrade_mount_deps(Unit *u) {
+static void device_upgrade_mount_deps(Unit *u) {
Unit *other;
Iterator i;
void *v;
@@ -426,9 +420,8 @@ static int device_upgrade_mount_deps(Unit *u) {
r = unit_add_dependency(other, UNIT_BINDS_TO, u, true, UNIT_DEPENDENCY_UDEV);
if (r < 0)
- return r;
+ log_unit_warning_errno(u, r, "Failed to add BindsTo= dependency between device and mount unit, ignoring: %m");
}
- return 0;
}
static int device_setup_unit(Manager *m, struct udev_device *dev, const char *path, bool main) {
@@ -443,8 +436,10 @@ static int device_setup_unit(Manager *m, struct udev_device *dev, const char *pa
if (dev) {
sysfs = udev_device_get_syspath(dev);
- if (!sysfs)
+ if (!sysfs) {
+ log_debug("Couldn't get syspath from udev device, ignoring.");
return 0;
+ }
}
r = unit_name_from_path(path, ".device", &e);
@@ -453,17 +448,21 @@ static int device_setup_unit(Manager *m, struct udev_device *dev, const char *pa
u = manager_get_unit(m, e);
if (u) {
- /* The device unit can still be present even if the device was unplugged: a mount unit can reference it hence
- * preventing the GC to have garbaged it. That's desired since the device unit may have a dependency on the
- * mount unit which was added during the loading of the later. */
- if (dev && DEVICE(u)->state == DEVICE_PLUGGED) {
-
- /* This unit is in plugged state: we're sure it's attached to a device. */
- if (!path_equal(DEVICE(u)->sysfs, sysfs)) {
- log_unit_debug(u, "Dev %s appeared twice with different sysfs paths %s and %s",
- e, DEVICE(u)->sysfs, sysfs);
- return -EEXIST;
- }
+ /* The device unit can still be present even if the device was unplugged: a mount unit can reference it
+ * hence preventing the GC to have garbaged it. That's desired since the device unit may have a
+ * dependency on the mount unit which was added during the loading of the later. When the device is
+ * plugged the sysfs might not be initialized yet, as we serialize the device's state but do not
+ * serialize the sysfs path across reloads/reexecs. Hence, when coming back from a reload/restart we
+ * might have the state valid, but not the sysfs path. Hence, let's filter out conflicting devices, but
+ * let's accept devices in any state with no sysfs path set. */
+
+ if (DEVICE(u)->state == DEVICE_PLUGGED &&
+ DEVICE(u)->sysfs &&
+ sysfs &&
+ !path_equal(DEVICE(u)->sysfs, sysfs)) {
+ log_unit_debug(u, "Device %s appeared twice with different sysfs paths %s and %s, ignoring the latter.",
+ e, DEVICE(u)->sysfs, sysfs);
+ return -EEXIST;
}
delete = false;
@@ -475,24 +474,26 @@ static int device_setup_unit(Manager *m, struct udev_device *dev, const char *pa
delete = true;
r = unit_new_for_name(m, sizeof(Device), e, &u);
- if (r < 0)
+ if (r < 0) {
+ log_error_errno(r, "Failed to allocate device unit %s: %m", e);
goto fail;
+ }
unit_add_to_load_queue(u);
}
- /* If this was created via some dependency and has not
- * actually been seen yet ->sysfs will not be
+ /* If this was created via some dependency and has not actually been seen yet ->sysfs will not be
* initialized. Hence initialize it if necessary. */
if (sysfs) {
r = device_set_sysfs(DEVICE(u), sysfs);
- if (r < 0)
+ if (r < 0) {
+ log_error_errno(r, "Failed to set sysfs path %s for device unit %s: %m", sysfs, e);
goto fail;
+ }
(void) device_update_description(u, dev, path);
- /* The additional systemd udev properties we only interpret
- * for the main object */
+ /* The additional systemd udev properties we only interpret for the main object */
if (main)
(void) device_add_udev_wants(u, dev);
}
@@ -504,13 +505,11 @@ static int device_setup_unit(Manager *m, struct udev_device *dev, const char *pa
device_upgrade_mount_deps(u);
/* Note that this won't dispatch the load queue, the caller has to do that if needed and appropriate */
-
unit_add_to_dbus_queue(u);
+
return 0;
fail:
- log_unit_warning_errno(u, r, "Failed to set up device unit: %m");
-
if (delete)
unit_free(u);
@@ -574,76 +573,81 @@ static int device_process_new(Manager *m, struct udev_device *dev) {
r = extract_first_word(&alias, &word, NULL, EXTRACT_QUOTES);
if (r == 0)
- return 0;
+ break;
if (r == -ENOMEM)
return log_oom();
if (r < 0)
return log_warning_errno(r, "Failed to add parse SYSTEMD_ALIAS for %s: %m", sysfs);
- if (path_is_absolute(word))
- (void) device_setup_unit(m, dev, word, false);
- else
+ if (!path_is_absolute(word))
log_warning("SYSTEMD_ALIAS for %s is not an absolute path, ignoring: %s", sysfs, word);
+ else if (!path_is_normalized(word))
+ log_warning("SYSTEMD_ALIAS for %s is not a normalized path, ignoring: %s", sysfs, word);
+ else
+ (void) device_setup_unit(m, dev, word, false);
}
-}
-static void device_update_found_one(Device *d, bool add, DeviceFound found, bool now) {
- DeviceFound n, previous;
+ return 0;
+}
+static void device_found_changed(Device *d, DeviceFound previous, DeviceFound now) {
assert(d);
- n = add ? (d->found | found) : (d->found & ~found);
- if (n == d->found)
- return;
-
- previous = d->found;
- d->found = n;
-
- if (!now)
- return;
-
/* Didn't exist before, but does now? if so, generate a new invocation ID for it */
- if (previous == DEVICE_NOT_FOUND && d->found != DEVICE_NOT_FOUND)
+ if (previous == DEVICE_NOT_FOUND && now != DEVICE_NOT_FOUND)
(void) unit_acquire_invocation_id(UNIT(d));
- if (d->found & DEVICE_FOUND_UDEV)
- /* When the device is known to udev we consider it
- * plugged. */
+ if (FLAGS_SET(now, DEVICE_FOUND_UDEV))
+ /* When the device is known to udev we consider it plugged. */
device_set_state(d, DEVICE_PLUGGED);
- else if (d->found != DEVICE_NOT_FOUND && (previous & DEVICE_FOUND_UDEV) == 0)
- /* If the device has not been seen by udev yet, but is
- * now referenced by the kernel, then we assume the
+ else if (now != DEVICE_NOT_FOUND && !FLAGS_SET(previous, DEVICE_FOUND_UDEV))
+ /* If the device has not been seen by udev yet, but is now referenced by the kernel, then we assume the
* kernel knows it now, and udev might soon too. */
device_set_state(d, DEVICE_TENTATIVE);
- else {
- /* If nobody sees the device, or if the device was
- * previously seen by udev and now is only referenced
- * from the kernel, then we consider the device is
- * gone, the kernel just hasn't noticed it yet. */
-
+ else
+ /* If nobody sees the device, or if the device was previously seen by udev and now is only referenced
+ * from the kernel, then we consider the device is gone, the kernel just hasn't noticed it yet. */
device_set_state(d, DEVICE_DEAD);
- device_unset_sysfs(d);
- }
+}
+static void device_update_found_one(Device *d, DeviceFound found, DeviceFound mask) {
+ assert(d);
+
+ if (MANAGER_IS_RUNNING(UNIT(d)->manager)) {
+ DeviceFound n, previous;
+
+ /* When we are already running, then apply the new mask right-away, and trigger state changes
+ * right-away */
+
+ n = (d->found & ~mask) | (found & mask);
+ if (n == d->found)
+ return;
+
+ previous = d->found;
+ d->found = n;
+
+ device_found_changed(d, previous, n);
+ } else
+ /* We aren't running yet, let's apply the new mask to the shadow variable instead, which we'll apply as
+ * soon as we catch-up with the state. */
+ d->enumerated_found = (d->enumerated_found & ~mask) | (found & mask);
}
-static int device_update_found_by_sysfs(Manager *m, const char *sysfs, bool add, DeviceFound found, bool now) {
+static void device_update_found_by_sysfs(Manager *m, const char *sysfs, DeviceFound found, DeviceFound mask) {
Device *d, *l, *n;
assert(m);
assert(sysfs);
- if (found == DEVICE_NOT_FOUND)
- return 0;
+ if (mask == 0)
+ return;
l = hashmap_get(m->devices_by_sysfs, sysfs);
LIST_FOREACH_SAFE(same_sysfs, d, n, l)
- device_update_found_one(d, add, found, now);
-
- return 0;
+ device_update_found_one(d, found, mask);
}
-static int device_update_found_by_name(Manager *m, const char *path, bool add, DeviceFound found, bool now) {
+static int device_update_found_by_name(Manager *m, const char *path, DeviceFound found, DeviceFound mask) {
_cleanup_free_ char *e = NULL;
Unit *u;
int r;
@@ -651,7 +655,7 @@ static int device_update_found_by_name(Manager *m, const char *path, bool add, D
assert(m);
assert(path);
- if (found == DEVICE_NOT_FOUND)
+ if (mask == 0)
return 0;
r = unit_name_from_path(path, ".device", &e);
@@ -662,7 +666,7 @@ static int device_update_found_by_name(Manager *m, const char *path, bool add, D
if (!u)
return 0;
- device_update_found_one(DEVICE(u), add, found, now);
+ device_update_found_one(DEVICE(u), found, mask);
return 0;
}
@@ -753,7 +757,7 @@ static void device_enumerate(Manager *m) {
if (!m->udev_monitor) {
m->udev_monitor = udev_monitor_new_from_netlink(m->udev, "udev");
if (!m->udev_monitor) {
- log_oom();
+ log_error_errno(errno, "Failed to allocate udev monitor: %m");
goto fail;
}
@@ -785,7 +789,7 @@ static void device_enumerate(Manager *m) {
e = udev_enumerate_new(m->udev);
if (!e) {
- log_oom();
+ log_error_errno(errno, "Failed to alloacte udev enumerator: %m");
goto fail;
}
@@ -816,7 +820,13 @@ static void device_enumerate(Manager *m) {
dev = udev_device_new_from_syspath(m->udev, sysfs);
if (!dev) {
- log_oom();
+ if (errno == ENOMEM) {
+ log_oom();
+ goto fail;
+ }
+
+ /* If we can't create a device, don't bother, it probably just disappeared. */
+ log_debug_errno(errno, "Failed to create udev device object for %s: %m", sysfs);
continue;
}
@@ -824,8 +834,7 @@ static void device_enumerate(Manager *m) {
continue;
(void) device_process_new(m, dev);
-
- device_update_found_by_sysfs(m, sysfs, true, DEVICE_FOUND_UDEV_DB, false);
+ device_update_found_by_sysfs(m, sysfs, DEVICE_FOUND_UDEV, DEVICE_FOUND_UDEV);
}
return;
@@ -834,6 +843,24 @@ fail:
device_shutdown(m);
}
+static void device_propagate_reload_by_sysfs(Manager *m, const char *sysfs) {
+ Device *d, *l, *n;
+ int r;
+
+ assert(m);
+ assert(sysfs);
+
+ l = hashmap_get(m->devices_by_sysfs, sysfs);
+ LIST_FOREACH_SAFE(same_sysfs, d, n, l) {
+ if (d->state == DEVICE_DEAD)
+ continue;
+
+ r = manager_propagate_reload(m, UNIT(d), JOB_REPLACE, NULL);
+ if (r < 0)
+ log_warning_errno(r, "Failed to propagate reload, ignoring: %m");
+ }
+}
+
static int device_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
_cleanup_(udev_device_unrefp) struct udev_device *dev = NULL;
Manager *m = userdata;
@@ -871,20 +898,8 @@ static int device_dispatch_io(sd_event_source *source, int fd, uint32_t revents,
return 0;
}
- if (streq(action, "change")) {
- Unit *u;
- Device *d, *l, *n;
-
- l = hashmap_get(m->devices_by_sysfs, sysfs);
- LIST_FOREACH_SAFE(same_sysfs, d, n, l) {
- u = &d->meta;
- if (u && UNIT_VTABLE(u)->active_state(u) == UNIT_ACTIVE) {
- r = manager_propagate_reload(m, u, JOB_REPLACE, NULL);
- if (r < 0)
- log_error_errno(r, "Failed to propagate reload: %m");
- }
- }
- }
+ if (streq(action, "change"))
+ device_propagate_reload_by_sysfs(m, sysfs);
/* A change event can signal that a device is becoming ready, in particular if
* the device is using the SYSTEMD_READY logic in udev
@@ -892,12 +907,12 @@ static int device_dispatch_io(sd_event_source *source, int fd, uint32_t revents,
if (streq(action, "remove")) {
r = swap_process_device_remove(m, dev);
if (r < 0)
- log_error_errno(r, "Failed to process swap device remove event: %m");
+ log_warning_errno(r, "Failed to process swap device remove event, ignoring: %m");
/* 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);
+ device_update_found_by_sysfs(m, sysfs, 0, DEVICE_FOUND_UDEV|DEVICE_FOUND_MOUNT|DEVICE_FOUND_SWAP);
} else if (device_is_ready(dev)) {
@@ -905,19 +920,19 @@ static int device_dispatch_io(sd_event_source *source, int fd, uint32_t revents,
r = swap_process_device_new(m, dev);
if (r < 0)
- log_error_errno(r, "Failed to process swap device new event: %m");
+ log_warning_errno(r, "Failed to process swap device new event, ignoring: %m");
manager_dispatch_load_queue(m);
/* The device is found now, set the udev found bit */
- device_update_found_by_sysfs(m, sysfs, true, DEVICE_FOUND_UDEV, true);
+ device_update_found_by_sysfs(m, sysfs, DEVICE_FOUND_UDEV, DEVICE_FOUND_UDEV);
} 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);
+ device_update_found_by_sysfs(m, sysfs, 0, DEVICE_FOUND_UDEV);
}
return 0;
@@ -935,61 +950,88 @@ static bool device_supported(void) {
return read_only <= 0;
}
-int device_found_node(Manager *m, const char *node, bool add, DeviceFound found, bool now) {
- _cleanup_(udev_device_unrefp) struct udev_device *dev = NULL;
+static int validate_node(Manager *m, const char *node, struct udev_device **ret) {
struct stat st;
+ int r;
assert(m);
assert(node);
+ assert(ret);
- if (!device_supported())
- return 0;
+ /* Validates a device node that showed up in /proc/swaps or /proc/self/mountinfo if it makes sense for us to
+ * track. Note that this validator is fine within missing device nodes, but not with badly set up ones! */
- /* 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 (!path_startswith(node, "/dev")) {
+ *ret = NULL;
+ return 0; /* bad! */
+ }
- if (add) {
- if (!path_startswith(node, "/dev"))
- return 0;
+ if (stat(node, &st) < 0) {
+ if (errno != ENOENT)
+ return log_error_errno(errno, "Failed to stat() device node file %s: %m", node);
- /* We make an extra check here, if the device node
- * actually exists. If it's missing, then this is an
- * indication that device was unplugged but is still
- * referenced in /proc/swaps or
- * /proc/self/mountinfo. Note that this check doesn't
- * really cover all cases where a device might be gone
- * away, since drives that can have a medium inserted
- * will still have a device node even when the medium
- * is not there... */
+ *ret = NULL;
+ return 1; /* good! (though missing) */
- if (stat(node, &st) >= 0) {
- if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode))
- return 0;
+ } else {
+ _cleanup_(udev_device_unrefp) struct udev_device *dev = NULL;
- dev = udev_device_new_from_devnum(m->udev, S_ISBLK(st.st_mode) ? 'b' : 'c', st.st_rdev);
- if (!dev && errno != ENOENT)
- return log_error_errno(errno, "Failed to get udev device from devnum %u:%u: %m", major(st.st_rdev), minor(st.st_rdev));
+ r = udev_device_new_from_stat_rdev(m->udev, &st, &dev);
+ if (r == -ENOENT) {
+ *ret = NULL;
+ return 1; /* good! (though missing) */
+ } else if (r == -ENOTTY) {
+ *ret = NULL;
+ return 0; /* bad! (not a device node but some other kind of file system node) */
+ } else if (r < 0)
+ return log_error_errno(r, "Failed to get udev device from devnum %u:%u: %m", major(st.st_rdev), minor(st.st_rdev));
+
+ *ret = TAKE_PTR(dev);
+ return 1; /* good! */
+ }
+}
- } else if (errno != ENOENT)
- return log_error_errno(errno, "Failed to stat device node file %s: %m", node);
+void device_found_node(Manager *m, const char *node, DeviceFound found, DeviceFound mask) {
+ int r;
- /* 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. */
+ assert(m);
+ assert(node);
+
+ if (!device_supported())
+ return;
+
+ if (mask == 0)
+ return;
+
+ /* 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.
+ *
+ * This takes a pair of DeviceFound flags parameters. The 'mask' parameter is a bit mask that indicates which
+ * bits of 'found' to copy into the per-device DeviceFound flags field. Thus, this function may be used to set
+ * and unset individual bits in a single call, while merging partially with previous state. */
+
+ if ((found & mask) != 0) {
+ _cleanup_(udev_device_unrefp) struct udev_device *dev = NULL;
+
+ /* 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. But first, let's validate if
+ * everything is alright with the device node. */
+
+ r = validate_node(m, node, &dev);
+ if (r <= 0)
+ return; /* Don't create a device unit for this if the device node is borked. */
(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);
+ (void) device_update_found_by_name(m, node, found, mask);
}
bool device_shall_be_bound_by(Unit *device, Unit *u) {
+ assert(device);
+ assert(u);
if (u->type != UNIT_MOUNT)
return false;
@@ -1011,6 +1053,7 @@ const UnitVTable device_vtable = {
.load = unit_load_fragment_and_dropin_optional,
.coldplug = device_coldplug,
+ .catchup = device_catchup,
.serialize = device_serialize,
.deserialize_item = device_deserialize_item,
diff --git a/src/core/device.h b/src/core/device.h
index f188640c59..51b27e71b5 100644
--- a/src/core/device.h
+++ b/src/core/device.h
@@ -11,33 +11,36 @@
typedef struct Device Device;
+/* A mask specifying where we have seen the device currently. This is a bitmask because the device might show up
+ * asynchronously from each other at various places. For example, in very common case a device might already be mounted
+ * before udev finished probing it (think: a script setting up a loopback block device, formatting it and mounting it
+ * in quick succession). Hence we need to track precisely where it is already visible and where not. */
typedef enum DeviceFound {
- DEVICE_NOT_FOUND = 0,
- DEVICE_FOUND_UDEV = 1 << 1,
- DEVICE_FOUND_UDEV_DB = 1 << 2,
- DEVICE_FOUND_MOUNT = 1 << 3,
- DEVICE_FOUND_SWAP = 1 << 4,
+ DEVICE_NOT_FOUND = 0,
+ DEVICE_FOUND_UDEV = 1U << 1, /* The device has shown up in the udev database */
+ DEVICE_FOUND_MOUNT = 1U << 2, /* The device has shown up in /proc/self/mountinfo */
+ DEVICE_FOUND_SWAP = 1U << 3, /* The device has shown up in /proc/swaps */
+ DEVICE_FOUND_MASK = DEVICE_FOUND_UDEV|DEVICE_FOUND_MOUNT|DEVICE_FOUND_SWAP,
} 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. */
+ /* 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, deserialized_state;
+ DeviceFound found, deserialized_found, enumerated_found;
bool bind_mounts;
};
extern const UnitVTable device_vtable;
-int device_found_node(Manager *m, const char *node, bool add, DeviceFound found, bool now);
+void device_found_node(Manager *m, const char *node, DeviceFound found, DeviceFound mask);
bool device_shall_be_bound_by(Unit *device, Unit *u);
DEFINE_CAST(DEVICE, Device);
diff --git a/src/core/manager.c b/src/core/manager.c
index 7fc31ce569..04e0c6fe3b 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -1322,13 +1322,30 @@ Manager* manager_free(Manager *m) {
return mfree(m);
}
+static void manager_enumerate_perpetual(Manager *m) {
+ UnitType c;
+
+ assert(m);
+
+ /* Let's ask every type to load all units from disk/kernel that it might know */
+ for (c = 0; c < _UNIT_TYPE_MAX; c++) {
+ if (!unit_type_supported(c)) {
+ log_debug("Unit type .%s is not supported on this system.", unit_type_to_string(c));
+ continue;
+ }
+
+ if (unit_vtable[c]->enumerate_perpetual)
+ unit_vtable[c]->enumerate_perpetual(m);
+ }
+}
+
+
static void manager_enumerate(Manager *m) {
UnitType c;
assert(m);
- /* Let's ask every type to load all units from disk/kernel
- * that it might know */
+ /* Let's ask every type to load all units from disk/kernel that it might know */
for (c = 0; c < _UNIT_TYPE_MAX; c++) {
if (!unit_type_supported(c)) {
log_debug("Unit type .%s is not supported on this system.", unit_type_to_string(c));
@@ -1350,7 +1367,9 @@ static void manager_coldplug(Manager *m) {
assert(m);
- /* Then, let's set up their initial state. */
+ log_debug("Invoking unit coldplug() handlers…");
+
+ /* Let's place the units back into their deserialized state */
HASHMAP_FOREACH_KEY(u, k, m->units, i) {
/* ignore aliases */
@@ -1363,6 +1382,26 @@ static void manager_coldplug(Manager *m) {
}
}
+static void manager_catchup(Manager *m) {
+ Iterator i;
+ Unit *u;
+ char *k;
+
+ assert(m);
+
+ log_debug("Invoking unit catchup() handlers…");
+
+ /* Let's catch up on any state changes that happened while we were reloading/reexecing */
+ HASHMAP_FOREACH_KEY(u, k, m->units, i) {
+
+ /* ignore aliases */
+ if (u->id != k)
+ continue;
+
+ unit_catchup(u);
+ }
+}
+
static void manager_build_unit_path_cache(Manager *m) {
char **i;
int r;
@@ -1463,6 +1502,48 @@ static bool manager_dbus_is_running(Manager *m, bool deserialized) {
return true;
}
+static void manager_setup_bus(Manager *m) {
+ assert(m);
+
+ /* Let's set up our private bus connection now, unconditionally */
+ (void) bus_init_private(m);
+
+ /* If we are in --user mode also connect to the system bus now */
+ if (MANAGER_IS_USER(m))
+ (void) bus_init_system(m);
+
+ /* Let's connect to the bus now, but only if the unit is supposed to be up */
+ if (manager_dbus_is_running(m, MANAGER_IS_RELOADING(m))) {
+ (void) bus_init_api(m);
+
+ if (MANAGER_IS_SYSTEM(m))
+ (void) bus_init_system(m);
+ }
+}
+
+static void manager_preset_all(Manager *m) {
+ int r;
+
+ assert(m);
+
+ if (m->first_boot <= 0)
+ return;
+
+ if (!MANAGER_IS_SYSTEM(m))
+ return;
+
+ if (m->test_run_flags != 0)
+ return;
+
+ /* If this is the first boot, and we are in the host system, then preset everything */
+ r = unit_file_preset_all(UNIT_FILE_SYSTEM, 0, NULL, UNIT_FILE_PRESET_ENABLE_ONLY, NULL, 0);
+ if (r < 0)
+ log_full_errno(r == -EEXIST ? LOG_NOTICE : LOG_WARNING, r,
+ "Failed to populate /etc with preset unit settings, ignoring: %m");
+ else
+ log_info("Populated /etc with preset unit settings.");
+}
+
int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
int r;
@@ -1486,19 +1567,7 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
if (r < 0)
return r;
- /* If this is the first boot, and we are in the host system, then preset everything */
- if (m->first_boot > 0 &&
- MANAGER_IS_SYSTEM(m) &&
- !m->test_run_flags) {
-
- r = unit_file_preset_all(UNIT_FILE_SYSTEM, 0, NULL, UNIT_FILE_PRESET_ENABLE_ONLY, NULL, 0);
- if (r < 0)
- log_full_errno(r == -EEXIST ? LOG_NOTICE : LOG_WARNING, r,
- "Failed to populate /etc with preset unit settings, ignoring: %m");
- else
- log_info("Populated /etc with preset unit settings.");
- }
-
+ manager_preset_all(m);
lookup_paths_reduce(&m->lookup_paths);
manager_build_unit_path_cache(m);
@@ -1510,6 +1579,7 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
/* First, enumerate what we can from all config files */
dual_timestamp_get(m->timestamps + MANAGER_TIMESTAMP_UNITS_LOAD_START);
+ manager_enumerate_perpetual(m);
manager_enumerate(m);
dual_timestamp_get(m->timestamps + MANAGER_TIMESTAMP_UNITS_LOAD_FINISH);
@@ -1543,20 +1613,8 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
/* This shouldn't fail, except if things are really broken. */
return r;
- /* Let's set up our private bus connection now, unconditionally */
- (void) bus_init_private(m);
-
- /* If we are in --user mode also connect to the system bus now */
- if (MANAGER_IS_USER(m))
- (void) bus_init_system(m);
-
- /* Let's connect to the bus now, but only if the unit is supposed to be up */
- if (manager_dbus_is_running(m, !!serialization)) {
- (void) bus_init_api(m);
-
- if (MANAGER_IS_SYSTEM(m))
- (void) bus_init_system(m);
- }
+ /* Connect to the bus if we are good for it */
+ manager_setup_bus(m);
/* Now that we are connected to all possible busses, let's deserialize who is tracking us. */
(void) bus_track_coldplug(m, &m->subscribed, false, m->deserialized_subscribed);
@@ -1584,6 +1642,9 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
m->send_reloading_done = true;
}
+ /* Let's finally catch up with any changes that took place while we were reloading/reexecing */
+ manager_catchup(m);
+
return 0;
}
@@ -3362,8 +3423,7 @@ int manager_reload(Manager *m) {
r = q;
}
- fclose(f);
- f = NULL;
+ f = safe_fclose(f);
/* Re-register notify_fd as event source */
q = manager_setup_notify(m);
@@ -3397,6 +3457,9 @@ int manager_reload(Manager *m) {
manager_recheck_journal(m);
manager_recheck_dbus(m);
+ /* Let's finally catch up with any changes that took place while we were reloading/reexecing */
+ manager_catchup(m);
+
/* Sync current state of bus names with our set of listening units */
q = manager_enqueue_sync_bus_names(m);
if (q < 0 && r >= 0)
diff --git a/src/core/mount.c b/src/core/mount.c
index 3e20f78021..dcc44657b2 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -1634,7 +1634,7 @@ static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) {
if (cunescape(path, UNESCAPE_RELAX, &p) < 0)
return log_oom();
- (void) device_found_node(m, d, true, DEVICE_FOUND_MOUNT, set_flags);
+ device_found_node(m, d, DEVICE_FOUND_MOUNT, DEVICE_FOUND_MOUNT);
k = mount_setup_unit(m, d, p, options, fstype, set_flags);
if (r == 0 && k < 0)
@@ -1671,7 +1671,7 @@ static int mount_get_timeout(Unit *u, usec_t *timeout) {
return 1;
}
-static int synthesize_root_mount(Manager *m) {
+static void mount_enumerate_perpetual(Manager *m) {
Unit *u;
int r;
@@ -1683,8 +1683,10 @@ static int synthesize_root_mount(Manager *m) {
u = manager_get_unit(m, SPECIAL_ROOT_MOUNT);
if (!u) {
r = unit_new_for_name(m, sizeof(Mount), SPECIAL_ROOT_MOUNT, &u);
- if (r < 0)
- return log_error_errno(r, "Failed to allocate the special " SPECIAL_ROOT_MOUNT " unit: %m");
+ if (r < 0) {
+ log_error_errno(r, "Failed to allocate the special " SPECIAL_ROOT_MOUNT " unit: %m");
+ return;
+ }
}
u->perpetual = true;
@@ -1692,8 +1694,6 @@ static int synthesize_root_mount(Manager *m) {
unit_add_to_load_queue(u);
unit_add_to_dbus_queue(u);
-
- return 0;
}
static bool mount_is_mounted(Mount *m) {
@@ -1707,10 +1707,6 @@ static void mount_enumerate(Manager *m) {
assert(m);
- r = synthesize_root_mount(m);
- if (r < 0)
- goto fail;
-
mnt_init_debug(0);
if (!m->mount_monitor) {
@@ -1896,7 +1892,7 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents,
continue;
/* Let the device units know that the device is no longer mounted */
- (void) device_found_node(m, what, false, DEVICE_FOUND_MOUNT, true);
+ device_found_node(m, what, 0, DEVICE_FOUND_MOUNT);
}
return 0;
@@ -2001,6 +1997,7 @@ const UnitVTable mount_vtable = {
.can_transient = true,
+ .enumerate_perpetual = mount_enumerate_perpetual,
.enumerate = mount_enumerate,
.shutdown = mount_shutdown,
diff --git a/src/core/scope.c b/src/core/scope.c
index 5db3296044..27ff545313 100644
--- a/src/core/scope.c
+++ b/src/core/scope.c
@@ -540,7 +540,7 @@ _pure_ static const char *scope_sub_state_to_string(Unit *u) {
return scope_state_to_string(SCOPE(u)->state);
}
-static void scope_enumerate(Manager *m) {
+static void scope_enumerate_perpetual(Manager *m) {
Unit *u;
int r;
@@ -622,5 +622,5 @@ const UnitVTable scope_vtable = {
.bus_set_property = bus_scope_set_property,
.bus_commit_properties = bus_scope_commit_properties,
- .enumerate = scope_enumerate,
+ .enumerate_perpetual = scope_enumerate_perpetual,
};
diff --git a/src/core/service.c b/src/core/service.c
index db0c1dc397..2d43779cfb 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -1947,7 +1947,7 @@ static void service_enter_start(Service *s) {
/* There's no command line configured for the main command? Hmm, that is strange. This can only
* happen if the configuration changes at runtime. In this case, let's enter a failure
* state. */
- log_unit_error(UNIT(s), "There's no 'start' task anymore we could start: %m");
+ log_unit_error(UNIT(s), "There's no 'start' task anymore we could start.");
r = -ENXIO;
goto fail;
}
diff --git a/src/core/slice.c b/src/core/slice.c
index 71614e4b89..e8a7c9a585 100644
--- a/src/core/slice.c
+++ b/src/core/slice.c
@@ -326,7 +326,7 @@ static int slice_make_perpetual(Manager *m, const char *name, Unit **ret) {
return 0;
}
-static void slice_enumerate(Manager *m) {
+static void slice_enumerate_perpetual(Manager *m) {
Unit *u;
int r;
@@ -383,7 +383,7 @@ const UnitVTable slice_vtable = {
.bus_set_property = bus_slice_set_property,
.bus_commit_properties = bus_slice_commit_properties,
- .enumerate = slice_enumerate,
+ .enumerate_perpetual = slice_enumerate_perpetual,
.status_message_formats = {
.finished_start_job = {
diff --git a/src/core/swap.c b/src/core/swap.c
index 6cd703c61d..398eb02aef 100644
--- a/src/core/swap.c
+++ b/src/core/swap.c
@@ -255,15 +255,19 @@ static int swap_load_devnode(Swap *s) {
_cleanup_(udev_device_unrefp) struct udev_device *d = NULL;
struct stat st;
const char *p;
+ int r;
assert(s);
if (stat(s->what, &st) < 0 || !S_ISBLK(st.st_mode))
return 0;
- d = udev_device_new_from_devnum(UNIT(s)->manager->udev, 'b', st.st_rdev);
- if (!d)
+ r = udev_device_new_from_stat_rdev(UNIT(s)->manager->udev, &st, &d);
+ if (r < 0) {
+ log_unit_full(UNIT(s), r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r,
+ "Failed to allocate udev device for swap %s: %m", s->what);
return 0;
+ }
p = udev_device_get_devnode(d);
if (!p)
@@ -443,9 +447,12 @@ static int swap_process_new(Manager *m, const char *device, int prio, bool set_f
if (stat(device, &st) < 0 || !S_ISBLK(st.st_mode))
return 0;
- d = udev_device_new_from_devnum(m->udev, 'b', st.st_rdev);
- if (!d)
+ r = udev_device_new_from_stat_rdev(m->udev, &st, &d);
+ if (r < 0) {
+ log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r,
+ "Failed to allocate udev device for swap %s: %m", device);
return 0;
+ }
/* Add the main device node */
dn = udev_device_get_devnode(d);
@@ -1112,7 +1119,7 @@ static int swap_load_proc_swaps(Manager *m, bool set_flags) {
if (cunescape(dev, UNESCAPE_RELAX, &d) < 0)
return log_oom();
- device_found_node(m, d, true, DEVICE_FOUND_SWAP, set_flags);
+ device_found_node(m, d, DEVICE_FOUND_SWAP, DEVICE_FOUND_SWAP);
k = swap_process_new(m, d, prio, set_flags);
if (k < 0)
@@ -1167,7 +1174,7 @@ static int swap_dispatch_io(sd_event_source *source, int fd, uint32_t revents, v
}
if (swap->what)
- device_found_node(m, swap->what, false, DEVICE_FOUND_SWAP, true);
+ device_found_node(m, swap->what, 0, DEVICE_FOUND_SWAP);
} else if (swap->just_activated) {
@@ -1270,9 +1277,7 @@ static void swap_shutdown(Manager *m) {
assert(m);
m->swap_event_source = sd_event_source_unref(m->swap_event_source);
-
m->proc_swaps = safe_fclose(m->proc_swaps);
-
m->swaps_by_devnode = hashmap_free(m->swaps_by_devnode);
}
@@ -1285,9 +1290,9 @@ static void swap_enumerate(Manager *m) {
m->proc_swaps = fopen("/proc/swaps", "re");
if (!m->proc_swaps) {
if (errno == ENOENT)
- log_debug("Not swap enabled, skipping enumeration");
+ log_debug_errno(errno, "Not swap enabled, skipping enumeration.");
else
- log_error_errno(errno, "Failed to open /proc/swaps: %m");
+ log_warning_errno(errno, "Failed to open /proc/swaps, ignoring: %m");
return;
}
diff --git a/src/core/unit.c b/src/core/unit.c
index 7265f95c95..a82e5fd9eb 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -2298,7 +2298,6 @@ static void unit_update_on_console(Unit *u) {
manager_ref_console(u->manager);
else
manager_unref_console(u->manager);
-
}
void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, UnitNotifyFlags flags) {
@@ -3730,8 +3729,7 @@ int unit_coldplug(Unit *u) {
assert(u);
- /* Make sure we don't enter a loop, when coldplugging
- * recursively. */
+ /* Make sure we don't enter a loop, when coldplugging recursively. */
if (u->coldplugged)
return 0;
@@ -3759,6 +3757,13 @@ int unit_coldplug(Unit *u) {
return r;
}
+void unit_catchup(Unit *u) {
+ assert(u);
+
+ if (UNIT_VTABLE(u)->catchup)
+ UNIT_VTABLE(u)->catchup(u);
+}
+
static bool fragment_mtime_newer(const char *path, usec_t mtime, bool path_masked) {
struct stat st;
diff --git a/src/core/unit.h b/src/core/unit.h
index 32bdd10643..cff3825ddc 100644
--- a/src/core/unit.h
+++ b/src/core/unit.h
@@ -437,10 +437,14 @@ typedef struct UnitVTable {
* UNIT_STUB if no configuration could be found. */
int (*load)(Unit *u);
- /* If a lot of units got created via enumerate(), this is
- * where to actually set the state and call unit_notify(). */
+ /* During deserialization we only record the intended state to return to. With coldplug() we actually put the
+ * deserialized state in effect. This is where unit_notify() should be called to start things up. */
int (*coldplug)(Unit *u);
+ /* This is called shortly after all units' coldplug() call was invoked. It's supposed to catch up state changes
+ * we missed so far (for example because they took place while we were reloading/reexecing) */
+ void (*catchup)(Unit *u);
+
void (*dump)(Unit *u, FILE *f, const char *prefix);
int (*start)(Unit *u);
@@ -531,11 +535,15 @@ typedef struct UnitVTable {
/* Returns true if the unit currently needs access to the console */
bool (*needs_console)(Unit *u);
- /* This is called for each unit type and should be used to
- * enumerate existing devices and load them. However,
- * everything that is loaded here should still stay in
- * inactive state. It is the job of the coldplug() call above
- * to put the units into the initial state. */
+ /* Like the enumerate() callback further down, but only enumerates the perpetual units, i.e. all units that
+ * unconditionally exist and are always active. The main reason to keep both enumeration functions separate is
+ * philosophical: the state of perpetual units should be put in place by coldplug(), while the state of those
+ * discovered through regular enumeration should be put in place by catchup(), see below. */
+ void (*enumerate_perpetual)(Manager *m);
+
+ /* This is called for each unit type and should be used to enumerate units already existing in the system
+ * internally and load them. However, everything that is loaded here should still stay in inactive state. It is
+ * the job of the catchup() call above to put the units into the discovered state. */
void (*enumerate)(Manager *m);
/* Type specific cleanups. */
@@ -687,6 +695,7 @@ void unit_serialize_item_format(Unit *u, FILE *f, const char *key, const char *v
int unit_add_node_dependency(Unit *u, const char *what, bool wants, UnitDependency d, UnitDependencyMask mask);
int unit_coldplug(Unit *u);
+void unit_catchup(Unit *u);
void unit_status_printf(Unit *u, const char *status, const char *unit_status_msg_format) _printf_(3, 0);
void unit_status_emit_starting_stopping_reloading(Unit *u, JobType t);
diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c
index 61d29986ce..9ad1b31fa3 100644
--- a/src/journal/journalctl.c
+++ b/src/journal/journalctl.c
@@ -182,11 +182,11 @@ typedef struct BootId {
} BootId;
static int add_matches_for_device(sd_journal *j, const char *devpath) {
- int r;
_cleanup_(udev_unrefp) struct udev *udev = NULL;
_cleanup_(udev_device_unrefp) struct udev_device *device = NULL;
struct udev_device *d = NULL;
struct stat st;
+ int r;
assert(j);
assert(devpath);
@@ -200,13 +200,12 @@ static int add_matches_for_device(sd_journal *j, const char *devpath) {
if (!udev)
return log_oom();
- r = stat(devpath, &st);
- if (r < 0)
- log_error_errno(errno, "Couldn't stat file: %m");
+ if (stat(devpath, &st) < 0)
+ return log_error_errno(errno, "Couldn't stat file: %m");
- d = device = udev_device_new_from_devnum(udev, S_ISBLK(st.st_mode) ? 'b' : 'c', st.st_rdev);
- if (!device)
- return log_error_errno(errno, "Failed to get udev device from devnum %u:%u: %m", major(st.st_rdev), minor(st.st_rdev));
+ r = udev_device_new_from_stat_rdev(udev, &st, &device);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get udev device from devnum %u:%u: %m", major(st.st_rdev), minor(st.st_rdev));
while (d) {
_cleanup_free_ char *match = NULL;
diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c
index 93a0cc2288..4975aba016 100644
--- a/src/journal/journald-stream.c
+++ b/src/journal/journald-stream.c
@@ -766,8 +766,8 @@ int server_restore_streams(Server *s, FDSet *fds) {
/* No file descriptor? Then let's delete the state file */
log_debug("Cannot restore stream file %s", de->d_name);
if (unlinkat(dirfd(d), de->d_name, 0) < 0)
- log_warning("Failed to remove /run/systemd/journal/streams/%s: %m",
- de->d_name);
+ log_warning_errno(errno, "Failed to remove /run/systemd/journal/streams/%s: %m",
+ de->d_name);
continue;
}
diff --git a/src/network/netdev/netdev.c b/src/network/netdev/netdev.c
index da6a50c5a2..bff478f96a 100644
--- a/src/network/netdev/netdev.c
+++ b/src/network/netdev/netdev.c
@@ -245,7 +245,7 @@ static int netdev_enslave_ready(NetDev *netdev, Link* link, sd_netlink_message_h
r = sd_netlink_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
if (r < 0)
- return log_netdev_error(netdev, "Could not send rtnetlink message: %m");
+ return log_netdev_error_errno(netdev, r, "Could not send rtnetlink message: %m");
link_ref(link);
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index 5792ae9587..7f11d66062 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -2930,7 +2930,7 @@ static int outer_child(
if (l < 0)
return log_error_errno(errno, "Failed to send cgroup mode: %m");
if (l != sizeof(arg_unified_cgroup_hierarchy)) {
- log_error("Short write while sending cgroup mode: %m");
+ log_error("Short write while sending cgroup mode.");
return -EIO;
}
diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
index 539a7b4d9d..64b7ac8d69 100644
--- a/src/shared/bus-unit-util.c
+++ b/src/shared/bus-unit-util.c
@@ -1156,8 +1156,10 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
r = extract_first_word(&w, &path, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
if (r < 0)
return log_error_errno(r, "Failed to parse argument: %m");
- if (r == 0)
- return log_error("Failed to parse argument: %m");
+ if (r == 0) {
+ log_error("Failed to parse argument: %s", p);
+ return -EINVAL;
+ }
r = sd_bus_message_append(m, "(ss)", path, w);
if (r < 0)
diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c
index 4ad83b806b..b5ee07c9bd 100644
--- a/src/shared/conf-parser.c
+++ b/src/shared/conf-parser.c
@@ -303,8 +303,8 @@ int config_parse(const char *unit,
/* Only log on request, except for ENOENT,
* since we return 0 to the caller. */
if ((flags & CONFIG_PARSE_WARN) || errno == ENOENT)
- log_full(errno == ENOENT ? LOG_DEBUG : LOG_ERR,
- "Failed to open configuration file '%s': %m", filename);
+ log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno,
+ "Failed to open configuration file '%s': %m", filename);
return errno == ENOENT ? 0 : -errno;
}
}
diff --git a/src/shared/dropin.c b/src/shared/dropin.c
index d9362f6919..0fc02914ac 100644
--- a/src/shared/dropin.c
+++ b/src/shared/dropin.c
@@ -210,7 +210,7 @@ static int unit_file_find_dirs(
type = unit_name_to_type(name);
if (type < 0) {
- log_error("Failed to to derive unit type from unit name: %m");
+ log_error("Failed to to derive unit type from unit name: %s", name);
return -EINVAL;
}
diff --git a/src/shared/udev-util.c b/src/shared/udev-util.c
index 29484226c6..a5ed8e8f6b 100644
--- a/src/shared/udev-util.c
+++ b/src/shared/udev-util.c
@@ -42,3 +42,26 @@ int udev_parse_config(void) {
return 0;
}
+
+int udev_device_new_from_stat_rdev(struct udev *udev, const struct stat *st, struct udev_device **ret) {
+ struct udev_device *nd;
+ char type;
+
+ assert(udev);
+ assert(st);
+ assert(ret);
+
+ if (S_ISBLK(st->st_mode))
+ type = 'b';
+ else if (S_ISCHR(st->st_mode))
+ type = 'c';
+ else
+ return -ENOTTY;
+
+ nd = udev_device_new_from_devnum(udev, type, st->st_rdev);
+ if (!nd)
+ return -errno;
+
+ *ret = nd;
+ return 0;
+}
diff --git a/src/shared/udev-util.h b/src/shared/udev-util.h
index 0f270f7fbd..ce40f695e8 100644
--- a/src/shared/udev-util.h
+++ b/src/shared/udev-util.h
@@ -21,3 +21,5 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_ctrl_msg*, udev_ctrl_msg_unref);
DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_monitor*, udev_monitor_unref);
int udev_parse_config(void);
+
+int udev_device_new_from_stat_rdev(struct udev *udev, const struct stat *st, struct udev_device **ret);