Systemd/src/core/device.c

1121 lines
38 KiB
C
Raw Normal View History

/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <errno.h>
2010-01-29 06:45:59 +01:00
#include <sys/epoll.h>
#include "alloc-util.h"
#include "bus-error.h"
2010-04-18 03:08:16 +02:00
#include "dbus-device.h"
#include "dbus-unit.h"
2018-08-22 07:43:11 +02:00
#include "device-private.h"
#include "device-util.h"
#include "device.h"
#include "log.h"
#include "parse-util.h"
2012-05-07 21:36:12 +02:00
#include "path-util.h"
#include "serialize.h"
#include "stat-util.h"
#include "string-util.h"
#include "swap.h"
#include "udev-util.h"
#include "unit-name.h"
#include "unit.h"
2010-01-29 03:18:09 +01:00
static const UnitActiveState state_translation_table[_DEVICE_STATE_MAX] = {
[DEVICE_DEAD] = UNIT_INACTIVE,
[DEVICE_TENTATIVE] = UNIT_ACTIVATING,
[DEVICE_PLUGGED] = UNIT_ACTIVE,
2010-01-29 03:18:09 +01:00
};
static int device_dispatch_io(sd_device_monitor *monitor, sd_device *dev, void *userdata);
static void device_update_found_one(Device *d, DeviceFound found, DeviceFound mask);
static void device_unset_sysfs(Device *d) {
2013-11-25 15:25:01 +01:00
Hashmap *devices;
Device *first;
assert(d);
if (!d->sysfs)
return;
/* Remove this unit from the chain of devices which share the
* same sysfs path. */
2013-11-25 15:25:01 +01:00
devices = UNIT(d)->manager->devices_by_sysfs;
first = hashmap_get(devices, d->sysfs);
LIST_REMOVE(same_sysfs, first, d);
if (first)
2013-11-25 15:25:01 +01:00
hashmap_remove_and_replace(devices, d->sysfs, first->sysfs, first);
else
2013-11-25 15:25:01 +01:00
hashmap_remove(devices, d->sysfs);
d->sysfs = mfree(d->sysfs);
}
static int device_set_sysfs(Device *d, const char *sysfs) {
_cleanup_free_ char *copy = NULL;
Device *first;
int r;
assert(d);
if (streq_ptr(d->sysfs, sysfs))
return 0;
r = hashmap_ensure_allocated(&UNIT(d)->manager->devices_by_sysfs, &path_hash_ops);
if (r < 0)
return r;
copy = strdup(sysfs);
if (!copy)
return -ENOMEM;
device_unset_sysfs(d);
first = hashmap_get(UNIT(d)->manager->devices_by_sysfs, sysfs);
LIST_PREPEND(same_sysfs, first, d);
r = hashmap_replace(UNIT(d)->manager->devices_by_sysfs, copy, first);
if (r < 0) {
LIST_REMOVE(same_sysfs, first, d);
return r;
}
d->sysfs = TAKE_PTR(copy);
unit_add_to_dbus_queue(UNIT(d));
return 0;
}
static void device_init(Unit *u) {
Device *d = DEVICE(u);
assert(d);
assert(UNIT(d)->load_state == UNIT_STUB);
/* In contrast to all other unit types we timeout jobs waiting
* for devices by default. This is because they otherwise wait
* indefinitely for plugged in devices, something which cannot
* happen for the other units since their operations time out
* anyway. */
u->job_running_timeout = u->manager->default_timeout_start_usec;
2013-11-25 15:25:01 +01:00
u->ignore_on_isolate = true;
d->deserialized_state = _DEVICE_STATE_INVALID;
}
2010-01-26 21:39:06 +01:00
static void device_done(Unit *u) {
Device *d = DEVICE(u);
assert(d);
device_unset_sysfs(d);
d->wants_property = strv_free(d->wants_property);
}
static int device_load(Unit *u) {
int r;
r = unit_load_fragment_and_dropin(u, false);
if (r < 0)
return r;
if (!u->description) {
/* Generate a description based on the path, to be used until the
device is initialized properly */
r = unit_name_to_path(u->id, &u->description);
if (r < 0)
log_unit_debug_errno(u, r, "Failed to unescape name: %m");
}
return 0;
}
2010-01-29 03:18:09 +01:00
static void device_set_state(Device *d, DeviceState state) {
DeviceState old_state;
assert(d);
if (d->state != state)
bus_unit_send_pending_change_signal(UNIT(d), false);
2010-01-29 03:18:09 +01:00
old_state = d->state;
d->state = state;
if (state == DEVICE_DEAD)
device_unset_sysfs(d);
if (state != old_state)
core,network: major per-object logging rework This changes log_unit_info() (and friends) to take a real Unit* object insted of just a unit name as parameter. The call will now prefix all logged messages with the unit name, thus allowing the unit name to be dropped from the various passed romat strings, simplifying invocations drastically, and unifying log output across messages. Also, UNIT= vs. USER_UNIT= is now derived from the Manager object attached to the Unit object, instead of getpid(). This has the benefit of correcting the field for --test runs. Also contains a couple of other logging improvements: - Drops a couple of strerror() invocations in favour of using %m. - Not only .mount units now warn if a symlinks exist for the mount point already, .automount units do that too, now. - A few invocations of log_struct() that didn't actually pass any additional structured data have been replaced by simpler invocations of log_unit_info() and friends. - For structured data a new LOG_UNIT_MESSAGE() macro has been added, that works like LOG_MESSAGE() but prefixes the message with the unit name. Similar, there's now LOG_LINK_MESSAGE() and LOG_NETDEV_MESSAGE(). - For structured data new LOG_UNIT_ID(), LOG_LINK_INTERFACE(), LOG_NETDEV_INTERFACE() macros have been added that generate the necessary per object fields. The old log_unit_struct() call has been removed in favour of these new macros used in raw log_struct() invocations. In addition to removing one more function call this allows generated structured log messages that contain two object fields, as necessary for example for network interfaces that are joined into another network interface, and whose messages shall be indexed by both. - The LOG_ERRNO() macro has been removed, in favour of log_struct_errno(). The latter has the benefit of ensuring that %m in format strings is properly resolved to the specified error number. - A number of logging messages have been converted to use log_unit_info() instead of log_info() - The client code in sysv-generator no longer #includes core code from src/core/. - log_unit_full_errno() has been removed, log_unit_full() instead takes an errno now, too. - log_unit_info(), log_link_info(), log_netdev_info() and friends, now avoid double evaluation of their parameters
2015-05-11 20:38:21 +02:00
log_unit_debug(UNIT(d), "Changed %s -> %s", device_state_to_string(old_state), device_state_to_string(state));
2010-01-29 03:18:09 +01:00
unit_notify(UNIT(d), state_translation_table[old_state], state_translation_table[state], 0);
2010-01-29 03:18:09 +01:00
}
static int device_coldplug(Unit *u) {
2010-01-29 03:18:09 +01:00
Device *d = DEVICE(u);
assert(d);
assert(d->state == DEVICE_DEAD);
/* First, let's put the deserialized state and found mask into effect, if we have it. */
device: make sure to always retroactively start device dependencies PID1 updates the state of device units upon 2 different events: - when it processes an event sent by udev and in this case the device deps are started if the device enters in the "plugged" state. - when it enumerates all devices during its startup or when it is asked to reload its configuration data but in this case the device deps (if any) are not retroactively started. When udev processes a new "add" kernel event, it first registers the new device in its databases then sends an event to systemd. If for any reason, systemd is asked to reload its configuration between the previous 2 steps, it might see for the first time the new device while scanning /sys for all devices. Only during a second step, udev will send the event for the new device. In this peculiar case the device deps wont be started (even though the device is first seen by PID1). Indeed when reloading its configurations, PID1 will put the device unit in the "plugged" state but without starting the device deps. Thereafter PID1 will get the event from udev for the new device but the device unit will be in "plugged" state already therefore it won't see any need to start the device dependencies. Rather than assuming that during the reloading of systemd manager configuration all devices listed in udev DBs have been already processed and should be put in the "plugged" state (done by device_coldplug()), this patch does that only for devices which have been processed via an udev event (device_dispatch_io()) previously. In this case we set "d->found" to "DEVICE_FOUND_UDEV" and we make also sure to no more initialize "d->found" while enumerating devices. Instead this field is now saved/restored while devices are serialized.
2018-04-06 15:02:10 +02:00
if (d->deserialized_state < 0 ||
(d->deserialized_state == d->state &&
d->deserialized_found == d->found))
return 0;
2010-01-29 03:18:09 +01:00
d->found = d->deserialized_found;
device_set_state(d, d->deserialized_state);
2010-01-29 03:18:09 +01:00
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_MOUNT, "found-mount" },
{ DEVICE_FOUND_SWAP, "found-swap" },
};
static int device_found_to_string_many(DeviceFound flags, char **ret) {
_cleanup_free_ char *s = NULL;
unsigned i;
assert(ret);
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))
return -ENOMEM;
}
*ret = TAKE_PTR(s);
return 0;
}
static int device_found_from_string_many(const char *name, DeviceFound *ret) {
DeviceFound flags = 0;
int r;
assert(ret);
for (;;) {
_cleanup_free_ char *word = NULL;
DeviceFound f = 0;
unsigned i;
r = extract_first_word(&name, &word, ",", 0);
if (r < 0)
return r;
if (r == 0)
break;
for (i = 0; i < ELEMENTSOF(device_found_map); i++)
if (streq(word, device_found_map[i].name)) {
f = device_found_map[i].flag;
break;
}
if (f == 0)
return -EINVAL;
flags |= f;
}
*ret = flags;
return 0;
}
static int device_serialize(Unit *u, FILE *f, FDSet *fds) {
_cleanup_free_ char *s = NULL;
Device *d = DEVICE(u);
assert(u);
assert(f);
assert(fds);
(void) serialize_item(f, "state", device_state_to_string(d->state));
if (device_found_to_string_many(d->found, &s) >= 0)
(void) serialize_item(f, "found", s);
return 0;
}
static int device_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
Device *d = DEVICE(u);
int r;
assert(u);
assert(key);
assert(value);
assert(fds);
if (streq(key, "state")) {
DeviceState state;
state = device_state_from_string(value);
if (state < 0)
2018-06-04 20:29:04 +02:00
log_unit_debug(u, "Failed to parse state value, ignoring: %s", value);
else
d->deserialized_state = state;
device: make sure to always retroactively start device dependencies PID1 updates the state of device units upon 2 different events: - when it processes an event sent by udev and in this case the device deps are started if the device enters in the "plugged" state. - when it enumerates all devices during its startup or when it is asked to reload its configuration data but in this case the device deps (if any) are not retroactively started. When udev processes a new "add" kernel event, it first registers the new device in its databases then sends an event to systemd. If for any reason, systemd is asked to reload its configuration between the previous 2 steps, it might see for the first time the new device while scanning /sys for all devices. Only during a second step, udev will send the event for the new device. In this peculiar case the device deps wont be started (even though the device is first seen by PID1). Indeed when reloading its configurations, PID1 will put the device unit in the "plugged" state but without starting the device deps. Thereafter PID1 will get the event from udev for the new device but the device unit will be in "plugged" state already therefore it won't see any need to start the device dependencies. Rather than assuming that during the reloading of systemd manager configuration all devices listed in udev DBs have been already processed and should be put in the "plugged" state (done by device_coldplug()), this patch does that only for devices which have been processed via an udev event (device_dispatch_io()) previously. In this case we set "d->found" to "DEVICE_FOUND_UDEV" and we make also sure to no more initialize "d->found" while enumerating devices. Instead this field is now saved/restored while devices are serialized.
2018-04-06 15:02:10 +02:00
} else if (streq(key, "found")) {
r = device_found_from_string_many(value, &d->deserialized_found);
if (r < 0)
log_unit_debug_errno(u, r, "Failed to parse found value '%s', ignoring: %m", value);
device: make sure to always retroactively start device dependencies PID1 updates the state of device units upon 2 different events: - when it processes an event sent by udev and in this case the device deps are started if the device enters in the "plugged" state. - when it enumerates all devices during its startup or when it is asked to reload its configuration data but in this case the device deps (if any) are not retroactively started. When udev processes a new "add" kernel event, it first registers the new device in its databases then sends an event to systemd. If for any reason, systemd is asked to reload its configuration between the previous 2 steps, it might see for the first time the new device while scanning /sys for all devices. Only during a second step, udev will send the event for the new device. In this peculiar case the device deps wont be started (even though the device is first seen by PID1). Indeed when reloading its configurations, PID1 will put the device unit in the "plugged" state but without starting the device deps. Thereafter PID1 will get the event from udev for the new device but the device unit will be in "plugged" state already therefore it won't see any need to start the device dependencies. Rather than assuming that during the reloading of systemd manager configuration all devices listed in udev DBs have been already processed and should be put in the "plugged" state (done by device_coldplug()), this patch does that only for devices which have been processed via an udev event (device_dispatch_io()) previously. In this case we set "d->found" to "DEVICE_FOUND_UDEV" and we make also sure to no more initialize "d->found" while enumerating devices. Instead this field is now saved/restored while devices are serialized.
2018-04-06 15:02:10 +02:00
} else
core,network: major per-object logging rework This changes log_unit_info() (and friends) to take a real Unit* object insted of just a unit name as parameter. The call will now prefix all logged messages with the unit name, thus allowing the unit name to be dropped from the various passed romat strings, simplifying invocations drastically, and unifying log output across messages. Also, UNIT= vs. USER_UNIT= is now derived from the Manager object attached to the Unit object, instead of getpid(). This has the benefit of correcting the field for --test runs. Also contains a couple of other logging improvements: - Drops a couple of strerror() invocations in favour of using %m. - Not only .mount units now warn if a symlinks exist for the mount point already, .automount units do that too, now. - A few invocations of log_struct() that didn't actually pass any additional structured data have been replaced by simpler invocations of log_unit_info() and friends. - For structured data a new LOG_UNIT_MESSAGE() macro has been added, that works like LOG_MESSAGE() but prefixes the message with the unit name. Similar, there's now LOG_LINK_MESSAGE() and LOG_NETDEV_MESSAGE(). - For structured data new LOG_UNIT_ID(), LOG_LINK_INTERFACE(), LOG_NETDEV_INTERFACE() macros have been added that generate the necessary per object fields. The old log_unit_struct() call has been removed in favour of these new macros used in raw log_struct() invocations. In addition to removing one more function call this allows generated structured log messages that contain two object fields, as necessary for example for network interfaces that are joined into another network interface, and whose messages shall be indexed by both. - The LOG_ERRNO() macro has been removed, in favour of log_struct_errno(). The latter has the benefit of ensuring that %m in format strings is properly resolved to the specified error number. - A number of logging messages have been converted to use log_unit_info() instead of log_info() - The client code in sysv-generator no longer #includes core code from src/core/. - log_unit_full_errno() has been removed, log_unit_full() instead takes an errno now, too. - log_unit_info(), log_link_info(), log_netdev_info() and friends, now avoid double evaluation of their parameters
2015-05-11 20:38:21 +02:00
log_unit_debug(u, "Unknown serialization key: %s", key);
return 0;
}
2010-01-29 03:18:09 +01:00
static void device_dump(Unit *u, FILE *f, const char *prefix) {
Device *d = DEVICE(u);
_cleanup_free_ char *s = NULL;
assert(d);
(void) device_found_to_string_many(d->found, &s);
fprintf(f,
"%sDevice State: %s\n"
"%sSysfs Path: %s\n"
"%sFound: %s\n",
2010-04-21 03:27:44 +02:00
prefix, device_state_to_string(d->state),
prefix, strna(d->sysfs),
prefix, strna(s));
if (!strv_isempty(d->wants_property)) {
char **i;
STRV_FOREACH(i, d->wants_property)
fprintf(f, "%sudev SYSTEMD_WANTS: %s\n",
prefix, *i);
}
2010-01-29 03:18:09 +01:00
}
_pure_ static UnitActiveState device_active_state(Unit *u) {
2010-01-29 03:18:09 +01:00
assert(u);
return state_translation_table[DEVICE(u)->state];
}
_pure_ static const char *device_sub_state_to_string(Unit *u) {
assert(u);
2010-04-21 03:27:44 +02:00
return device_state_to_string(DEVICE(u)->state);
}
2018-08-22 07:43:11 +02:00
static int device_update_description(Unit *u, sd_device *dev, const char *path) {
_cleanup_free_ char *j = NULL;
const char *model, *label, *desc;
int r;
assert(u);
assert(path);
desc = path;
if (dev &&
(sd_device_get_property_value(dev, "ID_MODEL_FROM_DATABASE", &model) >= 0 ||
sd_device_get_property_value(dev, "ID_MODEL", &model) >= 0)) {
desc = model;
/* Try to concatenate the device model string with a label, if there is one */
2018-08-22 07:43:11 +02:00
if (sd_device_get_property_value(dev, "ID_FS_LABEL", &label) >= 0 ||
sd_device_get_property_value(dev, "ID_PART_ENTRY_NAME", &label) >= 0 ||
sd_device_get_property_value(dev, "ID_PART_ENTRY_NUMBER", &label) >= 0) {
desc = j = strjoin(model, " ", label);
if (!j)
return log_oom();
}
}
r = unit_set_description(u, desc);
if (r < 0)
return log_unit_error_errno(u, r, "Failed to set device description: %m");
return 0;
}
2018-08-22 07:43:11 +02:00
static int device_add_udev_wants(Unit *u, sd_device *dev) {
_cleanup_strv_free_ char **added = NULL;
const char *wants, *property;
Device *d = DEVICE(u);
int r;
assert(d);
assert(dev);
property = MANAGER_IS_USER(u->manager) ? "SYSTEMD_USER_WANTS" : "SYSTEMD_WANTS";
2018-08-22 07:43:11 +02:00
r = sd_device_get_property_value(dev, property, &wants);
if (r < 0)
return 0;
for (;;) {
_cleanup_free_ char *word = NULL, *k = NULL;
r = extract_first_word(&wants, &word, NULL, EXTRACT_UNQUOTE);
if (r == 0)
break;
if (r == -ENOMEM)
return log_oom();
if (r < 0)
return log_unit_error_errno(u, r, "Failed to parse property %s with value %s: %m", property, wants);
if (unit_name_is_valid(word, UNIT_NAME_TEMPLATE) && d->sysfs) {
_cleanup_free_ char *escaped = NULL;
/* If the unit name is specified as template, then automatically fill in the sysfs path of the
* device as instance name, properly escaped. */
r = unit_name_path_escape(d->sysfs, &escaped);
if (r < 0)
return log_unit_error_errno(u, r, "Failed to escape %s: %m", d->sysfs);
r = unit_name_replace_instance(word, escaped, &k);
if (r < 0)
return log_unit_error_errno(u, r, "Failed to build %s instance of template %s: %m", escaped, word);
} else {
/* If this is not a template, then let's mangle it so, that it becomes a valid unit name. */
r = unit_name_mangle(word, UNIT_NAME_MANGLE_WARN, &k);
if (r < 0)
return log_unit_error_errno(u, r, "Failed to mangle unit name \"%s\": %m", word);
}
r = unit_add_dependency_by_name(u, UNIT_WANTS, k, true, UNIT_DEPENDENCY_UDEV);
if (r < 0)
return log_unit_error_errno(u, r, "Failed to add Wants= dependency: %m");
r = strv_push(&added, k);
if (r < 0)
return log_oom();
k = NULL;
}
if (d->state != DEVICE_DEAD) {
char **i;
/* So here's a special hack, to compensate for the fact that the udev database's reload cycles are not
* synchronized with our own reload cycles: when we detect that the SYSTEMD_WANTS property of a device
* changes while the device unit is already up, let's manually trigger any new units listed in it not
2019-04-27 02:22:40 +02:00
* seen before. This typically happens during the boot-time switch root transition, as udev devices
* will generally already be up in the initrd, but SYSTEMD_WANTS properties get then added through udev
* rules only available on the host system, and thus only when the initial udev coldplug trigger runs.
*
* We do this only if the device has been up already when we parse this, as otherwise the usual
* dependency logic that is run from the dead plugged transition will trigger these deps. */
STRV_FOREACH(i, added) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
if (strv_contains(d->wants_property, *i)) /* Was this unit already listed before? */
continue;
r = manager_add_job_by_name(u->manager, JOB_START, *i, JOB_FAIL, NULL, &error, NULL);
if (r < 0)
log_unit_warning_errno(u, r, "Failed to enqueue SYSTEMD_WANTS= job, ignoring: %s", bus_error_message(&error, r));
}
}
return strv_free_and_replace(d->wants_property, added);
}
2018-08-22 07:43:11 +02:00
static bool device_is_bound_by_mounts(Device *d, sd_device *dev) {
const char *bound_by;
int r;
assert(d);
assert(dev);
2018-08-22 07:43:11 +02:00
if (sd_device_get_property_value(dev, "SYSTEMD_MOUNT_DEVICE_BOUND", &bound_by) >= 0) {
r = parse_boolean(bound_by);
if (r < 0)
2018-10-22 05:30:51 +02:00
log_device_warning_errno(dev, r, "Failed to parse SYSTEMD_MOUNT_DEVICE_BOUND='%s' udev property, ignoring: %m", bound_by);
d->bind_mounts = r > 0;
} else
d->bind_mounts = false;
return d->bind_mounts;
}
static void device_upgrade_mount_deps(Unit *u) {
Unit *other;
void *v;
int r;
/* Let's upgrade Requires= to BindsTo= on us. (Used when SYSTEMD_MOUNT_DEVICE_BOUND is set) */
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_REQUIRED_BY]) {
if (other->type != UNIT_MOUNT)
continue;
r = unit_add_dependency(other, UNIT_BINDS_TO, u, true, UNIT_DEPENDENCY_UDEV);
if (r < 0)
log_unit_warning_errno(u, r, "Failed to add BindsTo= dependency between device and mount unit, ignoring: %m");
}
}
2018-08-22 07:43:11 +02:00
static int device_setup_unit(Manager *m, sd_device *dev, const char *path, bool main) {
_cleanup_free_ char *e = NULL;
const char *sysfs = NULL;
Unit *u = NULL;
bool delete;
int r;
assert(m);
assert(path);
if (dev) {
2018-08-22 07:43:11 +02:00
r = sd_device_get_syspath(dev, &sysfs);
if (r < 0) {
2018-10-22 05:30:51 +02:00
log_device_debug_errno(dev, r, "Couldn't get syspath from device, ignoring: %m");
return 0;
}
}
r = unit_name_from_path(path, ".device", &e);
if (r < 0)
2018-10-22 05:30:51 +02:00
return log_device_error_errno(dev, r, "Failed to generate unit name from device path: %m");
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. 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 &&
2020-11-26 01:27:21 +01:00
!path_equal(DEVICE(u)->sysfs, sysfs))
return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EEXIST),
"Device %s appeared twice with different sysfs paths %s and %s, ignoring the latter.",
e, DEVICE(u)->sysfs, sysfs);
delete = false;
2019-04-27 02:22:40 +02:00
/* Let's remove all dependencies generated due to udev properties. We'll re-add whatever is configured
* now below. */
unit_remove_dependencies(u, UNIT_DEPENDENCY_UDEV);
} else {
delete = true;
r = unit_new_for_name(m, sizeof(Device), e, &u);
if (r < 0) {
2018-10-22 05:30:51 +02:00
log_device_error_errno(dev, 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
* initialized. Hence initialize it if necessary. */
if (sysfs) {
r = device_set_sysfs(DEVICE(u), sysfs);
if (r < 0) {
2018-10-22 05:30:51 +02:00
log_unit_error_errno(u, r, "Failed to set sysfs path %s: %m", sysfs);
goto fail;
}
/* The additional systemd udev properties we only interpret for the main object */
if (main)
(void) device_add_udev_wants(u, dev);
}
(void) device_update_description(u, dev, path);
/* So the user wants the mount units to be bound to the device but a mount unit might has been seen by systemd
* before the device appears on its radar. In this case the device unit is partially initialized and includes
* the deps on the mount unit but at that time the "bind mounts" flag wasn't not present. Fix this up now. */
if (dev && device_is_bound_by_mounts(DEVICE(u), dev))
device_upgrade_mount_deps(u);
2010-01-29 06:45:59 +01:00
return 0;
fail:
if (delete)
unit_free(u);
2010-07-13 02:17:26 +02:00
return r;
}
2018-08-22 07:43:11 +02:00
static int device_process_new(Manager *m, sd_device *dev) {
2013-11-25 15:25:01 +01:00
const char *sysfs, *dn, *alias;
2018-08-22 07:43:11 +02:00
dev_t devnum;
int r;
assert(m);
2018-08-22 07:43:11 +02:00
if (sd_device_get_syspath(dev, &sysfs) < 0)
2013-11-25 15:25:01 +01:00
return 0;
/* Add the main unit named after the sysfs path */
r = device_setup_unit(m, dev, sysfs, true);
if (r < 0)
return r;
/* Add an additional unit for the device node */
2018-08-22 07:43:11 +02:00
if (sd_device_get_devname(dev, &dn) >= 0)
(void) device_setup_unit(m, dev, dn, false);
/* Add additional units for all symlinks */
2018-08-22 07:43:11 +02:00
if (sd_device_get_devnum(dev, &devnum) >= 0) {
const char *p;
2018-08-22 07:43:11 +02:00
FOREACH_DEVICE_DEVLINK(dev, p) {
struct stat st;
2018-08-22 07:43:11 +02:00
if (PATH_STARTSWITH_SET(p, "/dev/block/", "/dev/char/"))
continue;
2018-08-22 07:43:11 +02:00
/* Verify that the symlink in the FS actually belongs
* to this device. This is useful to deal with
* conflicting devices, e.g. when two disks want the
* same /dev/disk/by-label/xxx link because they have
* the same label. We want to make sure that the same
* device that won the symlink wins in systemd, so we
* check the device node major/minor */
if (stat(p, &st) >= 0 &&
((!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode)) ||
st.st_rdev != devnum))
continue;
2018-08-22 07:43:11 +02:00
(void) device_setup_unit(m, dev, p, false);
}
}
2018-08-22 07:43:11 +02:00
/* Add additional units for all explicitly configured aliases */
if (sd_device_get_property_value(dev, "SYSTEMD_ALIAS", &alias) < 0)
return 0;
for (;;) {
2016-12-06 00:07:34 +01:00
_cleanup_free_ char *word = NULL;
r = extract_first_word(&alias, &word, NULL, EXTRACT_UNQUOTE);
if (r == 0)
2018-06-04 20:29:04 +02:00
break;
if (r == -ENOMEM)
return log_oom();
if (r < 0)
2019-01-22 04:02:48 +01:00
return log_device_warning_errno(dev, r, "Failed to parse SYSTEMD_ALIAS property: %m");
2013-11-25 15:25:01 +01:00
if (!path_is_absolute(word))
2018-10-22 05:30:51 +02:00
log_device_warning(dev, "SYSTEMD_ALIAS is not an absolute path, ignoring: %s", word);
else if (!path_is_normalized(word))
2018-10-22 05:30:51 +02:00
log_device_warning(dev, "SYSTEMD_ALIAS is not a normalized path, ignoring: %s", word);
else
(void) device_setup_unit(m, dev, word, false);
}
2018-06-04 20:29:04 +02:00
return 0;
}
static void device_found_changed(Device *d, DeviceFound previous, DeviceFound now) {
assert(d);
core: add "invocation ID" concept to service manager This adds a new invocation ID concept to the service manager. The invocation ID identifies each runtime cycle of a unit uniquely. A new randomized 128bit ID is generated each time a unit moves from and inactive to an activating or active state. The primary usecase for this concept is to connect the runtime data PID 1 maintains about a service with the offline data the journal stores about it. Previously we'd use the unit name plus start/stop times, which however is highly racy since the journal will generally process log data after the service already ended. The "invocation ID" kinda matches the "boot ID" concept of the Linux kernel, except that it applies to an individual unit instead of the whole system. The invocation ID is passed to the activated processes as environment variable. It is additionally stored as extended attribute on the cgroup of the unit. The latter is used by journald to automatically retrieve it for each log logged message and attach it to the log entry. The environment variable is very easily accessible, even for unprivileged services. OTOH the extended attribute is only accessible to privileged processes (this is because cgroupfs only supports the "trusted." xattr namespace, not "user."). The environment variable may be altered by services, the extended attribute may not be, hence is the better choice for the journal. Note that reading the invocation ID off the extended attribute from journald is racy, similar to the way reading the unit name for a logging process is. This patch adds APIs to read the invocation ID to sd-id128: sd_id128_get_invocation() may be used in a similar fashion to sd_id128_get_boot(). PID1's own logging is updated to always include the invocation ID when it logs information about a unit. A new bus call GetUnitByInvocationID() is added that allows retrieving a bus path to a unit by its invocation ID. The bus path is built using the invocation ID, thus providing a path for referring to a unit that is valid only for the current runtime cycleof it. Outlook for the future: should the kernel eventually allow passing of cgroup information along AF_UNIX/SOCK_DGRAM messages via a unique cgroup id, then we can alter the invocation ID to be generated as hash from that rather than entirely randomly. This way we can derive the invocation race-freely from the messages.
2016-08-30 23:18:46 +02:00
/* Didn't exist before, but does now? if so, generate a new invocation ID for it */
if (previous == DEVICE_NOT_FOUND && now != DEVICE_NOT_FOUND)
core: add "invocation ID" concept to service manager This adds a new invocation ID concept to the service manager. The invocation ID identifies each runtime cycle of a unit uniquely. A new randomized 128bit ID is generated each time a unit moves from and inactive to an activating or active state. The primary usecase for this concept is to connect the runtime data PID 1 maintains about a service with the offline data the journal stores about it. Previously we'd use the unit name plus start/stop times, which however is highly racy since the journal will generally process log data after the service already ended. The "invocation ID" kinda matches the "boot ID" concept of the Linux kernel, except that it applies to an individual unit instead of the whole system. The invocation ID is passed to the activated processes as environment variable. It is additionally stored as extended attribute on the cgroup of the unit. The latter is used by journald to automatically retrieve it for each log logged message and attach it to the log entry. The environment variable is very easily accessible, even for unprivileged services. OTOH the extended attribute is only accessible to privileged processes (this is because cgroupfs only supports the "trusted." xattr namespace, not "user."). The environment variable may be altered by services, the extended attribute may not be, hence is the better choice for the journal. Note that reading the invocation ID off the extended attribute from journald is racy, similar to the way reading the unit name for a logging process is. This patch adds APIs to read the invocation ID to sd-id128: sd_id128_get_invocation() may be used in a similar fashion to sd_id128_get_boot(). PID1's own logging is updated to always include the invocation ID when it logs information about a unit. A new bus call GetUnitByInvocationID() is added that allows retrieving a bus path to a unit by its invocation ID. The bus path is built using the invocation ID, thus providing a path for referring to a unit that is valid only for the current runtime cycleof it. Outlook for the future: should the kernel eventually allow passing of cgroup information along AF_UNIX/SOCK_DGRAM messages via a unique cgroup id, then we can alter the invocation ID to be generated as hash from that rather than entirely randomly. This way we can derive the invocation race-freely from the messages.
2016-08-30 23:18:46 +02:00
(void) unit_acquire_invocation_id(UNIT(d));
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 (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. */
device_set_state(d, DEVICE_DEAD);
}
static void device_update_found_one(Device *d, DeviceFound found, DeviceFound mask) {
Manager *m;
assert(d);
m = UNIT(d)->manager;
if (MANAGER_IS_RUNNING(m) && (m->honor_device_enumeration || MANAGER_IS_USER(m))) {
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 (mask == 0)
return;
2013-11-25 15:25:01 +01:00
l = hashmap_get(m->devices_by_sysfs, sysfs);
LIST_FOREACH_SAFE(same_sysfs, d, n, l)
device_update_found_one(d, found, mask);
}
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;
2010-01-29 06:45:59 +01:00
assert(m);
assert(path);
2010-01-29 06:45:59 +01:00
if (mask == 0)
return 0;
2010-01-29 06:45:59 +01:00
r = unit_name_from_path(path, ".device", &e);
if (r < 0)
return log_error_errno(r, "Failed to generate unit name from device path: %m");
2010-01-29 06:45:59 +01:00
u = manager_get_unit(m, e);
if (!u)
return 0;
device_update_found_one(DEVICE(u), found, mask);
2010-01-29 06:45:59 +01:00
return 0;
}
2018-08-22 07:43:11 +02:00
static bool device_is_ready(sd_device *dev) {
2013-11-25 15:25:01 +01:00
const char *ready;
assert(dev);
if (device_is_renaming(dev) > 0)
return false;
/* Is it really tagged as 'systemd' right now? */
if (sd_device_has_current_tag(dev, "systemd") <= 0)
return false;
2018-08-22 07:43:11 +02:00
if (sd_device_get_property_value(dev, "SYSTEMD_READY", &ready) < 0)
2013-11-25 15:25:01 +01:00
return true;
return parse_boolean(ready) != 0;
}
static Unit *device_following(Unit *u) {
Device *d = DEVICE(u);
Device *other, *first = NULL;
assert(d);
if (startswith(u->id, "sys-"))
return NULL;
/* Make everybody follow the unit that's named after the sysfs path */
LIST_FOREACH_AFTER(same_sysfs, other, d)
if (startswith(UNIT(other)->id, "sys-"))
return UNIT(other);
LIST_FOREACH_BEFORE(same_sysfs, other, d) {
if (startswith(UNIT(other)->id, "sys-"))
return UNIT(other);
first = other;
}
return UNIT(first);
}
2013-11-25 15:25:01 +01:00
static int device_following_set(Unit *u, Set **_set) {
Device *d = DEVICE(u), *other;
_cleanup_set_free_ Set *set = NULL;
int r;
assert(d);
2013-11-25 15:25:01 +01:00
assert(_set);
2013-11-25 15:25:01 +01:00
if (LIST_JUST_US(same_sysfs, d)) {
*_set = NULL;
return 0;
}
set = set_new(NULL);
2013-11-25 15:25:01 +01:00
if (!set)
return -ENOMEM;
2013-11-25 15:25:01 +01:00
LIST_FOREACH_AFTER(same_sysfs, other, d) {
r = set_put(set, other);
if (r < 0)
2018-05-11 18:43:40 +02:00
return r;
2013-11-25 15:25:01 +01:00
}
2013-11-25 15:25:01 +01:00
LIST_FOREACH_BEFORE(same_sysfs, other, d) {
r = set_put(set, other);
if (r < 0)
2018-05-11 18:43:40 +02:00
return r;
2013-11-25 15:25:01 +01:00
}
2018-05-11 18:43:40 +02:00
*_set = TAKE_PTR(set);
return 1;
}
static void device_shutdown(Manager *m) {
assert(m);
m->device_monitor = sd_device_monitor_unref(m->device_monitor);
m->devices_by_sysfs = hashmap_free(m->devices_by_sysfs);
}
static void device_enumerate(Manager *m) {
2018-08-22 07:43:11 +02:00
_cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
sd_device *dev;
int r;
assert(m);
if (!m->device_monitor) {
r = sd_device_monitor_new(&m->device_monitor);
if (r < 0) {
log_error_errno(r, "Failed to allocate device monitor: %m");
2010-04-21 03:27:44 +02:00
goto fail;
}
2010-01-29 06:45:59 +01:00
/* This will fail if we are unprivileged, but that
* should not matter much, as user instances won't run
* during boot. */
(void) sd_device_monitor_set_receive_buffer_size(m->device_monitor, 128*1024*1024);
r = sd_device_monitor_filter_add_match_tag(m->device_monitor, "systemd");
if (r < 0) {
log_error_errno(r, "Failed to add udev tag match: %m");
goto fail;
}
r = sd_device_monitor_attach_event(m->device_monitor, m->event);
if (r < 0) {
log_error_errno(r, "Failed to attach event to device monitor: %m");
2010-04-21 03:27:44 +02:00
goto fail;
}
2010-01-29 06:45:59 +01:00
r = sd_device_monitor_start(m->device_monitor, device_dispatch_io, m);
if (r < 0) {
log_error_errno(r, "Failed to start device monitor: %m");
goto fail;
}
2010-04-21 03:27:44 +02:00
}
2010-01-29 06:45:59 +01:00
2018-08-22 07:43:11 +02:00
r = sd_device_enumerator_new(&e);
if (r < 0) {
2018-12-06 11:27:40 +01:00
log_error_errno(r, "Failed to allocate device enumerator: %m");
goto fail;
}
2018-08-22 07:43:11 +02:00
r = sd_device_enumerator_add_match_tag(e, "systemd");
if (r < 0) {
2018-08-22 07:43:11 +02:00
log_error_errno(r, "Failed to set tag for device enumeration: %m");
goto fail;
}
FOREACH_DEVICE(e, dev) {
const char *sysfs;
if (!device_is_ready(dev))
continue;
(void) device_process_new(m, dev);
2018-08-22 07:43:11 +02:00
if (sd_device_get_syspath(dev, &sysfs) < 0)
continue;
device_update_found_by_sysfs(m, sysfs, DEVICE_FOUND_UDEV, DEVICE_FOUND_UDEV);
}
return;
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_remove_old(Manager *m, sd_device *dev) {
_cleanup_free_ char *syspath_old = NULL, *e = NULL;
const char *devpath_old;
int r;
r = sd_device_get_property_value(dev, "DEVPATH_OLD", &devpath_old);
if (r < 0) {
log_device_debug_errno(dev, r, "Failed to get DEVPATH_OLD= property on 'move' uevent, ignoring: %m");
return 0;
}
syspath_old = path_join("/sys", devpath_old);
if (!syspath_old)
return log_oom();
r = unit_name_from_path(syspath_old, ".device", &e);
if (r < 0)
return log_device_error_errno(dev, r, "Failed to generate unit name from old device path: %m");
device_update_found_by_sysfs(m, syspath_old, 0, DEVICE_FOUND_UDEV|DEVICE_FOUND_MOUNT|DEVICE_FOUND_SWAP);
return 0;
}
static int device_dispatch_io(sd_device_monitor *monitor, sd_device *dev, void *userdata) {
Manager *m = userdata;
2019-03-09 02:28:11 +01:00
DeviceAction action;
const char *sysfs;
int r;
2010-01-29 06:45:59 +01:00
assert(m);
assert(dev);
2010-01-29 06:45:59 +01:00
2018-08-22 07:43:11 +02:00
r = sd_device_get_syspath(dev, &sysfs);
if (r < 0) {
2018-10-22 05:30:51 +02:00
log_device_error_errno(dev, r, "Failed to get device sys path: %m");
return 0;
}
2019-03-09 02:28:11 +01:00
r = device_get_action(dev, &action);
2018-08-22 07:43:11 +02:00
if (r < 0) {
2019-03-09 02:28:11 +01:00
log_device_error_errno(dev, r, "Failed to get udev action: %m");
return 0;
2010-01-29 06:45:59 +01:00
}
if (!IN_SET(action, DEVICE_ACTION_ADD, DEVICE_ACTION_REMOVE, DEVICE_ACTION_MOVE))
device_propagate_reload_by_sysfs(m, sysfs);
if (action == DEVICE_ACTION_MOVE)
(void) device_remove_old(m, dev);
/* A change event can signal that a device is becoming ready, in particular if the device is using
* the SYSTEMD_READY logic in udev so we need to reach the else block of the following if, even for
* change events */
2019-03-09 02:28:11 +01:00
if (action == DEVICE_ACTION_REMOVE) {
r = swap_process_device_remove(m, dev);
if (r < 0)
2018-10-22 05:30:51 +02:00
log_device_warning_errno(dev, 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, 0, DEVICE_FOUND_UDEV|DEVICE_FOUND_MOUNT|DEVICE_FOUND_SWAP);
2013-11-25 15:25:01 +01:00
} else if (device_is_ready(dev)) {
(void) device_process_new(m, dev);
r = swap_process_device_new(m, dev);
if (r < 0)
2018-10-22 05:30:51 +02:00
log_device_warning_errno(dev, r, "Failed to process swap device new event, ignoring: %m");
2013-11-25 15:25:01 +01:00
manager_dispatch_load_queue(m);
/* The device is found now, set the udev found bit */
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, 0, DEVICE_FOUND_UDEV);
2010-01-29 06:45:59 +01:00
return 0;
2010-01-29 06:45:59 +01:00
}
static bool device_supported(void) {
static int read_only = -1;
/* If /sys is read-only we don't support device units, and any
* attempts to start one should fail immediately. */
if (read_only < 0)
read_only = path_is_read_only_fs("/sys");
return read_only <= 0;
}
2018-08-22 07:43:11 +02:00
static int validate_node(Manager *m, const char *node, sd_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 {
2018-08-22 07:43:11 +02:00
_cleanup_(sd_device_unrefp) sd_device *dev = NULL;
2018-08-22 07:43:11 +02:00
r = device_new_from_stat_rdev(&dev, &st);
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;
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) {
2018-08-22 07:43:11 +02:00
_cleanup_(sd_device_unrefp) sd_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 */
(void) device_update_found_by_name(m, node, found, mask);
}
bool device_shall_be_bound_by(Unit *device, Unit *u) {
2018-06-04 20:29:04 +02:00
assert(device);
assert(u);
if (u->type != UNIT_MOUNT)
return false;
return DEVICE(device)->bind_mounts;
}
2010-01-26 21:39:06 +01:00
const UnitVTable device_vtable = {
.object_size = sizeof(Device),
.sections =
"Unit\0"
"Device\0"
"Install\0",
.gc_jobs = true,
.init = device_init,
.done = device_done,
.load = device_load,
2010-01-29 03:18:09 +01:00
.coldplug = device_coldplug,
.catchup = device_catchup,
2010-01-29 03:18:09 +01:00
.serialize = device_serialize,
.deserialize_item = device_deserialize_item,
.dump = device_dump,
2010-01-29 03:18:09 +01:00
.active_state = device_active_state,
.sub_state_to_string = device_sub_state_to_string,
.following = device_following,
.following_set = device_following_set,
2010-01-29 03:18:09 +01:00
.enumerate = device_enumerate,
.shutdown = device_shutdown,
.supported = device_supported,
.status_message_formats = {
.starting_stopping = {
[0] = "Expecting device %s...",
},
.finished_start_job = {
[JOB_DONE] = "Found device %s.",
[JOB_TIMEOUT] = "Timed out waiting for device %s.",
},
},
};