]>
Commit | Line | Data |
---|---|---|
53e1b683 | 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
fe8db0c5 TG |
2 | /*** |
3 | This file is part of systemd. | |
4 | ||
5 | Copyright 2011 Lennart Poettering | |
6 | Copyright 2014 Tom Gundersen | |
7 | ||
8 | systemd is free software; you can redistribute it and/or modify it | |
9 | under the terms of the GNU Lesser General Public License as published by | |
10 | the Free Software Foundation; either version 2.1 of the License, or | |
11 | (at your option) any later version. | |
12 | ||
13 | systemd is distributed in the hope that it will be useful, but | |
14 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 | Lesser General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU Lesser General Public License | |
19 | along with systemd; If not, see <http://www.gnu.org/licenses/>. | |
20 | ***/ | |
21 | ||
fe8db0c5 | 22 | #include <errno.h> |
0a6f50c0 | 23 | #include <poll.h> |
07630cea LP |
24 | #include <string.h> |
25 | #include <sys/inotify.h> | |
fe8db0c5 | 26 | |
07630cea LP |
27 | #include "sd-network.h" |
28 | ||
b5efdb8a | 29 | #include "alloc-util.h" |
77601719 | 30 | #include "fd-util.h" |
07630cea | 31 | #include "fileio.h" |
77601719 | 32 | #include "fs-util.h" |
fe8db0c5 | 33 | #include "macro.h" |
77601719 | 34 | #include "parse-util.h" |
5f02f341 | 35 | #include "stdio-util.h" |
07630cea | 36 | #include "string-util.h" |
fe8db0c5 | 37 | #include "strv.h" |
07630cea | 38 | #include "util.h" |
fe8db0c5 | 39 | |
03cc0fd1 LP |
40 | _public_ int sd_network_get_operational_state(char **state) { |
41 | _cleanup_free_ char *s = NULL; | |
deb2e523 TG |
42 | int r; |
43 | ||
fe8db0c5 TG |
44 | assert_return(state, -EINVAL); |
45 | ||
03cc0fd1 | 46 | r = parse_env_file("/run/systemd/netif/state", NEWLINE, "OPER_STATE", &s, NULL); |
cb6fa44c TG |
47 | if (r == -ENOENT) |
48 | return -ENODATA; | |
03cc0fd1 | 49 | if (r < 0) |
fe8db0c5 | 50 | return r; |
03cc0fd1 LP |
51 | if (isempty(s)) |
52 | return -ENODATA; | |
deb2e523 TG |
53 | |
54 | *state = s; | |
55 | s = NULL; | |
56 | ||
57 | return 0; | |
58 | } | |
59 | ||
03cc0fd1 LP |
60 | static 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 | ||
67 | r = parse_env_file("/run/systemd/netif/state", NEWLINE, key, &s, NULL); | |
68 | if (r == -ENOENT) | |
69 | return -ENODATA; | |
70 | if (r < 0) | |
71 | return r; | |
72 | if (isempty(s)) { | |
73 | *ret = NULL; | |
74 | return 0; | |
75 | } | |
76 | ||
77 | a = strv_split(s, " "); | |
78 | if (!a) | |
79 | return -ENOMEM; | |
80 | ||
81 | strv_uniq(a); | |
82 | r = strv_length(a); | |
83 | ||
84 | *ret = a; | |
85 | a = NULL; | |
86 | ||
87 | return r; | |
88 | } | |
89 | ||
90 | _public_ int sd_network_get_dns(char ***ret) { | |
91 | return network_get_strv("DNS", ret); | |
92 | } | |
93 | ||
94 | _public_ int sd_network_get_ntp(char ***ret) { | |
95 | return network_get_strv("NTP", ret); | |
96 | } | |
97 | ||
3df9bec5 | 98 | _public_ int sd_network_get_search_domains(char ***ret) { |
8612e936 LP |
99 | return network_get_strv("DOMAINS", ret); |
100 | } | |
101 | ||
3df9bec5 LP |
102 | _public_ int sd_network_get_route_domains(char ***ret) { |
103 | return network_get_strv("ROUTE_DOMAINS", ret); | |
104 | } | |
105 | ||
38e5900f | 106 | static int network_link_get_string(int ifindex, const char *field, char **ret) { |
fbd0b64f | 107 | char path[STRLEN("/run/systemd/netif/links/") + DECIMAL_STR_MAX(ifindex) + 1]; |
5f02f341 | 108 | _cleanup_free_ char *s = NULL; |
03cc0fd1 LP |
109 | int r; |
110 | ||
111 | assert_return(ifindex > 0, -EINVAL); | |
38e5900f | 112 | assert_return(ret, -EINVAL); |
bbf7c048 | 113 | |
5f02f341 | 114 | xsprintf(path, "/run/systemd/netif/links/%i", ifindex); |
03cc0fd1 | 115 | |
5f02f341 | 116 | r = parse_env_file(path, NEWLINE, field, &s, NULL); |
bbf7c048 TG |
117 | if (r == -ENOENT) |
118 | return -ENODATA; | |
03cc0fd1 | 119 | if (r < 0) |
bbf7c048 | 120 | return r; |
03cc0fd1 LP |
121 | if (isempty(s)) |
122 | return -ENODATA; | |
bbf7c048 | 123 | |
38e5900f | 124 | *ret = s; |
bbf7c048 TG |
125 | s = NULL; |
126 | ||
127 | return 0; | |
128 | } | |
129 | ||
8a516214 | 130 | static int network_link_get_strv(int ifindex, const char *key, char ***ret) { |
fbd0b64f | 131 | char path[STRLEN("/run/systemd/netif/links/") + DECIMAL_STR_MAX(ifindex) + 1]; |
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 | ||
5f02f341 LP |
139 | xsprintf(path, "/run/systemd/netif/links/%i", ifindex); |
140 | r = parse_env_file(path, NEWLINE, key, &s, NULL); | |
8a516214 LP |
141 | if (r == -ENOENT) |
142 | return -ENODATA; | |
143 | if (r < 0) | |
144 | return r; | |
145 | if (isempty(s)) { | |
146 | *ret = NULL; | |
147 | return 0; | |
148 | } | |
149 | ||
150 | a = strv_split(s, " "); | |
151 | if (!a) | |
152 | return -ENOMEM; | |
153 | ||
154 | strv_uniq(a); | |
155 | r = strv_length(a); | |
156 | ||
157 | *ret = a; | |
158 | a = NULL; | |
159 | ||
160 | return r; | |
161 | } | |
162 | ||
38e5900f LP |
163 | _public_ int sd_network_link_get_setup_state(int ifindex, char **state) { |
164 | return network_link_get_string(ifindex, "ADMIN_STATE", state); | |
165 | } | |
adc5b2e2 | 166 | |
38e5900f LP |
167 | _public_ int sd_network_link_get_network_file(int ifindex, char **filename) { |
168 | return network_link_get_string(ifindex, "NETWORK_FILE", filename); | |
adc5b2e2 TG |
169 | } |
170 | ||
d6731e4c | 171 | _public_ int sd_network_link_get_operational_state(int ifindex, char **state) { |
38e5900f | 172 | return network_link_get_string(ifindex, "OPER_STATE", state); |
fe8db0c5 TG |
173 | } |
174 | ||
c1a38904 MTL |
175 | _public_ int sd_network_link_get_required_for_online(int ifindex) { |
176 | _cleanup_free_ char *s = NULL; | |
177 | int r; | |
178 | ||
179 | r = network_link_get_string(ifindex, "REQUIRED_FOR_ONLINE", &s); | |
180 | if (r < 0) { | |
181 | /* Handle -ENODATA as RequiredForOnline=yes, for compatibility */ | |
182 | if (r == -ENODATA) | |
183 | return true; | |
184 | return r; | |
185 | } | |
186 | ||
187 | return parse_boolean(s); | |
188 | } | |
189 | ||
d6731e4c | 190 | _public_ int sd_network_link_get_llmnr(int ifindex, char **llmnr) { |
38e5900f | 191 | return network_link_get_string(ifindex, "LLMNR", llmnr); |
bd8f6538 TG |
192 | } |
193 | ||
aaa297d4 LP |
194 | _public_ int sd_network_link_get_mdns(int ifindex, char **mdns) { |
195 | return network_link_get_string(ifindex, "MDNS", mdns); | |
196 | } | |
197 | ||
ad6c0475 LP |
198 | _public_ int sd_network_link_get_dnssec(int ifindex, char **dnssec) { |
199 | return network_link_get_string(ifindex, "DNSSEC", dnssec); | |
200 | } | |
201 | ||
8a516214 LP |
202 | _public_ int sd_network_link_get_dnssec_negative_trust_anchors(int ifindex, char ***nta) { |
203 | return network_link_get_strv(ifindex, "DNSSEC_NTA", nta); | |
204 | } | |
205 | ||
34437b4f | 206 | _public_ int sd_network_link_get_timezone(int ifindex, char **ret) { |
38e5900f | 207 | return network_link_get_string(ifindex, "TIMEZONE", ret); |
8eb9058d | 208 | } |
49699bac | 209 | |
d6731e4c | 210 | _public_ int sd_network_link_get_dns(int ifindex, char ***ret) { |
8a516214 | 211 | return network_link_get_strv(ifindex, "DNS", ret); |
bcb7a07e TG |
212 | } |
213 | ||
d6731e4c | 214 | _public_ int sd_network_link_get_ntp(int ifindex, char ***ret) { |
8a516214 | 215 | return network_link_get_strv(ifindex, "NTP", ret); |
bcb7a07e TG |
216 | } |
217 | ||
3df9bec5 | 218 | _public_ int sd_network_link_get_search_domains(int ifindex, char ***ret) { |
8a516214 | 219 | return network_link_get_strv(ifindex, "DOMAINS", ret); |
9b4d1882 TG |
220 | } |
221 | ||
3df9bec5 LP |
222 | _public_ int sd_network_link_get_route_domains(int ifindex, char ***ret) { |
223 | return network_link_get_strv(ifindex, "ROUTE_DOMAINS", ret); | |
224 | } | |
225 | ||
b295beea | 226 | static int network_link_get_ifindexes(int ifindex, const char *key, int **ret) { |
fbd0b64f | 227 | char path[STRLEN("/run/systemd/netif/links/") + DECIMAL_STR_MAX(ifindex) + 1]; |
b295beea | 228 | _cleanup_free_ int *ifis = NULL; |
5f02f341 | 229 | _cleanup_free_ char *s = NULL; |
b295beea LP |
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 | ||
5f02f341 LP |
237 | xsprintf(path, "/run/systemd/netif/links/%i", ifindex); |
238 | r = parse_env_file(path, NEWLINE, key, &s, NULL); | |
b295beea LP |
239 | if (r == -ENOENT) |
240 | return -ENODATA; | |
241 | if (r < 0) | |
242 | return r; | |
b295beea | 243 | |
c58bd76a | 244 | for (x = s;;) { |
b295beea LP |
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 | ||
c58bd76a | 257 | if (!GREEDY_REALLOC(ifis, allocated, c + 2)) |
b295beea LP |
258 | return -ENOMEM; |
259 | ||
260 | ifis[c++] = ifindex; | |
261 | } | |
262 | ||
c58bd76a | 263 | if (ifis) |
13e785f7 | 264 | ifis[c] = 0; /* Let's add a 0 ifindex to the end, to be nice */ |
b295beea LP |
265 | |
266 | *ret = ifis; | |
267 | ifis = NULL; | |
268 | ||
269 | return c; | |
270 | } | |
271 | ||
272 | _public_ int sd_network_link_get_carrier_bound_to(int ifindex, int **ret) { | |
273 | return network_link_get_ifindexes(ifindex, "CARRIER_BOUND_TO", ret); | |
0d4ad91d AR |
274 | } |
275 | ||
b295beea LP |
276 | _public_ int sd_network_link_get_carrier_bound_by(int ifindex, int **ret) { |
277 | return network_link_get_ifindexes(ifindex, "CARRIER_BOUND_BY", ret); | |
0d4ad91d AR |
278 | } |
279 | ||
fe8db0c5 TG |
280 | static inline int MONITOR_TO_FD(sd_network_monitor *m) { |
281 | return (int) (unsigned long) m - 1; | |
282 | } | |
283 | ||
284 | static inline sd_network_monitor* FD_TO_MONITOR(int fd) { | |
285 | return (sd_network_monitor*) (unsigned long) (fd + 1); | |
286 | } | |
287 | ||
870395a4 TG |
288 | static int monitor_add_inotify_watch(int fd) { |
289 | int k; | |
290 | ||
291 | k = inotify_add_watch(fd, "/run/systemd/netif/links/", IN_MOVED_TO|IN_DELETE); | |
292 | if (k >= 0) | |
293 | return 0; | |
294 | else if (errno != ENOENT) | |
295 | return -errno; | |
296 | ||
297 | k = inotify_add_watch(fd, "/run/systemd/netif/", IN_CREATE|IN_ISDIR); | |
298 | if (k >= 0) | |
299 | return 0; | |
300 | else if (errno != ENOENT) | |
301 | return -errno; | |
302 | ||
303 | k = inotify_add_watch(fd, "/run/systemd/", IN_CREATE|IN_ISDIR); | |
304 | if (k < 0) | |
305 | return -errno; | |
306 | ||
307 | return 0; | |
308 | } | |
309 | ||
0014a4ad | 310 | _public_ int sd_network_monitor_new(sd_network_monitor **m, const char *category) { |
870395a4 TG |
311 | _cleanup_close_ int fd = -1; |
312 | int k; | |
fe8db0c5 TG |
313 | bool good = false; |
314 | ||
315 | assert_return(m, -EINVAL); | |
316 | ||
317 | fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); | |
318 | if (fd < 0) | |
319 | return -errno; | |
320 | ||
7e141e49 | 321 | if (!category || streq(category, "links")) { |
870395a4 TG |
322 | k = monitor_add_inotify_watch(fd); |
323 | if (k < 0) | |
324 | return k; | |
fe8db0c5 TG |
325 | |
326 | good = true; | |
327 | } | |
328 | ||
3de1c8ce | 329 | if (!good) |
fe8db0c5 | 330 | return -EINVAL; |
fe8db0c5 TG |
331 | |
332 | *m = FD_TO_MONITOR(fd); | |
870395a4 TG |
333 | fd = -1; |
334 | ||
fe8db0c5 TG |
335 | return 0; |
336 | } | |
337 | ||
338 | _public_ sd_network_monitor* sd_network_monitor_unref(sd_network_monitor *m) { | |
339 | int fd; | |
340 | ||
0b347626 TG |
341 | if (m) { |
342 | fd = MONITOR_TO_FD(m); | |
343 | close_nointr(fd); | |
344 | } | |
fe8db0c5 TG |
345 | |
346 | return NULL; | |
347 | } | |
348 | ||
349 | _public_ int sd_network_monitor_flush(sd_network_monitor *m) { | |
870395a4 TG |
350 | union inotify_event_buffer buffer; |
351 | struct inotify_event *e; | |
352 | ssize_t l; | |
353 | int fd, k; | |
fe8db0c5 TG |
354 | |
355 | assert_return(m, -EINVAL); | |
356 | ||
870395a4 TG |
357 | fd = MONITOR_TO_FD(m); |
358 | ||
359 | l = read(fd, &buffer, sizeof(buffer)); | |
360 | if (l < 0) { | |
945c2931 | 361 | if (IN_SET(errno, EAGAIN, EINTR)) |
870395a4 TG |
362 | return 0; |
363 | ||
364 | return -errno; | |
365 | } | |
366 | ||
367 | FOREACH_INOTIFY_EVENT(e, buffer, l) { | |
368 | if (e->mask & IN_ISDIR) { | |
369 | k = monitor_add_inotify_watch(fd); | |
370 | if (k < 0) | |
371 | return k; | |
372 | ||
373 | k = inotify_rm_watch(fd, e->wd); | |
374 | if (k < 0) | |
375 | return -errno; | |
376 | } | |
377 | } | |
378 | ||
379 | return 0; | |
fe8db0c5 TG |
380 | } |
381 | ||
382 | _public_ int sd_network_monitor_get_fd(sd_network_monitor *m) { | |
383 | ||
384 | assert_return(m, -EINVAL); | |
385 | ||
386 | return MONITOR_TO_FD(m); | |
387 | } | |
388 | ||
389 | _public_ int sd_network_monitor_get_events(sd_network_monitor *m) { | |
390 | ||
391 | assert_return(m, -EINVAL); | |
392 | ||
393 | /* For now we will only return POLLIN here, since we don't | |
394 | * need anything else ever for inotify. However, let's have | |
395 | * this API to keep our options open should we later on need | |
396 | * it. */ | |
397 | return POLLIN; | |
398 | } | |
399 | ||
400 | _public_ int sd_network_monitor_get_timeout(sd_network_monitor *m, uint64_t *timeout_usec) { | |
401 | ||
402 | assert_return(m, -EINVAL); | |
403 | assert_return(timeout_usec, -EINVAL); | |
404 | ||
405 | /* For now we will only return (uint64_t) -1, since we don't | |
406 | * need any timeout. However, let's have this API to keep our | |
407 | * options open should we later on need it. */ | |
408 | *timeout_usec = (uint64_t) -1; | |
409 | return 0; | |
410 | } |