From: Zbigniew Jędrzejewski-Szmek Date: Tue, 12 May 2026 13:27:52 +0000 (+0200) Subject: loginctl: convert to OPTION and VERB macros X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=5b4d4b41a909de80545bcd65eda74ea20df36678;p=thirdparty%2Fsystemd.git loginctl: convert to OPTION and VERB macros --help output is the same, except for the expected formatting changes and moving of --no-pager/--no-legend/--no-ask-password to the end. Co-developed-by: Claude Opus 4.7 --- diff --git a/src/login/loginctl.c b/src/login/loginctl.c index 73dbc1f7163..5647621ba54 100644 --- a/src/login/loginctl.c +++ b/src/login/loginctl.c @@ -1,6 +1,5 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ -#include #include #include @@ -8,6 +7,7 @@ #include "sd-journal.h" #include "alloc-util.h" +#include "ansi-color.h" #include "build.h" #include "bus-error.h" #include "bus-locator.h" @@ -19,14 +19,15 @@ #include "cgroup-util.h" #include "format-table.h" #include "format-util.h" +#include "help-util.h" #include "log.h" #include "logs-show.h" #include "main-func.h" +#include "options.h" #include "pager.h" #include "parse-argument.h" #include "parse-util.h" #include "polkit-agent.h" -#include "pretty-print.h" #include "process-util.h" #include "runtime-scope.h" #include "string-table.h" @@ -266,6 +267,10 @@ static int list_sessions_table_add_fallback(Table *table, sd_bus_message *reply, return 0; } +VERB_GROUP("Session Commands"); + +VERB(verb_list_sessions, "list-sessions", NULL, VERB_ANY, 1, VERB_DEFAULT, + "List sessions"); static int verb_list_sessions(int argc, char *argv[], uintptr_t _data, void *userdata) { sd_bus *bus = ASSERT_PTR(userdata); _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -918,6 +923,10 @@ static int get_bus_path_by_id( return strdup_to(ret, path); } +VERB(verb_show_session, "session-status", "[ID…]", VERB_ANY, VERB_ANY, 0, + "Show session status"); +VERB(verb_show_session, "show-session", "[ID…]", VERB_ANY, VERB_ANY, 0, + "Show properties of sessions or the manager"); static int verb_show_session(int argc, char *argv[], uintptr_t _data, void *userdata) { sd_bus *bus = ASSERT_PTR(userdata); bool properties; @@ -964,6 +973,12 @@ static int verb_show_session(int argc, char *argv[], uintptr_t _data, void *user return 0; } +VERB(verb_activate, "activate", "[ID]", VERB_ANY, 2, 0, + "Activate a session"); +VERB(verb_activate, "lock-session", "[ID…]", VERB_ANY, VERB_ANY, 0, + "Screen lock one or more sessions"); +VERB(verb_activate, "unlock-session", "[ID…]", VERB_ANY, VERB_ANY, 0, + "Screen unlock one or more sessions"); static int verb_activate(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; sd_bus *bus = ASSERT_PTR(userdata); @@ -1007,6 +1022,8 @@ static int verb_activate(int argc, char *argv[], uintptr_t _data, void *userdata return 0; } +VERB_NOARG(verb_lock_sessions, "lock-sessions", "Screen lock all current sessions"); +VERB_NOARG(verb_lock_sessions, "unlock-sessions", "Screen unlock all current sessions"); static int verb_lock_sessions(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; sd_bus *bus = ASSERT_PTR(userdata); @@ -1028,6 +1045,12 @@ static int verb_lock_sessions(int argc, char *argv[], uintptr_t _data, void *use return 0; } +/* The implementation is above, but we put this here to preserve the logical order in --help. */ +VERB(verb_activate, "terminate-session", "ID…", 2, VERB_ANY, 0, + "Terminate one or more sessions"); + +VERB(verb_kill_session, "kill-session", "ID…", 2, VERB_ANY, 0, + "Send signal to processes of a session"); static int verb_kill_session(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; sd_bus *bus = ASSERT_PTR(userdata); @@ -1054,6 +1077,9 @@ static int verb_kill_session(int argc, char *argv[], uintptr_t _data, void *user return 0; } +VERB_GROUP("User Commands"); + +VERB_NOARG(verb_list_users, "list-users", "List users"); static int verb_list_users(int argc, char *argv[], uintptr_t _data, void *userdata) { static const struct bus_properties_map property_map[] = { @@ -1130,6 +1156,10 @@ static int verb_list_users(int argc, char *argv[], uintptr_t _data, void *userda return list_table_print(table, "users"); } +VERB(verb_show_user, "user-status", "[USER…]", VERB_ANY, VERB_ANY, 0, + "Show user status"); +VERB(verb_show_user, "show-user", "[USER…]", VERB_ANY, VERB_ANY, 0, + "Show properties of users or the manager"); static int verb_show_user(int argc, char *argv[], uintptr_t _data, void *userdata) { sd_bus *bus = ASSERT_PTR(userdata); bool properties; @@ -1181,6 +1211,10 @@ static int verb_show_user(int argc, char *argv[], uintptr_t _data, void *userdat return 0; } +VERB(verb_enable_linger, "enable-linger", "[USER…]", VERB_ANY, VERB_ANY, 0, + "Enable linger state of one or more users"); +VERB(verb_enable_linger, "disable-linger", "[USER…]", VERB_ANY, VERB_ANY, 0, + "Disable linger state of one or more users"); static int verb_enable_linger(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; sd_bus *bus = ASSERT_PTR(userdata); @@ -1229,6 +1263,8 @@ static int verb_enable_linger(int argc, char *argv[], uintptr_t _data, void *use return 0; } +VERB(verb_terminate_user, "terminate-user", "USER…", 2, VERB_ANY, 0, + "Terminate all sessions of one or more users"); static int verb_terminate_user(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; sd_bus *bus = ASSERT_PTR(userdata); @@ -1259,6 +1295,8 @@ static int verb_terminate_user(int argc, char *argv[], uintptr_t _data, void *us return 0; } +VERB(verb_kill_user, "kill-user", "USER…", 2, VERB_ANY, 0, + "Send signal to processes of a user"); static int verb_kill_user(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; sd_bus *bus = ASSERT_PTR(userdata); @@ -1297,6 +1335,9 @@ static int verb_kill_user(int argc, char *argv[], uintptr_t _data, void *userdat return 0; } +VERB_GROUP("Seat Commands"); + +VERB_NOARG(verb_list_seats, "list-seats", "List seats"); static int verb_list_seats(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; @@ -1341,6 +1382,10 @@ static int verb_list_seats(int argc, char *argv[], uintptr_t _data, void *userda return list_table_print(table, "seats"); } +VERB(verb_show_seat, "seat-status", "[NAME…]", VERB_ANY, VERB_ANY, 0, + "Show seat status"); +VERB(verb_show_seat, "show-seat", "[NAME…]", VERB_ANY, VERB_ANY, 0, + "Show properties of seats or the manager"); static int verb_show_seat(int argc, char *argv[], uintptr_t _data, void *userdata) { sd_bus *bus = ASSERT_PTR(userdata); bool properties; @@ -1387,6 +1432,8 @@ static int verb_show_seat(int argc, char *argv[], uintptr_t _data, void *userdat return 0; } +VERB(verb_attach, "attach", "NAME DEVICE…", 3, VERB_ANY, 0, + "Attach one or more devices to a seat"); static int verb_attach(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; sd_bus *bus = ASSERT_PTR(userdata); @@ -1411,6 +1458,7 @@ static int verb_attach(int argc, char *argv[], uintptr_t _data, void *userdata) return 0; } +VERB_NOARG(verb_flush_devices, "flush-devices", "Flush all device associations"); static int verb_flush_devices(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; sd_bus *bus = ASSERT_PTR(userdata); @@ -1427,6 +1475,8 @@ static int verb_flush_devices(int argc, char *argv[], uintptr_t _data, void *use return 0; } +VERB(verb_terminate_seat, "terminate-seat", "NAME…", 2, VERB_ANY, 0, + "Terminate all sessions on one or more seats"); static int verb_terminate_seat(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; sd_bus *bus = ASSERT_PTR(userdata); @@ -1447,190 +1497,127 @@ static int verb_terminate_seat(int argc, char *argv[], uintptr_t _data, void *us } static int help(void) { - _cleanup_free_ char *link = NULL; + static const char *const groups[] = { + "Session Commands", + "User Commands", + "Seat Commands", + }; + + Table *vtables[ELEMENTSOF(groups)] = {}; + CLEANUP_ELEMENTS(vtables, table_unref_array_clear); + _cleanup_(table_unrefp) Table *options = NULL; int r; pager_open(arg_pager_flags); - r = terminal_urlify_man("loginctl", "1", &link); + for (size_t i = 0; i < ELEMENTSOF(groups); i++) { + r = verbs_get_help_table_group(groups[i], &vtables[i]); + if (r < 0) + return r; + } + + r = option_parser_get_help_table(&options); if (r < 0) - return log_oom(); + return r; - printf("%1$s [OPTIONS...] COMMAND ...\n\n" - "%5$sSend control commands to or query the login manager.%6$s\n" - "\n%3$sSession Commands:%4$s\n" - " list-sessions List sessions\n" - " session-status [ID...] Show session status\n" - " show-session [ID...] Show properties of sessions or the manager\n" - " activate [ID] Activate a session\n" - " lock-session [ID...] Screen lock one or more sessions\n" - " unlock-session [ID...] Screen unlock one or more sessions\n" - " lock-sessions Screen lock all current sessions\n" - " unlock-sessions Screen unlock all current sessions\n" - " terminate-session ID... Terminate one or more sessions\n" - " kill-session ID... Send signal to processes of a session\n" - "\n%3$sUser Commands:%4$s\n" - " list-users List users\n" - " user-status [USER...] Show user status\n" - " show-user [USER...] Show properties of users or the manager\n" - " enable-linger [USER...] Enable linger state of one or more users\n" - " disable-linger [USER...] Disable linger state of one or more users\n" - " terminate-user USER... Terminate all sessions of one or more users\n" - " kill-user USER... Send signal to processes of a user\n" - "\n%3$sSeat Commands:%4$s\n" - " list-seats List seats\n" - " seat-status [NAME...] Show seat status\n" - " show-seat [NAME...] Show properties of seats or the manager\n" - " attach NAME DEVICE... Attach one or more devices to a seat\n" - " flush-devices Flush all device associations\n" - " terminate-seat NAME... Terminate all sessions on one or more seats\n" - "\n%3$sOptions:%4$s\n" - " -h --help Show this help\n" - " --version Show package version\n" - " -H --host=[USER@]HOST Operate on remote host\n" - " -M --machine=CONTAINER Operate on local container\n" - " -p --property=NAME Show only properties by this name\n" - " -P NAME Equivalent to --value --property=NAME\n" - " -a --all Show all properties, including empty ones\n" - " --value When showing properties, only print the value\n" - " -l --full Do not ellipsize output\n" - " --kill-whom=WHOM Whom to send signal to\n" - " -s --signal=SIGNAL Which signal to send\n" - " -n --lines=INTEGER Number of journal entries to show\n" - " --json=MODE Generate JSON output for list-sessions/users/seats\n" - " (takes one of pretty, short, or off)\n" - " -j Same as --json=pretty on tty, --json=short otherwise\n" - " -o --output=MODE Change journal output mode (short, short-precise,\n" - " short-iso, short-iso-precise, short-full,\n" - " short-monotonic, short-unix, short-delta,\n" - " json, json-pretty, json-sse, json-seq, cat,\n" - " verbose, export, with-unit)\n" - " --no-pager Do not pipe output into a pager\n" - " --no-legend Do not show the headers and footers\n" - " --no-ask-password Don't prompt for password\n" - "\nSee the %2$s for details.\n", - program_invocation_short_name, - link, - ansi_underline(), - ansi_normal(), - ansi_highlight(), - ansi_normal()); + assert_cc(ELEMENTSOF(vtables) == 3); + (void) table_sync_column_widths(0, vtables[0], vtables[1], vtables[2], options); - return 0; -} + help_cmdline("[OPTIONS…] COMMAND …"); + help_abstract("Send control commands to or query the login manager."); -static int verb_help(int argc, char *argv[], uintptr_t _data, void *userdata) { - return help(); -} + for (size_t i = 0; i < ELEMENTSOF(groups); i++) { + help_section(groups[i]); + r = table_print_or_warn(vtables[i]); + if (r < 0) + return r; + } -static int parse_argv(int argc, char *argv[]) { - enum { - ARG_VERSION = 0x100, - ARG_VALUE, - ARG_NO_PAGER, - ARG_NO_LEGEND, - ARG_JSON, - ARG_KILL_WHOM, - ARG_NO_ASK_PASSWORD, - }; + help_section("Options"); + r = table_print_or_warn(options); + if (r < 0) + return r; - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "property", required_argument, NULL, 'p' }, - { "all", no_argument, NULL, 'a' }, - { "value", no_argument, NULL, ARG_VALUE }, - { "full", no_argument, NULL, 'l' }, - { "no-pager", no_argument, NULL, ARG_NO_PAGER }, - { "no-legend", no_argument, NULL, ARG_NO_LEGEND }, - { "json", required_argument, NULL, ARG_JSON }, - { "kill-whom", required_argument, NULL, ARG_KILL_WHOM }, - { "signal", required_argument, NULL, 's' }, - { "host", required_argument, NULL, 'H' }, - { "machine", required_argument, NULL, 'M' }, - { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, - { "lines", required_argument, NULL, 'n' }, - { "output", required_argument, NULL, 'o' }, - {} - }; + help_man_page_reference("loginctl", "1"); + return 0; +} - int c, r; +VERB_COMMON_HELP_HIDDEN(help); + +static int parse_argv(int argc, char *argv[], char ***remaining_args) { + int r; assert(argc >= 0); assert(argv); + assert(remaining_args); - while ((c = getopt_long(argc, argv, "hp:P:als:H:M:n:o:j", options, NULL)) >= 0) + OptionParser opts = { argc, argv }; + FOREACH_OPTION_OR_RETURN(c, &opts) switch (c) { - case 'h': + OPTION_COMMON_HELP: return help(); - case ARG_VERSION: + OPTION_COMMON_VERSION: return version(); - case 'H': + OPTION_COMMON_HOST: arg_transport = BUS_TRANSPORT_REMOTE; - arg_host = optarg; + arg_host = opts.arg; break; - case 'M': - r = parse_machine_argument(optarg, &arg_host, &arg_transport); + OPTION_COMMON_MACHINE: + r = parse_machine_argument(opts.arg, &arg_host, &arg_transport); if (r < 0) return r; break; - case 'P': - SET_FLAG(arg_print_flags, BUS_PRINT_PROPERTY_ONLY_VALUE, true); - _fallthrough_; - - case 'p': { - r = strv_extend(&arg_property, optarg); + OPTION('p', "property", "NAME", "Show only properties by this name"): {} + OPTION_SHORT('P', "NAME", "Equivalent to --value --property=NAME"): + r = strv_extend(&arg_property, opts.arg); if (r < 0) return log_oom(); - /* If the user asked for a particular - * property, show it to them, even if it is + /* If the user asked for a particular property, show it to them, even if it is * empty. */ SET_FLAG(arg_print_flags, BUS_PRINT_PROPERTY_SHOW_EMPTY, true); + + if (opts.opt->short_code == 'P') + SET_FLAG(arg_print_flags, BUS_PRINT_PROPERTY_ONLY_VALUE, true); + break; - } - case 'a': + OPTION('a', "all", NULL, "Show all properties, including empty ones"): SET_FLAG(arg_print_flags, BUS_PRINT_PROPERTY_SHOW_EMPTY, true); break; - case ARG_VALUE: + OPTION_LONG("value", NULL, "When showing properties, only print the value"): SET_FLAG(arg_print_flags, BUS_PRINT_PROPERTY_ONLY_VALUE, true); break; - case 'l': + OPTION('l', "full", NULL, "Do not ellipsize output"): arg_full = true; break; - case 'n': - if (safe_atou(optarg, &arg_lines) < 0) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Failed to parse lines '%s'", optarg); + OPTION_LONG("kill-whom", "WHOM", "Whom to send signal to"): + arg_kill_whom = opts.arg; break; - case 'o': - 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) - return log_error_errno(arg_output, "Unknown output '%s'.", optarg); - + OPTION('s', "signal", "SIGNAL", "Which signal to send"): + r = parse_signal_argument(opts.arg, &arg_signal); + if (r <= 0) + return r; break; - case 'j': - arg_json_format_flags = SD_JSON_FORMAT_PRETTY_AUTO|SD_JSON_FORMAT_COLOR_AUTO; - arg_legend = false; + OPTION('n', "lines", "INTEGER", "Number of journal entries to show"): + if (safe_atou(opts.arg, &arg_lines) < 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Failed to parse lines '%s'", opts.arg); break; - case ARG_JSON: - r = parse_json_argument(optarg, &arg_json_format_flags); + OPTION_COMMON_JSON: + r = parse_json_argument(opts.arg, &arg_json_format_flags); if (r <= 0) return r; @@ -1639,78 +1626,50 @@ static int parse_argv(int argc, char *argv[]) { break; - case ARG_KILL_WHOM: - arg_kill_whom = optarg; + OPTION_COMMON_LOWERCASE_J: + arg_json_format_flags = SD_JSON_FORMAT_PRETTY_AUTO|SD_JSON_FORMAT_COLOR_AUTO; + arg_legend = false; break; - case 's': - r = parse_signal_argument(optarg, &arg_signal); - if (r <= 0) - return r; + OPTION('o', "output", "MODE", + "Change journal output mode (short, short-precise, short-iso, " + "short-iso-precise, short-full, short-monotonic, short-unix, short-delta, " + "json, json-pretty, json-sse, json-seq, cat, verbose, export, with-unit)"): + if (streq(opts.arg, "help")) + return DUMP_STRING_TABLE(output_mode, OutputMode, _OUTPUT_MODE_MAX); + + arg_output = output_mode_from_string(opts.arg); + if (arg_output < 0) + return log_error_errno(arg_output, "Unknown output '%s'.", opts.arg); + break; - case ARG_NO_PAGER: + OPTION_COMMON_NO_PAGER: arg_pager_flags |= PAGER_DISABLE; break; - case ARG_NO_LEGEND: + OPTION_COMMON_NO_LEGEND: arg_legend = false; break; - case ARG_NO_ASK_PASSWORD: + OPTION_COMMON_NO_ASK_PASSWORD: arg_ask_password = false; break; - - case '?': - return -EINVAL; - - default: - assert_not_reached(); } + *remaining_args = option_parser_get_args(&opts); return 1; } -static int loginctl_main(int argc, char *argv[], sd_bus *bus) { - static const Verb verbs[] = { - { "help", VERB_ANY, VERB_ANY, 0, verb_help }, - { "list-sessions", VERB_ANY, 1, VERB_DEFAULT, verb_list_sessions }, - { "session-status", VERB_ANY, VERB_ANY, 0, verb_show_session }, - { "show-session", VERB_ANY, VERB_ANY, 0, verb_show_session }, - { "activate", VERB_ANY, 2, 0, verb_activate }, - { "lock-session", VERB_ANY, VERB_ANY, 0, verb_activate }, - { "unlock-session", VERB_ANY, VERB_ANY, 0, verb_activate }, - { "lock-sessions", VERB_ANY, 1, 0, verb_lock_sessions }, - { "unlock-sessions", VERB_ANY, 1, 0, verb_lock_sessions }, - { "terminate-session", 2, VERB_ANY, 0, verb_activate }, - { "kill-session", 2, VERB_ANY, 0, verb_kill_session }, - { "list-users", VERB_ANY, 1, 0, verb_list_users }, - { "user-status", VERB_ANY, VERB_ANY, 0, verb_show_user }, - { "show-user", VERB_ANY, VERB_ANY, 0, verb_show_user }, - { "enable-linger", VERB_ANY, VERB_ANY, 0, verb_enable_linger }, - { "disable-linger", VERB_ANY, VERB_ANY, 0, verb_enable_linger }, - { "terminate-user", 2, VERB_ANY, 0, verb_terminate_user }, - { "kill-user", 2, VERB_ANY, 0, verb_kill_user }, - { "list-seats", VERB_ANY, 1, 0, verb_list_seats }, - { "seat-status", VERB_ANY, VERB_ANY, 0, verb_show_seat }, - { "show-seat", VERB_ANY, VERB_ANY, 0, verb_show_seat }, - { "attach", 3, VERB_ANY, 0, verb_attach }, - { "flush-devices", VERB_ANY, 1, 0, verb_flush_devices }, - { "terminate-seat", 2, VERB_ANY, 0, verb_terminate_seat }, - {} - }; - - return dispatch_verb(argc, argv, verbs, bus); -} - static int run(int argc, char *argv[]) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + char **args = NULL; int r; setlocale(LC_ALL, ""); log_setup(); - r = parse_argv(argc, argv); + r = parse_argv(argc, argv, &args); if (r <= 0) return r; @@ -1722,7 +1681,7 @@ static int run(int argc, char *argv[]) { (void) sd_bus_set_allow_interactive_authorization(bus, arg_ask_password); - return loginctl_main(argc, argv, bus); + return dispatch_verb_with_args(args, bus); } DEFINE_MAIN_FUNCTION(run);