]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-network/sd-network.c
sd-network: introduce functions for new link and manager states
[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_dns(int ifindex, char ***ret) {
233 return network_link_get_strv(ifindex, "DNS", ret);
234 }
235
236 _public_ int sd_network_link_get_ntp(int ifindex, char ***ret) {
237 return network_link_get_strv(ifindex, "NTP", ret);
238 }
239
240 _public_ int sd_network_link_get_search_domains(int ifindex, char ***ret) {
241 return network_link_get_strv(ifindex, "DOMAINS", ret);
242 }
243
244 _public_ int sd_network_link_get_route_domains(int ifindex, char ***ret) {
245 return network_link_get_strv(ifindex, "ROUTE_DOMAINS", ret);
246 }
247
248 _public_ int sd_network_link_get_dns_default_route(int ifindex) {
249 char path[STRLEN("/run/systemd/netif/links/") + DECIMAL_STR_MAX(ifindex) + 1];
250 _cleanup_free_ char *s = NULL;
251 int r;
252
253 assert_return(ifindex > 0, -EINVAL);
254
255 xsprintf(path, "/run/systemd/netif/links/%i", ifindex);
256
257 r = parse_env_file(NULL, path, "DNS_DEFAULT_ROUTE", &s);
258 if (r == -ENOENT)
259 return -ENODATA;
260 if (r < 0)
261 return r;
262 if (isempty(s))
263 return -ENODATA;
264 return parse_boolean(s);
265 }
266
267 static int network_link_get_ifindexes(int ifindex, const char *key, int **ret) {
268 char path[STRLEN("/run/systemd/netif/links/") + DECIMAL_STR_MAX(ifindex) + 1];
269 _cleanup_free_ int *ifis = NULL;
270 _cleanup_free_ char *s = NULL;
271 size_t allocated = 0, c = 0;
272 const char *x;
273 int r;
274
275 assert_return(ifindex > 0, -EINVAL);
276 assert_return(ret, -EINVAL);
277
278 xsprintf(path, "/run/systemd/netif/links/%i", ifindex);
279 r = parse_env_file(NULL, path, key, &s);
280 if (r == -ENOENT)
281 return -ENODATA;
282 if (r < 0)
283 return r;
284
285 for (x = s;;) {
286 _cleanup_free_ char *word = NULL;
287
288 r = extract_first_word(&x, &word, NULL, 0);
289 if (r < 0)
290 return r;
291 if (r == 0)
292 break;
293
294 r = parse_ifindex(word, &ifindex);
295 if (r < 0)
296 return r;
297
298 if (!GREEDY_REALLOC(ifis, allocated, c + 2))
299 return -ENOMEM;
300
301 ifis[c++] = ifindex;
302 }
303
304 if (ifis)
305 ifis[c] = 0; /* Let's add a 0 ifindex to the end, to be nice */
306
307 *ret = TAKE_PTR(ifis);
308
309 return c;
310 }
311
312 _public_ int sd_network_link_get_carrier_bound_to(int ifindex, int **ret) {
313 return network_link_get_ifindexes(ifindex, "CARRIER_BOUND_TO", ret);
314 }
315
316 _public_ int sd_network_link_get_carrier_bound_by(int ifindex, int **ret) {
317 return network_link_get_ifindexes(ifindex, "CARRIER_BOUND_BY", ret);
318 }
319
320 static int MONITOR_TO_FD(sd_network_monitor *m) {
321 return (int) (unsigned long) m - 1;
322 }
323
324 static sd_network_monitor* FD_TO_MONITOR(int fd) {
325 return (sd_network_monitor*) (unsigned long) (fd + 1);
326 }
327
328 static int monitor_add_inotify_watch(int fd) {
329 int k;
330
331 k = inotify_add_watch(fd, "/run/systemd/netif/links/", IN_MOVED_TO|IN_DELETE);
332 if (k >= 0)
333 return 0;
334 else if (errno != ENOENT)
335 return -errno;
336
337 k = inotify_add_watch(fd, "/run/systemd/netif/", IN_CREATE|IN_ISDIR);
338 if (k >= 0)
339 return 0;
340 else if (errno != ENOENT)
341 return -errno;
342
343 k = inotify_add_watch(fd, "/run/systemd/", IN_CREATE|IN_ISDIR);
344 if (k < 0)
345 return -errno;
346
347 return 0;
348 }
349
350 _public_ int sd_network_monitor_new(sd_network_monitor **m, const char *category) {
351 _cleanup_close_ int fd = -1;
352 int k;
353 bool good = false;
354
355 assert_return(m, -EINVAL);
356
357 fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
358 if (fd < 0)
359 return -errno;
360
361 if (!category || streq(category, "links")) {
362 k = monitor_add_inotify_watch(fd);
363 if (k < 0)
364 return k;
365
366 good = true;
367 }
368
369 if (!good)
370 return -EINVAL;
371
372 *m = FD_TO_MONITOR(fd);
373 fd = -1;
374
375 return 0;
376 }
377
378 _public_ sd_network_monitor* sd_network_monitor_unref(sd_network_monitor *m) {
379 int fd;
380
381 if (m) {
382 fd = MONITOR_TO_FD(m);
383 close_nointr(fd);
384 }
385
386 return NULL;
387 }
388
389 _public_ int sd_network_monitor_flush(sd_network_monitor *m) {
390 union inotify_event_buffer buffer;
391 struct inotify_event *e;
392 ssize_t l;
393 int fd, k;
394
395 assert_return(m, -EINVAL);
396
397 fd = MONITOR_TO_FD(m);
398
399 l = read(fd, &buffer, sizeof(buffer));
400 if (l < 0) {
401 if (IN_SET(errno, EAGAIN, EINTR))
402 return 0;
403
404 return -errno;
405 }
406
407 FOREACH_INOTIFY_EVENT(e, buffer, l) {
408 if (e->mask & IN_ISDIR) {
409 k = monitor_add_inotify_watch(fd);
410 if (k < 0)
411 return k;
412
413 k = inotify_rm_watch(fd, e->wd);
414 if (k < 0)
415 return -errno;
416 }
417 }
418
419 return 0;
420 }
421
422 _public_ int sd_network_monitor_get_fd(sd_network_monitor *m) {
423
424 assert_return(m, -EINVAL);
425
426 return MONITOR_TO_FD(m);
427 }
428
429 _public_ int sd_network_monitor_get_events(sd_network_monitor *m) {
430
431 assert_return(m, -EINVAL);
432
433 /* For now we will only return POLLIN here, since we don't
434 * need anything else ever for inotify. However, let's have
435 * this API to keep our options open should we later on need
436 * it. */
437 return POLLIN;
438 }
439
440 _public_ int sd_network_monitor_get_timeout(sd_network_monitor *m, uint64_t *timeout_usec) {
441
442 assert_return(m, -EINVAL);
443 assert_return(timeout_usec, -EINVAL);
444
445 /* For now we will only return (uint64_t) -1, since we don't
446 * need any timeout. However, let's have this API to keep our
447 * options open should we later on need it. */
448 *timeout_usec = (uint64_t) -1;
449 return 0;
450 }