]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-network/sd-network.c
network: Show network and link file dropins in networkctl status
[thirdparty/systemd.git] / src / libsystemd / sd-network / sd-network.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
fe8db0c5 2
fe8db0c5 3#include <errno.h>
0a6f50c0 4#include <poll.h>
07630cea 5#include <sys/inotify.h>
fe8db0c5 6
07630cea
LP
7#include "sd-network.h"
8
b5efdb8a 9#include "alloc-util.h"
686d13b9 10#include "env-file.h"
77601719 11#include "fd-util.h"
77601719 12#include "fs-util.h"
9e5fd717 13#include "inotify-util.h"
fe8db0c5 14#include "macro.h"
77601719 15#include "parse-util.h"
5f02f341 16#include "stdio-util.h"
07630cea 17#include "string-util.h"
fe8db0c5 18#include "strv.h"
fe8db0c5 19
ac999bf0 20static int network_get_string(const char *field, char **ret) {
03cc0fd1 21 _cleanup_free_ char *s = NULL;
deb2e523
TG
22 int r;
23
ac999bf0 24 assert_return(ret, -EINVAL);
fe8db0c5 25
ac999bf0 26 r = parse_env_file(NULL, "/run/systemd/netif/state", field, &s);
03cc0fd1 27 if (r < 0)
fe8db0c5 28 return r;
03cc0fd1
LP
29 if (isempty(s))
30 return -ENODATA;
deb2e523 31
ac999bf0 32 *ret = TAKE_PTR(s);
deb2e523
TG
33 return 0;
34}
35
e7c1b3f7
YW
36int sd_network_get_operational_state(char **ret) {
37 return network_get_string("OPER_STATE", ret);
ac999bf0
YW
38}
39
e7c1b3f7
YW
40int sd_network_get_carrier_state(char **ret) {
41 return network_get_string("CARRIER_STATE", ret);
ac999bf0
YW
42}
43
e7c1b3f7
YW
44int sd_network_get_address_state(char **ret) {
45 return network_get_string("ADDRESS_STATE", ret);
ac999bf0
YW
46}
47
e7c1b3f7
YW
48int sd_network_get_ipv4_address_state(char **ret) {
49 return network_get_string("IPV4_ADDRESS_STATE", ret);
bbea8813
L
50}
51
e7c1b3f7
YW
52int sd_network_get_ipv6_address_state(char **ret) {
53 return network_get_string("IPV6_ADDRESS_STATE", ret);
bbea8813
L
54}
55
e7c1b3f7
YW
56int sd_network_get_online_state(char **ret) {
57 return network_get_string("ONLINE_STATE", ret);
bcdcc596
58}
59
03cc0fd1
LP
60static int network_get_strv(const char *key, char ***ret) {
61 _cleanup_strv_free_ char **a = NULL;
bbf7c048
TG
62 _cleanup_free_ char *s = NULL;
63 int r;
64
03cc0fd1
LP
65 assert_return(ret, -EINVAL);
66
13df9c39 67 r = parse_env_file(NULL, "/run/systemd/netif/state", key, &s);
03cc0fd1
LP
68 if (r < 0)
69 return r;
e05dd771
YW
70 if (isempty(s))
71 return -ENODATA;
03cc0fd1 72
27b13df4 73 a = strv_split(s, NULL);
03cc0fd1
LP
74 if (!a)
75 return -ENOMEM;
76
77 strv_uniq(a);
da6053d0 78 r = (int) strv_length(a);
03cc0fd1 79
ae2a15bc 80 *ret = TAKE_PTR(a);
03cc0fd1
LP
81 return r;
82}
83
73fc8522 84int sd_network_get_dns(char ***ret) {
03cc0fd1
LP
85 return network_get_strv("DNS", ret);
86}
87
73fc8522 88int sd_network_get_ntp(char ***ret) {
03cc0fd1
LP
89 return network_get_strv("NTP", ret);
90}
91
73fc8522 92int sd_network_get_search_domains(char ***ret) {
8612e936
LP
93 return network_get_strv("DOMAINS", ret);
94}
95
73fc8522 96int sd_network_get_route_domains(char ***ret) {
3df9bec5
LP
97 return network_get_strv("ROUTE_DOMAINS", ret);
98}
99
38e5900f 100static int network_link_get_string(int ifindex, const char *field, char **ret) {
f4af5f00 101 char path[STRLEN("/run/systemd/netif/links/") + DECIMAL_STR_MAX(ifindex)];
5f02f341 102 _cleanup_free_ char *s = NULL;
03cc0fd1
LP
103 int r;
104
105 assert_return(ifindex > 0, -EINVAL);
38e5900f 106 assert_return(ret, -EINVAL);
bbf7c048 107
5f02f341 108 xsprintf(path, "/run/systemd/netif/links/%i", ifindex);
03cc0fd1 109
13df9c39 110 r = parse_env_file(NULL, path, field, &s);
03cc0fd1 111 if (r < 0)
bbf7c048 112 return r;
03cc0fd1
LP
113 if (isempty(s))
114 return -ENODATA;
bbf7c048 115
ae2a15bc 116 *ret = TAKE_PTR(s);
bbf7c048
TG
117 return 0;
118}
119
c9d22489
YW
120static int network_link_get_boolean(int ifindex, const char *key) {
121 _cleanup_free_ char *s = NULL;
122 int r;
123
124 r = network_link_get_string(ifindex, key, &s);
125 if (r < 0)
126 return r;
127
128 return parse_boolean(s);
129}
130
8a516214 131static int network_link_get_strv(int ifindex, const char *key, char ***ret) {
8a516214 132 _cleanup_strv_free_ char **a = NULL;
5f02f341 133 _cleanup_free_ char *s = NULL;
8a516214
LP
134 int r;
135
136 assert_return(ifindex > 0, -EINVAL);
137 assert_return(ret, -EINVAL);
138
e05dd771 139 r = network_link_get_string(ifindex, key, &s);
8a516214
LP
140 if (r < 0)
141 return r;
8a516214 142
27b13df4 143 a = strv_split(s, NULL);
8a516214
LP
144 if (!a)
145 return -ENOMEM;
146
147 strv_uniq(a);
da6053d0 148 r = (int) strv_length(a);
8a516214 149
ae2a15bc 150 *ret = TAKE_PTR(a);
8a516214
LP
151 return r;
152}
153
e7c1b3f7
YW
154int sd_network_link_get_setup_state(int ifindex, char **ret) {
155 return network_link_get_string(ifindex, "ADMIN_STATE", ret);
38e5900f 156}
adc5b2e2 157
e7c1b3f7
YW
158int sd_network_link_get_network_file(int ifindex, char **ret) {
159 return network_link_get_string(ifindex, "NETWORK_FILE", ret);
adc5b2e2
TG
160}
161
a2640646
DDM
162int sd_network_link_get_network_file_dropins(int ifindex, char ***ret) {
163 _cleanup_free_ char **sv = NULL, *joined = NULL;
164 int r;
165
166 assert_return(ifindex > 0, -EINVAL);
167 assert_return(ret, -EINVAL);
168
169 r = network_link_get_string(ifindex, "NETWORK_FILE_DROPINS", &joined);
170 if (r < 0)
171 return r;
172
173 r = strv_split_full(&sv, joined, ":", EXTRACT_CUNESCAPE);
174 if (r < 0)
175 return r;
176
177 *ret = TAKE_PTR(sv);
178 return 0;
179}
180
e7c1b3f7
YW
181int sd_network_link_get_operational_state(int ifindex, char **ret) {
182 return network_link_get_string(ifindex, "OPER_STATE", ret);
fe8db0c5
TG
183}
184
e7c1b3f7 185int sd_network_link_get_required_family_for_online(int ifindex, char **ret) {
778e3da9 186 return network_link_get_string(ifindex, "REQUIRED_FAMILY_FOR_ONLINE", ret);
bbea8813
L
187}
188
e7c1b3f7
YW
189int sd_network_link_get_carrier_state(int ifindex, char **ret) {
190 return network_link_get_string(ifindex, "CARRIER_STATE", ret);
ac999bf0
YW
191}
192
e7c1b3f7
YW
193int sd_network_link_get_address_state(int ifindex, char **ret) {
194 return network_link_get_string(ifindex, "ADDRESS_STATE", ret);
ac999bf0
YW
195}
196
e7c1b3f7
YW
197int sd_network_link_get_ipv4_address_state(int ifindex, char **ret) {
198 return network_link_get_string(ifindex, "IPV4_ADDRESS_STATE", ret);
bbea8813
L
199}
200
e7c1b3f7
YW
201int sd_network_link_get_ipv6_address_state(int ifindex, char **ret) {
202 return network_link_get_string(ifindex, "IPV6_ADDRESS_STATE", ret);
bbea8813
L
203}
204
e7c1b3f7
YW
205int sd_network_link_get_online_state(int ifindex, char **ret) {
206 return network_link_get_string(ifindex, "ONLINE_STATE", ret);
bcdcc596
207}
208
e7c1b3f7
YW
209int sd_network_link_get_dhcp6_client_iaid_string(int ifindex, char **ret) {
210 return network_link_get_string(ifindex, "DHCP6_CLIENT_IAID", ret);
01dd1380
SS
211}
212
e7c1b3f7
YW
213int sd_network_link_get_dhcp6_client_duid_string(int ifindex, char **ret) {
214 return network_link_get_string(ifindex, "DHCP6_CLIENT_DUID", ret);
a9deab2e
SS
215}
216
73fc8522 217int sd_network_link_get_required_for_online(int ifindex) {
778e3da9 218 return network_link_get_boolean(ifindex, "REQUIRED_FOR_ONLINE");
c1a38904
MTL
219}
220
e7c1b3f7 221int sd_network_link_get_required_operstate_for_online(int ifindex, char **ret) {
778e3da9 222 return network_link_get_string(ifindex, "REQUIRED_OPER_STATE_FOR_ONLINE", ret);
22eab27c
YW
223}
224
e7c1b3f7 225int sd_network_link_get_activation_policy(int ifindex, char **ret) {
778e3da9 226 return network_link_get_string(ifindex, "ACTIVATION_POLICY", ret);
a853652a
DS
227}
228
e7c1b3f7
YW
229int sd_network_link_get_llmnr(int ifindex, char **ret) {
230 return network_link_get_string(ifindex, "LLMNR", ret);
bd8f6538
TG
231}
232
e7c1b3f7
YW
233int sd_network_link_get_mdns(int ifindex, char **ret) {
234 return network_link_get_string(ifindex, "MDNS", ret);
aaa297d4
LP
235}
236
e7c1b3f7
YW
237int sd_network_link_get_dns_over_tls(int ifindex, char **ret) {
238 return network_link_get_string(ifindex, "DNS_OVER_TLS", ret);
d050561a
IT
239}
240
e7c1b3f7
YW
241int sd_network_link_get_dnssec(int ifindex, char **ret) {
242 return network_link_get_string(ifindex, "DNSSEC", ret);
ad6c0475
LP
243}
244
e7c1b3f7
YW
245int sd_network_link_get_dnssec_negative_trust_anchors(int ifindex, char ***ret) {
246 return network_link_get_strv(ifindex, "DNSSEC_NTA", ret);
8a516214
LP
247}
248
73fc8522 249int sd_network_link_get_dns(int ifindex, char ***ret) {
8a516214 250 return network_link_get_strv(ifindex, "DNS", ret);
bcb7a07e
TG
251}
252
73fc8522 253int sd_network_link_get_ntp(int ifindex, char ***ret) {
8a516214 254 return network_link_get_strv(ifindex, "NTP", ret);
bcb7a07e
TG
255}
256
73fc8522 257int sd_network_link_get_sip(int ifindex, char ***ret) {
eb46288c
SS
258 return network_link_get_strv(ifindex, "SIP", ret);
259}
260
73fc8522 261int sd_network_link_get_search_domains(int ifindex, char ***ret) {
8a516214 262 return network_link_get_strv(ifindex, "DOMAINS", ret);
9b4d1882
TG
263}
264
73fc8522 265int sd_network_link_get_route_domains(int ifindex, char ***ret) {
3df9bec5
LP
266 return network_link_get_strv(ifindex, "ROUTE_DOMAINS", ret);
267}
268
73fc8522 269int sd_network_link_get_dns_default_route(int ifindex) {
c9d22489 270 return network_link_get_boolean(ifindex, "DNS_DEFAULT_ROUTE");
c629354e
LP
271}
272
b295beea 273static int network_link_get_ifindexes(int ifindex, const char *key, int **ret) {
b295beea 274 _cleanup_free_ int *ifis = NULL;
5f02f341 275 _cleanup_free_ char *s = NULL;
319a4f4b 276 size_t c = 0;
b295beea
LP
277 int r;
278
279 assert_return(ifindex > 0, -EINVAL);
280 assert_return(ret, -EINVAL);
281
e05dd771 282 r = network_link_get_string(ifindex, key, &s);
b295beea
LP
283 if (r < 0)
284 return r;
b295beea 285
597da51b 286 for (const char *x = s;;) {
b295beea
LP
287 _cleanup_free_ char *word = NULL;
288
289 r = extract_first_word(&x, &word, NULL, 0);
290 if (r < 0)
291 return r;
292 if (r == 0)
293 break;
294
319a4f4b 295 if (!GREEDY_REALLOC(ifis, c + 2))
b295beea
LP
296 return -ENOMEM;
297
597da51b
ZJS
298 r = ifis[c++] = parse_ifindex(word);
299 if (r < 0)
300 return r;
b295beea
LP
301 }
302
c58bd76a 303 if (ifis)
13e785f7 304 ifis[c] = 0; /* Let's add a 0 ifindex to the end, to be nice */
b295beea 305
ae2a15bc 306 *ret = TAKE_PTR(ifis);
b295beea
LP
307 return c;
308}
309
73fc8522 310int sd_network_link_get_carrier_bound_to(int ifindex, int **ret) {
b295beea 311 return network_link_get_ifindexes(ifindex, "CARRIER_BOUND_TO", ret);
0d4ad91d
AR
312}
313
73fc8522 314int sd_network_link_get_carrier_bound_by(int ifindex, int **ret) {
b295beea 315 return network_link_get_ifindexes(ifindex, "CARRIER_BOUND_BY", ret);
0d4ad91d
AR
316}
317
8e0bacab
YW
318int sd_network_link_get_stat(int ifindex, struct stat *ret) {
319 char path[STRLEN("/run/systemd/netif/links/") + DECIMAL_STR_MAX(ifindex)];
320 struct stat st;
321
322 assert_return(ifindex > 0, -EINVAL);
323
324 xsprintf(path, "/run/systemd/netif/links/%i", ifindex);
325
326 if (stat(path, &st) < 0)
327 return -errno;
328
329 if (ret)
330 *ret = st;
331
332 return 0;
333}
334
a1e92eee 335static int MONITOR_TO_FD(sd_network_monitor *m) {
fe8db0c5
TG
336 return (int) (unsigned long) m - 1;
337}
338
a1e92eee 339static sd_network_monitor* FD_TO_MONITOR(int fd) {
fe8db0c5
TG
340 return (sd_network_monitor*) (unsigned long) (fd + 1);
341}
342
870395a4 343static int monitor_add_inotify_watch(int fd) {
206c0897 344 int wd;
870395a4 345
206c0897
DDM
346 wd = inotify_add_watch(fd, "/run/systemd/netif/links/", IN_MOVED_TO|IN_DELETE);
347 if (wd >= 0)
348 return wd;
870395a4
TG
349 else if (errno != ENOENT)
350 return -errno;
351
206c0897
DDM
352 wd = inotify_add_watch(fd, "/run/systemd/netif/", IN_CREATE|IN_ISDIR);
353 if (wd >= 0)
354 return wd;
870395a4
TG
355 else if (errno != ENOENT)
356 return -errno;
357
206c0897
DDM
358 wd = inotify_add_watch(fd, "/run/systemd/", IN_CREATE|IN_ISDIR);
359 if (wd < 0)
870395a4
TG
360 return -errno;
361
206c0897 362 return wd;
870395a4
TG
363}
364
73fc8522 365int sd_network_monitor_new(sd_network_monitor **m, const char *category) {
254d1313 366 _cleanup_close_ int fd = -EBADF;
870395a4 367 int k;
fe8db0c5
TG
368 bool good = false;
369
370 assert_return(m, -EINVAL);
371
372 fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
373 if (fd < 0)
374 return -errno;
375
7e141e49 376 if (!category || streq(category, "links")) {
870395a4
TG
377 k = monitor_add_inotify_watch(fd);
378 if (k < 0)
379 return k;
fe8db0c5
TG
380
381 good = true;
382 }
383
3de1c8ce 384 if (!good)
fe8db0c5 385 return -EINVAL;
fe8db0c5 386
d52e1c42 387 *m = FD_TO_MONITOR(TAKE_FD(fd));
fe8db0c5
TG
388 return 0;
389}
390
73fc8522 391sd_network_monitor* sd_network_monitor_unref(sd_network_monitor *m) {
d52e1c42 392 if (m)
b87dfaa2 393 (void) close_nointr(MONITOR_TO_FD(m));
fe8db0c5
TG
394
395 return NULL;
396}
397
73fc8522 398int sd_network_monitor_flush(sd_network_monitor *m) {
870395a4 399 union inotify_event_buffer buffer;
870395a4 400 ssize_t l;
206c0897 401 int fd;
fe8db0c5
TG
402
403 assert_return(m, -EINVAL);
404
870395a4
TG
405 fd = MONITOR_TO_FD(m);
406
407 l = read(fd, &buffer, sizeof(buffer));
408 if (l < 0) {
8add30a0 409 if (ERRNO_IS_TRANSIENT(errno))
870395a4
TG
410 return 0;
411
412 return -errno;
413 }
414
415 FOREACH_INOTIFY_EVENT(e, buffer, l) {
416 if (e->mask & IN_ISDIR) {
206c0897
DDM
417 int wd;
418
419 wd = monitor_add_inotify_watch(fd);
420 if (wd < 0)
421 return wd;
870395a4 422
206c0897
DDM
423 if (wd != e->wd) {
424 if (inotify_rm_watch(fd, e->wd) < 0)
425 return -errno;
426 }
870395a4
TG
427 }
428 }
429
430 return 0;
fe8db0c5
TG
431}
432
73fc8522 433int sd_network_monitor_get_fd(sd_network_monitor *m) {
fe8db0c5
TG
434 assert_return(m, -EINVAL);
435
436 return MONITOR_TO_FD(m);
437}
438
73fc8522 439int sd_network_monitor_get_events(sd_network_monitor *m) {
fe8db0c5
TG
440 assert_return(m, -EINVAL);
441
442 /* For now we will only return POLLIN here, since we don't
443 * need anything else ever for inotify. However, let's have
444 * this API to keep our options open should we later on need
445 * it. */
446 return POLLIN;
447}
448
e7c1b3f7 449int sd_network_monitor_get_timeout(sd_network_monitor *m, uint64_t *ret_usec) {
fe8db0c5 450 assert_return(m, -EINVAL);
e7c1b3f7 451 assert_return(ret_usec, -EINVAL);
fe8db0c5 452
f5fbe71d 453 /* For now we will only return UINT64_MAX, since we don't
fe8db0c5
TG
454 * need any timeout. However, let's have this API to keep our
455 * options open should we later on need it. */
e7c1b3f7 456 *ret_usec = UINT64_MAX;
fe8db0c5
TG
457 return 0;
458}