diff --git a/man/udev.xml b/man/udev.xml index 72e74758520..232dd4021cb 100644 --- a/man/udev.xml +++ b/man/udev.xml @@ -720,6 +720,15 @@ SUBSYSTEM=="net", OPTIONS="log_level=debug" + + + + Dump the status of the event currently processing. It may be useful for debugging + udev rules by inserting this option. + + + + diff --git a/src/udev/meson.build b/src/udev/meson.build index f008ea1f665..b1f41c4c5cf 100644 --- a/src/udev/meson.build +++ b/src/udev/meson.build @@ -31,6 +31,7 @@ libudevd_core_sources = files( 'udev-builtin.c', 'udev-config.c', 'udev-ctrl.c', + 'udev-dump.c', 'udev-event.c', 'udev-format.c', 'udev-manager.c', diff --git a/src/udev/udev-dump.c b/src/udev/udev-dump.c new file mode 100644 index 00000000000..5900eef8aff --- /dev/null +++ b/src/udev/udev-dump.c @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "ansi-color.h" +#include "device-private.h" +#include "device-util.h" +#include "format-util.h" +#include "fs-util.h" +#include "udev-builtin.h" +#include "udev-dump.h" +#include "udev-event.h" +#include "user-util.h" + +void dump_event(UdevEvent *event, FILE *f) { + sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev); + + if (!f) + f = stdout; + + fprintf(f, "%sProperties:%s\n", ansi_highlight(), ansi_normal()); + FOREACH_DEVICE_PROPERTY(dev, key, value) + fprintf(f, " %s=%s\n", key, value); + + if (sd_device_get_tag_first(dev)) { + fprintf(f, "%sTags:%s\n", ansi_highlight(), ansi_normal()); + FOREACH_DEVICE_TAG(dev, tag) + fprintf(f, " %s\n", tag); + } + + if (sd_device_get_devnum(dev, NULL) >= 0) { + + if (sd_device_get_devlink_first(dev)) { + int prio; + device_get_devlink_priority(dev, &prio); + fprintf(f, "%sDevice node symlinks:%s (priority=%i)\n", ansi_highlight(), ansi_normal(), prio); + FOREACH_DEVICE_DEVLINK(dev, devlink) + fprintf(f, " %s\n", devlink); + } + + fprintf(f, "%sInotify watch:%s\n %s\n", ansi_highlight(), ansi_normal(), enabled_disabled(event->inotify_watch)); + + uid_t uid = event->uid; + if (!uid_is_valid(uid)) + (void) device_get_devnode_uid(dev, &uid); + if (uid_is_valid(uid)) { + _cleanup_free_ char *user = uid_to_name(uid); + fprintf(f, "%sDevice node owner:%s\n %s (uid="UID_FMT")\n", ansi_highlight(), ansi_normal(), strna(user), uid); + } + + gid_t gid = event->gid; + if (!gid_is_valid(uid)) + (void) device_get_devnode_gid(dev, &gid); + if (gid_is_valid(gid)) { + _cleanup_free_ char *group = gid_to_name(gid); + fprintf(f, "%sDevice node group:%s\n %s (gid="GID_FMT")\n", ansi_highlight(), ansi_normal(), strna(group), gid); + } + + mode_t mode = event->mode; + if (mode == MODE_INVALID) + (void) device_get_devnode_mode(dev, &mode); + if (mode != MODE_INVALID) + fprintf(f, "%sDevice node permission:%s\n %04o\n", ansi_highlight(), ansi_normal(), mode); + + if (!ordered_hashmap_isempty(event->seclabel_list)) { + const char *name, *label; + fprintf(f, "%sDevice node security label:%s\n", ansi_highlight(), ansi_normal()); + ORDERED_HASHMAP_FOREACH_KEY(label, name, event->seclabel_list) + fprintf(f, " %s : %s\n", name, label); + } + } + + if (sd_device_get_ifindex(dev, NULL) >= 0) { + if (!isempty(event->name)) + fprintf(f, "%sNetwork interface name:%s\n %s\n", ansi_highlight(), ansi_normal(), event->name); + + if (!strv_isempty(event->altnames)) { + bool space = true; + fprintf(f, "%sAlternative interface names:%s", ansi_highlight(), ansi_normal()); + fputstrv(f, event->altnames, "\n ", &space); + fputs("\n", f); + } + } + + if (!ordered_hashmap_isempty(event->run_list)) { + void *val; + const char *command; + fprintf(f, "%sQueued commands:%s\n", ansi_highlight(), ansi_normal()); + ORDERED_HASHMAP_FOREACH_KEY(val, command, event->run_list) { + UdevBuiltinCommand builtin_cmd = PTR_TO_UDEV_BUILTIN_CMD(val); + + if (builtin_cmd != _UDEV_BUILTIN_INVALID) + fprintf(f, " RUN{builtin} : %s\n", command); + else + fprintf(f, " RUN{program} : %s\n", command); + } + } +} diff --git a/src/udev/udev-dump.h b/src/udev/udev-dump.h new file mode 100644 index 00000000000..6e3f1368ce4 --- /dev/null +++ b/src/udev/udev-dump.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#pragma once + +#include + +typedef struct UdevEvent UdevEvent; + +void dump_event(UdevEvent *event, FILE *f); diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c index 041c642aa97..d94fc6fdbd3 100644 --- a/src/udev/udev-rules.c +++ b/src/udev/udev-rules.c @@ -18,6 +18,7 @@ #include "fs-util.h" #include "glob-util.h" #include "list.h" +#include "memstream-util.h" #include "mkdir.h" #include "netif-naming-scheme.h" #include "nulstr-util.h" @@ -32,6 +33,7 @@ #include "sysctl-util.h" #include "syslog-util.h" #include "udev-builtin.h" +#include "udev-dump.h" #include "udev-event.h" #include "udev-format.h" #include "udev-node.h" @@ -117,6 +119,7 @@ typedef enum { #define _TK_A_MIN _TK_M_MAX /* lvalues which take one of assign operators */ + TK_A_OPTIONS_DUMP, /* no argument */ TK_A_OPTIONS_STRING_ESCAPE_NONE, /* no argument */ TK_A_OPTIONS_STRING_ESCAPE_REPLACE, /* no argument */ TK_A_OPTIONS_DB_PERSIST, /* no argument */ @@ -991,7 +994,9 @@ static int parse_token( if (op == OP_ADD) op = OP_ASSIGN; - if (streq(value, "string_escape=none")) + if (streq(value, "dump")) + r = rule_line_add_token(rule_line, TK_A_OPTIONS_DUMP, op, NULL, NULL, /* is_case_insensitive = */ false, token_str); + else if (streq(value, "string_escape=none")) r = rule_line_add_token(rule_line, TK_A_OPTIONS_STRING_ESCAPE_NONE, op, NULL, NULL, /* is_case_insensitive = */ false, token_str); else if (streq(value, "string_escape=replace")) r = rule_line_add_token(rule_line, TK_A_OPTIONS_STRING_ESCAPE_REPLACE, op, NULL, NULL, /* is_case_insensitive = */ false, token_str); @@ -2551,6 +2556,32 @@ static int udev_rule_apply_token_to_event( case TK_M_RESULT: return token_match_string(event, token, event->program_result, /* log_result = */ true); + case TK_A_OPTIONS_DUMP: { + log_event_info(event, token, "Dumping current state:"); + + if (event->event_mode == EVENT_UDEV_WORKER) { + _cleanup_(memstream_done) MemStream m = {}; + FILE *f = memstream_init(&m); + if (!f) + return log_oom(); + + dump_event(event, f); + + _cleanup_free_ char *buf = NULL; + r = memstream_finalize(&m, &buf, NULL); + if (r < 0) + log_event_warning_errno(event, token, r, "Failed to finalize memory stream, ignoring: %m"); + else + log_info("%s", buf); + } else { + puts("============================"); + dump_event(event, NULL); + puts("============================"); + } + + log_event_info(event, token, "DONE"); + return true; + } case TK_A_OPTIONS_STRING_ESCAPE_NONE: event->esc = ESCAPE_NONE; return log_event_done(event, token); diff --git a/src/udev/udevadm-test.c b/src/udev/udevadm-test.c index 088522b915f..5ceb6a5f289 100644 --- a/src/udev/udevadm-test.c +++ b/src/udev/udevadm-test.c @@ -3,33 +3,19 @@ * Copyright © 2003-2004 Greg Kroah-Hartman */ -#include #include #include -#include #include -#include -#include -#include #include "sd-device.h" -#include "ansi-color.h" #include "device-private.h" -#include "device-util.h" -#include "format-util.h" -#include "path-util.h" -#include "string-util.h" -#include "strv.h" -#include "strxcpyx.h" -#include "terminal-util.h" #include "udev-builtin.h" +#include "udev-dump.h" #include "udev-event.h" -#include "udev-format.h" #include "udev-rules.h" #include "udevadm-util.h" #include "udevadm.h" -#include "user-util.h" static sd_device_action_t arg_action = SD_DEVICE_ADD; static ResolveNameTiming arg_resolve_name_timing = RESOLVE_NAME_EARLY; @@ -153,83 +139,7 @@ int test_main(int argc, char *argv[], void *userdata) { puts("Processing udev rules done."); puts(""); - printf("%sProperties:%s\n", ansi_highlight(), ansi_normal()); - FOREACH_DEVICE_PROPERTY(dev, key, value) - printf(" %s=%s\n", key, value); - - if (sd_device_get_tag_first(dev)) { - printf("%sTags:%s\n", ansi_highlight(), ansi_normal()); - FOREACH_DEVICE_TAG(dev, tag) - printf(" %s\n", tag); - } - - if (sd_device_get_devnum(dev, NULL) >= 0) { - - if (sd_device_get_devlink_first(dev)) { - int prio; - device_get_devlink_priority(dev, &prio); - printf("%sDevice node symlinks:%s (priority=%i)\n", ansi_highlight(), ansi_normal(), prio); - FOREACH_DEVICE_DEVLINK(dev, devlink) - printf(" %s\n", devlink); - } - - printf("%sInotify watch:%s\n %s\n", ansi_highlight(), ansi_normal(), enabled_disabled(event->inotify_watch)); - - uid_t uid = event->uid; - if (!uid_is_valid(uid)) - (void) device_get_devnode_uid(dev, &uid); - if (uid_is_valid(uid)) { - _cleanup_free_ char *user = uid_to_name(uid); - printf("%sDevice node owner:%s\n %s (uid="UID_FMT")\n", ansi_highlight(), ansi_normal(), strna(user), uid); - } - - gid_t gid = event->gid; - if (!gid_is_valid(uid)) - (void) device_get_devnode_gid(dev, &gid); - if (gid_is_valid(gid)) { - _cleanup_free_ char *group = gid_to_name(gid); - printf("%sDevice node group:%s\n %s (gid="GID_FMT")\n", ansi_highlight(), ansi_normal(), strna(group), gid); - } - - mode_t mode = event->mode; - if (mode == MODE_INVALID) - (void) device_get_devnode_mode(dev, &mode); - if (mode != MODE_INVALID) - printf("%sDevice node permission:%s\n %04o\n", ansi_highlight(), ansi_normal(), mode); - - if (!ordered_hashmap_isempty(event->seclabel_list)) { - const char *name, *label; - printf("%sDevice node security label:%s\n", ansi_highlight(), ansi_normal()); - ORDERED_HASHMAP_FOREACH_KEY(label, name, event->seclabel_list) - printf(" %s : %s\n", name, label); - } - } - - if (sd_device_get_ifindex(dev, NULL) >= 0) { - if (!isempty(event->name)) - printf("%sNetwork interface name:%s\n %s\n", ansi_highlight(), ansi_normal(), event->name); - - if (!strv_isempty(event->altnames)) { - bool space = true; - printf("%sAlternative interface names:%s", ansi_highlight(), ansi_normal()); - fputstrv(stdout, event->altnames, "\n ", &space); - puts(""); - } - } - - if (!ordered_hashmap_isempty(event->run_list)) { - void *val; - const char *command; - printf("%sQueued commands:%s\n", ansi_highlight(), ansi_normal()); - ORDERED_HASHMAP_FOREACH_KEY(val, command, event->run_list) { - UdevBuiltinCommand builtin_cmd = PTR_TO_UDEV_BUILTIN_CMD(val); - - if (builtin_cmd != _UDEV_BUILTIN_INVALID) - printf(" RUN{builtin} : %s\n", command); - else - printf(" RUN{program} : %s\n", command); - } - } + dump_event(event, NULL); r = 0; out: