+/* SPDX-License-Identifier: LGPL-2.1+ */
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <errno.h>
if (streq_ptr(d->sysfs, sysfs))
return 0;
- r = hashmap_ensure_allocated(&UNIT(d)->manager->devices_by_sysfs, &string_hash_ops);
+ r = hashmap_ensure_allocated(&UNIT(d)->manager->devices_by_sysfs, &path_hash_ops);
if (r < 0)
return r;
}
static int device_add_udev_wants(Unit *u, struct udev_device *dev) {
- const char *wants, *property, *p;
+ const char *wants, *property;
int r;
assert(u);
assert(dev);
property = MANAGER_IS_USER(u->manager) ? "SYSTEMD_USER_WANTS" : "SYSTEMD_WANTS";
+
wants = udev_device_get_property_value(dev, property);
- for (p = wants;;) {
+ if (!wants)
+ return 0;
+
+ for (;;) {
_cleanup_free_ char *word = NULL, *k = NULL;
- r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
+ r = extract_first_word(&wants, &word, NULL, EXTRACT_QUOTES);
if (r == 0)
return 0;
if (r == -ENOMEM)
return log_oom();
if (r < 0)
- return log_unit_error_errno(u, r, "Failed to add parse %s: %m", property);
+ return log_unit_error_errno(u, r, "Failed to parse property %s with value %s: %m", property, wants);
- r = unit_name_mangle(word, UNIT_NAME_NOGLOB, &k);
- if (r < 0)
- return log_unit_error_errno(u, r, "Failed to mangle unit name \"%s\": %m", word);
+ if (unit_name_is_valid(word, UNIT_NAME_TEMPLATE) && DEVICE(u)->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(DEVICE(u)->sysfs, &escaped);
+ if (r < 0)
+ return log_unit_error_errno(u, r, "Failed to escape %s: %m", DEVICE(u)->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, NULL, true, UNIT_DEPENDENCY_UDEV);
if (r < 0)
- return log_unit_error_errno(u, r, "Failed to add wants dependency: %m");
+ return log_unit_error_errno(u, r, "Failed to add Wants= dependency: %m");
}
}
return log_error_errno(r, "Failed to generate unit name from device path: %m");
u = manager_get_unit(m, e);
-
- /* 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 && u && DEVICE(u)->state == DEVICE_PLUGGED) {
- /* This unit is in plugged state: we're sure it's
- * attached to a device. */
- if (!path_equal(DEVICE(u)->sysfs, sysfs)) {
- log_unit_debug(u, "Dev %s appeared twice with different sysfs paths %s and %s",
- e, DEVICE(u)->sysfs, sysfs);
- return -EEXIST;
+ if (u) {
+ /* The device unit can still be present even if the device was unplugged: a mount unit can reference it hence
+ * preventing the GC to have garbaged it. That's desired since the device unit may have a dependency on the
+ * mount unit which was added during the loading of the later. */
+ if (dev && DEVICE(u)->state == DEVICE_PLUGGED) {
+
+ /* This unit is in plugged state: we're sure it's attached to a device. */
+ if (!path_equal(DEVICE(u)->sysfs, sysfs)) {
+ log_unit_debug(u, "Dev %s appeared twice with different sysfs paths %s and %s",
+ e, DEVICE(u)->sysfs, sysfs);
+ return -EEXIST;
+ }
}
- }
- if (!u) {
+ delete = false;
+
+ /* Let's remove all dependencies generated due to udev properties. We'll readd 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);
goto fail;
unit_add_to_load_queue(u);
- } else
- delete = false;
+ }
/* If this was created via some dependency and has not
* actually been seen yet ->sysfs will not be
if (dev && device_is_bound_by_mounts(DEVICE(u), dev))
device_upgrade_mount_deps(u);
- /* Note that this won't dispatch the load queue, the caller
- * has to do that if needed and appropriate */
+ /* 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;
assert(m);
m->udev_event_source = sd_event_source_unref(m->udev_event_source);
-
- if (m->udev_monitor) {
- udev_monitor_unref(m->udev_monitor);
- m->udev_monitor = NULL;
- }
-
+ m->udev_monitor = udev_monitor_unref(m->udev_monitor);
m->devices_by_sysfs = hashmap_free(m->devices_by_sysfs);
}
}
if (streq(action, "change")) {
- _cleanup_free_ char *e = NULL;
Unit *u;
+ Device *d, *l, *n;
- r = unit_name_from_path(sysfs, ".device", &e);
- if (r < 0)
- log_error_errno(r, "Failed to generate unit name from device path: %m");
- else {
- u = manager_get_unit(m, e);
+ 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)