Merge pull request #9200 from poettering/device-state-fix
core: rework device state serialization/enumeration
This commit is contained in:
commit
3ceca73a59
6
TODO
6
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.
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.");
|
||||
|
|
|
@ -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) {
|
||||
/* 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. */
|
||||
|
||||
/* 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;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void device_update_found_one(Device *d, bool add, DeviceFound found, bool now) {
|
||||
DeviceFound n, previous;
|
||||
|
||||
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 int device_update_found_by_sysfs(Manager *m, const char *sysfs, bool add, DeviceFound found, bool now) {
|
||||
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 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);
|
||||
|
||||
/* 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! */
|
||||
|
||||
if (!path_startswith(node, "/dev")) {
|
||||
*ret = NULL;
|
||||
return 0; /* bad! */
|
||||
}
|
||||
|
||||
if (stat(node, &st) < 0) {
|
||||
if (errno != ENOENT)
|
||||
return log_error_errno(errno, "Failed to stat() device node file %s: %m", node);
|
||||
|
||||
*ret = NULL;
|
||||
return 1; /* good! (though missing) */
|
||||
|
||||
} else {
|
||||
_cleanup_(udev_device_unrefp) struct udev_device *dev = NULL;
|
||||
|
||||
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! */
|
||||
}
|
||||
}
|
||||
|
||||
void device_found_node(Manager *m, const char *node, DeviceFound found, DeviceFound mask) {
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(node);
|
||||
|
||||
if (!device_supported())
|
||||
return 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. */
|
||||
if (mask == 0)
|
||||
return;
|
||||
|
||||
if (add) {
|
||||
if (!path_startswith(node, "/dev"))
|
||||
return 0;
|
||||
/* 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. */
|
||||
|
||||
/* 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... */
|
||||
if ((found & mask) != 0) {
|
||||
_cleanup_(udev_device_unrefp) struct udev_device *dev = NULL;
|
||||
|
||||
if (stat(node, &st) >= 0) {
|
||||
if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode))
|
||||
return 0;
|
||||
/* 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. */
|
||||
|
||||
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));
|
||||
|
||||
} else if (errno != ENOENT)
|
||||
return log_error_errno(errno, "Failed to stat device node file %s: %m", node);
|
||||
|
||||
/* 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. */
|
||||
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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue