diff --git a/man/udevadm.xml b/man/udevadm.xml
index ec26cc3c07..b731f808bc 100644
--- a/man/udevadm.xml
+++ b/man/udevadm.xml
@@ -212,6 +212,13 @@
Do not actually trigger the event.
+
+
+
+
+ Suppress error logging in triggering events.
+
+
diff --git a/shell-completion/bash/udevadm b/shell-completion/bash/udevadm
index 8b1b962f2d..33d998395e 100644
--- a/shell-completion/bash/udevadm
+++ b/shell-completion/bash/udevadm
@@ -51,7 +51,7 @@ _udevadm() {
[INFO_STANDALONE]='-r --root -a --attribute-walk -x --export -e --export-db -c --cleanup-db
-w --wait-for-initialization'
[INFO_ARG]='-q --query -p --path -n --name -P --export-prefix -d --device-id-of-file'
- [TRIGGER_STANDALONE]='-v --verbose -n --dry-run -w --settle --wait-daemon'
+ [TRIGGER_STANDALONE]='-v --verbose -n --dry-run -q --quiet -w --settle --wait-daemon'
[TRIGGER_ARG]='-t --type -c --action -s --subsystem-match -S --subsystem-nomatch
-a --attr-match -A --attr-nomatch -p --property-match
-g --tag-match -y --sysname-match --name-match -b --parent-match'
diff --git a/shell-completion/zsh/_udevadm b/shell-completion/zsh/_udevadm
index ae82d8aa70..5e989b4a1d 100644
--- a/shell-completion/zsh/_udevadm
+++ b/shell-completion/zsh/_udevadm
@@ -21,6 +21,7 @@ _udevadm_trigger(){
_arguments \
'--verbose[Print the list of devices which will be triggered.]' \
'--dry-run[Do not actually trigger the event.]' \
+ '--quiet[Suppress error logging in triggering events.]' \
'--type=[Trigger a specific type of devices.]:types:(devices subsystems failed)' \
'--action=[Type of event to be triggered.]:actions:(add change remove)' \
'--subsystem-match=[Trigger events for devices which belong to a matching subsystem.]' \
diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym
index 78454acbc0..74eff7fcf0 100644
--- a/src/libsystemd/libsystemd.sym
+++ b/src/libsystemd/libsystemd.sym
@@ -749,4 +749,5 @@ global:
sd_device_get_action;
sd_device_get_seqnum;
sd_device_new_from_stat_rdev;
+ sd_device_trigger;
} LIBSYSTEMD_247;
diff --git a/src/libsystemd/sd-device/device-enumerator.c b/src/libsystemd/sd-device/device-enumerator.c
index e3c0af3429..c98b029af2 100644
--- a/src/libsystemd/sd-device/device-enumerator.c
+++ b/src/libsystemd/sd-device/device-enumerator.c
@@ -285,13 +285,11 @@ int device_enumerator_add_device(sd_device_enumerator *enumerator, sd_device *de
static bool match_sysattr_value(sd_device *device, const char *sysattr, const char *match_value) {
const char *value;
- int r;
assert(device);
assert(sysattr);
- r = sd_device_get_sysattr_value(device, sysattr, &value);
- if (r < 0)
+ if (sd_device_get_sysattr_value(device, sysattr, &value) < 0)
return false;
if (!match_value)
diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c
index bf9397c03d..1ef7f4d69c 100644
--- a/src/libsystemd/sd-device/sd-device.c
+++ b/src/libsystemd/sd-device/sd-device.c
@@ -198,7 +198,7 @@ int device_set_syspath(sd_device *device, const char *_syspath, bool verify) {
devpath = syspath + STRLEN("/sys");
- if (devpath[0] == '\0')
+ if (devpath[0] != '/')
/* '/sys' alone is not a valid device path */
return -ENODEV;
@@ -519,7 +519,7 @@ int device_read_uevent_file(sd_device *device) {
return 0;
}
if (r == -ENOENT)
- /* some devices may not have uevent files, see set_syspath() */
+ /* some devices may not have uevent files, see device_set_syspath() */
return 0;
if (r < 0)
return log_device_debug_errno(device, r, "sd-device: Failed to read uevent file '%s': %m", path);
@@ -674,11 +674,11 @@ _public_ int sd_device_new_from_device_id(sd_device **ret, const char *id) {
_public_ int sd_device_get_syspath(sd_device *device, const char **ret) {
assert_return(device, -EINVAL);
- assert_return(ret, -EINVAL);
assert(path_startswith(device->syspath, "/sys/"));
- *ret = device->syspath;
+ if (ret)
+ *ret = device->syspath;
return 0;
}
@@ -720,8 +720,6 @@ static int device_new_from_child(sd_device **ret, sd_device *child) {
}
_public_ int sd_device_get_parent(sd_device *child, sd_device **ret) {
-
- assert_return(ret, -EINVAL);
assert_return(child, -EINVAL);
if (!child->parent_set) {
@@ -733,7 +731,8 @@ _public_ int sd_device_get_parent(sd_device *child, sd_device **ret) {
if (!child->parent)
return -ENOENT;
- *ret = child->parent;
+ if (ret)
+ *ret = child->parent;
return 0;
}
@@ -779,7 +778,6 @@ _public_ int sd_device_get_subsystem(sd_device *device, const char **ret) {
const char *syspath, *drivers = NULL;
int r;
- assert_return(ret, -EINVAL);
assert_return(device, -EINVAL);
r = sd_device_get_syspath(device, &syspath);
@@ -836,7 +834,8 @@ _public_ int sd_device_get_subsystem(sd_device *device, const char **ret) {
if (!device->subsystem)
return -ENOENT;
- *ret = device->subsystem;
+ if (ret)
+ *ret = device->subsystem;
return 0;
}
@@ -868,10 +867,11 @@ _public_ int sd_device_get_parent_with_subsystem_devtype(sd_device *child, const
r = sd_device_get_parent(child, &parent);
while (r >= 0) {
const char *parent_subsystem = NULL;
- const char *parent_devtype = NULL;
(void) sd_device_get_subsystem(parent, &parent_subsystem);
if (streq_ptr(parent_subsystem, subsystem)) {
+ const char *parent_devtype = NULL;
+
if (!devtype)
break;
@@ -885,7 +885,8 @@ _public_ int sd_device_get_parent_with_subsystem_devtype(sd_device *child, const
if (r < 0)
return r;
- *ret = parent;
+ if (ret)
+ *ret = parent;
return 0;
}
@@ -928,7 +929,6 @@ int device_set_driver(sd_device *device, const char *_driver) {
_public_ int sd_device_get_driver(sd_device *device, const char **ret) {
assert_return(device, -EINVAL);
- assert_return(ret, -EINVAL);
if (!device->driver_set) {
_cleanup_free_ char *driver = NULL;
@@ -955,18 +955,19 @@ _public_ int sd_device_get_driver(sd_device *device, const char **ret) {
if (!device->driver)
return -ENOENT;
- *ret = device->driver;
+ if (ret)
+ *ret = device->driver;
return 0;
}
_public_ int sd_device_get_devpath(sd_device *device, const char **devpath) {
assert_return(device, -EINVAL);
- assert_return(devpath, -EINVAL);
assert(device->devpath);
assert(device->devpath[0] == '/');
- *devpath = device->devpath;
+ if (devpath)
+ *devpath = device->devpath;
return 0;
}
@@ -974,7 +975,6 @@ _public_ int sd_device_get_devname(sd_device *device, const char **devname) {
int r;
assert_return(device, -EINVAL);
- assert_return(devname, -EINVAL);
r = device_read_uevent_file(device);
if (r < 0)
@@ -985,7 +985,8 @@ _public_ int sd_device_get_devname(sd_device *device, const char **devname) {
assert(path_startswith(device->devname, "/dev/"));
- *devname = device->devname;
+ if (devname)
+ *devname = device->devname;
return 0;
}
@@ -1035,7 +1036,6 @@ _public_ int sd_device_get_sysname(sd_device *device, const char **ret) {
int r;
assert_return(device, -EINVAL);
- assert_return(ret, -EINVAL);
if (!device->sysname_set) {
r = device_set_sysname(device);
@@ -1045,7 +1045,8 @@ _public_ int sd_device_get_sysname(sd_device *device, const char **ret) {
assert_return(device->sysname, -ENOENT);
- *ret = device->sysname;
+ if (ret)
+ *ret = device->sysname;
return 0;
}
@@ -1053,7 +1054,6 @@ _public_ int sd_device_get_sysnum(sd_device *device, const char **ret) {
int r;
assert_return(device, -EINVAL);
- assert_return(ret, -EINVAL);
if (!device->sysname_set) {
r = device_set_sysname(device);
@@ -1064,7 +1064,8 @@ _public_ int sd_device_get_sysnum(sd_device *device, const char **ret) {
if (!device->sysnum)
return -ENOENT;
- *ret = device->sysnum;
+ if (ret)
+ *ret = device->sysnum;
return 0;
}
@@ -1434,7 +1435,6 @@ _public_ int sd_device_get_usec_since_initialized(sd_device *device, uint64_t *u
int r;
assert_return(device, -EINVAL);
- assert_return(usec, -EINVAL);
r = device_read_db(device);
if (r < 0)
@@ -1451,7 +1451,8 @@ _public_ int sd_device_get_usec_since_initialized(sd_device *device, uint64_t *u
if (now_ts < device->usec_initialized)
return -EIO;
- *usec = now_ts - device->usec_initialized;
+ if (usec)
+ *usec = now_ts - device->usec_initialized;
return 0;
}
@@ -1799,8 +1800,8 @@ _public_ int sd_device_has_current_tag(sd_device *device, const char *tag) {
return set_contains(device->current_tags, tag);
}
-_public_ int sd_device_get_property_value(sd_device *device, const char *key, const char **_value) {
- char *value;
+_public_ int sd_device_get_property_value(sd_device *device, const char *key, const char **ret_value) {
+ const char *value;
int r;
assert_return(device, -EINVAL);
@@ -1814,12 +1815,12 @@ _public_ int sd_device_get_property_value(sd_device *device, const char *key, co
if (!value)
return -ENOENT;
- if (_value)
- *_value = value;
+ if (ret_value)
+ *ret_value = value;
return 0;
}
-static int device_add_sysattr_value(sd_device *device, const char *key, char *value) {
+static int device_cache_sysattr_value(sd_device *device, const char *key, char *value) {
_cleanup_free_ char *new_key = NULL, *old_value = NULL;
int r;
@@ -1829,6 +1830,7 @@ static int device_add_sysattr_value(sd_device *device, const char *key, char *va
/* This takes the reference of the input value. The input value may be NULL.
* This replaces the value if it already exists. */
+ /* First, remove the old cache entry. So, we do not need to clear cache on error. */
old_value = hashmap_remove2(device->sysattr_values, key, (void **) &new_key);
if (!new_key) {
new_key = strdup(key);
@@ -1845,7 +1847,7 @@ static int device_add_sysattr_value(sd_device *device, const char *key, char *va
return 0;
}
-static int device_get_sysattr_value(sd_device *device, const char *_key, const char **_value) {
+static int device_get_cached_sysattr_value(sd_device *device, const char *_key, const char **_value) {
const char *key = NULL, *value;
assert(device);
@@ -1862,7 +1864,7 @@ static int device_get_sysattr_value(sd_device *device, const char *_key, const c
/* We cache all sysattr lookups. If an attribute does not exist, it is stored
* with a NULL value in the cache, otherwise the returned string is stored */
-_public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, const char **_value) {
+_public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, const char **ret_value) {
_cleanup_free_ char *value = NULL;
const char *path, *syspath, *cached_value = NULL;
struct stat statbuf;
@@ -1872,7 +1874,7 @@ _public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr,
assert_return(sysattr, -EINVAL);
/* look for possibly already cached result */
- r = device_get_sysattr_value(device, sysattr, &cached_value);
+ r = device_get_cached_sysattr_value(device, sysattr, &cached_value);
if (r != -ENOENT) {
if (r < 0)
return r;
@@ -1881,8 +1883,8 @@ _public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr,
/* we looked up the sysattr before and it did not exist */
return -ENOENT;
- if (_value)
- *_value = cached_value;
+ if (ret_value)
+ *ret_value = cached_value;
return 0;
}
@@ -1894,12 +1896,16 @@ _public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr,
path = prefix_roota(syspath, sysattr);
r = lstat(path, &statbuf);
if (r < 0) {
- /* remember that we could not access the sysattr */
- r = device_add_sysattr_value(device, sysattr, NULL);
- if (r < 0)
- return r;
+ int k;
- return -ENOENT;
+ /* remember that we could not access the sysattr */
+ k = device_cache_sysattr_value(device, sysattr, NULL);
+ if (k < 0)
+ log_device_debug_errno(device, k,
+ "sd-device: failed to cache attribute '%s' with NULL, ignoring: %m",
+ sysattr);
+
+ return r;
} else if (S_ISLNK(statbuf.st_mode)) {
/* Some core links return only the last element of the target path,
* these are just values, the paths should not be exposed. */
@@ -1909,35 +1915,38 @@ _public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr,
return r;
} else
return -EINVAL;
- } else if (S_ISDIR(statbuf.st_mode)) {
+ } else if (S_ISDIR(statbuf.st_mode))
/* skip directories */
- return -EINVAL;
- } else if (!(statbuf.st_mode & S_IRUSR)) {
+ return -EISDIR;
+ else if (!(statbuf.st_mode & S_IRUSR))
/* skip non-readable files */
return -EPERM;
- } else {
- size_t size;
-
+ else {
/* read attribute value */
- r = read_full_virtual_file(path, &value, &size);
+ r = read_full_virtual_file(path, &value, NULL);
if (r < 0)
return r;
/* drop trailing newlines */
- while (size > 0 && value[--size] == '\n')
- value[size] = '\0';
+ delete_trailing_chars(value, "\n");
}
- r = device_add_sysattr_value(device, sysattr, value);
- if (r < 0)
- return r;
-
- *_value = TAKE_PTR(value);
+ /* Unfortunately, we need to return 'const char*' instead of 'char*'. Hence, failure in caching
+ * sysattr value is critical unlike the other places. */
+ r = device_cache_sysattr_value(device, sysattr, value);
+ if (r < 0) {
+ log_device_debug_errno(device, r,
+ "sd-device: failed to cache attribute '%s' with '%s'%s: %m",
+ sysattr, value, ret_value ? "" : ", ignoring");
+ if (ret_value)
+ return r;
+ } else if (ret_value)
+ *ret_value = TAKE_PTR(value);
return 0;
}
-static void device_remove_sysattr_value(sd_device *device, const char *_key) {
+static void device_remove_cached_sysattr_value(sd_device *device, const char *_key) {
_cleanup_free_ char *key = NULL;
assert(device);
@@ -1946,8 +1955,6 @@ static void device_remove_sysattr_value(sd_device *device, const char *_key) {
free(hashmap_remove2(device->sysattr_values, _key, (void **) &key));
}
-/* set the attribute and save it in the cache. If a NULL value is passed the
- * attribute is cleared from the cache */
_public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr, const char *_value) {
_cleanup_free_ char *value = NULL;
const char *syspath, *path;
@@ -1957,8 +1964,11 @@ _public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr,
assert_return(device, -EINVAL);
assert_return(sysattr, -EINVAL);
+ /* Set the attribute and save it in the cache. */
+
if (!_value) {
- device_remove_sysattr_value(device, sysattr);
+ /* If input value is NULL, then clear cache and not write anything. */
+ device_remove_cached_sysattr_value(device, sysattr);
return 0;
}
@@ -1971,7 +1981,7 @@ _public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr,
len = strlen(_value);
/* drop trailing newlines */
- while (len > 0 && _value[len - 1] == '\n')
+ while (len > 0 && strchr(NEWLINE, _value[len - 1]))
len --;
/* value length is limited to 4k */
@@ -1984,27 +1994,22 @@ _public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr,
r = write_string_file(path, value, WRITE_STRING_FILE_DISABLE_BUFFER | WRITE_STRING_FILE_NOFOLLOW);
if (r < 0) {
- if (r == -ELOOP)
- return -EINVAL;
- if (r == -EISDIR)
- return r;
-
- r = free_and_strdup(&value, "");
- if (r < 0)
- return r;
-
- r = device_add_sysattr_value(device, sysattr, value);
- if (r < 0)
- return r;
- TAKE_PTR(value);
-
- return -ENXIO;
+ /* On failure, clear cache entry, as we do not know how it fails. */
+ device_remove_cached_sysattr_value(device, sysattr);
+ return r;
}
- r = device_add_sysattr_value(device, sysattr, value);
+ /* Do not cache action string written into uevent file. */
+ if (streq(sysattr, "uevent"))
+ return 0;
+
+ r = device_cache_sysattr_value(device, sysattr, value);
if (r < 0)
- return r;
- TAKE_PTR(value);
+ log_device_debug_errno(device, r,
+ "sd-device: failed to cache attribute '%s' with '%s', ignoring: %m",
+ sysattr, value);
+ else
+ TAKE_PTR(value);
return 0;
}
@@ -2018,7 +2023,7 @@ _public_ int sd_device_set_sysattr_valuef(sd_device *device, const char *sysattr
assert_return(sysattr, -EINVAL);
if (!format) {
- device_remove_sysattr_value(device, sysattr);
+ device_remove_cached_sysattr_value(device, sysattr);
return 0;
}
@@ -2031,3 +2036,15 @@ _public_ int sd_device_set_sysattr_valuef(sd_device *device, const char *sysattr
return sd_device_set_sysattr_value(device, sysattr, value);
}
+
+_public_ int sd_device_trigger(sd_device *device, sd_device_action_t action) {
+ const char *s;
+
+ assert_return(device, -EINVAL);
+
+ s = device_action_to_string(action);
+ if (!s)
+ return -EINVAL;
+
+ return sd_device_set_sysattr_value(device, "uevent", s);
+}
diff --git a/src/libsystemd/sd-device/test-sd-device.c b/src/libsystemd/sd-device/test-sd-device.c
index fd30d1a1d0..aaa16f740d 100644
--- a/src/libsystemd/sd-device/test-sd-device.c
+++ b/src/libsystemd/sd-device/test-sd-device.c
@@ -4,6 +4,7 @@
#include "device-internal.h"
#include "device-private.h"
#include "device-util.h"
+#include "errno-util.h"
#include "hashmap.h"
#include "nulstr-util.h"
#include "string-util.h"
@@ -51,7 +52,7 @@ static void test_sd_device_one(sd_device *d) {
}
r = sd_device_get_sysattr_value(d, "name_assign_type", &val);
- assert_se(r >= 0 || IN_SET(r, -ENOENT, -EINVAL));
+ assert_se(r >= 0 || ERRNO_IS_PRIVILEGE(r) || IN_SET(r, -ENOENT, -EINVAL));
r = sd_device_get_property_value(d, "ID_NET_DRIVER", &val);
assert_se(r >= 0 || r == -ENOENT);
diff --git a/src/mount/mount-tool.c b/src/mount/mount-tool.c
index c1be0e0afb..153f67f93b 100644
--- a/src/mount/mount-tool.c
+++ b/src/mount/mount-tool.c
@@ -1197,7 +1197,7 @@ static int acquire_removable(sd_device *d) {
return 0;
for (;;) {
- if (sd_device_get_sysattr_value(d, "removable", &v) > 0)
+ if (sd_device_get_sysattr_value(d, "removable", &v) >= 0)
break;
if (sd_device_get_parent(d, &d) < 0)
diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c
index 6d24c3fd9f..8de1738264 100644
--- a/src/shared/dissect-image.c
+++ b/src/shared/dissect-image.c
@@ -150,7 +150,8 @@ static int device_is_partition(sd_device *d, blkid_partition pp) {
return false;
r = sd_device_get_sysattr_value(d, "partition", &v);
- if (r == -ENOENT) /* Not a partition device */
+ if (r == -ENOENT || /* Not a partition device */
+ ERRNO_IS_PRIVILEGE(r)) /* Not ready to access? */
return false;
if (r < 0)
return r;
diff --git a/src/shared/udev-util.c b/src/shared/udev-util.c
index 3a3f0198fd..80b70c18a0 100644
--- a/src/shared/udev-util.c
+++ b/src/shared/udev-util.c
@@ -10,6 +10,7 @@
#include "device-private.h"
#include "device-util.h"
#include "env-file.h"
+#include "errno-util.h"
#include "escape.h"
#include "fd-util.h"
#include "log.h"
@@ -525,12 +526,12 @@ int udev_resolve_subsys_kernel(const char *string, char *result, size_t maxsize,
if (read_value) {
r = sd_device_get_sysattr_value(dev, attr, &val);
- if (r < 0 && r != -ENOENT)
+ if (r < 0 && !ERRNO_IS_PRIVILEGE(r) && r != -ENOENT)
return r;
- if (r == -ENOENT)
- result[0] = '\0';
- else
+ if (r >= 0)
strscpy(result, maxsize, val);
+ else
+ result[0] = '\0';
log_debug("value '[%s/%s]%s' is '%s'", subsys, sysname, attr, result);
} else {
r = sd_device_get_syspath(dev, &val);
diff --git a/src/systemd/sd-device.h b/src/systemd/sd-device.h
index af0e26e2d7..310fcaa278 100644
--- a/src/systemd/sd-device.h
+++ b/src/systemd/sd-device.h
@@ -99,6 +99,7 @@ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, const ch
int sd_device_set_sysattr_value(sd_device *device, const char *sysattr, const char *value);
int sd_device_set_sysattr_valuef(sd_device *device, const char *sysattr, const char *format, ...) _sd_printf_(3, 4);
+int sd_device_trigger(sd_device *device, sd_device_action_t action);
/* device enumerator */
diff --git a/src/test/test-path-util.c b/src/test/test-path-util.c
index 58b185494d..59308473c1 100644
--- a/src/test/test-path-util.c
+++ b/src/test/test-path-util.c
@@ -481,6 +481,9 @@ static void test_path_startswith(void) {
assert_se(!path_startswith("/foo/bar/barfoo/", ""));
assert_se(!path_startswith("/foo/bar/barfoo/", "/bar/foo"));
assert_se(!path_startswith("/foo/bar/barfoo/", "/f/b/b/"));
+ assert_se(!path_startswith("/foo/bar/barfoo/", "/foo/bar/barfo"));
+ assert_se(!path_startswith("/foo/bar/barfoo/", "/foo/bar/bar"));
+ assert_se(!path_startswith("/foo/bar/barfoo/", "/fo"));
}
static void test_prefix_root_one(const char *r, const char *p, const char *expected) {
diff --git a/src/udev/udevadm-trigger.c b/src/udev/udevadm-trigger.c
index 2d16f34d86..ade92286d4 100644
--- a/src/udev/udevadm-trigger.c
+++ b/src/udev/udevadm-trigger.c
@@ -8,6 +8,7 @@
#include "device-enumerator-private.h"
#include "device-private.h"
+#include "device-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "path-util.h"
@@ -22,36 +23,63 @@
static bool arg_verbose = false;
static bool arg_dry_run = false;
+static bool arg_quiet = false;
-static int exec_list(sd_device_enumerator *e, const char *action, Set **settle_set) {
+static int exec_list(sd_device_enumerator *e, sd_device_action_t action, Set **settle_set) {
+ const char *action_str;
sd_device *d;
int r, ret = 0;
+ action_str = device_action_to_string(action);
+
FOREACH_DEVICE_AND_SUBSYSTEM(e, d) {
- _cleanup_free_ char *filename = NULL;
const char *syspath;
if (sd_device_get_syspath(d, &syspath) < 0)
continue;
if (arg_verbose)
- printf("%s\n", syspath);
+ printf("%s\n", strna(syspath));
+
if (arg_dry_run)
continue;
- filename = path_join(syspath, "uevent");
- if (!filename)
- return log_oom();
-
- r = write_string_file(filename, action, WRITE_STRING_FILE_DISABLE_BUFFER);
+ r = sd_device_trigger(d, action);
if (r < 0) {
- bool ignore = IN_SET(r, -ENOENT, -ENODEV);
+ /* ENOENT may be returned when a device does not have /uevent or is already
+ * removed. Hence, this is logged at debug level and ignored.
+ *
+ * ENODEV may be returned by some buggy device drivers e.g. /sys/devices/vio.
+ * See,
+ * https://github.com/systemd/systemd/issues/13652#issuecomment-535129791 and
+ * https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1845319.
+ * So, this error is ignored, but logged at warning level to encourage people to
+ * fix the driver.
+ *
+ * EROFS is returned when /sys is read only. In that case, all subsequent
+ * writes will also fail, hence return immediately.
+ *
+ * EACCES or EPERM may be returned when this is invoked by non-priviledged user.
+ * We do NOT return immediately, but continue operation and propagate the error.
+ * Why? Some device can be owned by a user, e.g., network devices configured in
+ * a network namespace. See, https://github.com/systemd/systemd/pull/18559 and
+ * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ebb4a4bf76f164457184a3f43ebc1552416bc823
+ *
+ * All other errors are logged at error level, but let's continue the operation,
+ * and propagate the error.
+ */
- log_full_errno(ignore ? LOG_DEBUG : LOG_ERR, r,
- "Failed to write '%s' to '%s'%s: %m",
- action, filename, ignore ? ", ignoring" : "");
- if (IN_SET(r, -EACCES, -EROFS))
- /* Inovoked by unprivileged user, or read only filesystem. Return earlier. */
+ bool ignore = IN_SET(r, -ENOENT, -ENODEV);
+ int level =
+ arg_quiet ? LOG_DEBUG :
+ r == -ENOENT ? LOG_DEBUG :
+ r == -ENODEV ? LOG_WARNING : LOG_ERR;
+
+ log_device_full_errno(d, level, r,
+ "Failed to write '%s' to '%s/uevent'%s: %m",
+ action_str, syspath, ignore ? ", ignoring" : "");
+
+ if (r == -EROFS)
return r;
if (ret == 0 && !ignore)
ret = r;
@@ -118,6 +146,7 @@ static int help(void) {
" -V --version Show package version\n"
" -v --verbose Print the list of devices while running\n"
" -n --dry-run Do not actually trigger the events\n"
+ " -q --quiet Suppress error logging in triggering events\n"
" -t --type= Type of events to trigger\n"
" devices sysfs devices (default)\n"
" subsystems sysfs subsystems and drivers\n"
@@ -148,6 +177,7 @@ int trigger_main(int argc, char *argv[], void *userdata) {
static const struct option options[] = {
{ "verbose", no_argument, NULL, 'v' },
{ "dry-run", no_argument, NULL, 'n' },
+ { "quiet", no_argument, NULL, 'q' },
{ "type", required_argument, NULL, 't' },
{ "action", required_argument, NULL, 'c' },
{ "subsystem-match", required_argument, NULL, 's' },
@@ -169,7 +199,7 @@ int trigger_main(int argc, char *argv[], void *userdata) {
TYPE_DEVICES,
TYPE_SUBSYSTEMS,
} device_type = TYPE_DEVICES;
- const char *action = "change";
+ sd_device_action_t action = SD_DEVICE_CHANGE;
_cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
_cleanup_(sd_device_monitor_unrefp) sd_device_monitor *m = NULL;
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
@@ -191,7 +221,7 @@ int trigger_main(int argc, char *argv[], void *userdata) {
if (r < 0)
return r;
- while ((c = getopt_long(argc, argv, "vnt:c:s:S:a:A:p:g:y:b:wVh", options, NULL)) >= 0) {
+ while ((c = getopt_long(argc, argv, "vnqt:c:s:S:a:A:p:g:y:b:wVh", options, NULL)) >= 0) {
_cleanup_free_ char *buf = NULL;
const char *key, *val;
@@ -202,6 +232,9 @@ int trigger_main(int argc, char *argv[], void *userdata) {
case 'n':
arg_dry_run = true;
break;
+ case 'q':
+ arg_quiet = true;
+ break;
case 't':
if (streq(optarg, "devices"))
device_type = TYPE_DEVICES;
@@ -211,18 +244,14 @@ int trigger_main(int argc, char *argv[], void *userdata) {
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown type --type=%s", optarg);
break;
case 'c': {
- sd_device_action_t a;
-
if (streq(optarg, "help")) {
dump_device_action_table();
return 0;
}
- a = device_action_from_string(optarg);
- if (a < 0)
- return log_error_errno(a, "Unknown action '%s'", optarg);
-
- action = device_action_to_string(a);
+ action = device_action_from_string(optarg);
+ if (action < 0)
+ return log_error_errno(action, "Unknown action '%s'", optarg);
break;
}
case 's':
diff --git a/src/udev/udevd.c b/src/udev/udevd.c
index 0657bf1b6d..6fffd8407b 100644
--- a/src/udev/udevd.c
+++ b/src/udev/udevd.c
@@ -1155,56 +1155,54 @@ static int on_ctrl_msg(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, co
return 1;
}
-static int synthesize_change_one(sd_device *dev, const char *syspath) {
- const char *filename;
+static int synthesize_change_one(sd_device *dev, sd_device *target) {
int r;
- filename = strjoina(syspath, "/uevent");
- log_device_debug(dev, "device is closed, synthesising 'change' on %s", syspath);
- r = write_string_file(filename, "change", WRITE_STRING_FILE_DISABLE_BUFFER);
+ if (DEBUG_LOGGING) {
+ const char *syspath = NULL;
+ (void) sd_device_get_syspath(target, &syspath);
+ log_device_debug(dev, "device is closed, synthesising 'change' on %s", strna(syspath));
+ }
+
+ r = sd_device_trigger(target, SD_DEVICE_CHANGE);
if (r < 0)
- return log_device_debug_errno(dev, r, "Failed to write 'change' to %s: %m", filename);
+ return log_device_debug_errno(target, r, "Failed to trigger 'change' uevent: %m");
+
return 0;
}
static int synthesize_change(sd_device *dev) {
- const char *subsystem, *sysname, *devname, *syspath, *devtype;
+ const char *subsystem, *sysname, *devtype;
int r;
r = sd_device_get_subsystem(dev, &subsystem);
if (r < 0)
return r;
- r = sd_device_get_sysname(dev, &sysname);
- if (r < 0)
- return r;
-
- r = sd_device_get_devname(dev, &devname);
- if (r < 0)
- return r;
-
- r = sd_device_get_syspath(dev, &syspath);
- if (r < 0)
- return r;
-
r = sd_device_get_devtype(dev, &devtype);
if (r < 0)
return r;
- if (streq_ptr("block", subsystem) &&
- streq_ptr("disk", devtype) &&
+ r = sd_device_get_sysname(dev, &sysname);
+ if (r < 0)
+ return r;
+
+ if (streq_ptr(subsystem, "block") &&
+ streq_ptr(devtype, "disk") &&
!startswith(sysname, "dm-")) {
_cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
bool part_table_read = false, has_partitions = false;
+ const char *devname;
sd_device *d;
int fd;
- /*
- * Try to re-read the partition table. This only succeeds if
- * none of the devices is busy. The kernel returns 0 if no
- * partition table is found, and we will not get an event for
- * the disk.
- */
+ r = sd_device_get_devname(dev, &devname);
+ if (r < 0)
+ return r;
+
+ /* Try to re-read the partition table. This only succeeds if none of the devices is
+ * busy. The kernel returns 0 if no partition table is found, and we will not get an
+ * event for the disk. */
fd = open(devname, O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK);
if (fd >= 0) {
r = flock(fd, LOCK_EX|LOCK_NB);
@@ -1236,44 +1234,33 @@ static int synthesize_change(sd_device *dev) {
FOREACH_DEVICE(e, d) {
const char *t;
- if (sd_device_get_devtype(d, &t) < 0 ||
- !streq("partition", t))
+ if (sd_device_get_devtype(d, &t) < 0 || !streq(t, "partition"))
continue;
has_partitions = true;
break;
}
- /*
- * We have partitions and re-read the table, the kernel already sent
- * out a "change" event for the disk, and "remove/add" for all
- * partitions.
- */
+ /* We have partitions and re-read the table, the kernel already sent out a "change"
+ * event for the disk, and "remove/add" for all partitions. */
if (part_table_read && has_partitions)
return 0;
- /*
- * We have partitions but re-reading the partition table did not
- * work, synthesize "change" for the disk and all partitions.
- */
- (void) synthesize_change_one(dev, syspath);
+ /* We have partitions but re-reading the partition table did not work, synthesize
+ * "change" for the disk and all partitions. */
+ (void) synthesize_change_one(dev, dev);
FOREACH_DEVICE(e, d) {
- const char *t, *n, *s;
+ const char *t;
- if (sd_device_get_devtype(d, &t) < 0 ||
- !streq("partition", t))
+ if (sd_device_get_devtype(d, &t) < 0 || !streq(t, "partition"))
continue;
- if (sd_device_get_devname(d, &n) < 0 ||
- sd_device_get_syspath(d, &s) < 0)
- continue;
-
- (void) synthesize_change_one(dev, s);
+ (void) synthesize_change_one(dev, d);
}
} else
- (void) synthesize_change_one(dev, syspath);
+ (void) synthesize_change_one(dev, dev);
return 0;
}
diff --git a/units/systemd-udev-trigger.service b/units/systemd-udev-trigger.service
index 8b2d88e185..3d0f7b4f20 100644
--- a/units/systemd-udev-trigger.service
+++ b/units/systemd-udev-trigger.service
@@ -19,5 +19,5 @@ ConditionPathIsReadWrite=/sys
[Service]
Type=oneshot
RemainAfterExit=yes
-ExecStart=udevadm trigger --type=subsystems --action=add
-ExecStart=udevadm trigger --type=devices --action=add
+ExecStart=-udevadm trigger --type=subsystems --action=add
+ExecStart=-udevadm trigger --type=devices --action=add