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