event: pull in sd-event.h from event-util.h
[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
c92e531c
LP
117 assert(ret);
118
091a364c
TG
119 m = new0(Manager, 1);
120 if (!m)
121 return -ENOMEM;
122
123 r = set_fallback_dns(m, DNS_SERVERS);
124 if (r < 0)
125 return r;
126
127 r = manager_parse_config_file(m);
128 if (r < 0)
129 return r;
130
131 r = sd_event_default(&m->event);
132 if (r < 0)
133 return r;
134
135 sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
136 sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
137
138 sd_event_set_watchdog(m->event, true);
139
140 *ret = m;
141 m = NULL;
142
143 return 0;
144}
145
146void manager_free(Manager *m) {
147 Address *address;
148
149 if (!m)
150 return;
151
152 sd_event_unref(m->event);
153
154 while ((address = m->fallback_dns)) {
155 LIST_REMOVE(addresses, m->fallback_dns, address);
156 free(address);
157 }
158
159 free(m);
160}
161
162static void append_dns(FILE *f, void *dns, unsigned char family, unsigned *count) {
163 char buf[INET6_ADDRSTRLEN];
164 const char *address;
165
166 assert(f);
167 assert(dns);
168 assert(count);
169
170 address = inet_ntop(family, dns, buf, INET6_ADDRSTRLEN);
171 if (!address) {
172 log_warning("Invalid DNS address. Ignoring.");
173 return;
174 }
175
176 if (*count == MAXNS)
177 fputs("# Too many DNS servers configured, the following entries "
178 "may be ignored\n", f);
179
180 fprintf(f, "nameserver %s\n", address);
181
182 (*count) ++;
183}
184
185int manager_update_resolv_conf(Manager *m) {
b686acb2 186 const char *path = "/run/systemd/resolve/resolv.conf";
091a364c
TG
187 _cleanup_free_ char *temp_path = NULL;
188 _cleanup_fclose_ FILE *f = NULL;
189 _cleanup_free_ unsigned *indices = NULL;
190 Address *address;
191 unsigned count = 0;
192 int n, r, i;
193
194 assert(m);
195
b686acb2 196 r = fopen_temporary(path, &f, &temp_path);
091a364c
TG
197 if (r < 0)
198 return r;
199
200 fchmod(fileno(f), 0644);
201
202 fputs("# This file is managed by systemd-resolved(8). Do not edit.\n#\n"
203 "# Third party programs must not access this file directly, but\n"
204 "# only through the symlink at /etc/resolv.conf. To manage\n"
205 "# resolv.conf(5) in a different way, replace the symlink by a\n"
206 "# static file or a different symlink.\n\n", f);
207
208 n = sd_network_get_ifindices(&indices);
209 if (n < 0)
210 n = 0;
211
212 for (i = 0; i < n; i++) {
213 _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
214 struct in_addr *nameservers;
215 struct in6_addr *nameservers6;
216 size_t nameservers_size;
217
218 r = sd_network_dhcp_use_dns(indices[i]);
219 if (r > 0) {
220 r = sd_network_get_dhcp_lease(indices[i], &lease);
221 if (r >= 0) {
222 r = sd_dhcp_lease_get_dns(lease, &nameservers, &nameservers_size);
223 if (r >= 0) {
224 unsigned j;
225
226 for (j = 0; j < nameservers_size; j++)
227 append_dns(f, &nameservers[j], AF_INET, &count);
228 }
229 }
230 }
231
232 r = sd_network_get_dns(indices[i], &nameservers, &nameservers_size);
233 if (r >= 0) {
234 unsigned j;
235
236 for (j = 0; j < nameservers_size; j++)
237 append_dns(f, &nameservers[j], AF_INET, &count);
238
239 free(nameservers);
240 }
241
242 r = sd_network_get_dns6(indices[i], &nameservers6, &nameservers_size);
243 if (r >= 0) {
244 unsigned j;
245
246 for (j = 0; j < nameservers_size; j++)
247 append_dns(f, &nameservers6[j], AF_INET6, &count);
248
249 free(nameservers6);
250 }
251 }
252
253 LIST_FOREACH(addresses, address, m->fallback_dns)
254 append_dns(f, &address->in_addr, address->family, &count);
255
256 fflush(f);
257
b686acb2 258 if (ferror(f) || rename(temp_path, path) < 0) {
091a364c 259 r = -errno;
b686acb2 260 unlink(path);
091a364c
TG
261 unlink(temp_path);
262 return r;
263 }
264
265 return 0;
266}
267
268static int manager_network_event_handler(sd_event_source *s, int fd, uint32_t revents,
269 void *userdata) {
270 Manager *m = userdata;
271 int r;
272
273 assert(m);
274
275 r = manager_update_resolv_conf(m);
276 if (r < 0)
277 log_warning("Could not update resolv.conf: %s", strerror(-r));
278
279 sd_network_monitor_flush(m->network_monitor);
280
281 return 0;
282}
283
284int manager_network_monitor_listen(Manager *m) {
285 _cleanup_event_source_unref_ sd_event_source *event_source = NULL;
286 _cleanup_network_monitor_unref_ sd_network_monitor *monitor = NULL;
287 int r, fd, events;
288
289 r = sd_network_monitor_new(NULL, &monitor);
290 if (r < 0)
291 return r;
292
293 fd = sd_network_monitor_get_fd(monitor);
294 if (fd < 0)
295 return fd;
296
297 events = sd_network_monitor_get_events(monitor);
298 if (events < 0)
299 return events;
300
301 r = sd_event_add_io(m->event, &event_source, fd, events,
302 &manager_network_event_handler, m);
303 if (r < 0)
304 return r;
305
306 m->network_monitor = monitor;
307 m->network_event_source = event_source;
308 monitor = NULL;
309 event_source = NULL;
310
311 return 0;
312}