From 623edeade94ef18f503ccac4296629b31bdd7515 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 4 Dec 2024 02:26:02 +0900 Subject: [PATCH] udev-config: introduce UdevConfig Then, save configurations by their source: udev.conf, command line arguments, kernel command line options, and udev control. Preparation to support reloading udev.conf in a later commit. (cherry picked from commit 04fa5c1580ad388af3477ecfd7d4aa7d7f5cee30) Resolves: RHEL-75774 --- src/udev/udev-config.c | 142 ++++++++++++++++++++++++---------------- src/udev/udev-config.h | 21 +++++- src/udev/udev-event.c | 2 +- src/udev/udev-manager.c | 40 +++++------ src/udev/udev-manager.h | 13 ++-- src/udev/udev-spawn.c | 14 ++-- src/udev/udev-worker.c | 4 +- src/udev/udev-worker.h | 7 +- 8 files changed, 142 insertions(+), 101 deletions(-) diff --git a/src/udev/udev-config.c b/src/udev/udev-config.c index b774e7676e..eced080547 100644 --- a/src/udev/udev-config.c +++ b/src/udev/udev-config.c @@ -25,29 +25,20 @@ bool arg_daemonize = false; static DEFINE_CONFIG_PARSE_ENUM(config_parse_resolve_name_timing, resolve_name_timing, ResolveNameTiming); -static int manager_parse_udev_config(Manager *manager) { - int r, log_val = -1; - - assert(manager); +static void manager_parse_udev_config(UdevConfig *config) { + assert(config); const ConfigTableItem config_table[] = { - { NULL, "udev_log", config_parse_log_level, 0, &log_val }, - { NULL, "children_max", config_parse_unsigned, 0, &manager->children_max }, - { NULL, "exec_delay", config_parse_sec, 0, &manager->exec_delay_usec }, - { NULL, "event_timeout", config_parse_sec, 0, &manager->timeout_usec }, - { NULL, "resolve_names", config_parse_resolve_name_timing, 0, &manager->resolve_name_timing }, - { NULL, "timeout_signal", config_parse_signal, 0, &manager->timeout_signal }, + { NULL, "udev_log", config_parse_log_level, 0, &config->log_level }, + { NULL, "children_max", config_parse_unsigned, 0, &config->children_max }, + { NULL, "exec_delay", config_parse_sec, 0, &config->exec_delay_usec }, + { NULL, "event_timeout", config_parse_sec, 0, &config->timeout_usec }, + { NULL, "resolve_names", config_parse_resolve_name_timing, 0, &config->resolve_name_timing }, + { NULL, "timeout_signal", config_parse_signal, 0, &config->timeout_signal }, {} }; - r = udev_parse_config_full(config_table); - if (r < 0) - return r; - - if (log_val >= 0) - log_set_max_level(log_val); - - return 0; + (void) udev_parse_config_full(config_table); } /* @@ -59,7 +50,7 @@ static int manager_parse_udev_config(Manager *manager) { * udev.blockdev_read_only<=bool> mark all block devices read-only when they appear */ static int parse_proc_cmdline_item(const char *key, const char *value, void *data) { - Manager *manager = ASSERT_PTR(data); + UdevConfig *config = ASSERT_PTR(data); int r; assert(key); @@ -72,28 +63,28 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat r = log_level_from_string(value); if (r >= 0) - manager->log_level = r; + config->log_level = r; } else if (proc_cmdline_key_streq(key, "udev.event_timeout")) { if (proc_cmdline_value_missing(key, value)) return 0; - r = parse_sec(value, &manager->timeout_usec); + r = parse_sec(value, &config->timeout_usec); } else if (proc_cmdline_key_streq(key, "udev.children_max")) { if (proc_cmdline_value_missing(key, value)) return 0; - r = safe_atou(value, &manager->children_max); + r = safe_atou(value, &config->children_max); } else if (proc_cmdline_key_streq(key, "udev.exec_delay")) { if (proc_cmdline_value_missing(key, value)) return 0; - r = parse_sec(value, &manager->exec_delay_usec); + r = parse_sec(value, &config->exec_delay_usec); } else if (proc_cmdline_key_streq(key, "udev.timeout_signal")) { @@ -102,21 +93,21 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat r = signal_from_string(value); if (r > 0) - manager->timeout_signal = r; + config->timeout_signal = r; } else if (proc_cmdline_key_streq(key, "udev.blockdev_read_only")) { if (!value) - manager->blockdev_read_only = true; + config->blockdev_read_only = true; else { r = parse_boolean(value); if (r < 0) log_warning_errno(r, "Failed to parse udev.blockdev-read-only argument, ignoring: %s", value); else - manager->blockdev_read_only = r; + config->blockdev_read_only = r; } - if (manager->blockdev_read_only) + if (config->blockdev_read_only) log_notice("All physical block devices will be marked read-only."); return 0; @@ -160,7 +151,7 @@ static int help(void) { return 0; } -static int parse_argv(int argc, char *argv[], Manager *manager) { +static int parse_argv(int argc, char *argv[], UdevConfig *config) { enum { ARG_TIMEOUT_SIGNAL, }; @@ -182,7 +173,7 @@ static int parse_argv(int argc, char *argv[], Manager *manager) { assert(argc >= 0); assert(argv); - assert(manager); + assert(config); while ((c = getopt_long(argc, argv, "c:de:Dt:N:hV", options, NULL)) >= 0) { switch (c) { @@ -191,12 +182,12 @@ static int parse_argv(int argc, char *argv[], Manager *manager) { arg_daemonize = true; break; case 'c': - r = safe_atou(optarg, &manager->children_max); + r = safe_atou(optarg, &config->children_max); if (r < 0) log_warning_errno(r, "Failed to parse --children-max= value '%s', ignoring: %m", optarg); break; case 'e': - r = parse_sec(optarg, &manager->exec_delay_usec); + r = parse_sec(optarg, &config->exec_delay_usec); if (r < 0) log_warning_errno(r, "Failed to parse --exec-delay= value '%s', ignoring: %m", optarg); break; @@ -205,16 +196,17 @@ static int parse_argv(int argc, char *argv[], Manager *manager) { if (r <= 0) log_warning_errno(r, "Failed to parse --timeout-signal= value '%s', ignoring: %m", optarg); else - manager->timeout_signal = r; + config->timeout_signal = r; break; case 't': - r = parse_sec(optarg, &manager->timeout_usec); + r = parse_sec(optarg, &config->timeout_usec); if (r < 0) log_warning_errno(r, "Failed to parse --event-timeout= value '%s', ignoring: %m", optarg); break; case 'D': arg_debug = true; + config->log_level = LOG_DEBUG; break; case 'N': { ResolveNameTiming t; @@ -223,7 +215,7 @@ static int parse_argv(int argc, char *argv[], Manager *manager) { if (t < 0) log_warning("Invalid --resolve-names= value '%s', ignoring.", optarg); else - manager->resolve_name_timing = t; + config->resolve_name_timing = t; break; } case 'h': @@ -242,13 +234,50 @@ static int parse_argv(int argc, char *argv[], Manager *manager) { return 1; } -void manager_set_default_children_max(Manager *manager) { +#define MERGE_NON_NEGATIVE(name, default_value) \ + manager->config.name = \ + manager->config_by_control.name >= 0 ? manager->config_by_control.name : \ + manager->config_by_kernel.name >= 0 ? manager->config_by_kernel.name : \ + manager->config_by_command.name >= 0 ? manager->config_by_command.name : \ + manager->config_by_udev_conf.name >= 0 ? manager->config_by_udev_conf.name : \ + default_value; + +#define MERGE_NON_ZERO(name, default_value) \ + manager->config.name = \ + manager->config_by_control.name ?: \ + manager->config_by_kernel.name ?: \ + manager->config_by_command.name ?: \ + manager->config_by_udev_conf.name ?: \ + default_value; + +#define MERGE_BOOL(name) \ + manager->config.name = \ + manager->config_by_control.name || \ + manager->config_by_kernel.name || \ + manager->config_by_command.name || \ + manager->config_by_udev_conf.name; + +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()); + 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); + MERGE_NON_ZERO(timeout_signal, SIGKILL); + MERGE_BOOL(blockdev_read_only); +} + +void udev_config_set_default_children_max(UdevConfig *config) { uint64_t cpu_limit, mem_limit, cpu_count = 1; int r; - assert(manager); + assert(config); - if (manager->children_max != 0) + if (config->children_max != 0) return; r = cpus_in_affinity_mask(); @@ -260,30 +289,30 @@ void manager_set_default_children_max(Manager *manager) { cpu_limit = cpu_count * 2 + 16; mem_limit = MAX(physical_memory() / (128*1024*1024), UINT64_C(10)); - manager->children_max = MIN3(cpu_limit, mem_limit, WORKER_NUM_MAX); - log_debug("Set children_max to %u", manager->children_max); + config->children_max = MIN3(cpu_limit, mem_limit, WORKER_NUM_MAX); + log_debug("Set children_max to %u", config->children_max); } -static void manager_adjust_config(Manager *manager) { - assert(manager); +static void manager_adjust_config(UdevConfig *config) { + assert(config); - if (manager->timeout_usec < MIN_WORKER_TIMEOUT_USEC) { + if (config->timeout_usec < MIN_WORKER_TIMEOUT_USEC) { log_debug("Timeout (%s) for processing event is too small, using the default: %s", - FORMAT_TIMESPAN(manager->timeout_usec, 1), + FORMAT_TIMESPAN(config->timeout_usec, 1), FORMAT_TIMESPAN(DEFAULT_WORKER_TIMEOUT_USEC, 1)); - manager->timeout_usec = DEFAULT_WORKER_TIMEOUT_USEC; + config->timeout_usec = DEFAULT_WORKER_TIMEOUT_USEC; } - if (manager->exec_delay_usec >= manager->timeout_usec) { + if (config->exec_delay_usec >= config->timeout_usec) { log_debug("Delay (%s) for executing RUN= commands is too large compared with the timeout (%s) for event execution, ignoring the delay.", - FORMAT_TIMESPAN(manager->exec_delay_usec, 1), - FORMAT_TIMESPAN(manager->timeout_usec, 1)); + FORMAT_TIMESPAN(config->exec_delay_usec, 1), + FORMAT_TIMESPAN(config->timeout_usec, 1)); - manager->exec_delay_usec = 0; + config->exec_delay_usec = 0; } - manager_set_default_children_max(manager); + udev_config_set_default_children_max(config); } int manager_load(Manager *manager, int argc, char *argv[]) { @@ -291,21 +320,22 @@ int manager_load(Manager *manager, int argc, char *argv[]) { assert(manager); - manager_parse_udev_config(manager); + manager_parse_udev_config(&manager->config_by_udev_conf); - r = parse_argv(argc, argv, manager); + r = parse_argv(argc, argv, &manager->config_by_command); if (r <= 0) return r; - r = proc_cmdline_parse(parse_proc_cmdline_item, manager, PROC_CMDLINE_STRIP_RD_PREFIX); + r = proc_cmdline_parse(parse_proc_cmdline_item, &manager->config_by_kernel, PROC_CMDLINE_STRIP_RD_PREFIX); if (r < 0) log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); - if (arg_debug) { + manager_merge_config(manager); + + if (arg_debug) log_set_target(LOG_TARGET_CONSOLE); - log_set_max_level(LOG_DEBUG); - } - manager_adjust_config(manager); + log_set_max_level(manager->config.log_level); + manager_adjust_config(&manager->config); return 1; } diff --git a/src/udev/udev-config.h b/src/udev/udev-config.h index 9a8a18821f..3b0997eeb0 100644 --- a/src/udev/udev-config.h +++ b/src/udev/udev-config.h @@ -3,9 +3,28 @@ #include +#include "time-util.h" +#include "udev-def.h" + extern bool arg_daemonize; typedef struct Manager Manager; +typedef struct UdevConfig { + int log_level; + ResolveNameTiming resolve_name_timing; + unsigned children_max; + usec_t exec_delay_usec; + usec_t timeout_usec; + int timeout_signal; + bool blockdev_read_only; +} UdevConfig; + +#define UDEV_CONFIG_INIT \ + (UdevConfig) { \ + .log_level = -1, \ + .resolve_name_timing = _RESOLVE_NAME_TIMING_INVALID, \ + } + int manager_load(Manager *manager, int argc, char *argv[]); -void manager_set_default_children_max(Manager *manager); +void udev_config_set_default_children_max(UdevConfig *c); diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c index 6a7c34b162..e3661f5bf8 100644 --- a/src/udev/udev-event.c +++ b/src/udev/udev-event.c @@ -19,7 +19,7 @@ #include "user-util.h" UdevEvent* udev_event_new(sd_device *dev, UdevWorker *worker, EventMode mode) { - int log_level = worker ? worker->log_level : log_get_max_level(); + int log_level = worker ? worker->config.log_level : log_get_max_level(); UdevEvent *event; assert(dev); diff --git a/src/udev/udev-manager.c b/src/udev/udev-manager.c index 23a57dbed3..7f7079bcd2 100644 --- a/src/udev/udev-manager.c +++ b/src/udev/udev-manager.c @@ -240,7 +240,7 @@ static void notify_ready(Manager *manager) { r = sd_notifyf(/* unset= */ false, "READY=1\n" - "STATUS=Processing with %u children at max", manager->children_max); + "STATUS=Processing with %u children at max", manager->config.children_max); if (r < 0) log_warning_errno(r, "Failed to send readiness notification, ignoring: %m"); } @@ -278,7 +278,7 @@ static void manager_reload(Manager *manager, bool force) { udev_builtin_exit(); udev_builtin_init(); - r = udev_rules_load(&rules, manager->resolve_name_timing); + r = udev_rules_load(&rules, manager->config.resolve_name_timing); if (r < 0) log_warning_errno(r, "Failed to read udev rules, using the previously loaded rules, ignoring: %m"); else @@ -303,7 +303,7 @@ static int on_event_timeout(sd_event_source *s, uint64_t usec, void *userdata) { assert(event->manager); assert(event->worker); - kill_and_sigcont(event->worker->pid, event->manager->timeout_signal); + kill_and_sigcont(event->worker->pid, event->manager->config.timeout_signal); event->worker->state = WORKER_KILLED; log_device_error(event->dev, "Worker ["PID_FMT"] processing SEQNUM=%"PRIu64" killed", event->worker->pid, event->seqnum); @@ -363,7 +363,7 @@ static void worker_attach_event(Worker *worker, Event *event) { event->worker = worker; (void) sd_event_add_time_relative(e, &event->timeout_warning_event, CLOCK_MONOTONIC, - udev_warn_timeout(manager->timeout_usec), USEC_PER_SEC, + udev_warn_timeout(manager->config.timeout_usec), USEC_PER_SEC, on_event_timeout_warning, event); /* Manager.timeout_usec is also used as the timeout for running programs specified in @@ -371,7 +371,7 @@ static void worker_attach_event(Worker *worker, Event *event) { * kills a worker, to make it possible that the worker detects timed out of spawned programs, * kills them, and finalizes the event. */ (void) sd_event_add_time_relative(e, &event->timeout_event, CLOCK_MONOTONIC, - usec_add(manager->timeout_usec, extra_timeout_usec()), USEC_PER_SEC, + usec_add(manager->config.timeout_usec, extra_timeout_usec()), USEC_PER_SEC, on_event_timeout, event); } @@ -405,11 +405,7 @@ static int worker_spawn(Manager *manager, Event *event) { .rules = TAKE_PTR(manager->rules), .pipe_fd = TAKE_FD(manager->worker_watch[WRITE_END]), .inotify_fd = TAKE_FD(manager->inotify_fd), - .exec_delay_usec = manager->exec_delay_usec, - .timeout_usec = manager->timeout_usec, - .timeout_signal = manager->timeout_signal, - .log_level = manager->log_level, - .blockdev_read_only = manager->blockdev_read_only, + .config = manager->config, }; /* Worker process */ @@ -458,10 +454,10 @@ static int event_run(Event *event) { return 1; /* event is now processing. */ } - if (hashmap_size(manager->workers) >= manager->children_max) { + if (hashmap_size(manager->workers) >= manager->config.children_max) { /* Avoid spamming the debug logs if the limit is already reached and * many events still need to be processed */ - if (log_children_max_reached && manager->children_max > 1) { + if (log_children_max_reached && manager->config.children_max > 1) { log_debug("Maximum number (%u) of children reached.", hashmap_size(manager->workers)); log_children_max_reached = false; } @@ -863,7 +859,7 @@ static int on_ctrl_msg(UdevCtrl *uctrl, UdevCtrlMessageType type, const UdevCtrl break; log_set_max_level(value->intval); - manager->log_level = value->intval; + manager->config.log_level = manager->config_by_control.log_level = value->intval; manager_kill_workers(manager, false); break; case UDEV_CTRL_STOP_EXEC_QUEUE: @@ -934,10 +930,11 @@ static int on_ctrl_msg(UdevCtrl *uctrl, UdevCtrlMessageType type, const UdevCtrl } log_debug("Received udev control message (SET_MAX_CHILDREN), setting children_max=%i", value->intval); - manager->children_max = value->intval; + manager->config_by_control.children_max = value->intval; /* When 0 is specified, determine the maximum based on the system resources. */ - manager_set_default_children_max(manager); + udev_config_set_default_children_max(&manager->config_by_control); + manager->config.children_max = manager->config_by_control.children_max; notify_ready(manager); break; @@ -1174,10 +1171,11 @@ Manager* manager_new(void) { *manager = (Manager) { .inotify_fd = -EBADF, .worker_watch = EBADF_PAIR, - .log_level = LOG_INFO, - .resolve_name_timing = RESOLVE_NAME_EARLY, - .timeout_usec = DEFAULT_WORKER_TIMEOUT_USEC, - .timeout_signal = SIGKILL, + .config_by_udev_conf = UDEV_CONFIG_INIT, + .config_by_command = UDEV_CONFIG_INIT, + .config_by_kernel = UDEV_CONFIG_INIT, + .config_by_control = UDEV_CONFIG_INIT, + .config = UDEV_CONFIG_INIT, }; return manager; @@ -1258,8 +1256,6 @@ int manager_init(Manager *manager) { (void) sd_device_monitor_set_description(manager->monitor, "manager"); - manager->log_level = log_get_max_level(); - r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &cgroup); if (r < 0) log_debug_errno(r, "Failed to get cgroup, ignoring: %m"); @@ -1365,7 +1361,7 @@ int manager_main(Manager *manager) { udev_builtin_init(); - r = udev_rules_load(&manager->rules, manager->resolve_name_timing); + r = udev_rules_load(&manager->rules, manager->config.resolve_name_timing); if (r < 0) return log_error_errno(r, "Failed to read udev rules: %m"); diff --git a/src/udev/udev-manager.h b/src/udev/udev-manager.h index 14b458bcdb..13c7242ea8 100644 --- a/src/udev/udev-manager.h +++ b/src/udev/udev-manager.h @@ -9,6 +9,7 @@ #include "hashmap.h" #include "macro.h" #include "time-util.h" +#include "udev-config.h" #include "udev-ctrl.h" #include "udev-def.h" @@ -21,7 +22,6 @@ typedef struct Manager { Hashmap *workers; LIST_HEAD(Event, events); char *cgroup; - int log_level; UdevRules *rules; Hashmap *properties; @@ -38,12 +38,11 @@ typedef struct Manager { usec_t last_usec; - ResolveNameTiming resolve_name_timing; - unsigned children_max; - usec_t exec_delay_usec; - usec_t timeout_usec; - int timeout_signal; - bool blockdev_read_only; + UdevConfig config_by_udev_conf; + UdevConfig config_by_command; + UdevConfig config_by_kernel; + UdevConfig config_by_control; + UdevConfig config; bool stop_exec_queue; bool exit; diff --git a/src/udev/udev-spawn.c b/src/udev/udev-spawn.c index b95141cf21..2d0f6455a5 100644 --- a/src/udev/udev-spawn.c +++ b/src/udev/udev-spawn.c @@ -240,8 +240,8 @@ int udev_event_spawn( return 0; } - int timeout_signal = event->worker ? event->worker->timeout_signal : SIGKILL; - usec_t timeout_usec = event->worker ? event->worker->timeout_usec : DEFAULT_WORKER_TIMEOUT_USEC; + int timeout_signal = event->worker ? event->worker->config.timeout_signal : SIGKILL; + usec_t timeout_usec = event->worker ? event->worker->config.timeout_usec : DEFAULT_WORKER_TIMEOUT_USEC; usec_t now_usec = now(CLOCK_MONOTONIC); usec_t age_usec = usec_sub_unsigned(now_usec, event->birth_usec); usec_t cmd_timeout_usec = usec_sub_unsigned(timeout_usec, age_usec); @@ -349,20 +349,20 @@ void udev_event_execute_run(UdevEvent *event) { if (r < 0) log_device_debug_errno(event->dev, r, "Failed to run built-in command \"%s\", ignoring: %m", command); } else { - if (event->worker && event->worker->exec_delay_usec > 0) { + if (event->worker && event->worker->config.exec_delay_usec > 0) { usec_t now_usec = now(CLOCK_MONOTONIC); usec_t age_usec = usec_sub_unsigned(now_usec, event->birth_usec); - if (event->worker->exec_delay_usec >= usec_sub_unsigned(event->worker->timeout_usec, age_usec)) { + if (event->worker->config.exec_delay_usec >= usec_sub_unsigned(event->worker->config.timeout_usec, age_usec)) { log_device_warning(event->dev, "Cannot delay execution of \"%s\" for %s, skipping.", - command, FORMAT_TIMESPAN(event->worker->exec_delay_usec, USEC_PER_SEC)); + command, FORMAT_TIMESPAN(event->worker->config.exec_delay_usec, USEC_PER_SEC)); continue; } log_device_debug(event->dev, "Delaying execution of \"%s\" for %s.", - command, FORMAT_TIMESPAN(event->worker->exec_delay_usec, USEC_PER_SEC)); - (void) usleep_safe(event->worker->exec_delay_usec); + command, FORMAT_TIMESPAN(event->worker->config.exec_delay_usec, USEC_PER_SEC)); + (void) usleep_safe(event->worker->config.exec_delay_usec); } log_device_debug(event->dev, "Running command \"%s\"", command); diff --git a/src/udev/udev-worker.c b/src/udev/udev-worker.c index 44287e4774..fc9072e5fd 100644 --- a/src/udev/udev-worker.c +++ b/src/udev/udev-worker.c @@ -195,7 +195,7 @@ static int worker_process_device(UdevWorker *worker, sd_device *dev) { if (r < 0) return r; - if (worker->blockdev_read_only) + if (worker->config.blockdev_read_only) (void) worker_mark_block_device_read_only(dev); /* Disable watch during event processing. */ @@ -322,7 +322,7 @@ static int worker_device_monitor_handler(sd_device_monitor *monitor, sd_device * log_device_warning_errno(dev, r, "Failed to send signal to main daemon, ignoring: %m"); /* Reset the log level, as it might be changed by "OPTIONS=log_level=". */ - log_set_max_level(worker->log_level); + log_set_max_level(worker->config.log_level); return 1; } diff --git a/src/udev/udev-worker.h b/src/udev/udev-worker.h index e9aefc5b04..d9dd88d472 100644 --- a/src/udev/udev-worker.h +++ b/src/udev/udev-worker.h @@ -10,6 +10,7 @@ #include "errno-list.h" #include "hashmap.h" #include "time-util.h" +#include "udev-config.h" #define DEFAULT_WORKER_TIMEOUT_USEC (3 * USEC_PER_MINUTE) #define MIN_WORKER_TIMEOUT_USEC (1 * USEC_PER_MSEC) @@ -27,11 +28,7 @@ typedef struct UdevWorker { int pipe_fd; int inotify_fd; /* Do not close! */ - usec_t exec_delay_usec; - usec_t timeout_usec; - int timeout_signal; - int log_level; - bool blockdev_read_only; + UdevConfig config; } UdevWorker; /* passed from worker to main process */