chiark / gitweb /
resolved: let config_parse() open the configuration file for us
[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         int r;
101
102         assert(m);
103
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);
107         if (r < 0)
108                 log_warning("Failed to parse configuration file: %s", strerror(-r));
109
110         return r;
111 }
112
113 int 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
144 void 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
160 static 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
183 int manager_update_resolv_conf(Manager *m) {
184         const char *path = "/run/systemd/resolve/resolv.conf";
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
194         r = fopen_temporary(path, &f, &temp_path);
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
256         if (ferror(f) || rename(temp_path, path) < 0) {
257                 r = -errno;
258                 unlink(path);
259                 unlink(temp_path);
260                 return r;
261         }
262
263         return 0;
264 }
265
266 static 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
282 int 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 }