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