From 5f21939ebe4ca3c23c33383336030b45b72f86b5 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 12 Jan 2025 00:12:52 +0900 Subject: [PATCH] udev-rules: logs result of format substitution This also drops redundant token string in log message on truncation. No functional change, but should improve debuggability. (cherry picked from commit a3ab06ab21528138160eb3d40c3e661fc53a8158) Resolves: RHEL-75774 --- src/udev/udev-rules.c | 243 ++++++++++++++++++------------------------ 1 file changed, 105 insertions(+), 138 deletions(-) diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c index c33d17aa47..3a61403338 100644 --- a/src/udev/udev-rules.c +++ b/src/udev/udev-rules.c @@ -297,6 +297,23 @@ static bool token_is_for_parents(UdevRuleToken *token) { #define log_event_trace(event, ...) \ _log_event_trace(event, UNIQ_T(e, UNIQ), __VA_ARGS__) +#define _log_event_truncated(event, token, token_u, what, format) \ + ({ \ + UdevRuleToken *token_u = ASSERT_PTR(token); \ + \ + token_u->type < _TK_M_MAX ? \ + log_event_debug(event, token_u, \ + "The %s is truncated while substituting into \"%s\", assuming the token fails.", \ + what, (const char*) format) : \ + log_event_warning( \ + event, token_u, \ + "The %s is truncated while substituting into \"%s\", refusing to apply the token.", \ + what, (const char*) format); \ + }) + +#define log_event_truncated(event, token, what, format) \ + _log_event_truncated(event, token, UNIQ_T(t, UNIQ), what, format) + /* Mainly used when parsing .rules files. */ #define log_file_full_errno_zerook(...) \ log_udev_rule_file_full(NULL, __VA_ARGS__) @@ -351,26 +368,6 @@ static void log_unknown_owner(sd_device *dev, UdevRuleLine *line, int error, con log_udev_rule_line_full(dev, line, LOG_ERR, error, "Failed to resolve %s '%s', ignoring: %m", entity, name); } -static void log_event_truncated( - UdevEvent *event, - UdevRuleToken *token, - const char *what, - const char *format, - const char *key, - bool is_match) { - - if (is_match) - log_event_debug(event, token, - "The %s is truncated while substituting into '%s', " - "assuming the %s key does not match.", - what, format, key); - else - log_event_warning(event, token, - "The %s is truncated while substituting into '%s', " - "refusing to apply the %s key.", - what, format, key); -} - /*** Other functions ***/ static UdevRuleToken* udev_rule_token_free(UdevRuleToken *token) { @@ -1776,6 +1773,54 @@ bool udev_rules_should_reload(UdevRules *rules) { return false; } +static bool apply_format_full( + UdevEvent *event, + UdevRuleToken *token, + const char *format, + char *result, + size_t result_size, + bool replace_whitespace, + const char *what) { + + assert(event); + assert(token); + assert(format); + assert(result); + assert(what); + + bool truncated = false; + (void) udev_event_apply_format(event, format, result, result_size, replace_whitespace, &truncated); + if (truncated) { + log_event_truncated(event, token, what, format); + return false; + } + + if (event->trace && !streq(format, result)) + log_event_trace(event, token, "Format substitution: \"%s\" -> \"%s\"", format, result); + + return true; +} + +static bool apply_format_value( + UdevEvent *event, + UdevRuleToken *token, + char *result, + size_t result_size, + const char *what) { + + return apply_format_full(event, token, token->value, result, result_size, /* replace_whitespace = */ false, what); +} + +static bool apply_format_attr( + UdevEvent *event, + UdevRuleToken *token, + char *result, + size_t result_size, + const char *what) { + + return apply_format_full(event, token, token->data, result, result_size, /* replace_whitespace = */ false, what); +} + static bool token_match_string(UdevRuleToken *token, const char *str) { const char *value; bool match = false, case_insensitive; @@ -1834,7 +1879,6 @@ static bool token_match_string(UdevRuleToken *token, const char *str) { static bool token_match_attr(UdevRuleToken *token, sd_device *dev, UdevEvent *event) { char nbuf[UDEV_NAME_SIZE], vbuf[UDEV_NAME_SIZE]; const char *name, *value; - bool truncated; assert(token); assert(IN_SET(token->type, TK_M_ATTR, TK_M_PARENTS_ATTR)); @@ -1845,12 +1889,8 @@ static bool token_match_attr(UdevRuleToken *token, sd_device *dev, UdevEvent *ev switch (token->attr_subst_type) { case SUBST_TYPE_FORMAT: - (void) udev_event_apply_format(event, name, nbuf, sizeof(nbuf), false, &truncated); - if (truncated) { - log_event_truncated(event, token, "sysfs attribute name", name, - token->type == TK_M_ATTR ? "ATTR" : "ATTRS", /* is_match = */ true); + if (!apply_format_attr(event, token, nbuf, sizeof(nbuf), "sysfs attribute name")) return false; - } name = nbuf; _fallthrough_; @@ -2118,13 +2158,9 @@ static int udev_rule_apply_token_to_event( case TK_M_SYSCTL: { _cleanup_free_ char *value = NULL; char buf[UDEV_PATH_SIZE]; - bool truncated; - (void) udev_event_apply_format(event, token->data, buf, sizeof(buf), false, &truncated); - if (truncated) { - log_event_truncated(event, token, "sysctl entry name", token->data, "SYSCTL", /* is_match = */ true); + if (!apply_format_attr(event, token, buf, sizeof(buf), "sysctl entry name")) return false; - } r = sysctl_read(sysctl_normalize(buf), &value); if (r < 0 && r != -ENOENT) @@ -2136,13 +2172,10 @@ static int udev_rule_apply_token_to_event( mode_t mode = PTR_TO_MODE(token->data); char buf[UDEV_PATH_SIZE]; struct stat statbuf; - bool match, truncated; + bool match; - (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated); - if (truncated) { - log_event_truncated(event, token, "file name", token->value, "TEST", /* is_match = */ true); + if (!apply_format_value(event, token, buf, sizeof(buf), "file name")) return false; - } if (!path_is_absolute(buf) && udev_resolve_subsys_kernel(buf, buf, sizeof(buf), false) < 0) { @@ -2153,6 +2186,7 @@ static int udev_rule_apply_token_to_event( if (r < 0) return log_event_error_errno(event, token, r, "Failed to get syspath: %m"); + bool truncated; strscpy_full(tmp, sizeof(tmp), buf, &truncated); assert(!truncated); strscpyl_full(buf, sizeof(buf), &truncated, val, "/", tmp, NULL); @@ -2177,15 +2211,12 @@ static int udev_rule_apply_token_to_event( } case TK_M_PROGRAM: { char buf[UDEV_LINE_SIZE], result[UDEV_LINE_SIZE]; - bool truncated; size_t count; event->program_result = mfree(event->program_result); - (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated); - if (truncated) { - log_event_truncated(event, token, "command", token->value, "PROGRAM", /* is_match = */ true); + + if (!apply_format_value(event, token, buf, sizeof(buf), "command")) return false; - } log_event_debug(event, token, "Running PROGRAM=\"%s\"", buf); @@ -2211,13 +2242,9 @@ static int udev_rule_apply_token_to_event( case TK_M_IMPORT_FILE: { _cleanup_fclose_ FILE *f = NULL; char buf[UDEV_PATH_SIZE]; - bool truncated; - (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated); - if (truncated) { - log_event_truncated(event, token, "file name to be imported", token->value, "IMPORT", /* is_match = */ true); + if (!apply_format_value(event, token, buf, sizeof(buf), "file name to be imported")) return false; - } log_event_debug(event, token, "Importing properties from '%s'", buf); @@ -2264,11 +2291,8 @@ static int udev_rule_apply_token_to_event( char buf[UDEV_LINE_SIZE], result[UDEV_LINE_SIZE]; bool truncated; - (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated); - if (truncated) { - log_event_truncated(event, token, "command", token->value, "IMPORT", /* is_match = */ true); + if (!apply_format_value(event, token, buf, sizeof(buf), "command")) return false; - } log_event_debug(event, token, "Importing properties from results of '%s'", buf); @@ -2329,7 +2353,6 @@ static int udev_rule_apply_token_to_event( assert(cmd >= 0 && cmd < _UDEV_BUILTIN_MAX); unsigned mask = 1U << (int) cmd; char buf[UDEV_LINE_SIZE]; - bool truncated; if (udev_builtin_run_once(cmd)) { /* check if we ran already */ @@ -2343,11 +2366,8 @@ static int udev_rule_apply_token_to_event( event->builtin_run |= mask; } - (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated); - if (truncated) { - log_event_truncated(event, token, "builtin command", token->value, "IMPORT", /* is_match = */ true); + if (!apply_format_value(event, token, buf, sizeof(buf), "builtin command")) return false; - } log_event_debug(event, token, "Importing properties from results of builtin command '%s'", buf); @@ -2397,13 +2417,9 @@ static int udev_rule_apply_token_to_event( } case TK_M_IMPORT_PARENT: { char buf[UDEV_PATH_SIZE]; - bool truncated; - (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated); - if (truncated) { - log_event_truncated(event, token, "property name", token->value, "IMPORT", /* is_match = */ true); + if (!apply_format_value(event, token, buf, sizeof(buf), "property name")) return false; - } r = import_parent_into_properties(dev, buf); if (r < 0) @@ -2460,18 +2476,14 @@ static int udev_rule_apply_token_to_event( case TK_A_OWNER: { char owner[UDEV_NAME_SIZE]; const char *ow = owner; - bool truncated; if (event->owner_final) break; if (token->op == OP_ASSIGN_FINAL) event->owner_final = true; - (void) udev_event_apply_format(event, token->value, owner, sizeof(owner), false, &truncated); - if (truncated) { - log_event_truncated(event, token, "user name", token->value, "OWNER", /* is_match = */ false); - break; - } + if (!apply_format_value(event, token, owner, sizeof(owner), "user name")) + return true; r = get_user_creds(&ow, &event->uid, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING); if (r < 0) @@ -2483,18 +2495,14 @@ static int udev_rule_apply_token_to_event( case TK_A_GROUP: { char group[UDEV_NAME_SIZE]; const char *gr = group; - bool truncated; if (event->group_final) break; if (token->op == OP_ASSIGN_FINAL) event->group_final = true; - (void) udev_event_apply_format(event, token->value, group, sizeof(group), false, &truncated); - if (truncated) { - log_event_truncated(event, token, "group name", token->value, "GROUP", /* is_match = */ false); - break; - } + if (!apply_format_value(event, token, group, sizeof(group), "group name")) + return true; r = get_group_creds(&gr, &event->gid, USER_CREDS_ALLOW_MISSING); if (r < 0) @@ -2505,18 +2513,14 @@ static int udev_rule_apply_token_to_event( } case TK_A_MODE: { char mode_str[UDEV_NAME_SIZE]; - bool truncated; if (event->mode_final) break; if (token->op == OP_ASSIGN_FINAL) event->mode_final = true; - (void) udev_event_apply_format(event, token->value, mode_str, sizeof(mode_str), false, &truncated); - if (truncated) { - log_event_truncated(event, token, "mode", token->value, "MODE", /* is_match = */ false); - break; - } + if (!apply_format_value(event, token, mode_str, sizeof(mode_str), "mode")) + return true; r = parse_mode(mode_str, &event->mode); if (r < 0) @@ -2558,17 +2562,13 @@ static int udev_rule_apply_token_to_event( case TK_A_SECLABEL: { _cleanup_free_ char *name = NULL, *label = NULL; char label_str[UDEV_LINE_SIZE] = {}; - bool truncated; name = strdup(token->data); if (!name) return log_oom(); - (void) udev_event_apply_format(event, token->value, label_str, sizeof(label_str), false, &truncated); - if (truncated) { - log_event_truncated(event, token, "security label", token->value, "SECLABEL", /* is_match = */ false); - break; - } + if (!apply_format_value(event, token, label_str, sizeof(label_str), "security label")) + return true; if (!isempty(label_str)) label = strdup(label_str); @@ -2596,7 +2596,6 @@ static int udev_rule_apply_token_to_event( const char *val, *name = token->data; char value_new[UDEV_NAME_SIZE], *p = value_new; size_t count, l = sizeof(value_new); - bool truncated; if (isempty(token->value)) { if (token->op == OP_ADD) @@ -2609,6 +2608,7 @@ static int udev_rule_apply_token_to_event( if (token->op == OP_ADD && device_get_property_value_with_fallback(dev, name, event->worker ? event->worker->properties : NULL, &val) >= 0) { + bool truncated; l = strpcpyl_full(&p, l, &truncated, val, " ", NULL); if (truncated) { log_event_warning(event, token, @@ -2618,13 +2618,8 @@ static int udev_rule_apply_token_to_event( } } - (void) udev_event_apply_format(event, token->value, p, l, false, &truncated); - if (truncated) { - _cleanup_free_ char *key_with_name = strjoin("ENV{", name, "}"); - log_event_truncated(event, token, "property value", token->value, - key_with_name ?: "ENV", /* is_match = */ false); - break; - } + if (!apply_format_value(event, token, p, l, "property value")) + return true; if (event->esc == ESCAPE_REPLACE) { count = udev_replace_chars(p, NULL); @@ -2641,13 +2636,9 @@ static int udev_rule_apply_token_to_event( } case TK_A_TAG: { char buf[UDEV_PATH_SIZE]; - bool truncated; - (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated); - if (truncated) { - log_event_truncated(event, token, "tag name", token->value, "TAG", /* is_match = */ false); - break; - } + if (!apply_format_value(event, token, buf, sizeof(buf), "tag name")) + return true; if (token->op == OP_ASSIGN) device_cleanup_tags(dev); @@ -2665,7 +2656,6 @@ static int udev_rule_apply_token_to_event( } case TK_A_NAME: { char buf[UDEV_PATH_SIZE]; - bool truncated; size_t count; if (event->name_final) @@ -2680,11 +2670,8 @@ static int udev_rule_apply_token_to_event( break; } - (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated); - if (truncated) { - log_event_truncated(event, token, "network interface name", token->value, "NAME", /* is_match = */ false); - break; - } + if (!apply_format_value(event, token, buf, sizeof(buf), "network interface name")) + return true; if (IN_SET(event->esc, ESCAPE_UNSET, ESCAPE_REPLACE)) { if (naming_scheme_has(NAMING_REPLACE_STRICTLY)) @@ -2705,7 +2692,6 @@ static int udev_rule_apply_token_to_event( } case TK_A_DEVLINK: { char buf[UDEV_PATH_SIZE]; - bool truncated; size_t count; if (event->devlink_final) @@ -2717,12 +2703,10 @@ static int udev_rule_apply_token_to_event( if (IN_SET(token->op, OP_ASSIGN, OP_ASSIGN_FINAL)) device_cleanup_devlinks(dev); - (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), - /* replace_whitespace = */ event->esc != ESCAPE_NONE, &truncated); - if (truncated) { - log_event_truncated(event, token, "symbolic link path", token->value, "SYMLINK", /* is_match = */ false); - break; - } + if (!apply_format_full(event, token, token->value, buf, sizeof(buf), + /* replace_whitespace = */ event->esc != ESCAPE_NONE, + "symbolic link path")) + return true; /* By default or string_escape=none, allow multiple symlinks separated by spaces. */ if (event->esc == ESCAPE_UNSET) @@ -2772,10 +2756,10 @@ 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; - bool truncated; 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); if (truncated) { log_event_warning(event, token, @@ -2790,11 +2774,9 @@ static int udev_rule_apply_token_to_event( log_event_error_errno(event, token, r, "Could not find file matches '%s', ignoring: %m", buf); break; } - (void) udev_event_apply_format(event, token->value, value, sizeof(value), false, &truncated); - if (truncated) { - log_event_truncated(event, token, "attribute value", token->value, "ATTR", /* is_match = */ false); - break; - } + + if (!apply_format_value(event, token, value, sizeof(value), "attribute value")) + return true; if (EVENT_MODE_DESTRUCTIVE(event)) { log_event_debug(event, token, "Writing ATTR{'%s'}=\"%s\".", buf, value); @@ -2812,21 +2794,12 @@ static int udev_rule_apply_token_to_event( } case TK_A_SYSCTL: { char buf[UDEV_PATH_SIZE], value[UDEV_NAME_SIZE]; - bool truncated; - (void) udev_event_apply_format(event, token->data, buf, sizeof(buf), false, &truncated); - if (truncated) { - log_event_truncated(event, token, "sysctl entry name", token->data, "SYSCTL", /* is_match = */ false); - break; - } + if (!apply_format_attr(event, token, buf, sizeof(buf), "sysctl entry name")) + return true; - (void) udev_event_apply_format(event, token->value, value, sizeof(value), false, &truncated); - if (truncated) { - _cleanup_free_ char *key_with_name = strjoin("SYSCTL{", buf, "}"); - log_event_truncated(event, token, "sysctl value", token->value, - key_with_name ?: "SYSCTL", /* is_match = */ false); - break; - } + if (!apply_format_value(event, token, value, sizeof(value), "sysctl value")) + return true; sysctl_normalize(buf); @@ -2844,7 +2817,6 @@ static int udev_rule_apply_token_to_event( case TK_A_RUN_PROGRAM: { _cleanup_free_ char *cmd = NULL; char buf[UDEV_LINE_SIZE]; - bool truncated; if (event->run_final) break; @@ -2854,13 +2826,8 @@ static int udev_rule_apply_token_to_event( if (IN_SET(token->op, OP_ASSIGN, OP_ASSIGN_FINAL)) ordered_hashmap_clear_free_key(event->run_list); - (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated); - if (truncated) { - log_event_truncated(event, token, "command", token->value, - token->type == TK_A_RUN_BUILTIN ? "RUN{builtin}" : "RUN{program}", - /* is_match = */ false); - break; - } + if (!apply_format_value(event, token, buf, sizeof(buf), "command")) + return true; cmd = strdup(buf); if (!cmd)