networkd: simplify signal handling of SIGTERM/SIGINT
[thirdparty/systemd.git] / src / resolve / resolved-manager.c
CommitLineData
091a364c
TG
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2014 Tom Gundersen <teg@jklm.no>
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
22#include <arpa/inet.h>
23#include <resolv.h>
24#include <linux/if.h>
25
26#include "resolved.h"
27#include "event-util.h"
28#include "network-util.h"
29#include "sd-dhcp-lease.h"
30#include "dhcp-lease-internal.h"
31#include "network-internal.h"
32#include "conf-parser.h"
091a364c
TG
33
34static int set_fallback_dns(Manager *m, const char *string) {
35 char *word, *state;
36 size_t length;
37 int r;
38
39 assert(m);
40 assert(string);
41
42 FOREACH_WORD_QUOTED(word, length, string, state) {
43 _cleanup_free_ Address *address = NULL;
44 Address *tail;
45 _cleanup_free_ char *addrstr = NULL;
46
47 address = new0(Address, 1);
48 if (!address)
49 return -ENOMEM;
50
51 addrstr = strndup(word, length);
52 if (!addrstr)
53 return -ENOMEM;
54
55 r = net_parse_inaddr(addrstr, &address->family, &address->in_addr);
56 if (r < 0) {
57 log_debug("Ignoring invalid DNS address '%s'", addrstr);
58 continue;
59 }
60
61 LIST_FIND_TAIL(addresses, m->fallback_dns, tail);
62 LIST_INSERT_AFTER(addresses, m->fallback_dns, tail, address);
63 address = NULL;
64 }
65
66 return 0;
67}
68
69int config_parse_dnsv(
70 const char *unit,
71 const char *filename,
72 unsigned line,
73 const char *section,
74 unsigned section_line,
75 const char *lvalue,
76 int ltype,
77 const char *rvalue,
78 void *data,
79 void *userdata) {
80
81 Manager *m = userdata;
82 Address *address;
83
84 assert(filename);
85 assert(lvalue);
86 assert(rvalue);
87 assert(m);
88
89 while ((address = m->fallback_dns)) {
90 LIST_REMOVE(addresses, m->fallback_dns, address);
91 free(address);
92 }
93
94 set_fallback_dns(m, rvalue);
95
96 return 0;
97}
98
99static int manager_parse_config_file(Manager *m) {
091a364c
TG
100 int r;
101
102 assert(m);
103
987d561f
LP
104 r = config_parse(NULL, "/etc/systemd/resolved.conf", NULL,
105 "Resolve\0", config_item_perf_lookup, (void*) resolved_gperf_lookup,
106 false, false, m);
091a364c
TG
107 if (r < 0)
108 log_warning("Failed to parse configuration file: %s", strerror(-r));
109
110 return r;
111}
112
113int manager_new(Manager **ret) {
114 _cleanup_manager_free_ Manager *m = NULL;
115 int r;
116
117 m = new0(Manager, 1);
118 if (!m)
119 return -ENOMEM;
120
121 r = set_fallback_dns(m, DNS_SERVERS);
122 if (r < 0)
123 return r;
124
125 r = manager_parse_config_file(m);
126 if (r < 0)
127 return r;
128
129 r = sd_event_default(&m->event);
130 if (r < 0)
131 return r;
132
133 sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
134 sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
135
136 sd_event_set_watchdog(m->event, true);
137
138 *ret = m;
139 m = NULL;
140
141 return 0;
142}
143
144void manager_free(Manager *m) {
145 Address *address;
146
147 if (!m)
148 return;
149
150 sd_event_unref(m->event);
151
152 while ((address = m->fallback_dns)) {
153 LIST_REMOVE(addresses, m->fallback_dns, address);
154 free(address);
155 }
156
157 free(m);
158}
159
160static void append_dns(FILE *f, void *dns, unsigned char family, unsigned *count) {
161 char buf[INET6_ADDRSTRLEN];
162 const char *address;
163
164 assert(f);
165 assert(dns);
166 assert(count);
167
168 address = inet_ntop(family, dns, buf, INET6_ADDRSTRLEN);
169 if (!address) {
170 log_warning("Invalid DNS address. Ignoring.");
171 return;
172 }
173
174 if (*count == MAXNS)
175 fputs("# Too many DNS servers configured, the following entries "
176 "may be ignored\n", f);
177
178 fprintf(f, "nameserver %s\n", address);
179
180 (*count) ++;
181}
182
183int manager_update_resolv_conf(Manager *m) {
b686acb2 184 const char *path = "/run/systemd/resolve/resolv.conf";
091a364c
TG
185 _cleanup_free_ char *temp_path = NULL;
186 _cleanup_fclose_ FILE *f = NULL;
187 _cleanup_free_ unsigned *indices = NULL;
188 Address *address;
189 unsigned count = 0;
190 int n, r, i;
191
192 assert(m);
193
b686acb2 194 r = fopen_temporary(path, &f, &temp_path);
091a364c
TG
195 if (r < 0)
196 return r;
197
198 fchmod(fileno(f), 0644);
199
200 fputs("# This file is managed by systemd-resolved(8). Do not edit.\n#\n"
201 "# Third party programs must not access this file directly, but\n"
202 "# only through the symlink at /etc/resolv.conf. To manage\n"
203 "# resolv.conf(5) in a different way, replace the symlink by a\n"
204 "# static file or a different symlink.\n\n", f);
205
206 n = sd_network_get_ifindices(&indices);
207 if (n < 0)
208 n = 0;
209
210 for (i = 0; i < n; i++) {
211 _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
212 struct in_addr *nameservers;
213 struct in6_addr *nameservers6;
214 size_t nameservers_size;
215
216 r = sd_network_dhcp_use_dns(indices[i]);
217 if (r > 0) {
218 r = sd_network_get_dhcp_lease(indices[i], &lease);
219 if (r >= 0) {
220 r = sd_dhcp_lease_get_dns(lease, &nameservers, &nameservers_size);
221 if (r >= 0) {
222 unsigned j;
223
224 for (j = 0; j < nameservers_size; j++)
225 append_dns(f, &nameservers[j], AF_INET, &count);
226 }
227 }
228 }
229
230 r = sd_network_get_dns(indices[i], &nameservers, &nameservers_size);
231 if (r >= 0) {
232 unsigned j;
233
234 for (j = 0; j < nameservers_size; j++)
235 append_dns(f, &nameservers[j], AF_INET, &count);
236
237 free(nameservers);
238 }
239
240 r = sd_network_get_dns6(indices[i], &nameservers6, &nameservers_size);
241 if (r >= 0) {
242 unsigned j;
243
244 for (j = 0; j < nameservers_size; j++)
245 append_dns(f, &nameservers6[j], AF_INET6, &count);
246
247 free(nameservers6);
248 }
249 }
250
251 LIST_FOREACH(addresses, address, m->fallback_dns)
252 append_dns(f, &address->in_addr, address->family, &count);
253
254 fflush(f);
255
b686acb2 256 if (ferror(f) || rename(temp_path, path) < 0) {
091a364c 257 r = -errno;
b686acb2 258 unlink(path);
091a364c
TG
259 unlink(temp_path);
260 return r;
261 }
262
263 return 0;
264}
265
266static int manager_network_event_handler(sd_event_source *s, int fd, uint32_t revents,
267 void *userdata) {
268 Manager *m = userdata;
269 int r;
270
271 assert(m);
272
273 r = manager_update_resolv_conf(m);
274 if (r < 0)
275 log_warning("Could not update resolv.conf: %s", strerror(-r));
276
277 sd_network_monitor_flush(m->network_monitor);
278
279 return 0;
280}
281
282int manager_network_monitor_listen(Manager *m) {
283 _cleanup_event_source_unref_ sd_event_source *event_source = NULL;
284 _cleanup_network_monitor_unref_ sd_network_monitor *monitor = NULL;
285 int r, fd, events;
286
287 r = sd_network_monitor_new(NULL, &monitor);
288 if (r < 0)
289 return r;
290
291 fd = sd_network_monitor_get_fd(monitor);
292 if (fd < 0)
293 return fd;
294
295 events = sd_network_monitor_get_events(monitor);
296 if (events < 0)
297 return events;
298
299 r = sd_event_add_io(m->event, &event_source, fd, events,
300 &manager_network_event_handler, m);
301 if (r < 0)
302 return r;
303
304 m->network_monitor = monitor;
305 m->network_event_source = event_source;
306 monitor = NULL;
307 event_source = NULL;
308
309 return 0;
310}