From: Zbigniew Jędrzejewski-Szmek Date: Thu, 7 May 2026 07:57:43 +0000 (+0200) Subject: resolvectl: convert to OPTION and VERB macros X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7cea71e4c112177e74140b39898e9836f334e588;p=thirdparty%2Fsystemd.git resolvectl: convert to OPTION and VERB macros Use OPTION_NAMESPACE() to keep the resolvectl and systemd-resolve option sets separate. The resolvconf-compat path (resolvconf invocation) keeps its own getopt-based parsing. --help output has the expected changes to formatting. Synopis for [status] is now shows that the verb is optional. Co-developed-by: Claude Opus 4.7 --- diff --git a/src/resolve/resolvectl.c b/src/resolve/resolvectl.c index 4088c39b1da..015345fdba7 100644 --- a/src/resolve/resolvectl.c +++ b/src/resolve/resolvectl.c @@ -1,6 +1,5 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ -#include #include #include @@ -13,6 +12,7 @@ #include "af-list.h" #include "alloc-util.h" +#include "ansi-color.h" #include "argv-util.h" #include "build.h" #include "bus-common-errors.h" @@ -30,17 +30,19 @@ #include "escape.h" #include "format-ifname.h" #include "format-table.h" +#include "glyph-util.h" +#include "help-util.h" #include "hostname-util.h" #include "json-util.h" #include "main-func.h" #include "missing-network.h" #include "netlink-util.h" +#include "options.h" #include "ordered-set.h" #include "pager.h" #include "parse-argument.h" #include "parse-util.h" #include "polkit-agent.h" -#include "pretty-print.h" #include "resolvconf-compat.h" #include "resolve-util.h" #include "resolvectl.h" @@ -799,6 +801,8 @@ invalid: "Invalid DNS URI: %s", name); } +VERB(verb_query, "query", "HOSTNAME|ADDRESS…", 2, VERB_ANY, 0, + "Resolve domain names, IPv4 and IPv6 addresses"); static int verb_query(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; int ret = 0, r; @@ -1009,6 +1013,8 @@ static int resolve_service(sd_bus *bus, const char *name, const char *type, cons return 0; } +VERB(verb_service, "service", "[[NAME] TYPE] DOMAIN", 2, 4, 0, + "Resolve service (SRV)"); static int verb_service(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; int r; @@ -1091,6 +1097,8 @@ static int resolve_openpgp(sd_bus *bus, const char *address) { } #endif +VERB(verb_openpgp, "openpgp", "EMAIL@DOMAIN…", 2, VERB_ANY, 0, + "Query OpenPGP public key"); static int verb_openpgp(int argc, char *argv[], uintptr_t _data, void *userdata) { #if HAVE_OPENSSL _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; @@ -1148,6 +1156,8 @@ static bool service_family_is_valid(const char *s) { return STR_IN_SET(s, "tcp", "udp", "sctp"); } +VERB(verb_tlsa, "tlsa", "DOMAIN[:PORT]…", 2, VERB_ANY, 0, + "Query TLS public key"); static int verb_tlsa(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; const char *family = "tcp"; @@ -1763,10 +1773,14 @@ static int status_ifindex(int ifindex, StatusMode mode) { return status_full(mode, STRV_MAKE(ifname)); } +VERB(verb_status, "status", "[LINK…]", VERB_ANY, VERB_ANY, VERB_DEFAULT, + "Show link and server status"); static int verb_status(int argc, char *argv[], uintptr_t _data, void *userdata) { return status_full(STATUS_ALL, strv_skip(argv, 1)); } +VERB(verb_show_statistics, "statistics", NULL, VERB_ANY, 1, 0, + "Show resolver statistics"); static int verb_show_statistics(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(table_unrefp) Table *table = NULL; sd_json_variant *reply = NULL; @@ -1928,6 +1942,8 @@ static int verb_show_statistics(int argc, char *argv[], uintptr_t _data, void *u return table_print_or_warn(table); } +VERB(verb_reset_statistics, "reset-statistics", NULL, VERB_ANY, 1, 0, + "Reset resolver statistics"); static int verb_reset_statistics(int argc, char *argv[], uintptr_t _data, void *userdata) { sd_json_variant *reply = NULL; _cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL; @@ -1953,6 +1969,8 @@ static int verb_reset_statistics(int argc, char *argv[], uintptr_t _data, void * return 0; } +VERB(verb_flush_caches, "flush-caches", NULL, VERB_ANY, 1, 0, + "Flush all local DNS caches"); static int verb_flush_caches(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -1969,6 +1987,8 @@ static int verb_flush_caches(int argc, char *argv[], uintptr_t _data, void *user return 0; } +VERB(verb_reset_server_features, "reset-server-features", NULL, VERB_ANY, 1, 0, + "Forget learnt DNS server feature levels"); static int verb_reset_server_features(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -2168,6 +2188,8 @@ static int monitor_reply( return 0; } +VERB(verb_monitor, "monitor", NULL, VERB_ANY, 1, 0, + "Monitor DNS queries"); static int verb_monitor(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_event_unrefp) sd_event *event = NULL; _cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL; @@ -2414,6 +2436,8 @@ static int dump_cache_scope(sd_json_variant *scope) { return 0; } +VERB(verb_show_cache, "show-cache", NULL, VERB_ANY, 1, 0, + "Show cache contents"); static int verb_show_cache(int argc, char *argv[], uintptr_t _data, void *userdata) { sd_json_variant *reply = NULL, *d = NULL; _cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL; @@ -2590,6 +2614,8 @@ static int dump_server_state(sd_json_variant *server) { return table_print_or_warn(table); } +VERB(verb_show_server_state, "show-server-state", NULL, VERB_ANY, 1, 0, + "Show servers state"); static int verb_show_server_state(int argc, char *argv[], uintptr_t _data, void *userdata) { sd_json_variant *reply = NULL, *d = NULL; _cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL; @@ -2633,6 +2659,8 @@ static int verb_show_server_state(int argc, char *argv[], uintptr_t _data, void return sd_json_variant_dump(d, arg_json_format_flags, NULL, NULL); } +VERB(verb_dns, "dns", "[LINK [SERVER…]]", VERB_ANY, VERB_ANY, 0, + "Get/set per-interface DNS server address"); static int verb_dns(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -2718,6 +2746,8 @@ static int call_domain(sd_bus *bus, char **domain, const BusLocator *locator, sd return sd_bus_call(bus, req, 0, error, NULL); } +VERB(verb_domain, "domain", "[LINK [DOMAIN…]]", VERB_ANY, VERB_ANY, 0, + "Get/set per-interface search domain"); static int verb_domain(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -2757,6 +2787,8 @@ static int verb_domain(int argc, char *argv[], uintptr_t _data, void *userdata) return 0; } +VERB(verb_default_route, "default-route", "[LINK [BOOL]]", VERB_ANY, 3, 0, + "Get/set per-interface default route flag"); static int verb_default_route(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -2801,6 +2833,8 @@ static int verb_default_route(int argc, char *argv[], uintptr_t _data, void *use return 0; } +VERB(verb_llmnr, "llmnr", "[LINK [MODE]]", VERB_ANY, 3, 0, + "Get/set per-interface LLMNR mode"); static int verb_llmnr(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -2859,6 +2893,8 @@ static int verb_llmnr(int argc, char *argv[], uintptr_t _data, void *userdata) { return 0; } +VERB(verb_mdns, "mdns", "[LINK [MODE]]", VERB_ANY, 3, 0, + "Get/set per-interface MulticastDNS mode"); static int verb_mdns(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -2923,6 +2959,8 @@ static int verb_mdns(int argc, char *argv[], uintptr_t _data, void *userdata) { return 0; } +VERB(verb_dns_over_tls, "dnsovertls", "[LINK [MODE]]", VERB_ANY, 3, 0, + "Get/set per-interface DNS-over-TLS mode"); static int verb_dns_over_tls(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -2969,6 +3007,8 @@ static int verb_dns_over_tls(int argc, char *argv[], uintptr_t _data, void *user return 0; } +VERB(verb_dnssec, "dnssec", "[LINK [MODE]]", VERB_ANY, 3, 0, + "Get/set per-interface DNSSEC mode"); static int verb_dnssec(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -3030,6 +3070,8 @@ static int call_nta(sd_bus *bus, char **nta, const BusLocator *locator, sd_bus_ return sd_bus_call(bus, req, 0, error, NULL); } +VERB(verb_nta, "nta", "[LINK [DOMAIN…]]", VERB_ANY, VERB_ANY, 0, + "Get/set per-interface DNSSEC NTA"); static int verb_nta(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -3088,6 +3130,8 @@ static int verb_nta(int argc, char *argv[], uintptr_t _data, void *userdata) { return 0; } +VERB(verb_revert_link, "revert", "LINK", VERB_ANY, 2, 0, + "Revert per-interface configuration"); static int verb_revert_link(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -3125,6 +3169,8 @@ static int verb_revert_link(int argc, char *argv[], uintptr_t _data, void *userd return 0; } +VERB(verb_log_level, "log-level", "[LEVEL]", VERB_ANY, 2, 0, + "Get/set logging threshold for systemd-resolved"); static int verb_log_level(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; int r; @@ -3187,251 +3233,116 @@ static void help_dns_classes(void) { } static int compat_help(void) { - _cleanup_free_ char *link = NULL; + _cleanup_(table_unrefp) Table *options = NULL; int r; - r = terminal_urlify_man("resolvectl", "1", &link); + r = option_parser_get_help_table_ns("systemd-resolve", &options); if (r < 0) - return log_oom(); + return r; pager_open(arg_pager_flags); - printf("%1$s [OPTIONS...] HOSTNAME|ADDRESS...\n" - "%1$s [OPTIONS...] --service [[NAME] TYPE] DOMAIN\n" - "%1$s [OPTIONS...] --openpgp EMAIL@DOMAIN...\n" - "%1$s [OPTIONS...] --statistics\n" - "%1$s [OPTIONS...] --reset-statistics\n" - "\n" - "%2$sResolve domain names, IPv4 and IPv6 addresses, DNS records, and services.%3$s\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " -4 Resolve IPv4 addresses\n" - " -6 Resolve IPv6 addresses\n" - " -i --interface=INTERFACE Look on interface\n" - " -p --protocol=PROTO|help Look via protocol\n" - " -t --type=TYPE|help Query RR with DNS type\n" - " -c --class=CLASS|help Query RR with DNS class\n" - " --service Resolve service (SRV)\n" - " --service-address=BOOL Resolve address for services (default: yes)\n" - " --service-txt=BOOL Resolve TXT records for services (default: yes)\n" - " --openpgp Query OpenPGP public key\n" - " --tlsa Query TLS public key\n" - " --cname=BOOL Follow CNAME redirects (default: yes)\n" - " --search=BOOL Use search domains for single-label names\n" - " (default: yes)\n" - " --statistics Show resolver statistics\n" - " --reset-statistics Reset resolver statistics\n" - " --status Show link and server status\n" - " --flush-caches Flush all local DNS caches\n" - " --reset-server-features\n" - " Forget learnt DNS server feature levels\n" - " --set-dns=SERVER Set per-interface DNS server address\n" - " --set-domain=DOMAIN Set per-interface search domain\n" - " --set-llmnr=MODE Set per-interface LLMNR mode\n" - " --set-mdns=MODE Set per-interface MulticastDNS mode\n" - " --set-dnsovertls=MODE Set per-interface DNS-over-TLS mode\n" - " --set-dnssec=MODE Set per-interface DNSSEC mode\n" - " --set-nta=DOMAIN Set per-interface DNSSEC NTA\n" - " --revert Revert per-interface configuration\n" - " --raw[=payload|packet] Dump the answer as binary data\n" - " --no-pager Do not pipe output into a pager\n" - " --legend=BOOL Print headers and additional info (default: yes)\n" - "\nSee the %4$s for details.\n", - program_invocation_short_name, - ansi_highlight(), - ansi_normal(), - link); + help_cmdline("[OPTIONS…] HOSTNAME|ADDRESS…"); + help_cmdline("[OPTIONS…] --service [[NAME] TYPE] DOMAIN"); + help_cmdline("[OPTIONS…] --openpgp EMAIL@DOMAIN…"); + help_cmdline("[OPTIONS…] --statistics"); + help_cmdline("[OPTIONS…] --reset-statistics"); + help_abstract("Resolve domain names, IPv4 and IPv6 addresses, DNS records, and services."); + + help_section("Options"); + r = table_print_or_warn(options); + if (r < 0) + return r; + help_man_page_reference("resolvectl", "1"); return 0; } static int native_help(void) { - _cleanup_free_ char *link = NULL; + _cleanup_(table_unrefp) Table *verbs = NULL, *options = NULL; int r; - r = terminal_urlify_man("resolvectl", "1", &link); + r = verbs_get_help_table(&verbs); if (r < 0) - return log_oom(); + return r; + + r = option_parser_get_help_table_ns("resolvectl", &options); + if (r < 0) + return r; + + (void) table_sync_column_widths(0, verbs, options); pager_open(arg_pager_flags); - printf("%1$s [OPTIONS...] COMMAND ...\n" - "\n" - "%5$sSend control commands to the network name resolution manager, or%6$s\n" - "%5$sresolve domain names, IPv4 and IPv6 addresses, DNS records, and services.%6$s\n" - "\n%3$sCommands:%4$s\n" - " query HOSTNAME|ADDRESS... Resolve domain names, IPv4 and IPv6 addresses\n" - " service [[NAME] TYPE] DOMAIN Resolve service (SRV)\n" - " openpgp EMAIL@DOMAIN... Query OpenPGP public key\n" - " tlsa DOMAIN[:PORT]... Query TLS public key\n" - " status [LINK...] Show link and server status\n" - " statistics Show resolver statistics\n" - " reset-statistics Reset resolver statistics\n" - " flush-caches Flush all local DNS caches\n" - " reset-server-features Forget learnt DNS server feature levels\n" - " monitor Monitor DNS queries\n" - " show-cache Show cache contents\n" - " show-server-state Show servers state\n" - " dns [LINK [SERVER...]] Get/set per-interface DNS server address\n" - " domain [LINK [DOMAIN...]] Get/set per-interface search domain\n" - " default-route [LINK [BOOL]] Get/set per-interface default route flag\n" - " llmnr [LINK [MODE]] Get/set per-interface LLMNR mode\n" - " mdns [LINK [MODE]] Get/set per-interface MulticastDNS mode\n" - " dnsovertls [LINK [MODE]] Get/set per-interface DNS-over-TLS mode\n" - " dnssec [LINK [MODE]] Get/set per-interface DNSSEC mode\n" - " nta [LINK [DOMAIN...]] Get/set per-interface DNSSEC NTA\n" - " revert LINK Revert per-interface configuration\n" - " log-level [LEVEL] Get/set logging threshold for systemd-resolved\n" - "\n%3$sOptions:%4$s\n" - " -h --help Show this help\n" - " --version Show package version\n" - " -4 Resolve IPv4 addresses\n" - " -6 Resolve IPv6 addresses\n" - " -i --interface=INTERFACE Look on interface\n" - " -p --protocol=PROTO|help Look via protocol\n" - " -t --type=TYPE|help Query RR with DNS type\n" - " -c --class=CLASS|help Query RR with DNS class\n" - " --service-address=BOOL Resolve address for services (default: yes)\n" - " --service-txt=BOOL Resolve TXT records for services (default: yes)\n" - " --cname=BOOL Follow CNAME redirects (default: yes)\n" - " --validate=BOOL Allow DNSSEC validation (default: yes)\n" - " --synthesize=BOOL Allow synthetic response (default: yes)\n" - " --cache=BOOL Allow response from cache (default: yes)\n" - " --stale-data=BOOL Allow response from cache with stale data (default: yes)\n" - " --relax-single-label=BOOL Allow single label lookups to go upstream (default: no)\n" - " --zone=BOOL Allow response from locally registered mDNS/LLMNR\n" - " records (default: yes)\n" - " --trust-anchor=BOOL Allow response from local trust anchor (default:\n" - " yes)\n" - " --network=BOOL Allow response from network (default: yes)\n" - " --search=BOOL Use search domains for single-label names (default:\n" - " yes)\n" - " --raw[=payload|packet] Dump the answer as binary data\n" - " --no-pager Do not pipe output into a pager\n" - " --no-ask-password Do not prompt for password\n" - " --legend=BOOL Print headers and additional info (default: yes)\n" - " --json=MODE Output as JSON\n" - " -j Same as --json=pretty on tty, --json=short\n" - " otherwise\n" - "\nSee the %2$s for details.\n", - program_invocation_short_name, - link, - ansi_underline(), - ansi_normal(), - ansi_highlight(), - ansi_normal()); + help_cmdline("[OPTIONS…] COMMAND …"); + help_abstract("Send control commands to the network name resolution manager, or\n" + "resolve domain names, IPv4 and IPv6 addresses, DNS records, and services."); + + help_section("Commands"); + r = table_print_or_warn(verbs); + if (r < 0) + return r; + help_section("Options"); + r = table_print_or_warn(options); + if (r < 0) + return r; + + help_man_page_reference("resolvectl", "1"); return 0; } -static int verb_help(int argc, char *argv[], uintptr_t _data, void *userdata) { - return native_help(); -} - -static int compat_parse_argv(int argc, char *argv[]) { - enum { - ARG_VERSION = 0x100, - ARG_LEGEND, - ARG_SERVICE, - ARG_CNAME, - ARG_SERVICE_ADDRESS, - ARG_SERVICE_TXT, - ARG_OPENPGP, - ARG_TLSA, - ARG_RAW, - ARG_SEARCH, - ARG_STATISTICS, - ARG_RESET_STATISTICS, - ARG_STATUS, - ARG_FLUSH_CACHES, - ARG_RESET_SERVER_FEATURES, - ARG_NO_PAGER, - ARG_SET_DNS, - ARG_SET_DOMAIN, - ARG_SET_LLMNR, - ARG_SET_MDNS, - ARG_SET_DNS_OVER_TLS, - ARG_SET_DNSSEC, - ARG_SET_NTA, - ARG_REVERT_LINK, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "type", required_argument, NULL, 't' }, - { "class", required_argument, NULL, 'c' }, - { "legend", required_argument, NULL, ARG_LEGEND }, - { "interface", required_argument, NULL, 'i' }, - { "protocol", required_argument, NULL, 'p' }, - { "cname", required_argument, NULL, ARG_CNAME }, - { "service", no_argument, NULL, ARG_SERVICE }, - { "service-address", required_argument, NULL, ARG_SERVICE_ADDRESS }, - { "service-txt", required_argument, NULL, ARG_SERVICE_TXT }, - { "openpgp", no_argument, NULL, ARG_OPENPGP }, - { "tlsa", optional_argument, NULL, ARG_TLSA }, - { "raw", optional_argument, NULL, ARG_RAW }, - { "search", required_argument, NULL, ARG_SEARCH }, - { "statistics", no_argument, NULL, ARG_STATISTICS, }, - { "reset-statistics", no_argument, NULL, ARG_RESET_STATISTICS }, - { "status", no_argument, NULL, ARG_STATUS }, - { "flush-caches", no_argument, NULL, ARG_FLUSH_CACHES }, - { "reset-server-features", no_argument, NULL, ARG_RESET_SERVER_FEATURES }, - { "no-pager", no_argument, NULL, ARG_NO_PAGER }, - { "set-dns", required_argument, NULL, ARG_SET_DNS }, - { "set-domain", required_argument, NULL, ARG_SET_DOMAIN }, - { "set-llmnr", required_argument, NULL, ARG_SET_LLMNR }, - { "set-mdns", required_argument, NULL, ARG_SET_MDNS }, - { "set-dnsovertls", required_argument, NULL, ARG_SET_DNS_OVER_TLS }, - { "set-dnssec", required_argument, NULL, ARG_SET_DNSSEC }, - { "set-nta", required_argument, NULL, ARG_SET_NTA }, - { "revert", no_argument, NULL, ARG_REVERT_LINK }, - {} - }; +VERB_COMMON_HELP_HIDDEN(native_help); - int c, r; +static int compat_parse_argv(int argc, char *argv[], char ***remaining_args) { + int r; assert(argc >= 0); assert(argv); + assert(remaining_args); + + OptionParser opts = { argc, argv, .namespace = "systemd-resolve" }; - while ((c = getopt_long(argc, argv, "h46i:t:c:p:", options, NULL)) >= 0) + FOREACH_OPTION_OR_RETURN(c, &opts) switch (c) { - case 'h': + OPTION_NAMESPACE("systemd-resolve"): {} + + OPTION_COMMON_HELP: return compat_help(); - case ARG_VERSION: + OPTION_COMMON_VERSION: return version(); - case '4': + OPTION_SHORT('4', NULL, "Resolve IPv4 addresses"): arg_family = AF_INET; break; - case '6': + OPTION_SHORT('6', NULL, "Resolve IPv6 addresses"): arg_family = AF_INET6; break; - case 'i': - r = ifname_mangle(optarg); + OPTION('i', "interface", "INTERFACE", "Look on interface"): + r = ifname_mangle(opts.arg); if (r < 0) return r; break; - case 'p': - r = parse_protocol(optarg); + OPTION('p', "protocol", "PROTO|help", "Look via protocol"): + r = parse_protocol(opts.arg); if (r <= 0) return r; break; - case 't': - if (streq(optarg, "help")) { + OPTION('t', "type", "TYPE|help", "Query RR with DNS type"): + if (streq(opts.arg, "help")) { help_dns_types(); return 0; } - r = dns_type_from_string(optarg); + r = dns_type_from_string(opts.arg); if (r < 0) - return log_error_errno(r, "Failed to parse RR record type %s: %m", optarg); + return log_error_errno(r, "Failed to parse RR record type %s: %m", opts.arg); arg_type = (uint16_t) r; assert((int) arg_type == r); @@ -3439,167 +3350,162 @@ static int compat_parse_argv(int argc, char *argv[]) { arg_mode = MODE_RESOLVE_RECORD; break; - case 'c': - if (streq(optarg, "help")) { + OPTION('c', "class", "CLASS|help", "Query RR with DNS class"): + if (streq(opts.arg, "help")) { help_dns_classes(); return 0; } - r = dns_class_from_string(optarg); + r = dns_class_from_string(opts.arg); if (r < 0) - return log_error_errno(r, "Failed to parse RR record class %s: %m", optarg); + return log_error_errno(r, "Failed to parse RR record class %s: %m", opts.arg); arg_class = (uint16_t) r; assert((int) arg_class == r); break; - case ARG_SERVICE: + OPTION_LONG("service", NULL, "Resolve service (SRV)"): arg_mode = MODE_RESOLVE_SERVICE; break; - case ARG_SERVICE_ADDRESS: - r = parse_boolean_argument("--service-address=", optarg, NULL); + OPTION_LONG("service-address", "BOOL", "Resolve address for services (default: yes)"): + r = parse_boolean_argument("--service-address=", opts.arg, NULL); if (r < 0) return r; SET_FLAG(arg_flags, SD_RESOLVED_NO_ADDRESS, r == 0); break; - case ARG_SERVICE_TXT: - r = parse_boolean_argument("--service-txt=", optarg, NULL); + OPTION_LONG("service-txt", "BOOL", "Resolve TXT records for services (default: yes)"): + r = parse_boolean_argument("--service-txt=", opts.arg, NULL); if (r < 0) return r; SET_FLAG(arg_flags, SD_RESOLVED_NO_TXT, r == 0); break; - case ARG_OPENPGP: + OPTION_LONG("openpgp", NULL, "Query OpenPGP public key"): arg_mode = MODE_RESOLVE_OPENPGP; break; - case ARG_TLSA: + OPTION_LONG_FLAGS(OPTION_OPTIONAL_ARG, "tlsa", "FAMILY", "Query TLS public key"): arg_mode = MODE_RESOLVE_TLSA; - if (!optarg || service_family_is_valid(optarg)) - arg_service_family = optarg; + if (!opts.arg || service_family_is_valid(opts.arg)) + arg_service_family = opts.arg; else return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Unknown service family \"%s\".", optarg); + "Unknown service family \"%s\".", opts.arg); break; - case ARG_CNAME: - r = parse_boolean_argument("--cname=", optarg, NULL); + OPTION_LONG("cname", "BOOL", "Follow CNAME redirects (default: yes)"): + r = parse_boolean_argument("--cname=", opts.arg, NULL); if (r < 0) return r; SET_FLAG(arg_flags, SD_RESOLVED_NO_CNAME, r == 0); break; - - case ARG_SEARCH: - r = parse_boolean_argument("--search=", optarg, NULL); + OPTION_LONG("search", "BOOL", "Use search domains for single-label names (default: yes)"): + r = parse_boolean_argument("--search=", opts.arg, NULL); if (r < 0) return r; SET_FLAG(arg_flags, SD_RESOLVED_NO_SEARCH, r == 0); break; - case ARG_STATISTICS: + OPTION_LONG("statistics", NULL, "Show resolver statistics"): arg_mode = MODE_STATISTICS; break; - case ARG_RESET_STATISTICS: + OPTION_LONG("reset-statistics", NULL, "Reset resolver statistics"): arg_mode = MODE_RESET_STATISTICS; break; - case ARG_STATUS: + OPTION_LONG("status", NULL, "Show link and server status"): arg_mode = MODE_STATUS; break; - case ARG_FLUSH_CACHES: + OPTION_LONG("flush-caches", NULL, "Flush all local DNS caches"): arg_mode = MODE_FLUSH_CACHES; break; - case ARG_RESET_SERVER_FEATURES: + OPTION_LONG("reset-server-features", NULL, + "Forget learnt DNS server feature levels"): arg_mode = MODE_RESET_SERVER_FEATURES; break; - case ARG_SET_DNS: - r = strv_extend(&arg_set_dns, optarg); + OPTION_LONG("set-dns", "SERVER", "Set per-interface DNS server address"): + r = strv_extend(&arg_set_dns, opts.arg); if (r < 0) return log_oom(); arg_mode = MODE_SET_LINK; break; - case ARG_SET_DOMAIN: - r = strv_extend(&arg_set_domain, optarg); + OPTION_LONG("set-domain", "DOMAIN", "Set per-interface search domain"): + r = strv_extend(&arg_set_domain, opts.arg); if (r < 0) return log_oom(); arg_mode = MODE_SET_LINK; break; - case ARG_SET_LLMNR: - arg_set_llmnr = optarg; + OPTION_LONG("set-llmnr", "MODE", "Set per-interface LLMNR mode"): + arg_set_llmnr = opts.arg; arg_mode = MODE_SET_LINK; break; - case ARG_SET_MDNS: - arg_set_mdns = optarg; + OPTION_LONG("set-mdns", "MODE", "Set per-interface MulticastDNS mode"): + arg_set_mdns = opts.arg; arg_mode = MODE_SET_LINK; break; - case ARG_SET_DNS_OVER_TLS: - arg_set_dns_over_tls = optarg; + OPTION_LONG("set-dnsovertls", "MODE", "Set per-interface DNS-over-TLS mode"): + arg_set_dns_over_tls = opts.arg; arg_mode = MODE_SET_LINK; break; - case ARG_SET_DNSSEC: - arg_set_dnssec = optarg; + OPTION_LONG("set-dnssec", "MODE", "Set per-interface DNSSEC mode"): + arg_set_dnssec = opts.arg; arg_mode = MODE_SET_LINK; break; - case ARG_SET_NTA: - r = strv_extend(&arg_set_nta, optarg); + OPTION_LONG("set-nta", "DOMAIN", "Set per-interface DNSSEC NTA"): + r = strv_extend(&arg_set_nta, opts.arg); if (r < 0) return log_oom(); arg_mode = MODE_SET_LINK; break; - case ARG_REVERT_LINK: + OPTION_LONG("revert", NULL, "Revert per-interface configuration"): arg_mode = MODE_REVERT_LINK; break; - case ARG_RAW: + OPTION_LONG_FLAGS(OPTION_OPTIONAL_ARG, "raw", "payload|packet", + "Dump the answer as binary data"): if (on_tty()) return log_error_errno(SYNTHETIC_ERRNO(ENOTTY), "Refusing to write binary data to tty."); - if (optarg == NULL || streq(optarg, "payload")) + if (opts.arg == NULL || streq(opts.arg, "payload")) arg_raw = RAW_PAYLOAD; - else if (streq(optarg, "packet")) + else if (streq(opts.arg, "packet")) arg_raw = RAW_PACKET; else return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown --raw specifier \"%s\".", - optarg); + opts.arg); arg_legend = false; break; - case ARG_NO_PAGER: + OPTION_COMMON_NO_PAGER: arg_pager_flags |= PAGER_DISABLE; break; - case ARG_LEGEND: - r = parse_boolean_argument("--legend=", optarg, &arg_legend); + OPTION_LONG("legend", "BOOL", "Print headers and additional info (default: yes)"): + r = parse_boolean_argument("--legend=", opts.arg, &arg_legend); if (r < 0) return r; break; - - case '?': - return -EINVAL; - - default: - assert_not_reached(); } if (arg_type == 0 && arg_class != 0) @@ -3623,253 +3529,209 @@ static int compat_parse_argv(int argc, char *argv[]) { "--set-dns=, --set-domain=, --set-llmnr=, --set-mdns=, --set-dnsovertls=, --set-dnssec=, --set-nta= and --revert require --interface=."); } + *remaining_args = option_parser_get_args(&opts); return 1 /* work to do */; } -static int native_parse_argv(int argc, char *argv[]) { - enum { - ARG_VERSION = 0x100, - ARG_LEGEND, - ARG_CNAME, - ARG_VALIDATE, - ARG_SYNTHESIZE, - ARG_CACHE, - ARG_ZONE, - ARG_TRUST_ANCHOR, - ARG_NETWORK, - ARG_SERVICE_ADDRESS, - ARG_SERVICE_TXT, - ARG_RAW, - ARG_SEARCH, - ARG_NO_PAGER, - ARG_NO_ASK_PASSWORD, - ARG_JSON, - ARG_STALE_DATA, - ARG_RELAX_SINGLE_LABEL, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "type", required_argument, NULL, 't' }, - { "class", required_argument, NULL, 'c' }, - { "legend", required_argument, NULL, ARG_LEGEND }, - { "interface", required_argument, NULL, 'i' }, - { "protocol", required_argument, NULL, 'p' }, - { "cname", required_argument, NULL, ARG_CNAME }, - { "validate", required_argument, NULL, ARG_VALIDATE }, - { "synthesize", required_argument, NULL, ARG_SYNTHESIZE }, - { "cache", required_argument, NULL, ARG_CACHE }, - { "zone", required_argument, NULL, ARG_ZONE }, - { "trust-anchor", required_argument, NULL, ARG_TRUST_ANCHOR }, - { "network", required_argument, NULL, ARG_NETWORK }, - { "service-address", required_argument, NULL, ARG_SERVICE_ADDRESS }, - { "service-txt", required_argument, NULL, ARG_SERVICE_TXT }, - { "raw", optional_argument, NULL, ARG_RAW }, - { "search", required_argument, NULL, ARG_SEARCH }, - { "no-pager", no_argument, NULL, ARG_NO_PAGER }, - { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, - { "json", required_argument, NULL, ARG_JSON }, - { "stale-data", required_argument, NULL, ARG_STALE_DATA }, - { "relax-single-label", required_argument, NULL, ARG_RELAX_SINGLE_LABEL }, - {} - }; - - int c, r; +static int native_parse_argv(int argc, char *argv[], char ***remaining_args) { + int r; assert(argc >= 0); assert(argv); + assert(remaining_args); + + OptionParser opts = { argc, argv, .namespace = "resolvectl" }; - while ((c = getopt_long(argc, argv, "h46i:t:c:p:j", options, NULL)) >= 0) + FOREACH_OPTION_OR_RETURN(c, &opts) switch (c) { - case 'h': + OPTION_NAMESPACE("resolvectl"): {} + + OPTION_COMMON_HELP: return native_help(); - case ARG_VERSION: + OPTION_COMMON_VERSION: return version(); - case '4': + OPTION_SHORT('4', NULL, "Resolve IPv4 addresses"): arg_family = AF_INET; break; - case '6': + OPTION_SHORT('6', NULL, "Resolve IPv6 addresses"): arg_family = AF_INET6; break; - case 'i': - r = ifname_mangle(optarg); + OPTION('i', "interface", "INTERFACE", "Look on interface"): + r = ifname_mangle(opts.arg); if (r < 0) return r; break; - case 'p': - r = parse_protocol(optarg); + OPTION('p', "protocol", "PROTO|help", "Look via protocol"): + r = parse_protocol(opts.arg); if (r <= 0) return r; break; - case 't': - if (streq(optarg, "help")) { + OPTION('t', "type", "TYPE|help", "Query RR with DNS type"): + if (streq(opts.arg, "help")) { help_dns_types(); return 0; } - r = dns_type_from_string(optarg); + r = dns_type_from_string(opts.arg); if (r < 0) - return log_error_errno(r, "Failed to parse RR record type %s: %m", optarg); + return log_error_errno(r, "Failed to parse RR record type %s: %m", opts.arg); arg_type = (uint16_t) r; assert((int) arg_type == r); break; - case 'c': - if (streq(optarg, "help")) { + OPTION('c', "class", "CLASS|help", "Query RR with DNS class"): + if (streq(opts.arg, "help")) { help_dns_classes(); return 0; } - r = dns_class_from_string(optarg); + r = dns_class_from_string(opts.arg); if (r < 0) - return log_error_errno(r, "Failed to parse RR record class %s: %m", optarg); + return log_error_errno(r, "Failed to parse RR record class %s: %m", opts.arg); arg_class = (uint16_t) r; assert((int) arg_class == r); break; - case ARG_SERVICE_ADDRESS: - r = parse_boolean_argument("--service-address=", optarg, NULL); + OPTION_LONG("service-address", "BOOL", "Resolve address for services (default: yes)"): + r = parse_boolean_argument("--service-address=", opts.arg, NULL); if (r < 0) return r; SET_FLAG(arg_flags, SD_RESOLVED_NO_ADDRESS, r == 0); break; - case ARG_SERVICE_TXT: - r = parse_boolean_argument("--service-txt=", optarg, NULL); + OPTION_LONG("service-txt", "BOOL", "Resolve TXT records for services (default: yes)"): + r = parse_boolean_argument("--service-txt=", opts.arg, NULL); if (r < 0) return r; SET_FLAG(arg_flags, SD_RESOLVED_NO_TXT, r == 0); break; - case ARG_CNAME: - r = parse_boolean_argument("--cname=", optarg, NULL); + OPTION_LONG("cname", "BOOL", "Follow CNAME redirects (default: yes)"): + r = parse_boolean_argument("--cname=", opts.arg, NULL); if (r < 0) return r; SET_FLAG(arg_flags, SD_RESOLVED_NO_CNAME, r == 0); break; - case ARG_VALIDATE: - r = parse_boolean_argument("--validate=", optarg, NULL); + OPTION_LONG("validate", "BOOL", "Allow DNSSEC validation (default: yes)"): + r = parse_boolean_argument("--validate=", opts.arg, NULL); if (r < 0) return r; SET_FLAG(arg_flags, SD_RESOLVED_NO_VALIDATE, r == 0); break; - case ARG_SYNTHESIZE: - r = parse_boolean_argument("--synthesize=", optarg, NULL); + OPTION_LONG("synthesize", "BOOL", "Allow synthetic response (default: yes)"): + r = parse_boolean_argument("--synthesize=", opts.arg, NULL); if (r < 0) return r; SET_FLAG(arg_flags, SD_RESOLVED_NO_SYNTHESIZE, r == 0); break; - case ARG_CACHE: - r = parse_boolean_argument("--cache=", optarg, NULL); + OPTION_LONG("cache", "BOOL", "Allow response from cache (default: yes)"): + r = parse_boolean_argument("--cache=", opts.arg, NULL); if (r < 0) return r; SET_FLAG(arg_flags, SD_RESOLVED_NO_CACHE, r == 0); break; - case ARG_STALE_DATA: - r = parse_boolean_argument("--stale-data=", optarg, NULL); + OPTION_LONG("stale-data", "BOOL", + "Allow response from cache with stale data (default: yes)"): + r = parse_boolean_argument("--stale-data=", opts.arg, NULL); if (r < 0) return r; SET_FLAG(arg_flags, SD_RESOLVED_NO_STALE, r == 0); break; - case ARG_RELAX_SINGLE_LABEL: - r = parse_boolean_argument("--relax-single-label=", optarg, NULL); + OPTION_LONG("relax-single-label", "BOOL", + "Allow single label lookups to go upstream (default: no)"): + r = parse_boolean_argument("--relax-single-label=", opts.arg, NULL); if (r < 0) return r; SET_FLAG(arg_flags, SD_RESOLVED_RELAX_SINGLE_LABEL, r > 0); break; - case ARG_ZONE: - r = parse_boolean_argument("--zone=", optarg, NULL); + OPTION_LONG("zone", "BOOL", + "Allow response from locally registered mDNS/LLMNR records (default: yes)"): + r = parse_boolean_argument("--zone=", opts.arg, NULL); if (r < 0) return r; SET_FLAG(arg_flags, SD_RESOLVED_NO_ZONE, r == 0); break; - case ARG_TRUST_ANCHOR: - r = parse_boolean_argument("--trust-anchor=", optarg, NULL); + OPTION_LONG("trust-anchor", "BOOL", + "Allow response from local trust anchor (default: yes)"): + r = parse_boolean_argument("--trust-anchor=", opts.arg, NULL); if (r < 0) return r; SET_FLAG(arg_flags, SD_RESOLVED_NO_TRUST_ANCHOR, r == 0); break; - case ARG_NETWORK: - r = parse_boolean_argument("--network=", optarg, NULL); + OPTION_LONG("network", "BOOL", "Allow response from network (default: yes)"): + r = parse_boolean_argument("--network=", opts.arg, NULL); if (r < 0) return r; SET_FLAG(arg_flags, SD_RESOLVED_NO_NETWORK, r == 0); break; - case ARG_SEARCH: - r = parse_boolean_argument("--search=", optarg, NULL); + OPTION_LONG("search", "BOOL", "Use search domains for single-label names (default: yes)"): + r = parse_boolean_argument("--search=", opts.arg, NULL); if (r < 0) return r; SET_FLAG(arg_flags, SD_RESOLVED_NO_SEARCH, r == 0); break; - case ARG_RAW: + OPTION_LONG_FLAGS(OPTION_OPTIONAL_ARG, "raw", "payload|packet", + "Dump the answer as binary data"): if (on_tty()) return log_error_errno(SYNTHETIC_ERRNO(ENOTTY), "Refusing to write binary data to tty."); - if (optarg == NULL || streq(optarg, "payload")) + if (opts.arg == NULL || streq(opts.arg, "payload")) arg_raw = RAW_PAYLOAD; - else if (streq(optarg, "packet")) + else if (streq(opts.arg, "packet")) arg_raw = RAW_PACKET; else return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown --raw specifier \"%s\".", - optarg); + opts.arg); arg_legend = false; break; - case ARG_NO_PAGER: + OPTION_COMMON_NO_PAGER: arg_pager_flags |= PAGER_DISABLE; break; - case ARG_NO_ASK_PASSWORD: + OPTION_COMMON_NO_ASK_PASSWORD: arg_ask_password = false; break; - case ARG_LEGEND: - r = parse_boolean_argument("--legend=", optarg, &arg_legend); + OPTION_LONG("legend", "BOOL", "Print headers and additional info (default: yes)"): + r = parse_boolean_argument("--legend=", opts.arg, &arg_legend); if (r < 0) return r; 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; - break; - case 'j': + OPTION_COMMON_LOWERCASE_J: arg_json_format_flags = SD_JSON_FORMAT_PRETTY_AUTO|SD_JSON_FORMAT_COLOR_AUTO; break; - - case '?': - return -EINVAL; - - default: - assert_not_reached(); } if (arg_type == 0 && arg_class != 0) @@ -3882,140 +3744,107 @@ static int native_parse_argv(int argc, char *argv[]) { if (arg_class != 0 && arg_type == 0) arg_type = DNS_TYPE_A; + *remaining_args = option_parser_get_args(&opts); return 1 /* work to do */; } -static int native_main(int argc, char *argv[]) { - - static const Verb verbs[] = { - { "help", VERB_ANY, VERB_ANY, 0, verb_help }, - { "status", VERB_ANY, VERB_ANY, VERB_DEFAULT, verb_status }, - { "query", 2, VERB_ANY, 0, verb_query }, - { "service", 2, 4, 0, verb_service }, - { "openpgp", 2, VERB_ANY, 0, verb_openpgp }, - { "tlsa", 2, VERB_ANY, 0, verb_tlsa }, - { "statistics", VERB_ANY, 1, 0, verb_show_statistics }, - { "reset-statistics", VERB_ANY, 1, 0, verb_reset_statistics }, - { "flush-caches", VERB_ANY, 1, 0, verb_flush_caches }, - { "reset-server-features", VERB_ANY, 1, 0, verb_reset_server_features }, - { "dns", VERB_ANY, VERB_ANY, 0, verb_dns }, - { "domain", VERB_ANY, VERB_ANY, 0, verb_domain }, - { "default-route", VERB_ANY, 3, 0, verb_default_route }, - { "llmnr", VERB_ANY, 3, 0, verb_llmnr }, - { "mdns", VERB_ANY, 3, 0, verb_mdns }, - { "dnsovertls", VERB_ANY, 3, 0, verb_dns_over_tls }, - { "dnssec", VERB_ANY, 3, 0, verb_dnssec }, - { "nta", VERB_ANY, VERB_ANY, 0, verb_nta }, - { "revert", VERB_ANY, 2, 0, verb_revert_link }, - { "log-level", VERB_ANY, 2, 0, verb_log_level }, - { "monitor", VERB_ANY, 1, 0, verb_monitor }, - { "show-cache", VERB_ANY, 1, 0, verb_show_cache }, - { "show-server-state", VERB_ANY, 1, 0, verb_show_server_state }, - {} - }; - - return dispatch_verb(argc, argv, verbs, /* userdata= */ NULL); -} - -static int translate(const char *verb, const char *single_arg, size_t num_args, char **args) { +static int translate(const char *verb, const char *single_arg, char **args) { char **fake, **p; size_t num; assert(verb); - assert(num_args == 0 || args); - num = !!single_arg + num_args + 1; + num = !!single_arg + strv_length(args) + 1; p = fake = newa0(char *, num + 1); *p++ = (char *) verb; if (single_arg) *p++ = (char *) single_arg; - FOREACH_ARRAY(arg, args, num_args) - *p++ = *arg; + STRV_FOREACH(a, args) + *p++ = *a; - optind = 0; - return native_main((int) num, fake); + return dispatch_verb_with_args(fake, /* userdata= */ NULL); } -static int compat_main(int argc, char *argv[]) { +static int compat_main(char **args) { int r = 0; switch (arg_mode) { case MODE_RESOLVE_HOST: case MODE_RESOLVE_RECORD: - return translate("query", NULL, argc - optind, argv + optind); + return translate("query", NULL, args); case MODE_RESOLVE_SERVICE: - return translate("service", NULL, argc - optind, argv + optind); + return translate("service", NULL, args); case MODE_RESOLVE_OPENPGP: - return translate("openpgp", NULL, argc - optind, argv + optind); + return translate("openpgp", NULL, args); case MODE_RESOLVE_TLSA: - return translate("tlsa", arg_service_family, argc - optind, argv + optind); + return translate("tlsa", arg_service_family, args); case MODE_STATISTICS: - return translate("statistics", NULL, 0, NULL); + return translate("statistics", NULL, NULL); case MODE_RESET_STATISTICS: - return translate("reset-statistics", NULL, 0, NULL); + return translate("reset-statistics", NULL, NULL); case MODE_FLUSH_CACHES: - return translate("flush-caches", NULL, 0, NULL); + return translate("flush-caches", NULL, NULL); case MODE_RESET_SERVER_FEATURES: - return translate("reset-server-features", NULL, 0, NULL); + return translate("reset-server-features", NULL, NULL); case MODE_STATUS: - return translate("status", NULL, argc - optind, argv + optind); + return translate("status", NULL, args); case MODE_SET_LINK: assert(arg_ifname); if (arg_disable_default_route) { - r = translate("default-route", arg_ifname, 1, STRV_MAKE("no")); + r = translate("default-route", arg_ifname, STRV_MAKE("no")); if (r < 0) return r; } if (arg_set_dns) { - r = translate("dns", arg_ifname, strv_length(arg_set_dns), arg_set_dns); + r = translate("dns", arg_ifname, arg_set_dns); if (r < 0) return r; } if (arg_set_domain) { - r = translate("domain", arg_ifname, strv_length(arg_set_domain), arg_set_domain); + r = translate("domain", arg_ifname, arg_set_domain); if (r < 0) return r; } if (arg_set_nta) { - r = translate("nta", arg_ifname, strv_length(arg_set_nta), arg_set_nta); + r = translate("nta", arg_ifname, arg_set_nta); if (r < 0) return r; } if (arg_set_llmnr) { - r = translate("llmnr", arg_ifname, 1, (char **) &arg_set_llmnr); + r = translate("llmnr", arg_ifname, STRV_MAKE(arg_set_llmnr)); if (r < 0) return r; } if (arg_set_mdns) { - r = translate("mdns", arg_ifname, 1, (char **) &arg_set_mdns); + r = translate("mdns", arg_ifname, STRV_MAKE(arg_set_mdns)); if (r < 0) return r; } if (arg_set_dns_over_tls) { - r = translate("dnsovertls", arg_ifname, 1, (char **) &arg_set_dns_over_tls); + r = translate("dnsovertls", arg_ifname, STRV_MAKE(arg_set_dns_over_tls)); if (r < 0) return r; } if (arg_set_dnssec) { - r = translate("dnssec", arg_ifname, 1, (char **) &arg_set_dnssec); + r = translate("dnssec", arg_ifname, STRV_MAKE(arg_set_dnssec)); if (r < 0) return r; } @@ -4025,7 +3854,7 @@ static int compat_main(int argc, char *argv[]) { case MODE_REVERT_LINK: assert(arg_ifname); - return translate("revert", arg_ifname, 0, NULL); + return translate("revert", arg_ifname, NULL); case _MODE_INVALID: assert_not_reached(); @@ -4035,6 +3864,7 @@ static int compat_main(int argc, char *argv[]) { } static int run(int argc, char **argv) { + char **args = NULL; bool compat = false; int r; @@ -4046,16 +3876,16 @@ static int run(int argc, char **argv) { r = resolvconf_parse_argv(argc, argv); } else if (invoked_as(argv, "systemd-resolve")) { compat = true; - r = compat_parse_argv(argc, argv); + r = compat_parse_argv(argc, argv, &args); } else - r = native_parse_argv(argc, argv); + r = native_parse_argv(argc, argv, &args); if (r <= 0) return r; if (compat) - return compat_main(argc, argv); + return compat_main(args); - return native_main(argc, argv); + return dispatch_verb_with_args(args, /* userdata= */ NULL); } DEFINE_MAIN_FUNCTION(run);