From 854789543b5332776051a5a709e467ce4c22bbd3 Mon Sep 17 00:00:00 2001 From: David Tardon Date: Fri, 26 Sep 2025 15:25:02 +0200 Subject: [PATCH 01/14] export: use table to parse --format= --- src/import/export.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/import/export.c b/src/import/export.c index b3edc1a787e..d891ca18168 100644 --- a/src/import/export.c +++ b/src/import/export.c @@ -249,17 +249,8 @@ static int parse_argv(int argc, char *argv[]) { return version(); case ARG_FORMAT: - if (streq(optarg, "uncompressed")) - arg_compress = IMPORT_COMPRESS_UNCOMPRESSED; - else if (streq(optarg, "xz")) - arg_compress = IMPORT_COMPRESS_XZ; - else if (streq(optarg, "gzip")) - arg_compress = IMPORT_COMPRESS_GZIP; - else if (streq(optarg, "bzip2")) - arg_compress = IMPORT_COMPRESS_BZIP2; - else if (streq(optarg, "zstd")) - arg_compress = IMPORT_COMPRESS_ZSTD; - else + arg_compress = import_compress_type_from_string(optarg); + if (arg_compress < 0 || arg_compress == IMPORT_COMPRESS_UNKNOWN) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown format: %s", optarg); break; From 3a21f6aa6e44b59fa539837ce85eda458cc2ad33 Mon Sep 17 00:00:00 2001 From: David Tardon Date: Fri, 26 Sep 2025 10:59:31 +0200 Subject: [PATCH 02/14] cgtop: use table to parse --order= --- src/cgtop/cgtop.c | 52 ++++++++++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c index 9b56af7bebd..60d550c5190 100644 --- a/src/cgtop/cgtop.c +++ b/src/cgtop/cgtop.c @@ -20,6 +20,7 @@ #include "process-util.h" #include "procfs-util.h" #include "sort-util.h" +#include "string-table.h" #include "terminal-util.h" #include "time-util.h" #include "virt.h" @@ -54,6 +55,16 @@ typedef enum PidsCount { COUNT_PIDS, /* most, requires pids controller */ } PidsCount; +typedef enum { + ORDER_PATH, + ORDER_TASKS, + ORDER_CPU, + ORDER_MEMORY, + ORDER_IO, + _ORDER_MAX, + _ORDER_INVALID = -EINVAL, +} Order; + static unsigned arg_depth = 3; static unsigned arg_iterations = UINT_MAX; static bool arg_batch = false; @@ -63,22 +74,24 @@ static char* arg_machine = NULL; static char* arg_root = NULL; static bool arg_recursive = true; static bool arg_recursive_unset = false; - static PidsCount arg_count = COUNT_PIDS; - -static enum { - ORDER_PATH, - ORDER_TASKS, - ORDER_CPU, - ORDER_MEMORY, - ORDER_IO, -} arg_order = ORDER_CPU; +static Order arg_order = ORDER_CPU; static enum { CPU_PERCENT, CPU_TIME, } arg_cpu_type = CPU_PERCENT; +static const char *order_table[_ORDER_MAX] = { + [ORDER_PATH] = "path", + [ORDER_TASKS] = "tasks", + [ORDER_CPU] = "cpu", + [ORDER_MEMORY] = "memory", + [ORDER_IO] = "io", +}; + +DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(order, Order); + static Group *group_free(Group *g) { if (!g) return NULL; @@ -557,6 +570,12 @@ static int group_compare(Group * const *a, Group * const *b) { return -1; else if (y->io_valid) return 1; + + break; + + case _ORDER_MAX: + case _ORDER_INVALID: + assert_not_reached(); } return path_compare(x->path, y->path); @@ -810,18 +829,9 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_ORDER: - if (streq(optarg, "path")) - arg_order = ORDER_PATH; - else if (streq(optarg, "tasks")) - arg_order = ORDER_TASKS; - else if (streq(optarg, "cpu")) - arg_order = ORDER_CPU; - else if (streq(optarg, "memory")) - arg_order = ORDER_MEMORY; - else if (streq(optarg, "io")) - arg_order = ORDER_IO; - else - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + arg_order = order_from_string(optarg); + if (arg_order < 0) + return log_error_errno(arg_order, "Invalid argument to --order=: %s", optarg); break; From 048d813fcdc6d410fb20b0c858705e3db5755631 Mon Sep 17 00:00:00 2001 From: David Tardon Date: Fri, 26 Sep 2025 11:05:19 +0200 Subject: [PATCH 03/14] cgtop: use table to parse --cpu= --- src/cgtop/cgtop.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c index 60d550c5190..7e06ee12558 100644 --- a/src/cgtop/cgtop.c +++ b/src/cgtop/cgtop.c @@ -65,6 +65,13 @@ typedef enum { _ORDER_INVALID = -EINVAL, } Order; +typedef enum { + CPU_PERCENT, + CPU_TIME, + _CPU_MAX, + _CPU_INVALID = -EINVAL, +} CPUType; + static unsigned arg_depth = 3; static unsigned arg_iterations = UINT_MAX; static bool arg_batch = false; @@ -76,11 +83,7 @@ static bool arg_recursive = true; static bool arg_recursive_unset = false; static PidsCount arg_count = COUNT_PIDS; static Order arg_order = ORDER_CPU; - -static enum { - CPU_PERCENT, - CPU_TIME, -} arg_cpu_type = CPU_PERCENT; +static CPUType arg_cpu_type = CPU_PERCENT; static const char *order_table[_ORDER_MAX] = { [ORDER_PATH] = "path", @@ -92,6 +95,13 @@ static const char *order_table[_ORDER_MAX] = { DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(order, Order); +static const char *cpu_type_table[_CPU_MAX] = { + [CPU_PERCENT] = "percentage", + [CPU_TIME] = "time", +}; + +DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(cpu_type, CPUType); + static Group *group_free(Group *g) { if (!g) return NULL; @@ -758,12 +768,9 @@ static int parse_argv(int argc, char *argv[]) { case ARG_CPU_TYPE: if (optarg) { - if (streq(optarg, "time")) - arg_cpu_type = CPU_TIME; - else if (streq(optarg, "percentage")) - arg_cpu_type = CPU_PERCENT; - else - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + arg_cpu_type = cpu_type_from_string(optarg); + if (arg_cpu_type < 0) + return log_error_errno(arg_cpu_type, "Unknown argument to --cpu=: %s", optarg); } else From 9b3041d9fe3ffd66a28e73afeca5a7920aad9b07 Mon Sep 17 00:00:00 2001 From: David Tardon Date: Thu, 2 Oct 2025 16:13:20 +0200 Subject: [PATCH 04/14] cgtop: rename enum value to match its string repr. --- src/cgtop/cgtop.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c index 7e06ee12558..19ad6f68a9a 100644 --- a/src/cgtop/cgtop.c +++ b/src/cgtop/cgtop.c @@ -66,7 +66,7 @@ typedef enum { } Order; typedef enum { - CPU_PERCENT, + CPU_PERCENTAGE, CPU_TIME, _CPU_MAX, _CPU_INVALID = -EINVAL, @@ -83,7 +83,7 @@ static bool arg_recursive = true; static bool arg_recursive_unset = false; static PidsCount arg_count = COUNT_PIDS; static Order arg_order = ORDER_CPU; -static CPUType arg_cpu_type = CPU_PERCENT; +static CPUType arg_cpu_type = CPU_PERCENTAGE; static const char *order_table[_ORDER_MAX] = { [ORDER_PATH] = "path", @@ -96,8 +96,8 @@ static const char *order_table[_ORDER_MAX] = { DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(order, Order); static const char *cpu_type_table[_CPU_MAX] = { - [CPU_PERCENT] = "percentage", - [CPU_TIME] = "time", + [CPU_PERCENTAGE] = "percentage", + [CPU_TIME] = "time", }; DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(cpu_type, CPUType); @@ -530,7 +530,7 @@ static int group_compare(Group * const *a, Group * const *b) { break; case ORDER_CPU: - if (arg_cpu_type == CPU_PERCENT) { + if (arg_cpu_type == CPU_PERCENTAGE) { if (x->cpu_valid && y->cpu_valid) { r = CMP(y->cpu_fraction, x->cpu_fraction); if (r != 0) @@ -624,7 +624,7 @@ static void display(Hashmap *a) { if (on_tty()) { const char *on, *off; - int cpu_len = arg_cpu_type == CPU_PERCENT ? 6 : maxtcpu; + int cpu_len = arg_cpu_type == CPU_PERCENTAGE ? 6 : maxtcpu; path_columns = columns() - 36 - cpu_len; if (path_columns < 10) @@ -642,7 +642,7 @@ static void display(Hashmap *a) { arg_order == ORDER_TASKS ? off : "", arg_order == ORDER_CPU ? on : "", cpu_len, - arg_cpu_type == CPU_PERCENT ? "%CPU" : "CPU Time", + arg_cpu_type == CPU_PERCENTAGE ? "%CPU" : "CPU Time", arg_order == ORDER_CPU ? off : "", arg_order == ORDER_MEMORY ? on : "", "Memory", arg_order == ORDER_MEMORY ? off : "", @@ -672,7 +672,7 @@ static void display(Hashmap *a) { else fputs(" -", stdout); - if (arg_cpu_type == CPU_PERCENT) { + if (arg_cpu_type == CPU_PERCENTAGE) { if (g->cpu_valid) printf(" %6.1f", g->cpu_fraction*100); else @@ -976,7 +976,7 @@ static int loop(const char *root) { break; case '%': - arg_cpu_type = arg_cpu_type == CPU_TIME ? CPU_PERCENT : CPU_TIME; + arg_cpu_type = arg_cpu_type == CPU_TIME ? CPU_PERCENTAGE : CPU_TIME; break; case 'k': From 9033f923f9997c793a566b768ea10868991fd3eb Mon Sep 17 00:00:00 2001 From: David Tardon Date: Fri, 26 Sep 2025 15:17:12 +0200 Subject: [PATCH 05/14] homectl: use table to parse --export-format= --- src/home/homectl.c | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/src/home/homectl.c b/src/home/homectl.c index 6c83765766b..a73d94744ad 100644 --- a/src/home/homectl.c +++ b/src/home/homectl.c @@ -56,6 +56,7 @@ #include "rlimit-util.h" #include "runtime-scope.h" #include "stat-util.h" +#include "string-table.h" #include "string-util.h" #include "strv.h" #include "terminal-util.h" @@ -69,6 +70,14 @@ #include "userdb.h" #include "verbs.h" +typedef enum { + EXPORT_FORMAT_FULL, /* export the full record */ + EXPORT_FORMAT_STRIPPED, /* strip "state" + "binding", but leave signature in place */ + EXPORT_FORMAT_MINIMAL, /* also strip signature */ + _EXPORT_FORMAT_MAX, + _EXPORT_FORMAT_INVALID = -EINVAL, +} ExportFormat; + static PagerFlags arg_pager_flags = 0; static bool arg_legend = true; static bool arg_ask_password = true; @@ -97,11 +106,7 @@ static bool arg_recovery_key = false; static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF; static bool arg_and_resize = false; static bool arg_and_change_password = false; -static enum { - EXPORT_FORMAT_FULL, /* export the full record */ - EXPORT_FORMAT_STRIPPED, /* strip "state" + "binding", but leave signature in place */ - EXPORT_FORMAT_MINIMAL, /* also strip signature */ -} arg_export_format = EXPORT_FORMAT_FULL; +static ExportFormat arg_export_format = EXPORT_FORMAT_FULL; static uint64_t arg_capability_bounding_set = UINT64_MAX; static uint64_t arg_capability_ambient_set = UINT64_MAX; static char *arg_blob_dir = NULL; @@ -131,6 +136,14 @@ STATIC_DESTRUCTOR_REGISTER(arg_key_name, freep); static const BusLocator *bus_mgr; +static const char *export_format_table[_EXPORT_FORMAT_MAX] = { + [EXPORT_FORMAT_FULL] = "full", + [EXPORT_FORMAT_STRIPPED] = "stripped", + [EXPORT_FORMAT_MINIMAL] = "minimal", +}; + +DEFINE_PRIVATE_STRING_TABLE_LOOKUP(export_format, ExportFormat); + static bool identity_properties_specified(void) { return arg_identity || @@ -4732,18 +4745,12 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_EXPORT_FORMAT: - if (streq(optarg, "full")) - arg_export_format = EXPORT_FORMAT_FULL; - else if (streq(optarg, "stripped")) - arg_export_format = EXPORT_FORMAT_STRIPPED; - else if (streq(optarg, "minimal")) - arg_export_format = EXPORT_FORMAT_MINIMAL; - else if (streq(optarg, "help")) { - puts("full\n" - "stripped\n" - "minimal"); - return 0; - } + if (streq(optarg, "help")) + return DUMP_STRING_TABLE(export_format, ExportFormat, _EXPORT_FORMAT_MAX); + + arg_export_format = export_format_from_string(optarg); + if (arg_export_format < 0) + return log_error_errno(arg_export_format, "Invalid export format: %s", optarg); break; From 292928e964d785e5bdf68b924dfdfe323f800a92 Mon Sep 17 00:00:00 2001 From: David Tardon Date: Fri, 26 Sep 2025 15:47:30 +0200 Subject: [PATCH 06/14] vpick-tool: use table to parse --print= --- src/vpick/vpick-tool.c | 48 ++++++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/src/vpick/vpick-tool.c b/src/vpick/vpick-tool.c index c58bcb9d3c4..2ed0762de20 100644 --- a/src/vpick/vpick-tool.c +++ b/src/vpick/vpick-tool.c @@ -13,16 +13,12 @@ #include "path-util.h" #include "pretty-print.h" #include "stat-util.h" +#include "string-table.h" #include "string-util.h" #include "strv.h" #include "vpick.h" -static char *arg_filter_basename = NULL; -static char *arg_filter_version = NULL; -static Architecture arg_filter_architecture = _ARCHITECTURE_INVALID; -static char *arg_filter_suffix = NULL; -static uint32_t arg_filter_type_mask = 0; -static enum { +typedef enum { PRINT_PATH, PRINT_FILENAME, PRINT_VERSION, @@ -30,14 +26,34 @@ static enum { PRINT_ARCHITECTURE, PRINT_TRIES, PRINT_ALL, + _PRINT_MAX, _PRINT_INVALID = -EINVAL, -} arg_print = _PRINT_INVALID; +} Print; + +static char *arg_filter_basename = NULL; +static char *arg_filter_version = NULL; +static Architecture arg_filter_architecture = _ARCHITECTURE_INVALID; +static char *arg_filter_suffix = NULL; +static uint32_t arg_filter_type_mask = 0; +static Print arg_print = _PRINT_INVALID; static PickFlags arg_flags = PICK_ARCHITECTURE|PICK_TRIES; STATIC_DESTRUCTOR_REGISTER(arg_filter_basename, freep); STATIC_DESTRUCTOR_REGISTER(arg_filter_version, freep); STATIC_DESTRUCTOR_REGISTER(arg_filter_suffix, freep); +static const char *print_table[_PRINT_MAX] = { + [PRINT_PATH] = "path", + [PRINT_FILENAME] = "filename", + [PRINT_VERSION] = "version", + [PRINT_TYPE] = "type", + [PRINT_ARCHITECTURE] = "architecture", + [PRINT_TRIES] = "tries", + [PRINT_ALL] = "all", +}; + +DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(print, Print); + static int help(void) { _cleanup_free_ char *link = NULL; int r; @@ -173,22 +189,12 @@ static int parse_argv(int argc, char *argv[]) { break; case 'p': - if (streq(optarg, "path")) - arg_print = PRINT_PATH; - else if (streq(optarg, "filename")) - arg_print = PRINT_FILENAME; - else if (streq(optarg, "version")) - arg_print = PRINT_VERSION; - else if (streq(optarg, "type")) - arg_print = PRINT_TYPE; - else if (STR_IN_SET(optarg, "arch", "architecture")) + if (streq(optarg, "arch")) /* accept abbreviation too */ arg_print = PRINT_ARCHITECTURE; - else if (streq(optarg, "tries")) - arg_print = PRINT_TRIES; - else if (streq(optarg, "all")) - arg_print = PRINT_ALL; else - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown --print= argument: %s", optarg); + arg_print = print_from_string(optarg); + if (arg_print < 0) + return log_error_errno(arg_print, "Unknown --print= argument: %s", optarg); break; From a5fada5d5da0152143161762a35c7e53b5ff1947 Mon Sep 17 00:00:00 2001 From: David Tardon Date: Thu, 2 Oct 2025 15:57:10 +0200 Subject: [PATCH 07/14] userdbctl: require value for --output= Currently, when --output= is used with empty string, it resets the mode. E.g., # userdbctl user --output=json --output= ... will use the default output mode, not JSON. But that functionality is not documented and it seems to be of little practical use. Let's just drop it. --- src/userdb/userdbctl.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/userdb/userdbctl.c b/src/userdb/userdbctl.c index 9959869e7af..da3901a07eb 100644 --- a/src/userdb/userdbctl.c +++ b/src/userdb/userdbctl.c @@ -1652,9 +1652,7 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_OUTPUT: - if (isempty(optarg)) - arg_output = _OUTPUT_INVALID; - else if (streq(optarg, "classic")) + if (streq(optarg, "classic")) arg_output = OUTPUT_CLASSIC; else if (streq(optarg, "friendly")) arg_output = OUTPUT_FRIENDLY; From f29f6b6b7d749fc6f56a0659ffcf23e54f73a1e7 Mon Sep 17 00:00:00 2001 From: David Tardon Date: Fri, 26 Sep 2025 16:11:17 +0200 Subject: [PATCH 08/14] userdbctl: use table to parse --output= --- src/userdb/userdbctl.c | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/userdb/userdbctl.c b/src/userdb/userdbctl.c index da3901a07eb..4fbf8f671cd 100644 --- a/src/userdb/userdbctl.c +++ b/src/userdb/userdbctl.c @@ -26,6 +26,7 @@ #include "pretty-print.h" #include "recurse-dir.h" #include "socket-util.h" +#include "string-table.h" #include "string-util.h" #include "strv.h" #include "uid-classification.h" @@ -37,14 +38,16 @@ #include "verbs.h" #include "virt.h" -static enum { +typedef enum { OUTPUT_CLASSIC, OUTPUT_TABLE, OUTPUT_FRIENDLY, OUTPUT_JSON, + _OUTPUT_MAX, _OUTPUT_INVALID = -EINVAL, -} arg_output = _OUTPUT_INVALID; +} Output; +static Output arg_output = _OUTPUT_INVALID; static PagerFlags arg_pager_flags = 0; static bool arg_legend = true; static char** arg_services = NULL; @@ -61,6 +64,15 @@ static sd_json_variant *arg_from_file = NULL; STATIC_DESTRUCTOR_REGISTER(arg_services, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_from_file, sd_json_variant_unrefp); +static const char *output_table[_OUTPUT_MAX] = { + [OUTPUT_CLASSIC] = "classic", + [OUTPUT_TABLE] = "table", + [OUTPUT_FRIENDLY] = "friendly", + [OUTPUT_JSON] = "json", +}; + +DEFINE_PRIVATE_STRING_TABLE_LOOKUP(output, Output); + static const char *user_disposition_to_color(UserDisposition d) { assert(d >= 0); assert(d < _USER_DISPOSITION_MAX); @@ -1652,22 +1664,12 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_OUTPUT: - if (streq(optarg, "classic")) - arg_output = OUTPUT_CLASSIC; - else if (streq(optarg, "friendly")) - arg_output = OUTPUT_FRIENDLY; - else if (streq(optarg, "json")) - arg_output = OUTPUT_JSON; - else if (streq(optarg, "table")) - arg_output = OUTPUT_TABLE; - else if (streq(optarg, "help")) { - puts("classic\n" - "friendly\n" - "json\n" - "table"); - return 0; - } else - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid --output= mode: %s", optarg); + if (streq(optarg, "help")) + return DUMP_STRING_TABLE(output, Output, _OUTPUT_MAX); + + arg_output = output_from_string(optarg); + if (arg_output < 0) + return log_error_errno(arg_output, "Invalid --output= mode: %s", optarg); arg_json_format_flags = arg_output == OUTPUT_JSON ? SD_JSON_FORMAT_PRETTY|SD_JSON_FORMAT_COLOR_AUTO : SD_JSON_FORMAT_OFF; break; From 1da7dcf427f08ae3017dd0503bdc365083081bfc Mon Sep 17 00:00:00 2001 From: David Tardon Date: Fri, 26 Sep 2025 14:58:57 +0200 Subject: [PATCH 09/14] main: extract common code to a function --- src/core/main.c | 44 ++++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/src/core/main.c b/src/core/main.c index 4fc870d6c0e..0f363d5c8fa 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -255,6 +255,22 @@ static int console_setup(void) { return 0; } +static int parse_timeout(const char *value, usec_t *ret) { + int r = 0; + + assert(value); + assert(ret); + + if (streq(value, "default")) + *ret = USEC_INFINITY; + else if (streq(value, "off")) + *ret = 0; + else + r = parse_sec(value, ret); + + return r; +} + static int parse_proc_cmdline_item(const char *key, const char *value, void *data) { int r; @@ -456,16 +472,10 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat if (proc_cmdline_value_missing(key, value)) return 0; - if (streq(value, "default")) - arg_runtime_watchdog = USEC_INFINITY; - else if (streq(value, "off")) - arg_runtime_watchdog = 0; - else { - r = parse_sec(value, &arg_runtime_watchdog); - if (r < 0) { - log_warning_errno(r, "Failed to parse systemd.watchdog_sec= argument '%s', ignoring: %m", value); - return 0; - } + r = parse_timeout(value, &arg_runtime_watchdog); + if (r < 0) { + log_warning_errno(r, "Failed to parse systemd.watchdog_sec= argument '%s', ignoring: %m", value); + return 0; } arg_kexec_watchdog = arg_reboot_watchdog = arg_runtime_watchdog; @@ -475,16 +485,10 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat if (proc_cmdline_value_missing(key, value)) return 0; - if (streq(value, "default")) - arg_pretimeout_watchdog = USEC_INFINITY; - else if (streq(value, "off")) - arg_pretimeout_watchdog = 0; - else { - r = parse_sec(value, &arg_pretimeout_watchdog); - if (r < 0) { - log_warning_errno(r, "Failed to parse systemd.watchdog_pre_sec= argument '%s', ignoring: %m", value); - return 0; - } + r = parse_timeout(value, &arg_pretimeout_watchdog); + if (r < 0) { + log_warning_errno(r, "Failed to parse systemd.watchdog_pre_sec= argument '%s', ignoring: %m", value); + return 0; } } else if (proc_cmdline_key_streq(key, "systemd.watchdog_pretimeout_governor")) { From 25f1473c81fbf5311091e5e7fd800a85ab3e2243 Mon Sep 17 00:00:00 2001 From: David Tardon Date: Tue, 30 Sep 2025 09:56:09 +0200 Subject: [PATCH 10/14] nspawn: use table to parse --console= --- src/nspawn/nspawn-settings.c | 9 +++++++++ src/nspawn/nspawn-settings.h | 3 +++ src/nspawn/nspawn.c | 34 ++++++++++++---------------------- 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/src/nspawn/nspawn-settings.c b/src/nspawn/nspawn-settings.c index e4f4f5ebf11..47c1a30f1f2 100644 --- a/src/nspawn/nspawn-settings.c +++ b/src/nspawn/nspawn-settings.c @@ -914,6 +914,15 @@ static const char *const timezone_mode_table[_TIMEZONE_MODE_MAX] = { DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(timezone_mode, TimezoneMode, TIMEZONE_AUTO); +static const char *const console_mode_table[_CONSOLE_MODE_MAX] = { + [CONSOLE_INTERACTIVE] = "interactive", + [CONSOLE_READ_ONLY] = "read-only", + [CONSOLE_PASSIVE] = "passive", + [CONSOLE_PIPE] = "pipe", +}; + +DEFINE_STRING_TABLE_LOOKUP(console_mode, ConsoleMode); + DEFINE_CONFIG_PARSE_ENUM(config_parse_userns_ownership, user_namespace_ownership, UserNamespaceOwnership); static const char *const user_namespace_ownership_table[_USER_NAMESPACE_OWNERSHIP_MAX] = { diff --git a/src/nspawn/nspawn-settings.h b/src/nspawn/nspawn-settings.h index 84631e6558c..874e168664f 100644 --- a/src/nspawn/nspawn-settings.h +++ b/src/nspawn/nspawn-settings.h @@ -282,6 +282,9 @@ ResolvConfMode resolv_conf_mode_from_string(const char *s) _pure_; const char* timezone_mode_to_string(TimezoneMode a) _const_; TimezoneMode timezone_mode_from_string(const char *s) _pure_; +const char* console_mode_to_string(ConsoleMode m) _const_; +ConsoleMode console_mode_from_string(const char *s) _pure_; + const char* user_namespace_ownership_to_string(UserNamespaceOwnership a) _const_; UserNamespaceOwnership user_namespace_ownership_from_string(const char *s) _pure_; diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 6bcb0a06a71..c980b1f5631 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -288,35 +288,19 @@ STATIC_DESTRUCTOR_REGISTER(arg_background, freep); static int handle_arg_console(const char *arg) { if (streq(arg, "help")) { - puts("autopipe\n" - "interactive\n" - "passive\n" - "pipe\n" - "read-only"); - return 0; + puts("autopipe\n"); + return DUMP_STRING_TABLE(console_mode, ConsoleMode, _CONSOLE_MODE_MAX); } - if (streq(arg, "interactive")) - arg_console_mode = CONSOLE_INTERACTIVE; - else if (streq(arg, "read-only")) - arg_console_mode = CONSOLE_READ_ONLY; - else if (streq(arg, "passive")) - arg_console_mode = CONSOLE_PASSIVE; - else if (streq(arg, "pipe")) { - if (isatty_safe(STDIN_FILENO) && isatty_safe(STDOUT_FILENO)) - log_full(arg_quiet ? LOG_DEBUG : LOG_NOTICE, - "Console mode 'pipe' selected, but standard input/output are connected to an interactive TTY. " - "Most likely you want to use 'interactive' console mode for proper interactivity and shell job control. " - "Proceeding anyway."); - - arg_console_mode = CONSOLE_PIPE; - } else if (streq(arg, "autopipe")) { + if (streq(arg, "autopipe")) { if (isatty_safe(STDIN_FILENO) && isatty_safe(STDOUT_FILENO)) arg_console_mode = CONSOLE_INTERACTIVE; else arg_console_mode = CONSOLE_PIPE; } else - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown console mode: %s", optarg); + arg_console_mode = console_mode_from_string(optarg); + if (arg_console_mode < 0) + return log_error_errno(arg_console_mode, "Unknown console mode: %s", optarg); arg_settings_mask |= SETTING_CONSOLE_MODE; return 1; @@ -1738,6 +1722,12 @@ static int verify_arguments(void) { if (r < 0) return r; + if (arg_console_mode == CONSOLE_PIPE && isatty_safe(STDIN_FILENO) && isatty_safe(STDOUT_FILENO)) + log_full(arg_quiet ? LOG_DEBUG : LOG_NOTICE, + "Console mode 'pipe' selected, but standard input/output are connected to an interactive TTY. " + "Most likely you want to use 'interactive' console mode for proper interactivity and shell job control. " + "Proceeding anyway."); + return 0; } From 99df84ef23ac46fad32b90961474911b56884fd1 Mon Sep 17 00:00:00 2001 From: David Tardon Date: Wed, 1 Oct 2025 19:07:53 +0200 Subject: [PATCH 11/14] nspawn: postpone selection of console mode Moving it away from argument parsing code allows to simplify that. --- src/nspawn/nspawn-settings.c | 1 + src/nspawn/nspawn-settings.h | 1 + src/nspawn/nspawn.c | 19 +++++++++---------- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/nspawn/nspawn-settings.c b/src/nspawn/nspawn-settings.c index 47c1a30f1f2..c058ab28f71 100644 --- a/src/nspawn/nspawn-settings.c +++ b/src/nspawn/nspawn-settings.c @@ -915,6 +915,7 @@ static const char *const timezone_mode_table[_TIMEZONE_MODE_MAX] = { DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(timezone_mode, TimezoneMode, TIMEZONE_AUTO); static const char *const console_mode_table[_CONSOLE_MODE_MAX] = { + [CONSOLE_AUTOPIPE] = "autopipe", [CONSOLE_INTERACTIVE] = "interactive", [CONSOLE_READ_ONLY] = "read-only", [CONSOLE_PASSIVE] = "passive", diff --git a/src/nspawn/nspawn-settings.h b/src/nspawn/nspawn-settings.h index 874e168664f..5b36c2ad473 100644 --- a/src/nspawn/nspawn-settings.h +++ b/src/nspawn/nspawn-settings.h @@ -82,6 +82,7 @@ typedef enum TimezoneMode { } TimezoneMode; typedef enum ConsoleMode { + CONSOLE_AUTOPIPE, CONSOLE_INTERACTIVE, CONSOLE_READ_ONLY, CONSOLE_PASSIVE, diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index c980b1f5631..25af8520e23 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -287,18 +287,10 @@ STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep); STATIC_DESTRUCTOR_REGISTER(arg_background, freep); static int handle_arg_console(const char *arg) { - if (streq(arg, "help")) { - puts("autopipe\n"); + if (streq(arg, "help")) return DUMP_STRING_TABLE(console_mode, ConsoleMode, _CONSOLE_MODE_MAX); - } - if (streq(arg, "autopipe")) { - if (isatty_safe(STDIN_FILENO) && isatty_safe(STDOUT_FILENO)) - arg_console_mode = CONSOLE_INTERACTIVE; - else - arg_console_mode = CONSOLE_PIPE; - } else - arg_console_mode = console_mode_from_string(optarg); + arg_console_mode = console_mode_from_string(optarg); if (arg_console_mode < 0) return log_error_errno(arg_console_mode, "Unknown console mode: %s", optarg); @@ -5973,6 +5965,13 @@ static int run(int argc, char *argv[]) { arg_console_mode = isatty_safe(STDIN_FILENO) && isatty_safe(STDOUT_FILENO) ? CONSOLE_INTERACTIVE : CONSOLE_READ_ONLY; + if (arg_console_mode == CONSOLE_AUTOPIPE) { + if (isatty_safe(STDIN_FILENO) && isatty_safe(STDOUT_FILENO)) + arg_console_mode = CONSOLE_INTERACTIVE; + else + arg_console_mode = CONSOLE_PIPE; + } + if (arg_console_mode == CONSOLE_PIPE) /* if we pass STDERR on to the container, don't add our own logs into it too */ arg_quiet = true; From 65c4347d6e8cb3818e8ccf26af76f460196f5b62 Mon Sep 17 00:00:00 2001 From: David Tardon Date: Wed, 1 Oct 2025 19:24:05 +0200 Subject: [PATCH 12/14] nspawn: set arguments directly There's no point in going through handle_arg_console() now that all the checks have been moved out of it. --- src/nspawn/nspawn.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 25af8520e23..0b01dbc450e 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -1478,9 +1478,8 @@ static int parse_argv(int argc, char *argv[]) { case 'P': case ARG_PIPE: - r = handle_arg_console("pipe"); - if (r <= 0) - return r; + arg_console_mode = CONSOLE_PIPE; + arg_settings_mask |= SETTING_CONSOLE_MODE; break; case ARG_NO_PAGER: From 365399c4e4c7ee8ade764dfe479946764d6b5f4d Mon Sep 17 00:00:00 2001 From: David Tardon Date: Wed, 1 Oct 2025 19:27:23 +0200 Subject: [PATCH 13/14] nspawn: inline handle_arg_console() to its only call place --- src/nspawn/nspawn.c | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 0b01dbc450e..c13077c328c 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -286,18 +286,6 @@ STATIC_DESTRUCTOR_REGISTER(arg_settings_filename, freep); STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep); STATIC_DESTRUCTOR_REGISTER(arg_background, freep); -static int handle_arg_console(const char *arg) { - if (streq(arg, "help")) - return DUMP_STRING_TABLE(console_mode, ConsoleMode, _CONSOLE_MODE_MAX); - - arg_console_mode = console_mode_from_string(optarg); - if (arg_console_mode < 0) - return log_error_errno(arg_console_mode, "Unknown console mode: %s", optarg); - - arg_settings_mask |= SETTING_CONSOLE_MODE; - return 1; -} - static int help(void) { _cleanup_free_ char *link = NULL; int r; @@ -1471,9 +1459,15 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_CONSOLE: - r = handle_arg_console(optarg); - if (r <= 0) - return r; + if (streq(optarg, "help")) + return DUMP_STRING_TABLE(console_mode, ConsoleMode, _CONSOLE_MODE_MAX); + + arg_console_mode = console_mode_from_string(optarg); + if (arg_console_mode < 0) + return log_error_errno(arg_console_mode, "Unknown console mode: %s", optarg); + + arg_settings_mask |= SETTING_CONSOLE_MODE; + break; case 'P': From 188049d38cf06744073c1224d8b3d7a338b0dc35 Mon Sep 17 00:00:00 2001 From: David Tardon Date: Wed, 1 Oct 2025 19:31:17 +0200 Subject: [PATCH 14/14] tree-wide: combine DUMP_STRING_TABLE() and return --- src/analyze/analyze.c | 7 +++---- src/detect-virt/detect-virt.c | 6 ++---- src/import/importctl.c | 6 ++---- src/journal/journalctl.c | 6 ++---- src/login/loginctl.c | 6 ++---- src/machine/machinectl.c | 12 ++++-------- src/nspawn/nspawn.c | 37 ++++++++++++----------------------- src/shared/parse-argument.c | 6 ++---- src/sysext/sysext.c | 3 +-- src/systemctl/systemctl.c | 18 ++++++----------- 10 files changed, 37 insertions(+), 70 deletions(-) diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c index d76c09b5200..fc85388f309 100644 --- a/src/analyze/analyze.c +++ b/src/analyze/analyze.c @@ -472,10 +472,9 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_RECURSIVE_ERRORS: - if (streq(optarg, "help")) { - DUMP_STRING_TABLE(recursive_errors, RecursiveErrors, _RECURSIVE_ERRORS_MAX); - return 0; - } + if (streq(optarg, "help")) + return DUMP_STRING_TABLE(recursive_errors, RecursiveErrors, _RECURSIVE_ERRORS_MAX); + r = recursive_errors_from_string(optarg); if (r < 0) return log_error_errno(r, "Unknown mode passed to --recursive-errors='%s'.", optarg); diff --git a/src/detect-virt/detect-virt.c b/src/detect-virt/detect-virt.c index 3513f0b3b18..d28e3024805 100644 --- a/src/detect-virt/detect-virt.c +++ b/src/detect-virt/detect-virt.c @@ -109,16 +109,14 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_LIST: - DUMP_STRING_TABLE(virtualization, Virtualization, _VIRTUALIZATION_MAX); - return 0; + return DUMP_STRING_TABLE(virtualization, Virtualization, _VIRTUALIZATION_MAX); case ARG_CVM: arg_mode = ONLY_CVM; return 1; case ARG_LIST_CVM: - DUMP_STRING_TABLE(confidential_virtualization, ConfidentialVirtualization, _CONFIDENTIAL_VIRTUALIZATION_MAX); - return 0; + return DUMP_STRING_TABLE(confidential_virtualization, ConfidentialVirtualization, _CONFIDENTIAL_VIRTUALIZATION_MAX); case '?': return -EINVAL; diff --git a/src/import/importctl.c b/src/import/importctl.c index e5f004e1572..94140469c4b 100644 --- a/src/import/importctl.c +++ b/src/import/importctl.c @@ -1127,10 +1127,8 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_VERIFY: - if (streq(optarg, "help")) { - DUMP_STRING_TABLE(import_verify, ImportVerify, _IMPORT_VERIFY_MAX); - return 0; - } + if (streq(optarg, "help")) + return DUMP_STRING_TABLE(import_verify, ImportVerify, _IMPORT_VERIFY_MAX); r = import_verify_from_string(optarg); if (r < 0) diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 93c8d0dde6d..8c5fd2aed25 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -476,10 +476,8 @@ static int parse_argv(int argc, char *argv[]) { break; case 'o': - if (streq(optarg, "help")) { - DUMP_STRING_TABLE(output_mode, OutputMode, _OUTPUT_MODE_MAX); - return 0; - } + if (streq(optarg, "help")) + return DUMP_STRING_TABLE(output_mode, OutputMode, _OUTPUT_MODE_MAX); arg_output = output_mode_from_string(optarg); if (arg_output < 0) diff --git a/src/login/loginctl.c b/src/login/loginctl.c index 2c3394cc2cd..c86f694c2ba 100644 --- a/src/login/loginctl.c +++ b/src/login/loginctl.c @@ -1600,10 +1600,8 @@ static int parse_argv(int argc, char *argv[]) { break; case 'o': - if (streq(optarg, "help")) { - DUMP_STRING_TABLE(output_mode, OutputMode, _OUTPUT_MODE_MAX); - return 0; - } + if (streq(optarg, "help")) + return DUMP_STRING_TABLE(output_mode, OutputMode, _OUTPUT_MODE_MAX); arg_output = output_mode_from_string(optarg); if (arg_output < 0) diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index c64b8b5bbae..487a50813ef 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -2295,10 +2295,8 @@ static int parse_argv(int argc, char *argv[]) { break; case 'o': - if (streq(optarg, "help")) { - DUMP_STRING_TABLE(output_mode, OutputMode, _OUTPUT_MODE_MAX); - return 0; - } + if (streq(optarg, "help")) + return DUMP_STRING_TABLE(output_mode, OutputMode, _OUTPUT_MODE_MAX); r = output_mode_from_string(optarg); if (r < 0) @@ -2354,10 +2352,8 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_VERIFY: - if (streq(optarg, "help")) { - DUMP_STRING_TABLE(import_verify, ImportVerify, _IMPORT_VERIFY_MAX); - return 0; - } + if (streq(optarg, "help")) + return DUMP_STRING_TABLE(import_verify, ImportVerify, _IMPORT_VERIFY_MAX); r = import_verify_from_string(optarg); if (r < 0) diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index c13077c328c..1f057c904a4 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -1097,10 +1097,9 @@ static int parse_argv(int argc, char *argv[]) { if (!optarg) arg_volatile_mode = VOLATILE_YES; - else if (streq(optarg, "help")) { - DUMP_STRING_TABLE(volatile_mode, VolatileMode, _VOLATILE_MODE_MAX); - return 0; - } else { + else if (streq(optarg, "help")) + return DUMP_STRING_TABLE(volatile_mode, VolatileMode, _VOLATILE_MODE_MAX); + else { VolatileMode m; m = volatile_mode_from_string(optarg); @@ -1202,10 +1201,8 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_PRIVATE_USERS_OWNERSHIP: - if (streq(optarg, "help")) { - DUMP_STRING_TABLE(user_namespace_ownership, UserNamespaceOwnership, _USER_NAMESPACE_OWNERSHIP_MAX); - return 0; - } + if (streq(optarg, "help")) + return DUMP_STRING_TABLE(user_namespace_ownership, UserNamespaceOwnership, _USER_NAMESPACE_OWNERSHIP_MAX); arg_userns_ownership = user_namespace_ownership_from_string(optarg); if (arg_userns_ownership < 0) @@ -1215,10 +1212,8 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_KILL_SIGNAL: - if (streq(optarg, "help")) { - DUMP_STRING_TABLE(signal, int, _NSIG); - return 0; - } + if (streq(optarg, "help")) + return DUMP_STRING_TABLE(signal, int, _NSIG); arg_kill_signal = signal_from_string(optarg); if (arg_kill_signal < 0) @@ -1377,10 +1372,8 @@ static int parse_argv(int argc, char *argv[]) { _cleanup_free_ char *name = NULL; int rl; - if (streq(optarg, "help")) { - DUMP_STRING_TABLE(rlimit, int, _RLIMIT_MAX); - return 0; - } + if (streq(optarg, "help")) + return DUMP_STRING_TABLE(rlimit, int, _RLIMIT_MAX); eq = strchr(optarg, '='); if (!eq) @@ -1431,10 +1424,8 @@ static int parse_argv(int argc, char *argv[]) { } case ARG_RESOLV_CONF: - if (streq(optarg, "help")) { - DUMP_STRING_TABLE(resolv_conf_mode, ResolvConfMode, _RESOLV_CONF_MODE_MAX); - return 0; - } + if (streq(optarg, "help")) + return DUMP_STRING_TABLE(resolv_conf_mode, ResolvConfMode, _RESOLV_CONF_MODE_MAX); arg_resolv_conf = resolv_conf_mode_from_string(optarg); if (arg_resolv_conf < 0) @@ -1445,10 +1436,8 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_TIMEZONE: - if (streq(optarg, "help")) { - DUMP_STRING_TABLE(timezone_mode, TimezoneMode, _TIMEZONE_MODE_MAX); - return 0; - } + if (streq(optarg, "help")) + return DUMP_STRING_TABLE(timezone_mode, TimezoneMode, _TIMEZONE_MODE_MAX); arg_timezone = timezone_mode_from_string(optarg); if (arg_timezone < 0) diff --git a/src/shared/parse-argument.c b/src/shared/parse-argument.c index 107e3cb8ab1..58b914f2414 100644 --- a/src/shared/parse-argument.c +++ b/src/shared/parse-argument.c @@ -110,10 +110,8 @@ int parse_signal_argument(const char *s, int *ret) { assert(s); assert(ret); - if (streq(s, "help")) { - DUMP_STRING_TABLE(signal, int, _NSIG); - return 0; - } + if (streq(s, "help")) + return DUMP_STRING_TABLE(signal, int, _NSIG); if (streq(s, "list")) { _cleanup_(table_unrefp) Table *table = NULL; diff --git a/src/sysext/sysext.c b/src/sysext/sysext.c index c88c1042960..88278fc688d 100644 --- a/src/sysext/sysext.c +++ b/src/sysext/sysext.c @@ -2581,8 +2581,7 @@ static int parse_argv(int argc, char *argv[]) { if (arg_legend) puts("Known mutability modes:"); - DUMP_STRING_TABLE(mutable_mode, MutableMode, _MUTABLE_MAX); - return 0; + return DUMP_STRING_TABLE(mutable_mode, MutableMode, _MUTABLE_MAX); } r = parse_mutable_mode(optarg); diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 2eaf62a4105..01b5517d142 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -810,10 +810,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) { break; case 'o': - if (streq(optarg, "help")) { - DUMP_STRING_TABLE(output_mode, OutputMode, _OUTPUT_MODE_MAX); - return 0; - } + if (streq(optarg, "help")) + return DUMP_STRING_TABLE(output_mode, OutputMode, _OUTPUT_MODE_MAX); arg_output = output_mode_from_string(optarg); if (arg_output < 0) @@ -899,10 +897,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) { break; case ARG_PRESET_MODE: - if (streq(optarg, "help")) { - DUMP_STRING_TABLE(unit_file_preset_mode, UnitFilePresetMode, _UNIT_FILE_PRESET_MODE_MAX); - return 0; - } + if (streq(optarg, "help")) + return DUMP_STRING_TABLE(unit_file_preset_mode, UnitFilePresetMode, _UNIT_FILE_PRESET_MODE_MAX); arg_preset_mode = unit_file_preset_mode_from_string(optarg); if (arg_preset_mode < 0) @@ -964,10 +960,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) { break; case ARG_TIMESTAMP_STYLE: - if (streq(optarg, "help")) { - DUMP_STRING_TABLE(timestamp_style, TimestampStyle, _TIMESTAMP_STYLE_MAX); - return 0; - } + if (streq(optarg, "help")) + return DUMP_STRING_TABLE(timestamp_style, TimestampStyle, _TIMESTAMP_STYLE_MAX); arg_timestamp_style = timestamp_style_from_string(optarg); if (arg_timestamp_style < 0)