chiark / gitweb /
fstab-generator: fix trivial leak
[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_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
162 static 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
185 int manager_update_resolv_conf(Manager *m) {
186         const char *path = "/run/systemd/resolve/resolv.conf";
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
196         r = fopen_temporary(path, &f, &temp_path);
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
258         if (ferror(f) || rename(temp_path, path) < 0) {
259                 r = -errno;
260                 unlink(path);
261                 unlink(temp_path);
262                 return r;
263         }
264
265         return 0;
266 }
267
268 static 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
284 int 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 }