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