]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolvconf-compat.c
dissect: use DISKSEQ when waiting for block devices
[thirdparty/systemd.git] / src / resolve / resolvconf-compat.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <getopt.h>
4 #include <net/if.h>
5
6 #include "alloc-util.h"
7 #include "def.h"
8 #include "dns-domain.h"
9 #include "extract-word.h"
10 #include "fileio.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"
17 #include "strv.h"
18 #include "terminal-util.h"
19
20 static int resolvconf_help(void) {
21 _cleanup_free_ char *link = NULL;
22 int r;
23
24 r = terminal_urlify_man("resolvectl", "1", &link);
25 if (r < 0)
26 return log_oom();
27
28 printf("%1$s -a INTERFACE < FILE\n"
29 "%1$s -d INTERFACE\n"
30 "\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"
38 "\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,
48 link);
49
50 return 0;
51 }
52
53 static int parse_nameserver(const char *string) {
54 int r;
55
56 assert(string);
57
58 for (;;) {
59 _cleanup_free_ char *word = NULL;
60
61 r = extract_first_word(&string, &word, NULL, 0);
62 if (r < 0)
63 return r;
64 if (r == 0)
65 break;
66
67 if (strv_push(&arg_set_dns, word) < 0)
68 return log_oom();
69
70 word = NULL;
71 }
72
73 return 0;
74 }
75
76 static int parse_search_domain(const char *string) {
77 int r;
78
79 assert(string);
80
81 for (;;) {
82 _cleanup_free_ char *word = NULL;
83
84 r = extract_first_word(&string, &word, NULL, EXTRACT_UNQUOTE);
85 if (r < 0)
86 return r;
87 if (r == 0)
88 break;
89
90 if (strv_push(&arg_set_domain, word) < 0)
91 return log_oom();
92
93 word = NULL;
94 }
95
96 return 0;
97 }
98
99 int resolvconf_parse_argv(int argc, char *argv[]) {
100
101 enum {
102 ARG_VERSION = 0x100,
103 ARG_ENABLE_UPDATES,
104 ARG_DISABLE_UPDATES,
105 ARG_UPDATES_ARE_ENABLED,
106 };
107
108 static const struct option options[] = {
109 { "help", no_argument, NULL, 'h' },
110 { "version", no_argument, NULL, ARG_VERSION },
111
112 /* The following are specific to Debian's original resolvconf */
113 { "enable-updates", no_argument, NULL, ARG_ENABLE_UPDATES },
114 { "disable-updates", no_argument, NULL, ARG_DISABLE_UPDATES },
115 { "updates-are-enabled", no_argument, NULL, ARG_UPDATES_ARE_ENABLED },
116 {}
117 };
118
119 enum {
120 TYPE_REGULAR,
121 TYPE_PRIVATE, /* -p: Not supported, treated identically to TYPE_REGULAR */
122 TYPE_EXCLUSIVE, /* -x */
123 } type = TYPE_REGULAR;
124
125 int c, r;
126
127 assert(argc >= 0);
128 assert(argv);
129
130 /* openresolv checks these environment variables */
131 if (getenv("IF_EXCLUSIVE"))
132 type = TYPE_EXCLUSIVE;
133 if (getenv("IF_PRIVATE"))
134 type = TYPE_PRIVATE; /* not actually supported */
135
136 arg_mode = _MODE_INVALID;
137
138 while ((c = getopt_long(argc, argv, "hadxpfm:uIi:l:Rr:vV", options, NULL)) >= 0)
139 switch(c) {
140
141 case 'h':
142 return resolvconf_help();
143
144 case ARG_VERSION:
145 return version();
146
147 /* -a and -d is what everybody can agree on */
148 case 'a':
149 arg_mode = MODE_SET_LINK;
150 break;
151
152 case 'd':
153 arg_mode = MODE_REVERT_LINK;
154 break;
155
156 /* The exclusive/private/force stuff is an openresolv invention, we support in some skewed way */
157 case 'x':
158 type = TYPE_EXCLUSIVE;
159 break;
160
161 case 'p':
162 type = TYPE_PRIVATE; /* not actually supported */
163 break;
164
165 case 'f':
166 arg_ifindex_permissive = true;
167 break;
168
169 /* The metrics stuff is an openresolv invention we ignore (and don't really need) */
170 case 'm':
171 log_debug("Switch -%c ignored.", c);
172 break;
173
174 /* Everybody else can agree on the existence of -u but we don't support it. */
175 case 'u':
176
177 /* The following options are openresolv inventions we don't support. */
178 case 'I':
179 case 'i':
180 case 'l':
181 case 'R':
182 case 'r':
183 case 'v':
184 case 'V':
185 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
186 "Switch -%c not supported.", c);
187
188 /* The Debian resolvconf commands we don't support. */
189 case ARG_ENABLE_UPDATES:
190 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
191 "Switch --enable-updates not supported.");
192 case ARG_DISABLE_UPDATES:
193 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
194 "Switch --disable-updates not supported.");
195 case ARG_UPDATES_ARE_ENABLED:
196 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
197 "Switch --updates-are-enabled not supported.");
198
199 case '?':
200 return -EINVAL;
201
202 default:
203 assert_not_reached("Unhandled option");
204 }
205
206 if (arg_mode == _MODE_INVALID)
207 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
208 "Expected either -a or -d on the command line.");
209
210 if (optind+1 != argc)
211 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
212 "Expected interface name as argument.");
213
214 r = ifname_resolvconf_mangle(argv[optind]);
215 if (r <= 0)
216 return r;
217
218 optind++;
219
220 if (arg_mode == MODE_SET_LINK) {
221 unsigned n = 0;
222
223 for (;;) {
224 _cleanup_free_ char *line = NULL;
225 const char *a, *l;
226
227 r = read_line(stdin, LONG_LINE_MAX, &line);
228 if (r < 0)
229 return log_error_errno(r, "Failed to read from stdin: %m");
230 if (r == 0)
231 break;
232
233 n++;
234
235 l = strstrip(line);
236 if (IN_SET(*l, '#', ';', 0))
237 continue;
238
239 a = first_word(l, "nameserver");
240 if (a) {
241 (void) parse_nameserver(a);
242 continue;
243 }
244
245 a = first_word(l, "domain");
246 if (!a)
247 a = first_word(l, "search");
248 if (a) {
249 (void) parse_search_domain(a);
250 continue;
251 }
252
253 log_syntax(NULL, LOG_DEBUG, "stdin", n, 0, "Ignoring resolv.conf line: %s", l);
254 }
255
256 if (type == TYPE_EXCLUSIVE) {
257
258 /* If -x mode is selected, let's preferably route non-suffixed lookups to this interface. This
259 * somewhat matches the original -x behaviour */
260
261 r = strv_extend(&arg_set_domain, "~.");
262 if (r < 0)
263 return log_oom();
264
265 } else if (type == TYPE_PRIVATE)
266 log_debug("Private DNS server data not supported, ignoring.");
267
268 if (!arg_set_dns)
269 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
270 "No DNS servers specified, refusing operation.");
271 }
272
273 return 1; /* work to do */
274 }