]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-network/sd-network.c
sd-network: add new API sd_network_link_get_dns_default_route()
[thirdparty/systemd.git] / src / libsystemd / sd-network / sd-network.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <errno.h>
4 #include <poll.h>
5 #include <string.h>
6 #include <sys/inotify.h>
7
8 #include "sd-network.h"
9
10 #include "alloc-util.h"
11 #include "env-file.h"
12 #include "fd-util.h"
13 #include "fs-util.h"
14 #include "macro.h"
15 #include "parse-util.h"
16 #include "stdio-util.h"
17 #include "string-util.h"
18 #include "strv.h"
19 #include "util.h"
20
21 _public_ int sd_network_get_operational_state(char **state) {
22 _cleanup_free_ char *s = NULL;
23 int r;
24
25 assert_return(state, -EINVAL);
26
27 r = parse_env_file(NULL, "/run/systemd/netif/state", "OPER_STATE", &s);
28 if (r == -ENOENT)
29 return -ENODATA;
30 if (r < 0)
31 return r;
32 if (isempty(s))
33 return -ENODATA;
34
35 *state = TAKE_PTR(s);
36
37 return 0;
38 }
39
40 static int network_get_strv(const char *key, char ***ret) {
41 _cleanup_strv_free_ char **a = NULL;
42 _cleanup_free_ char *s = NULL;
43 int r;
44
45 assert_return(ret, -EINVAL);
46
47 r = parse_env_file(NULL, "/run/systemd/netif/state", key, &s);
48 if (r == -ENOENT)
49 return -ENODATA;
50 if (r < 0)
51 return r;
52 if (isempty(s)) {
53 *ret = NULL;
54 return 0;
55 }
56
57 a = strv_split(s, " ");
58 if (!a)
59 return -ENOMEM;
60
61 strv_uniq(a);
62 r = (int) strv_length(a);
63
64 *ret = TAKE_PTR(a);
65
66 return r;
67 }
68
69 _public_ int sd_network_get_dns(char ***ret) {
70 return network_get_strv("DNS", ret);
71 }
72
73 _public_ int sd_network_get_ntp(char ***ret) {
74 return network_get_strv("NTP", ret);
75 }
76
77 _public_ int sd_network_get_search_domains(char ***ret) {
78 return network_get_strv("DOMAINS", ret);
79 }
80
81 _public_ int sd_network_get_route_domains(char ***ret) {
82 return network_get_strv("ROUTE_DOMAINS", ret);
83 }
84
85 static int network_link_get_string(int ifindex, const char *field, char **ret) {
86 char path[STRLEN("/run/systemd/netif/links/") + DECIMAL_STR_MAX(ifindex) + 1];
87 _cleanup_free_ char *s = NULL;
88 int r;
89
90 assert_return(ifindex > 0, -EINVAL);
91 assert_return(ret, -EINVAL);
92
93 xsprintf(path, "/run/systemd/netif/links/%i", ifindex);
94
95 r = parse_env_file(NULL, path, field, &s);
96 if (r == -ENOENT)
97 return -ENODATA;
98 if (r < 0)
99 return r;
100 if (isempty(s))
101 return -ENODATA;
102
103 *ret = TAKE_PTR(s);
104
105 return 0;
106 }
107
108 static int network_link_get_strv(int ifindex, const char *key, char ***ret) {
109 char path[STRLEN("/run/systemd/netif/links/") + DECIMAL_STR_MAX(ifindex) + 1];
110 _cleanup_strv_free_ char **a = NULL;
111 _cleanup_free_ char *s = NULL;
112 int r;
113
114 assert_return(ifindex > 0, -EINVAL);
115 assert_return(ret, -EINVAL);
116
117 xsprintf(path, "/run/systemd/netif/links/%i", ifindex);
118 r = parse_env_file(NULL, path, key, &s);
119 if (r == -ENOENT)
120 return -ENODATA;
121 if (r < 0)
122 return r;
123 if (isempty(s)) {
124 *ret = NULL;
125 return 0;
126 }
127
128 a = strv_split(s, " ");
129 if (!a)
130 return -ENOMEM;
131
132 strv_uniq(a);
133 r = (int) strv_length(a);
134
135 *ret = TAKE_PTR(a);
136
137 return r;
138 }
139
140 _public_ int sd_network_link_get_setup_state(int ifindex, char **state) {
141 return network_link_get_string(ifindex, "ADMIN_STATE", state);
142 }
143
144 _public_ int sd_network_link_get_network_file(int ifindex, char **filename) {
145 return network_link_get_string(ifindex, "NETWORK_FILE", filename);
146 }
147
148 _public_ int sd_network_link_get_operational_state(int ifindex, char **state) {
149 return network_link_get_string(ifindex, "OPER_STATE", state);
150 }
151
152 _public_ int sd_network_link_get_required_for_online(int ifindex) {
153 _cleanup_free_ char *s = NULL;
154 int r;
155
156 r = network_link_get_string(ifindex, "REQUIRED_FOR_ONLINE", &s);
157 if (r < 0) {
158 /* Handle -ENODATA as RequiredForOnline=yes, for compatibility */
159 if (r == -ENODATA)
160 return true;
161 return r;
162 }
163
164 return parse_boolean(s);
165 }
166
167 _public_ int sd_network_link_get_llmnr(int ifindex, char **llmnr) {
168 return network_link_get_string(ifindex, "LLMNR", llmnr);
169 }
170
171 _public_ int sd_network_link_get_mdns(int ifindex, char **mdns) {
172 return network_link_get_string(ifindex, "MDNS", mdns);
173 }
174
175 _public_ int sd_network_link_get_dns_over_tls(int ifindex, char **dns_over_tls) {
176 return network_link_get_string(ifindex, "DNS_OVER_TLS", dns_over_tls);
177 }
178
179 _public_ int sd_network_link_get_dnssec(int ifindex, char **dnssec) {
180 return network_link_get_string(ifindex, "DNSSEC", dnssec);
181 }
182
183 _public_ int sd_network_link_get_dnssec_negative_trust_anchors(int ifindex, char ***nta) {
184 return network_link_get_strv(ifindex, "DNSSEC_NTA", nta);
185 }
186
187 _public_ int sd_network_link_get_timezone(int ifindex, char **ret) {
188 return network_link_get_string(ifindex, "TIMEZONE", ret);
189 }
190
191 _public_ int sd_network_link_get_dns(int ifindex, char ***ret) {
192 return network_link_get_strv(ifindex, "DNS", ret);
193 }
194
195 _public_ int sd_network_link_get_ntp(int ifindex, char ***ret) {
196 return network_link_get_strv(ifindex, "NTP", ret);
197 }
198
199 _public_ int sd_network_link_get_search_domains(int ifindex, char ***ret) {
200 return network_link_get_strv(ifindex, "DOMAINS", ret);
201 }
202
203 _public_ int sd_network_link_get_route_domains(int ifindex, char ***ret) {
204 return network_link_get_strv(ifindex, "ROUTE_DOMAINS", ret);
205 }
206
207 _public_ int sd_network_link_get_dns_default_route(int ifindex) {
208 char path[STRLEN("/run/systemd/netif/links/") + DECIMAL_STR_MAX(ifindex) + 1];
209 _cleanup_free_ char *s = NULL;
210 int r;
211
212 assert_return(ifindex > 0, -EINVAL);
213
214 xsprintf(path, "/run/systemd/netif/links/%i", ifindex);
215
216 r = parse_env_file(NULL, path, "DNS_DEFAULT_ROUTE", &s);
217 if (r == -ENOENT)
218 return -ENODATA;
219 if (r < 0)
220 return r;
221 if (isempty(s))
222 return -ENODATA;
223 return parse_boolean(s);
224 }
225
226 static int network_link_get_ifindexes(int ifindex, const char *key, int **ret) {
227 char path[STRLEN("/run/systemd/netif/links/") + DECIMAL_STR_MAX(ifindex) + 1];
228 _cleanup_free_ int *ifis = NULL;
229 _cleanup_free_ char *s = NULL;
230 size_t allocated = 0, c = 0;
231 const char *x;
232 int r;
233
234 assert_return(ifindex > 0, -EINVAL);
235 assert_return(ret, -EINVAL);
236
237 xsprintf(path, "/run/systemd/netif/links/%i", ifindex);
238 r = parse_env_file(NULL, path, key, &s);
239 if (r == -ENOENT)
240 return -ENODATA;
241 if (r < 0)
242 return r;
243
244 for (x = s;;) {
245 _cleanup_free_ char *word = NULL;
246
247 r = extract_first_word(&x, &word, NULL, 0);
248 if (r < 0)
249 return r;
250 if (r == 0)
251 break;
252
253 r = parse_ifindex(word, &ifindex);
254 if (r < 0)
255 return r;
256
257 if (!GREEDY_REALLOC(ifis, allocated, c + 2))
258 return -ENOMEM;
259
260 ifis[c++] = ifindex;
261 }
262
263 if (ifis)
264 ifis[c] = 0; /* Let's add a 0 ifindex to the end, to be nice */
265
266 *ret = TAKE_PTR(ifis);
267
268 return c;
269 }
270
271 _public_ int sd_network_link_get_carrier_bound_to(int ifindex, int **ret) {
272 return network_link_get_ifindexes(ifindex, "CARRIER_BOUND_TO", ret);
273 }
274
275 _public_ int sd_network_link_get_carrier_bound_by(int ifindex, int **ret) {
276 return network_link_get_ifindexes(ifindex, "CARRIER_BOUND_BY", ret);
277 }
278
279 static inline int MONITOR_TO_FD(sd_network_monitor *m) {
280 return (int) (unsigned long) m - 1;
281 }
282
283 static inline sd_network_monitor* FD_TO_MONITOR(int fd) {
284 return (sd_network_monitor*) (unsigned long) (fd + 1);
285 }
286
287 static int monitor_add_inotify_watch(int fd) {
288 int k;
289
290 k = inotify_add_watch(fd, "/run/systemd/netif/links/", IN_MOVED_TO|IN_DELETE);
291 if (k >= 0)
292 return 0;
293 else if (errno != ENOENT)
294 return -errno;
295
296 k = inotify_add_watch(fd, "/run/systemd/netif/", IN_CREATE|IN_ISDIR);
297 if (k >= 0)
298 return 0;
299 else if (errno != ENOENT)
300 return -errno;
301
302 k = inotify_add_watch(fd, "/run/systemd/", IN_CREATE|IN_ISDIR);
303 if (k < 0)
304 return -errno;
305
306 return 0;
307 }
308
309 _public_ int sd_network_monitor_new(sd_network_monitor **m, const char *category) {
310 _cleanup_close_ int fd = -1;
311 int k;
312 bool good = false;
313
314 assert_return(m, -EINVAL);
315
316 fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
317 if (fd < 0)
318 return -errno;
319
320 if (!category || streq(category, "links")) {
321 k = monitor_add_inotify_watch(fd);
322 if (k < 0)
323 return k;
324
325 good = true;
326 }
327
328 if (!good)
329 return -EINVAL;
330
331 *m = FD_TO_MONITOR(fd);
332 fd = -1;
333
334 return 0;
335 }
336
337 _public_ sd_network_monitor* sd_network_monitor_unref(sd_network_monitor *m) {
338 int fd;
339
340 if (m) {
341 fd = MONITOR_TO_FD(m);
342 close_nointr(fd);
343 }
344
345 return NULL;
346 }
347
348 _public_ int sd_network_monitor_flush(sd_network_monitor *m) {
349 union inotify_event_buffer buffer;
350 struct inotify_event *e;
351 ssize_t l;
352 int fd, k;
353
354 assert_return(m, -EINVAL);
355
356 fd = MONITOR_TO_FD(m);
357
358 l = read(fd, &buffer, sizeof(buffer));
359 if (l < 0) {
360 if (IN_SET(errno, EAGAIN, EINTR))
361 return 0;
362
363 return -errno;
364 }
365
366 FOREACH_INOTIFY_EVENT(e, buffer, l) {
367 if (e->mask & IN_ISDIR) {
368 k = monitor_add_inotify_watch(fd);
369 if (k < 0)
370 return k;
371
372 k = inotify_rm_watch(fd, e->wd);
373 if (k < 0)
374 return -errno;
375 }
376 }
377
378 return 0;
379 }
380
381 _public_ int sd_network_monitor_get_fd(sd_network_monitor *m) {
382
383 assert_return(m, -EINVAL);
384
385 return MONITOR_TO_FD(m);
386 }
387
388 _public_ int sd_network_monitor_get_events(sd_network_monitor *m) {
389
390 assert_return(m, -EINVAL);
391
392 /* For now we will only return POLLIN here, since we don't
393 * need anything else ever for inotify. However, let's have
394 * this API to keep our options open should we later on need
395 * it. */
396 return POLLIN;
397 }
398
399 _public_ int sd_network_monitor_get_timeout(sd_network_monitor *m, uint64_t *timeout_usec) {
400
401 assert_return(m, -EINVAL);
402 assert_return(timeout_usec, -EINVAL);
403
404 /* For now we will only return (uint64_t) -1, since we don't
405 * need any timeout. However, let's have this API to keep our
406 * options open should we later on need it. */
407 *timeout_usec = (uint64_t) -1;
408 return 0;
409 }