]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
varlinkctl: convert to the new option parser
authorZbigniew Jędrzejewski-Szmek <zbyszek@amutable.com>
Sat, 4 Apr 2026 18:16:28 +0000 (20:16 +0200)
committerZbigniew Jędrzejewski-Szmek <zbyszek@amutable.com>
Tue, 7 Apr 2026 08:55:52 +0000 (10:55 +0200)
The -E short option previously used fallthrough into the --more case;
since macro-generated case labels don't support fallthrough (with some
older compilers), the --more logic is now duplicated inline in the -E
handler.

Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com>
src/varlinkctl/varlinkctl.c

index 4124d0d570726976b4792609028fbb83cb9e7ae5..2dc191bff32bfe6452c1c0a97ce7a96d5f9afb09 100644 (file)
@@ -1,6 +1,5 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
-#include <getopt.h>
 #include <stdlib.h>
 #include <sys/stat.h>
 #include <unistd.h>
@@ -20,6 +19,7 @@
 #include "log.h"
 #include "main-func.h"
 #include "memfd-util.h"
+#include "options.h"
 #include "pager.h"
 #include "parse-argument.h"
 #include "parse-util.h"
@@ -72,17 +72,22 @@ STATIC_DESTRUCTOR_REGISTER(arg_push_fds, push_fds_done);
 
 static int help(void) {
         _cleanup_free_ char *link = NULL;
+        _cleanup_(table_unrefp) Table *options = NULL;
         int r;
 
         r = terminal_urlify_man("varlinkctl", "1", &link);
         if (r < 0)
                 return log_oom();
 
+        r = option_parser_get_help_table(&options);
+        if (r < 0)
+                return r;
+
         pager_open(arg_pager_flags);
 
         printf("%1$s [OPTIONS...] COMMAND ...\n\n"
-               "%5$sIntrospect Varlink Services.%6$s\n"
-               "\n%3$sCommands:%4$s\n"
+               "%3$sIntrospect Varlink Services.%4$s\n"
+               "\n%2$sCommands:%4$s\n"
                "  info ADDRESS           Show service information\n"
                "  list-interfaces ADDRESS\n"
                "                         List interfaces implemented by service\n"
@@ -98,33 +103,17 @@ static int help(void) {
                "  list-registry          Show list of services in the service registry\n"
                "  validate-idl [FILE]    Validate interface description\n"
                "  help                   Show this help\n"
-               "\n%3$sOptions:%4$s\n"
-               "  -h --help              Show this help\n"
-               "     --version           Show package version\n"
-               "     --no-ask-password   Do not prompt for password\n"
-               "     --no-pager          Do not pipe output into a pager\n"
-               "     --system            Enumerate system registry\n"
-               "     --user              Enumerate user registry\n"
-               "     --more              Request multiple responses\n"
-               "     --collect           Collect multiple responses in a JSON array\n"
-               "     --oneway            Do not request response\n"
-               "     --json=MODE         Output as JSON\n"
-               "  -j                     Same as --json=pretty on tty, --json=short otherwise\n"
-               "  -q --quiet             Do not output method reply\n"
-               "     --graceful=ERROR    Treat specified Varlink error as success\n"
-               "     --timeout=SECS      Maximum time to wait for method call completion\n"
-               "  -E                     Short for --more --timeout=infinity\n"
-               "     --upgrade           Request protocol upgrade (connection becomes raw\n"
-               "                         bidirectional pipe on stdin/stdout after reply)\n"
-               "     --push-fd=FD        Pass the specified fd along with method call\n"
-               "\nSee the %2$s for details.\n",
+               "\n%2$sOptions:%4$s\n",
                program_invocation_short_name,
-               link,
                ansi_underline(),
-               ansi_normal(),
                ansi_highlight(),
                ansi_normal());
 
+        r = table_print_or_warn(options);
+        if (r < 0)
+                return r;
+
+        printf("\nSee the %s for details.\n", link);
         return 0;
 }
 
@@ -133,145 +122,121 @@ static int verb_help(int argc, char *argv[], uintptr_t _data, void *userdata) {
 }
 
 static int parse_argv(int argc, char *argv[]) {
-
-        enum {
-                ARG_VERSION = 0x100,
-                ARG_NO_PAGER,
-                ARG_MORE,
-                ARG_ONEWAY,
-                ARG_JSON,
-                ARG_COLLECT,
-                ARG_GRACEFUL,
-                ARG_TIMEOUT,
-                ARG_EXEC,
-                ARG_UPGRADE,
-                ARG_PUSH_FD,
-                ARG_NO_ASK_PASSWORD,
-                ARG_USER,
-                ARG_SYSTEM,
-        };
-
-        static const struct option options[] = {
-                { "help",            no_argument,       NULL, 'h'                },
-                { "version",         no_argument,       NULL, ARG_VERSION         },
-                { "no-pager",        no_argument,       NULL, ARG_NO_PAGER        },
-                { "more",            no_argument,       NULL, ARG_MORE            },
-                { "oneway",          no_argument,       NULL, ARG_ONEWAY          },
-                { "json",            required_argument, NULL, ARG_JSON            },
-                { "collect",         no_argument,       NULL, ARG_COLLECT         },
-                { "quiet",           no_argument,       NULL, 'q'                 },
-                { "graceful",        required_argument, NULL, ARG_GRACEFUL        },
-                { "timeout",         required_argument, NULL, ARG_TIMEOUT         },
-                { "exec",            no_argument,       NULL, ARG_EXEC            },
-                { "upgrade",         no_argument,       NULL, ARG_UPGRADE         },
-                { "push-fd",         required_argument, NULL, ARG_PUSH_FD         },
-                { "no-ask-password", no_argument,       NULL, ARG_NO_ASK_PASSWORD },
-                { "user",            no_argument,       NULL, ARG_USER            },
-                { "system",          no_argument,       NULL, ARG_SYSTEM          },
-                {},
-        };
-
-        int c, r;
+        int r;
 
         assert(argc >= 0);
         assert(argv);
 
-        while ((c = getopt_long(argc, argv, "hjqE", options, NULL)) >= 0)
+        OptionParser state = { argc, argv };
+        const char *arg;
 
+        FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
                 switch (c) {
 
-                case 'h':
+                OPTION_COMMON_HELP:
                         return help();
 
-                case ARG_VERSION:
+                OPTION_COMMON_VERSION:
                         return version();
 
-                case ARG_NO_PAGER:
+                OPTION_COMMON_NO_ASK_PASSWORD:
+                        arg_ask_password = false;
+                        break;
+
+                OPTION_COMMON_NO_PAGER:
                         arg_pager_flags |= PAGER_DISABLE;
                         break;
 
-                case 'E':
-                        arg_timeout = USEC_INFINITY;
-                        _fallthrough_;
+                OPTION_LONG("system", NULL, "Enumerate system registry"):
+                        arg_runtime_scope = RUNTIME_SCOPE_SYSTEM;
+                        break;
 
-                case ARG_MORE:
-                        arg_method_flags = (arg_method_flags & ~SD_VARLINK_METHOD_ONEWAY) | SD_VARLINK_METHOD_MORE;
+                OPTION_LONG("user", NULL, "Enumerate user registry"):
+                        arg_runtime_scope = RUNTIME_SCOPE_USER;
                         break;
 
-                case ARG_ONEWAY:
-                        arg_method_flags = (arg_method_flags & ~SD_VARLINK_METHOD_MORE) | SD_VARLINK_METHOD_ONEWAY;
+                OPTION_LONG("more", NULL, "Request multiple responses"):
+                        arg_method_flags = (arg_method_flags & ~SD_VARLINK_METHOD_ONEWAY) | SD_VARLINK_METHOD_MORE;
                         break;
 
-                case ARG_COLLECT:
+                OPTION_LONG("collect", NULL, "Collect multiple responses in a JSON array"):
                         arg_collect = true;
                         break;
 
-                case ARG_JSON:
-                        r = parse_json_argument(optarg, &arg_json_format_flags);
+                OPTION_LONG("oneway", NULL, "Do not request response"):
+                        arg_method_flags = (arg_method_flags & ~SD_VARLINK_METHOD_MORE) | SD_VARLINK_METHOD_ONEWAY;
+                        break;
+
+                OPTION_COMMON_JSON:
+                        r = parse_json_argument(arg, &arg_json_format_flags);
                         if (r <= 0)
                                 return r;
-
                         break;
 
-                case 'j':
+                OPTION_COMMON_LOWERCASE_J:
                         arg_json_format_flags = SD_JSON_FORMAT_PRETTY_AUTO|SD_JSON_FORMAT_COLOR_AUTO;
                         break;
 
-                case 'q':
+                OPTION('q', "quiet", NULL, "Do not output method reply"):
                         arg_quiet = true;
                         break;
 
-                case ARG_GRACEFUL:
-                        r = varlink_idl_qualified_symbol_name_is_valid(optarg);
+                OPTION_LONG("graceful", "ERROR", "Treat specified Varlink error as success"):
+                        r = varlink_idl_qualified_symbol_name_is_valid(arg);
                         if (r < 0)
-                                return log_error_errno(r, "Failed to validate Varlink error name '%s': %m", optarg);
+                                return log_error_errno(r, "Failed to validate Varlink error name '%s': %m", arg);
                         if (r == 0)
-                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Not a valid Varlink error name: %s", optarg);
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Not a valid Varlink error name: %s", arg);
 
-                        if (strv_extend(&arg_graceful, optarg) < 0)
+                        if (strv_extend(&arg_graceful, arg) < 0)
                                 return log_oom();
-
                         break;
 
-                case ARG_TIMEOUT:
-                        if (isempty(optarg)) {
+                OPTION_LONG("timeout", "SECS", "Maximum time to wait for method call completion"):
+                        if (isempty(arg)) {
                                 arg_timeout = USEC_INFINITY;
                                 break;
                         }
 
-                        r = parse_sec(optarg, &arg_timeout);
+                        r = parse_sec(arg, &arg_timeout);
                         if (r < 0)
-                                return log_error_errno(r, "Failed to parse --timeout= parameter '%s': %m", optarg);
+                                return log_error_errno(r, "Failed to parse --timeout= parameter '%s': %m", arg);
 
                         if (arg_timeout == 0)
                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Timeout cannot be zero.");
-
                         break;
 
-                case ARG_EXEC:
-                        arg_exec = true;
+                OPTION_SHORT('E', NULL, "Short for --more --timeout=infinity"):
+                        arg_timeout = USEC_INFINITY;
+                        arg_method_flags = (arg_method_flags & ~SD_VARLINK_METHOD_ONEWAY) | SD_VARLINK_METHOD_MORE;
                         break;
 
-                case ARG_UPGRADE:
+                OPTION_LONG("upgrade", NULL,
+                            "Request protocol upgrade (connection becomes raw"
+                            " bidirectional pipe on stdin/stdout after reply)"):
                         arg_upgrade = true;
                         break;
 
-                case ARG_PUSH_FD: {
+                OPTION_LONG("exec", NULL, "Invoke method and pass response and fds to command"):
+                        arg_exec = true;
+                        break;
+
+                OPTION_LONG("push-fd", "FD", "Pass the specified fd along with method call"): {
                         if (!GREEDY_REALLOC(arg_push_fds.fds, arg_push_fds.n_fds + 1))
                                 return log_oom();
 
                         _cleanup_close_ int add_fd = -EBADF;
-                        if (STARTSWITH_SET(optarg, "/", "./")) {
+                        if (STARTSWITH_SET(arg, "/", "./")) {
                                 /* We usually expect a numeric fd spec, but as an extension let's treat this
                                  * as a path to open in read-only mode in case this is clearly an absolute or
                                  * relative path */
-                                add_fd = open(optarg, O_CLOEXEC|O_RDONLY|O_NOCTTY);
+                                add_fd = open(arg, O_CLOEXEC|O_RDONLY|O_NOCTTY);
                                 if (add_fd < 0)
-                                        return log_error_errno(errno, "Failed to open '%s': %m", optarg);
+                                        return log_error_errno(errno, "Failed to open '%s': %m", arg);
                         } else {
-                                int parsed_fd = parse_fd(optarg);
+                                int parsed_fd = parse_fd(arg);
                                 if (parsed_fd < 0)
-                                        return log_error_errno(parsed_fd, "Failed to parse --push-fd= parameter: %s", optarg);
+                                        return log_error_errno(parsed_fd, "Failed to parse --push-fd= parameter: %s", arg);
 
                                 /* Make a copy, so that the same fd could be used multiple times in a reasonable
                                  * way. This also validates the fd early */
@@ -283,24 +248,6 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_push_fds.fds[arg_push_fds.n_fds++] = TAKE_FD(add_fd);
                         break;
                 }
-
-                case ARG_NO_ASK_PASSWORD:
-                        arg_ask_password = false;
-                        break;
-
-                case ARG_USER:
-                        arg_runtime_scope = RUNTIME_SCOPE_USER;
-                        break;
-
-                case ARG_SYSTEM:
-                        arg_runtime_scope = RUNTIME_SCOPE_SYSTEM;
-                        break;
-
-                case '?':
-                        return -EINVAL;
-
-                default:
-                        assert_not_reached();
                 }
 
         /* If more than one reply is expected, imply JSON-SEQ output, and set SD_JSON_FORMAT_FLUSH */