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