1 /* SPDX-License-Identifier: LGPL-2.1+ */
6 #include "alloc-util.h"
8 #include "dns-domain.h"
9 #include "extract-word.h"
11 #include "parse-util.h"
12 #include "pretty-print.h"
13 #include "resolvconf-compat.h"
14 #include "resolvectl.h"
15 #include "resolved-def.h"
16 #include "string-util.h"
18 #include "terminal-util.h"
20 static int resolvconf_help(void) {
21 _cleanup_free_
char *link
= NULL
;
24 r
= terminal_urlify_man("resolvectl", "1", &link
);
28 printf("%1$s -a INTERFACE < FILE\n"
31 "Register DNS server and domain configuration with systemd-resolved.\n\n"
32 " -h --help Show this help\n"
33 " --version Show package version\n"
34 " -a Register per-interface DNS server and domain data\n"
35 " -d Unregister per-interface DNS server and domain data\n"
36 " -f Ignore if specified interface does not exist\n"
37 " -x Send DNS traffic preferably over this interface\n"
39 "This is a compatibility alias for the resolvectl(1) tool, providing native\n"
40 "command line compatibility with the resolvconf(8) tool of various Linux\n"
41 "distributions and BSD systems. Some options supported by other implementations\n"
42 "are not supported and are ignored: -m, -p. Various options supported by other\n"
43 "implementations are not supported and will cause the invocation to fail: -u,\n"
44 "-I, -i, -l, -R, -r, -v, -V, --enable-updates, --disable-updates,\n"
45 "--updates-are-enabled.\n"
46 "\nSee the %2$s for details.\n"
47 , program_invocation_short_name
54 static int parse_nameserver(const char *string
) {
60 _cleanup_free_
char *word
= NULL
;
62 r
= extract_first_word(&string
, &word
, NULL
, 0);
68 if (strv_push(&arg_set_dns
, word
) < 0)
77 static int parse_search_domain(const char *string
) {
83 _cleanup_free_
char *word
= NULL
;
85 r
= extract_first_word(&string
, &word
, NULL
, EXTRACT_QUOTES
);
91 if (strv_push(&arg_set_domain
, word
) < 0)
100 int resolvconf_parse_argv(int argc
, char *argv
[]) {
106 ARG_UPDATES_ARE_ENABLED
,
109 static const struct option options
[] = {
110 { "help", no_argument
, NULL
, 'h' },
111 { "version", no_argument
, NULL
, ARG_VERSION
},
113 /* The following are specific to Debian's original resolvconf */
114 { "enable-updates", no_argument
, NULL
, ARG_ENABLE_UPDATES
},
115 { "disable-updates", no_argument
, NULL
, ARG_DISABLE_UPDATES
},
116 { "updates-are-enabled", no_argument
, NULL
, ARG_UPDATES_ARE_ENABLED
},
122 TYPE_PRIVATE
, /* -p: Not supported, treated identically to TYPE_REGULAR */
123 TYPE_EXCLUSIVE
, /* -x */
124 } type
= TYPE_REGULAR
;
131 /* openresolv checks these environment variables */
132 if (getenv("IF_EXCLUSIVE"))
133 type
= TYPE_EXCLUSIVE
;
134 if (getenv("IF_PRIVATE"))
135 type
= TYPE_PRIVATE
; /* not actually supported */
137 arg_mode
= _MODE_INVALID
;
139 while ((c
= getopt_long(argc
, argv
, "hadxpfm:uIi:l:Rr:vV", options
, NULL
)) >= 0)
143 return resolvconf_help();
148 /* -a and -d is what everybody can agree on */
150 arg_mode
= MODE_SET_LINK
;
154 arg_mode
= MODE_REVERT_LINK
;
157 /* The exclusive/private/force stuff is an openresolv invention, we support in some skewed way */
159 type
= TYPE_EXCLUSIVE
;
163 type
= TYPE_PRIVATE
; /* not actually supported */
167 arg_ifindex_permissive
= true;
170 /* The metrics stuff is an openresolv invention we ignore (and don't really need) */
172 log_debug("Switch -%c ignored.", c
);
175 /* Everybody else can agree on the existence of -u but we don't support it. */
178 /* The following options are openresolv inventions we don't support. */
186 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
187 "Switch -%c not supported.", c
);
189 /* The Debian resolvconf commands we don't support. */
190 case ARG_ENABLE_UPDATES
:
191 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
192 "Switch --enable-updates not supported.");
193 case ARG_DISABLE_UPDATES
:
194 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
195 "Switch --disable-updates not supported.");
196 case ARG_UPDATES_ARE_ENABLED
:
197 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
198 "Switch --updates-are-enabled not supported.");
204 assert_not_reached("Unhandled option");
207 if (arg_mode
== _MODE_INVALID
)
208 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
209 "Expected either -a or -d on the command line.");
211 if (optind
+1 != argc
)
212 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
213 "Expected interface name as argument.");
215 r
= ifname_mangle(argv
[optind
]);
221 if (arg_mode
== MODE_SET_LINK
) {
225 _cleanup_free_
char *line
= NULL
;
228 r
= read_line(stdin
, LONG_LINE_MAX
, &line
);
230 return log_error_errno(r
, "Failed to read from stdin: %m");
237 if (IN_SET(*l
, '#', ';', 0))
240 a
= first_word(l
, "nameserver");
242 (void) parse_nameserver(a
);
246 a
= first_word(l
, "domain");
248 a
= first_word(l
, "search");
250 (void) parse_search_domain(a
);
254 log_syntax(NULL
, LOG_DEBUG
, "stdin", n
, 0, "Ignoring resolv.conf line: %s", l
);
257 if (type
== TYPE_EXCLUSIVE
) {
259 /* If -x mode is selected, let's preferably route non-suffixed lookups to this interface. This
260 * somewhat matches the original -x behaviour */
262 r
= strv_extend(&arg_set_domain
, "~.");
266 } else if (type
== TYPE_PRIVATE
)
267 log_debug("Private DNS server data not supported, ignoring.");
270 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
271 "No DNS servers specified, refusing operation.");
274 return 1; /* work to do */