From ce2b557c15680ec67accc5242d34dc651c2be3e7 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 4 Dec 2024 02:33:47 +0900 Subject: [PATCH] udev: move parsers for config file, kerenel command line, and positional arguments to udev-config.c No functional change, just refactoring and preparation for later commits. (cherry picked from commit 394a678aec3b8bba0f0b1a8d7b9427c62468fe68) Resolves: RHEL-75774 --- src/udev/meson.build | 1 + src/udev/udev-config.c | 311 ++++++++++++++++++++++++++++++++++++++++ src/udev/udev-config.h | 11 ++ src/udev/udev-manager.c | 49 +------ src/udev/udev-manager.h | 1 - src/udev/udevd.c | 250 +------------------------------- 6 files changed, 326 insertions(+), 297 deletions(-) create mode 100644 src/udev/udev-config.c create mode 100644 src/udev/udev-config.h diff --git a/src/udev/meson.build b/src/udev/meson.build index 921dfac39c..d7acbae6bb 100644 --- a/src/udev/meson.build +++ b/src/udev/meson.build @@ -19,6 +19,7 @@ udevadm_sources = files( libudevd_core_sources = files( 'net/link-config.c', + 'udev-config.c', 'udev-ctrl.c', 'udev-event.c', 'udev-format.c', diff --git a/src/udev/udev-config.c b/src/udev/udev-config.c new file mode 100644 index 0000000000..b774e7676e --- /dev/null +++ b/src/udev/udev-config.c @@ -0,0 +1,311 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include +#include + +#include "conf-parser.h" +#include "cpu-set-util.h" +#include "limits-util.h" +#include "parse-util.h" +#include "pretty-print.h" +#include "proc-cmdline.h" +#include "signal-util.h" +#include "syslog-util.h" +#include "udev-config.h" +#include "udev-manager.h" +#include "udev-rules.h" +#include "udev-util.h" +#include "udev-worker.h" +#include "version.h" + +#define WORKER_NUM_MAX UINT64_C(2048) + +static bool arg_debug = false; +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); + + 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 }, + {} + }; + + r = udev_parse_config_full(config_table); + if (r < 0) + return r; + + if (log_val >= 0) + log_set_max_level(log_val); + + return 0; +} + +/* + * read the kernel command line, in case we need to get into debug mode + * udev.log_level= syslog priority + * udev.children_max= events are fully serialized if set to 1 + * udev.exec_delay= delay execution of every executed program + * udev.event_timeout= seconds to wait before terminating an event + * 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); + int r; + + assert(key); + + if (proc_cmdline_key_streq(key, "udev.log_level") || + proc_cmdline_key_streq(key, "udev.log_priority")) { /* kept for backward compatibility */ + + if (proc_cmdline_value_missing(key, value)) + return 0; + + r = log_level_from_string(value); + if (r >= 0) + manager->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); + + } 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); + + } 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); + + } else if (proc_cmdline_key_streq(key, "udev.timeout_signal")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; + + r = signal_from_string(value); + if (r > 0) + manager->timeout_signal = r; + + } else if (proc_cmdline_key_streq(key, "udev.blockdev_read_only")) { + + if (!value) + manager->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; + } + + if (manager->blockdev_read_only) + log_notice("All physical block devices will be marked read-only."); + + return 0; + + } else { + if (startswith(key, "udev.")) + log_warning("Unknown udev kernel command line option \"%s\", ignoring.", key); + + return 0; + } + + if (r < 0) + log_warning_errno(r, "Failed to parse \"%s=%s\", ignoring: %m", key, value); + + return 0; +} + +static int help(void) { + _cleanup_free_ char *link = NULL; + int r; + + r = terminal_urlify_man("systemd-udevd.service", "8", &link); + if (r < 0) + return log_oom(); + + printf("%s [OPTIONS...]\n\n" + "Rule-based manager for device events and files.\n\n" + " -h --help Print this message\n" + " -V --version Print version of the program\n" + " -d --daemon Detach and run in the background\n" + " -D --debug Enable debug output\n" + " -c --children-max=INT Set maximum number of workers\n" + " -e --exec-delay=SECONDS Seconds to wait before executing RUN=\n" + " -t --event-timeout=SECONDS Seconds to wait before terminating an event\n" + " -N --resolve-names=early|late|never\n" + " When to resolve users and groups\n" + "\nSee the %s for details.\n", + program_invocation_short_name, + link); + + return 0; +} + +static int parse_argv(int argc, char *argv[], Manager *manager) { + enum { + ARG_TIMEOUT_SIGNAL, + }; + + static const struct option options[] = { + { "daemon", no_argument, NULL, 'd' }, + { "debug", no_argument, NULL, 'D' }, + { "children-max", required_argument, NULL, 'c' }, + { "exec-delay", required_argument, NULL, 'e' }, + { "event-timeout", required_argument, NULL, 't' }, + { "resolve-names", required_argument, NULL, 'N' }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + { "timeout-signal", required_argument, NULL, ARG_TIMEOUT_SIGNAL }, + {} + }; + + int c, r; + + assert(argc >= 0); + assert(argv); + assert(manager); + + while ((c = getopt_long(argc, argv, "c:de:Dt:N:hV", options, NULL)) >= 0) { + switch (c) { + + case 'd': + arg_daemonize = true; + break; + case 'c': + r = safe_atou(optarg, &manager->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); + if (r < 0) + log_warning_errno(r, "Failed to parse --exec-delay= value '%s', ignoring: %m", optarg); + break; + case ARG_TIMEOUT_SIGNAL: + r = signal_from_string(optarg); + if (r <= 0) + log_warning_errno(r, "Failed to parse --timeout-signal= value '%s', ignoring: %m", optarg); + else + manager->timeout_signal = r; + + break; + case 't': + r = parse_sec(optarg, &manager->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; + break; + case 'N': { + ResolveNameTiming t; + + t = resolve_name_timing_from_string(optarg); + if (t < 0) + log_warning("Invalid --resolve-names= value '%s', ignoring.", optarg); + else + manager->resolve_name_timing = t; + break; + } + case 'h': + return help(); + case 'V': + printf("%s\n", GIT_VERSION); + return 0; + case '?': + return -EINVAL; + default: + assert_not_reached(); + + } + } + + return 1; +} + +void manager_set_default_children_max(Manager *manager) { + uint64_t cpu_limit, mem_limit, cpu_count = 1; + int r; + + assert(manager); + + if (manager->children_max != 0) + return; + + r = cpus_in_affinity_mask(); + if (r < 0) + log_warning_errno(r, "Failed to determine number of local CPUs, ignoring: %m"); + else + cpu_count = r; + + 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); +} + +static void manager_adjust_config(Manager *manager) { + assert(manager); + + if (manager->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(DEFAULT_WORKER_TIMEOUT_USEC, 1)); + + manager->timeout_usec = DEFAULT_WORKER_TIMEOUT_USEC; + } + + if (manager->exec_delay_usec >= manager->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)); + + manager->exec_delay_usec = 0; + } + + manager_set_default_children_max(manager); +} + +int manager_load(Manager *manager, int argc, char *argv[]) { + int r; + + assert(manager); + + manager_parse_udev_config(manager); + + r = parse_argv(argc, argv, manager); + if (r <= 0) + return r; + + r = proc_cmdline_parse(parse_proc_cmdline_item, manager, PROC_CMDLINE_STRIP_RD_PREFIX); + if (r < 0) + log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); + + if (arg_debug) { + log_set_target(LOG_TARGET_CONSOLE); + log_set_max_level(LOG_DEBUG); + } + + manager_adjust_config(manager); + return 1; +} diff --git a/src/udev/udev-config.h b/src/udev/udev-config.h new file mode 100644 index 0000000000..9a8a18821f --- /dev/null +++ b/src/udev/udev-config.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#pragma once + +#include + +extern bool arg_daemonize; + +typedef struct Manager Manager; + +int manager_load(Manager *manager, int argc, char *argv[]); +void manager_set_default_children_max(Manager *manager); diff --git a/src/udev/udev-manager.c b/src/udev/udev-manager.c index 6e1935a731..23a57dbed3 100644 --- a/src/udev/udev-manager.c +++ b/src/udev/udev-manager.c @@ -3,7 +3,6 @@ #include "blockdev-util.h" #include "cgroup-util.h" #include "common-signal.h" -#include "cpu-set-util.h" #include "daemon-util.h" #include "device-monitor-private.h" #include "device-private.h" @@ -15,7 +14,6 @@ #include "hashmap.h" #include "inotify-util.h" #include "iovec-util.h" -#include "limits-util.h" #include "list.h" #include "mkdir.h" #include "process-util.h" @@ -25,6 +23,7 @@ #include "string-util.h" #include "syslog-util.h" #include "udev-builtin.h" +#include "udev-config.h" #include "udev-ctrl.h" #include "udev-event.h" #include "udev-manager.h" @@ -36,8 +35,6 @@ #include "udev-watch.h" #include "udev-worker.h" -#define WORKER_NUM_MAX UINT64_C(2048) - #define EVENT_RETRY_INTERVAL_USEC (200 * USEC_PER_MSEC) #define EVENT_RETRY_TIMEOUT_USEC (3 * USEC_PER_MINUTE) @@ -845,28 +842,6 @@ static int on_worker(sd_event_source *s, int fd, uint32_t revents, void *userdat return 1; } -static void manager_set_default_children_max(Manager *manager) { - uint64_t cpu_limit, mem_limit, cpu_count = 1; - int r; - - assert(manager); - - if (manager->children_max != 0) - return; - - r = cpus_in_affinity_mask(); - if (r < 0) - log_warning_errno(r, "Failed to determine number of local CPUs, ignoring: %m"); - else - cpu_count = r; - - 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); -} - /* receive the udevd message from userspace */ static int on_ctrl_msg(UdevCtrl *uctrl, UdevCtrlMessageType type, const UdevCtrlMessageValue *value, void *userdata) { Manager *manager = ASSERT_PTR(userdata); @@ -1208,26 +1183,6 @@ Manager* manager_new(void) { return manager; } -void manager_adjust_arguments(Manager *manager) { - assert(manager); - - if (manager->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(DEFAULT_WORKER_TIMEOUT_USEC, 1)); - - manager->timeout_usec = DEFAULT_WORKER_TIMEOUT_USEC; - } - - if (manager->exec_delay_usec >= manager->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)); - - manager->exec_delay_usec = 0; - } -} - static int listen_fds(int *ret_ctrl, int *ret_netlink) { _cleanup_strv_free_ char **names = NULL; _cleanup_close_ int ctrl_fd = -EBADF, netlink_fd = -EBADF; @@ -1319,8 +1274,6 @@ int manager_init(Manager *manager) { int manager_main(Manager *manager) { int fd_worker, r; - manager_set_default_children_max(manager); - /* unnamed socket from workers to the main daemon */ r = socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, manager->worker_watch); if (r < 0) diff --git a/src/udev/udev-manager.h b/src/udev/udev-manager.h index 7c20e29594..14b458bcdb 100644 --- a/src/udev/udev-manager.h +++ b/src/udev/udev-manager.h @@ -53,7 +53,6 @@ Manager* manager_new(void); Manager* manager_free(Manager *manager); DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free); -void manager_adjust_arguments(Manager *manager); int manager_init(Manager *manager); int manager_main(Manager *manager); diff --git a/src/udev/udevd.c b/src/udev/udevd.c index ef1c07a2ca..018a3cd6e7 100644 --- a/src/udev/udevd.c +++ b/src/udev/udevd.c @@ -5,250 +5,17 @@ * Copyright © 2009 Scott James Remnant */ -#include -#include - -#include "conf-parser.h" -#include "env-file.h" #include "errno-util.h" #include "fd-util.h" #include "mkdir.h" -#include "parse-util.h" -#include "pretty-print.h" -#include "proc-cmdline.h" #include "process-util.h" #include "rlimit-util.h" #include "selinux-util.h" -#include "signal-util.h" -#include "syslog-util.h" +#include "udev-config.h" #include "udev-manager.h" -#include "udev-rules.h" -#include "udev-util.h" #include "udevd.h" #include "version.h" -static bool arg_debug = false; -static int 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); - - 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 }, - {} - }; - - r = udev_parse_config_full(config_table); - if (r < 0) - return r; - - if (log_val >= 0) - log_set_max_level(log_val); - - return 0; -} - -/* - * read the kernel command line, in case we need to get into debug mode - * udev.log_level= syslog priority - * udev.children_max= events are fully serialized if set to 1 - * udev.exec_delay= delay execution of every executed program - * udev.event_timeout= seconds to wait before terminating an event - * 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); - int r; - - assert(key); - - if (proc_cmdline_key_streq(key, "udev.log_level") || - proc_cmdline_key_streq(key, "udev.log_priority")) { /* kept for backward compatibility */ - - if (proc_cmdline_value_missing(key, value)) - return 0; - - r = log_level_from_string(value); - if (r >= 0) - log_set_max_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); - - } 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); - - } 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); - - } else if (proc_cmdline_key_streq(key, "udev.timeout_signal")) { - - if (proc_cmdline_value_missing(key, value)) - return 0; - - r = signal_from_string(value); - if (r > 0) - manager->timeout_signal = r; - - } else if (proc_cmdline_key_streq(key, "udev.blockdev_read_only")) { - - if (!value) - manager->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; - } - - if (manager->blockdev_read_only) - log_notice("All physical block devices will be marked read-only."); - - return 0; - - } else { - if (startswith(key, "udev.")) - log_warning("Unknown udev kernel command line option \"%s\", ignoring.", key); - - return 0; - } - - if (r < 0) - log_warning_errno(r, "Failed to parse \"%s=%s\", ignoring: %m", key, value); - - return 0; -} - -static int help(void) { - _cleanup_free_ char *link = NULL; - int r; - - r = terminal_urlify_man("systemd-udevd.service", "8", &link); - if (r < 0) - return log_oom(); - - printf("%s [OPTIONS...]\n\n" - "Rule-based manager for device events and files.\n\n" - " -h --help Print this message\n" - " -V --version Print version of the program\n" - " -d --daemon Detach and run in the background\n" - " -D --debug Enable debug output\n" - " -c --children-max=INT Set maximum number of workers\n" - " -e --exec-delay=SECONDS Seconds to wait before executing RUN=\n" - " -t --event-timeout=SECONDS Seconds to wait before terminating an event\n" - " -N --resolve-names=early|late|never\n" - " When to resolve users and groups\n" - "\nSee the %s for details.\n", - program_invocation_short_name, - link); - - return 0; -} - -static int parse_argv(int argc, char *argv[], Manager *manager) { - enum { - ARG_TIMEOUT_SIGNAL, - }; - - static const struct option options[] = { - { "daemon", no_argument, NULL, 'd' }, - { "debug", no_argument, NULL, 'D' }, - { "children-max", required_argument, NULL, 'c' }, - { "exec-delay", required_argument, NULL, 'e' }, - { "event-timeout", required_argument, NULL, 't' }, - { "resolve-names", required_argument, NULL, 'N' }, - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, 'V' }, - { "timeout-signal", required_argument, NULL, ARG_TIMEOUT_SIGNAL }, - {} - }; - - int c, r; - - assert(argc >= 0); - assert(argv); - assert(manager); - - while ((c = getopt_long(argc, argv, "c:de:Dt:N:hV", options, NULL)) >= 0) { - switch (c) { - - case 'd': - arg_daemonize = true; - break; - case 'c': - r = safe_atou(optarg, &manager->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); - if (r < 0) - log_warning_errno(r, "Failed to parse --exec-delay= value '%s', ignoring: %m", optarg); - break; - case ARG_TIMEOUT_SIGNAL: - r = signal_from_string(optarg); - if (r <= 0) - log_warning_errno(r, "Failed to parse --timeout-signal= value '%s', ignoring: %m", optarg); - else - manager->timeout_signal = r; - - break; - case 't': - r = parse_sec(optarg, &manager->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; - break; - case 'N': { - ResolveNameTiming t; - - t = resolve_name_timing_from_string(optarg); - if (t < 0) - log_warning("Invalid --resolve-names= value '%s', ignoring.", optarg); - else - manager->resolve_name_timing = t; - break; - } - case 'h': - return help(); - case 'V': - printf("%s\n", GIT_VERSION); - return 0; - case '?': - return -EINVAL; - default: - assert_not_reached(); - - } - } - - return 1; -} - int run_udevd(int argc, char *argv[]) { _cleanup_(manager_freep) Manager *manager = NULL; int r; @@ -259,23 +26,10 @@ int run_udevd(int argc, char *argv[]) { if (!manager) return log_oom(); - manager_parse_udev_config(manager); - - r = parse_argv(argc, argv, manager); + r = manager_load(manager, argc, argv); if (r <= 0) return r; - r = proc_cmdline_parse(parse_proc_cmdline_item, manager, PROC_CMDLINE_STRIP_RD_PREFIX); - if (r < 0) - log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); - - if (arg_debug) { - log_set_target(LOG_TARGET_CONSOLE); - log_set_max_level(LOG_DEBUG); - } - - manager_adjust_arguments(manager); - r = must_be_root(); if (r < 0) return r;