diff --git a/man/kernel-command-line.xml b/man/kernel-command-line.xml index 528f23b8093..6cf16e5b554 100644 --- a/man/kernel-command-line.xml +++ b/man/kernel-command-line.xml @@ -318,6 +318,8 @@ udev.log_level= rd.udev.log_level= + udev.trace= + rd.udev.trace= udev.children_max= rd.udev.children_max= udev.exec_delay= diff --git a/man/systemd-udevd.service.xml b/man/systemd-udevd.service.xml index ad0be5bdab2..ab50fb65a25 100644 --- a/man/systemd-udevd.service.xml +++ b/man/systemd-udevd.service.xml @@ -167,6 +167,16 @@ + + udev.trace[=BOOL] + rd.udev.trace[=BOOL] + + Enable/disable trace logging. When enabled, udev.log_level= will be + ignored, and debug level is assumed. + + + + udev.children_max= rd.udev.children_max= diff --git a/man/udevadm.xml b/man/udevadm.xml index 07169f2e504..01d662a0767 100644 --- a/man/udevadm.xml +++ b/man/udevadm.xml @@ -743,6 +743,14 @@ + + + + Enable/disable trace logging in systemd-udevd. + + + + diff --git a/shell-completion/bash/udevadm b/shell-completion/bash/udevadm index c4aea7039c6..c8a08f68d74 100644 --- a/shell-completion/bash/udevadm +++ b/shell-completion/bash/udevadm @@ -66,7 +66,7 @@ _udevadm() { [SETTLE]='-t --timeout -E --exit-if-exists' [CONTROL_STANDALONE]='-e --exit -s --stop-exec-queue -S --start-exec-queue -R --reload --ping --load-credentials' - [CONTROL_ARG]='-l --log-priority -p --property -m --children-max -t --timeout' + [CONTROL_ARG]='-l --log-priority -p --property -m --children-max -t --timeout --trace' [MONITOR_STANDALONE]='-k --kernel -u --udev -p --property' [MONITOR_ARG]='-s --subsystem-match -t --tag-match' [TEST_STANDALONE]='-v --verbose' @@ -191,6 +191,9 @@ _udevadm() { -l|--log-priority) comps='alert crit debug emerg err info notice warning' ;; + --trace) + comps='yes no' + ;; *) comps='' ;; diff --git a/shell-completion/zsh/_udevadm b/shell-completion/zsh/_udevadm index c0141e5cd38..8a88aa694e0 100644 --- a/shell-completion/zsh/_udevadm +++ b/shell-completion/zsh/_udevadm @@ -66,6 +66,7 @@ _udevadm_control(){ '(-R --reload)'{-R,--reload}'[Signal systemd-udevd to reload the rules files and other databases like the kernel module index.]' \ '(-p --property)'{-p,--property=}'[Set a global property for all events.]:KEY=VALUE' \ '(-m --children-max=)'{-m,--children-max=}'[Set the maximum number of events.]:N' \ + '--trace=[Enable/disable trace logging.]:BOOL' \ '(-t --timeout=)'{-t,--timeout=}'[The maximum number of seconds to wait for a reply from systemd-udevd.]:SECONDS' } diff --git a/src/shared/varlink-io.systemd.Udev.c b/src/shared/varlink-io.systemd.Udev.c index fb34036b426..09c1ca70ce1 100644 --- a/src/shared/varlink-io.systemd.Udev.c +++ b/src/shared/varlink-io.systemd.Udev.c @@ -2,6 +2,11 @@ #include "varlink-io.systemd.Udev.h" +static SD_VARLINK_DEFINE_METHOD( + SetTrace, + SD_VARLINK_FIELD_COMMENT("Enable/disable."), + SD_VARLINK_DEFINE_INPUT(enable, SD_VARLINK_BOOL, 0)); + static SD_VARLINK_DEFINE_METHOD( SetChildrenMax, SD_VARLINK_FIELD_COMMENT("The maximum number of child processes. When 0 is specified, the maximum is determined based on the system resources."), @@ -22,6 +27,8 @@ SD_VARLINK_DEFINE_INTERFACE( io_systemd_Udev, "io.systemd.Udev", SD_VARLINK_INTERFACE_COMMENT("An interface for controlling systemd-udevd."), + SD_VARLINK_SYMBOL_COMMENT("Enable/disable trace logging."), + &vl_method_SetTrace, SD_VARLINK_SYMBOL_COMMENT("Sets the maximum number of child processes."), &vl_method_SetChildrenMax, SD_VARLINK_SYMBOL_COMMENT("Sets the global udev properties."), diff --git a/src/udev/udev-config.c b/src/udev/udev-config.c index e60464360db..6f02d25f455 100644 --- a/src/udev/udev-config.c +++ b/src/udev/udev-config.c @@ -112,6 +112,20 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat return 0; + } else if (proc_cmdline_key_streq(key, "udev.trace")) { + + if (!value) + config->trace = true; + else { + r = parse_boolean(value); + if (r < 0) + log_warning_errno(r, "Failed to parse udev.trace argument, ignoring: %s", value); + else + config->trace = r; + } + + return 0; + } else { if (startswith(key, "udev.")) log_warning("Unknown udev kernel command line option \"%s\", ignoring.", key); @@ -257,13 +271,24 @@ static int parse_argv(int argc, char *argv[], UdevConfig *config) { manager->config_by_command.name || \ manager->config_by_udev_conf.name; +static void manager_merge_config_log_level(Manager *manager) { + assert(manager); + + MERGE_BOOL(trace); + + if (manager->config.trace) + manager->config.log_level = LOG_DEBUG; + else + MERGE_NON_NEGATIVE(log_level, log_get_max_level()); +} + static void manager_merge_config(Manager *manager) { assert(manager); /* udev.conf has the lowest priority, then followed by command line arguments, kernel command line options, and values set by udev control. */ - MERGE_NON_NEGATIVE(log_level, log_get_max_level()); + manager_merge_config_log_level(manager); MERGE_NON_NEGATIVE(resolve_name_timing, RESOLVE_NAME_EARLY); MERGE_NON_ZERO(exec_delay_usec, 0); MERGE_NON_ZERO(timeout_usec, DEFAULT_WORKER_TIMEOUT_USEC); @@ -310,16 +335,36 @@ void manager_set_log_level(Manager *manager, int log_level) { int old = log_get_max_level(); - log_set_max_level(log_level); - manager->config.log_level = manager->config_by_control.log_level = log_level; + manager->config_by_control.log_level = log_level; + manager_merge_config_log_level(manager); - if (log_level != old) - manager_kill_workers(manager, /* force = */ false); + if (manager->config.log_level == old) + return; + + log_set_max_level(manager->config.log_level); + manager_kill_workers(manager, /* force = */ false); +} + +void manager_set_trace(Manager *manager, bool enable) { + assert(manager); + + bool old = manager->config.trace; + + manager->config_by_control.trace = enable; + manager_merge_config_log_level(manager); + + if (manager->config.trace == old) + return; + + log_set_max_level(manager->config.log_level); + manager_kill_workers(manager, /* force = */ false); } static void manager_adjust_config(UdevConfig *config) { assert(config); + log_set_max_level(config->log_level); + if (config->timeout_usec < MIN_WORKER_TIMEOUT_USEC) { log_debug("Timeout (%s) for processing event is too small, using the default: %s", FORMAT_TIMESPAN(config->timeout_usec, 1), @@ -411,7 +456,6 @@ int manager_load(Manager *manager, int argc, char *argv[]) { if (arg_debug) log_set_target(LOG_TARGET_CONSOLE); - log_set_max_level(manager->config.log_level); manager_adjust_config(&manager->config); return 1; } @@ -424,7 +468,6 @@ UdevReloadFlags manager_reload_config(Manager *manager) { manager->config_by_udev_conf = UDEV_CONFIG_INIT; manager_parse_udev_config(&manager->config_by_udev_conf); manager_merge_config(manager); - log_set_max_level(manager->config.log_level); manager_adjust_config(&manager->config); if (manager->config.resolve_name_timing != old.resolve_name_timing) @@ -434,7 +477,8 @@ UdevReloadFlags manager_reload_config(Manager *manager) { manager->config.exec_delay_usec != old.exec_delay_usec || manager->config.timeout_usec != old.timeout_usec || manager->config.timeout_signal != old.timeout_signal || - manager->config.blockdev_read_only != old.blockdev_read_only) + manager->config.blockdev_read_only != old.blockdev_read_only || + manager->config.trace != old.trace) return UDEV_RELOAD_KILL_WORKERS; return 0; diff --git a/src/udev/udev-config.h b/src/udev/udev-config.h index 68bb1ea98cf..fd31979f6d7 100644 --- a/src/udev/udev-config.h +++ b/src/udev/udev-config.h @@ -18,6 +18,7 @@ typedef struct UdevConfig { usec_t timeout_usec; int timeout_signal; bool blockdev_read_only; + bool trace; } UdevConfig; #define UDEV_CONFIG_INIT \ @@ -28,6 +29,7 @@ typedef struct UdevConfig { void manager_set_children_max(Manager *manager, unsigned n); void manager_set_log_level(Manager *manager, int log_level); +void manager_set_trace(Manager *manager, bool enable); void manager_set_environment(Manager *manager, char * const *v); int manager_load(Manager *manager, int argc, char *argv[]); diff --git a/src/udev/udev-varlink.c b/src/udev/udev-varlink.c index 5d1a27219f5..41f7fe08f93 100644 --- a/src/udev/udev-varlink.c +++ b/src/udev/udev-varlink.c @@ -43,6 +43,26 @@ static int vl_method_set_log_level(sd_varlink *link, sd_json_variant *parameters return sd_varlink_reply(link, NULL); } +static int vl_method_set_trace(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) { + bool enable; + int r; + + static const sd_json_dispatch_field dispatch_table[] = { + { "enable", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, 0, SD_JSON_MANDATORY }, + {} + }; + + assert(link); + + r = sd_varlink_dispatch(link, parameters, dispatch_table, &enable); + if (r != 0) + return r; + + log_debug("Received io.systemd.service.SetTrace(%s)", yes_no(enable)); + manager_set_trace(userdata, enable); + return sd_varlink_reply(link, NULL); +} + static int vl_method_set_children_max(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) { unsigned n; int r; @@ -160,6 +180,7 @@ int manager_start_varlink_server(Manager *manager) { "io.systemd.service.Reload", vl_method_reload, "io.systemd.service.SetLogLevel", vl_method_set_log_level, "io.systemd.service.GetEnvironment", varlink_method_get_environment, + "io.systemd.Udev.SetTrace", vl_method_set_trace, "io.systemd.Udev.SetChildrenMax", vl_method_set_children_max, "io.systemd.Udev.SetEnvironment", vl_method_set_environment, "io.systemd.Udev.StartExecQueue", vl_method_start_stop_exec_queue, diff --git a/src/udev/udev-worker.c b/src/udev/udev-worker.c index 52b2203677a..5c7583a9ffe 100644 --- a/src/udev/udev-worker.c +++ b/src/udev/udev-worker.c @@ -182,6 +182,7 @@ static int worker_process_device(UdevWorker *worker, sd_device *dev) { udev_event = udev_event_new(dev, worker, EVENT_UDEV_WORKER); if (!udev_event) return -ENOMEM; + udev_event->trace = worker->config.trace; /* If this is a block device and the device is locked currently via the BSD advisory locks, * someone else is using it exclusively. We don't run our udev rules now to not interfere. diff --git a/src/udev/udevadm-control.c b/src/udev/udevadm-control.c index 3ccc6213570..1fa5a420182 100644 --- a/src/udev/udevadm-control.c +++ b/src/udev/udevadm-control.c @@ -10,6 +10,7 @@ #include "creds-util.h" #include "errno-util.h" +#include "parse-argument.h" #include "parse-util.h" #include "process-util.h" #include "static-destruct.h" @@ -30,6 +31,7 @@ static bool arg_exit = false; static int arg_max_children = -1; static int arg_log_level = -1; static int arg_start_exec_queue = -1; +static int arg_trace = -1; static bool arg_load_credentials = false; STATIC_DESTRUCTOR_REGISTER(arg_env, strv_freep); @@ -42,7 +44,8 @@ static bool arg_has_control_commands(void) { arg_reload || !strv_isempty(arg_env) || arg_max_children >= 0 || - arg_ping; + arg_ping || + arg_trace >= 0; } static int help(void) { @@ -58,6 +61,7 @@ static int help(void) { " -p --property=KEY=VALUE Set a global property for all events\n" " -m --children-max=N Maximum number of children\n" " --ping Wait for udev to respond to a ping message\n" + " --trace=BOOL Enable/disable trace logging\n" " -t --timeout=SECONDS Maximum time to block for a reply\n" " --load-credentials Load udev rules from credentials\n", program_invocation_short_name); @@ -68,6 +72,7 @@ static int help(void) { static int parse_argv(int argc, char *argv[]) { enum { ARG_PING = 0x100, + ARG_TRACE, ARG_LOAD_CREDENTIALS, }; @@ -83,6 +88,7 @@ static int parse_argv(int argc, char *argv[]) { { "env", required_argument, NULL, 'p' }, /* alias for -p */ { "children-max", required_argument, NULL, 'm' }, { "ping", no_argument, NULL, ARG_PING }, + { "trace", required_argument, NULL, ARG_TRACE }, { "timeout", required_argument, NULL, 't' }, { "load-credentials", no_argument, NULL, ARG_LOAD_CREDENTIALS }, { "version", no_argument, NULL, 'V' }, @@ -143,6 +149,14 @@ static int parse_argv(int argc, char *argv[]) { arg_ping = true; break; + case ARG_TRACE: + r = parse_boolean_argument("--trace=", optarg, NULL); + if (r < 0) + return r; + + arg_trace = r; + break; + case 't': r = parse_sec(optarg, &arg_timeout); if (r < 0) @@ -296,6 +310,13 @@ static int send_control_commands(void) { return r; } + if (arg_trace >= 0) { + r = varlink_callbo_and_log(link, "io.systemd.Udev.SetTrace", /* reply = */ NULL, + SD_JSON_BUILD_PAIR_BOOLEAN("enable", arg_trace)); + if (r < 0) + return r; + } + return 0; } diff --git a/test/units/TEST-17-UDEV.10.sh b/test/units/TEST-17-UDEV.10.sh index 5ee1cd3e429..8643e9e6bb4 100755 --- a/test/units/TEST-17-UDEV.10.sh +++ b/test/units/TEST-17-UDEV.10.sh @@ -52,6 +52,8 @@ udevadm control -R udevadm control -p HELLO=world udevadm control -m 42 udevadm control --ping -t 5 +udevadm control --trace yes +udevadm control --trace no udevadm control --load-credentials udevadm control -h