udev-rules: use sd_device_set_sysattr_value() to write sysfs attribute

Then, we can avoid that files outside of sysfs are written by udev ATTR key.

This also makes
- logs failure in udev_resolve_subsys_kernel(),
- failure in sd_device_get_syspath() critical, as that should not happen,
- cache the value to be write when running on test mode, to make it
  shown by OPTIONS="dump" or obtained by ATTR match token.
This commit is contained in:
Yu Watanabe
2025-01-12 08:22:53 +09:00
parent 2b3d4e3d94
commit a9559ebcbc

View File

@@ -2960,50 +2960,85 @@ static int udev_rule_apply_token_to_event(
}
case TK_A_ATTR: {
char buf[UDEV_PATH_SIZE], value[UDEV_NAME_SIZE];
const char *val, *key_name = token->data;
const char *key = token->data;
/* First, try to resolve "[<SUBSYSTEM>/<KERNEL>]<attribute>" format. */
r = udev_resolve_subsys_kernel(key, buf, sizeof(buf), false);
if (r == -ENOMEM)
return log_oom();
if (ERRNO_IS_NEG_DEVICE_ABSENT(r)) {
log_event_warning_errno(event, token, r, "Failed to resolve sysfs attribute \"%s\", ignoring: %m", key);
return true;
}
if (r < 0) {
/* If not, make the path to sysfs attribute absolute, to make '*' resolvable by attr_subst_subdir(). */
const char *syspath;
r = sd_device_get_syspath(dev, &syspath);
if (r < 0)
return log_event_error_errno(event, token, r, "Failed to get syspath: %m");
if (udev_resolve_subsys_kernel(key_name, buf, sizeof(buf), false) < 0 &&
sd_device_get_syspath(dev, &val) >= 0) {
bool truncated;
strscpyl_full(buf, sizeof(buf), &truncated, val, "/", key_name, NULL);
strscpyl_full(buf, sizeof(buf), &truncated, syspath, "/", key, NULL);
if (truncated) {
log_event_warning(event, token,
"The path to the attribute \"%s/%s\" is too long, refusing to set the attribute.",
val, key_name);
syspath, key);
return true;
}
/* Resolve '*' in the path. */
r = attr_subst_subdir(buf);
if (r < 0) {
log_event_error_errno(event, token, r, "Could not find file matches \"%s\", ignoring: %m", buf);
return true;
}
}
r = attr_subst_subdir(buf);
/* Then, make the path relative again. This also checks if the path being inside of the sysfs. */
_cleanup_free_ char *resolved = NULL;
_cleanup_close_ int fd = -EBADF;
r = device_chase(dev, buf, /* flags = */ 0, &resolved, &fd);
if (r < 0) {
log_event_error_errno(event, token, r, "Could not find file matches \"%s\", ignoring: %m", buf);
log_event_error_errno(event, token, r, "Could not chase sysfs attribute \"%s\", ignoring: %m", buf);
return true;
}
/* Apply formatting to the value. */
if (!apply_format_value(event, token, value, sizeof(value), "attribute value"))
return true;
if (EVENT_MODE_DESTRUCTIVE(event)) {
log_event_debug(event, token, "Writing \"%s\" to sysfs attribute \"%s\".", value, buf);
r = write_string_file(buf, value,
WRITE_STRING_FILE_VERIFY_ON_FAILURE |
WRITE_STRING_FILE_DISABLE_BUFFER |
WRITE_STRING_FILE_AVOID_NEWLINE |
WRITE_STRING_FILE_VERIFY_IGNORE_NEWLINE);
log_event_debug(event, token, "Writing \"%s\" to sysfs attribute \"%s\".", value, resolved);
r = sd_device_set_sysattr_value(dev, resolved, value);
if (r < 0)
log_event_error_errno(event, token, r, "Failed to write \"%s\" to sysfs attribute \"%s\", ignoring: %m", value, buf);
log_event_error_errno(event, token, r, "Failed to write \"%s\" to sysfs attribute \"%s\", ignoring: %m", value, resolved);
else {
event_cache_written_sysattr(event, buf, value);
event_cache_written_sysattr(event, resolved, value);
log_event_done(event, token);
}
} else {
log_event_debug(event, token, "Running in test mode, skipping writing \"%s\" to sysfs attribute \"%s\".", value, buf);
log_event_debug(event, token, "Running in test mode, skipping writing \"%s\" to sysfs attribute \"%s\".", value, resolved);
r = verify_regular_at(AT_FDCWD, buf, /* follow = */ false);
/* We assume the attribute is writable if the path points to a regular file, and cache
* the value to make it shown by OPTIONS="dump" or obtained by later ATTR match token. */
r = fd_verify_regular(fd);
if (r < 0 && !ERRNO_IS_NEG_PRIVILEGE(r))
log_event_error_errno(event, token, r, "Failed to verify sysfs attribute \"%s\" is a regular file: %m", buf);
else
event_cache_written_sysattr(event, buf, value);
log_event_error_errno(event, token, r, "Failed to verify sysfs attribute \"%s\" is a regular file: %m", resolved);
else {
event_cache_written_sysattr(event, resolved, value);
_cleanup_free_ char *copied = strdup(value);
if (!copied)
return log_oom();
r = device_cache_sysattr_value(dev, resolved, value, /* error = */ 0);
if (r < 0)
log_event_warning_errno(event, token, r, "Failed to cache sysfs attribute \"%s\", ignoring: %m", resolved);
else if (r > 0) {
TAKE_PTR(resolved);
TAKE_PTR(copied);
}
}
}
return true;
}