]>
Commit | Line | Data |
---|---|---|
6644c1c7 MT |
1 | From 5f4dc5c6ca50655ab14f572c7e30815ed74cd51a Mon Sep 17 00:00:00 2001 |
2 | From: Simon Kelley <simon@thekelleys.org.uk> | |
3 | Date: Tue, 20 Jan 2015 20:51:02 +0000 | |
697b4f04 | 4 | Subject: [PATCH 034/113] Add --dhcp-hostsdir config option. |
6644c1c7 MT |
5 | |
6 | --- | |
7 | CHANGELOG | 5 +++ | |
8 | man/dnsmasq.8 | 9 +++++ | |
9 | src/dnsmasq.c | 28 ++++++++++---- | |
10 | src/dnsmasq.h | 15 ++++++-- | |
11 | src/inotify.c | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--- | |
12 | src/option.c | 22 +++++++++-- | |
13 | 6 files changed, 177 insertions(+), 21 deletions(-) | |
14 | ||
15 | diff --git a/CHANGELOG b/CHANGELOG | |
16 | index bbd7e6619689..0076b557e95e 100644 | |
17 | --- a/CHANGELOG | |
18 | +++ b/CHANGELOG | |
19 | @@ -53,6 +53,11 @@ version 2.73 | |
20 | Cope with multiple interfaces with the same link-local | |
21 | address. (IPv6 addresses are scoped, so this is allowed.) | |
22 | Thanks to Cory Benfield for help with this. | |
23 | + | |
24 | + Add --dhcp-hostsdir. This allows addition of new host | |
25 | + configurations to a running dnsmasq instance much more | |
26 | + cheaply than having dnsmasq re-read all its existing | |
27 | + configuration each time. | |
28 | ||
29 | ||
30 | version 2.72 | |
31 | diff --git a/man/dnsmasq.8 b/man/dnsmasq.8 | |
32 | index 5cfa355dea4a..005b5cca8d1f 100644 | |
33 | --- a/man/dnsmasq.8 | |
34 | +++ b/man/dnsmasq.8 | |
35 | @@ -977,6 +977,15 @@ is given, then read all the files contained in that directory. The advantage of | |
36 | using this option is the same as for --dhcp-hostsfile: the | |
37 | dhcp-optsfile will be re-read when dnsmasq receives SIGHUP. Note that | |
38 | it is possible to encode the information in a | |
39 | +.TP | |
40 | +.B --dhcp-hostsdir=<path> | |
41 | +This is exactly equivalent to dhcp-hostfile, except for the following. The path MUST be a | |
42 | +directory, and not an individual file. Changed or new files within | |
43 | +the directory are read automatically, without the need to send SIGHUP. | |
44 | +If a file is deleted for changed after it has been read by dnsmasq, then the | |
45 | +host record it contained will remain until dnsmasq recieves a SIGHUP, or | |
46 | +is restarted; ie host records are only added dynamically. | |
47 | +.TP | |
48 | .B --dhcp-boot | |
49 | flag as DHCP options, using the options names bootfile-name, | |
50 | server-ip-address and tftp-server. This allows these to be included | |
51 | diff --git a/src/dnsmasq.c b/src/dnsmasq.c | |
52 | index c0c0589d4ce1..04cc98278f62 100644 | |
53 | --- a/src/dnsmasq.c | |
54 | +++ b/src/dnsmasq.c | |
55 | @@ -142,6 +142,9 @@ int main (int argc, char **argv) | |
56 | set_option_bool(OPT_NOWILD); | |
57 | reset_option_bool(OPT_CLEVERBIND); | |
58 | } | |
59 | + | |
60 | + if (daemon->inotify_hosts) | |
61 | + die(_("dhcp-hostsdir not supported on this platform"), NULL, EC_BADCONF); | |
62 | #endif | |
63 | ||
64 | if (option_bool(OPT_DNSSEC_VALID)) | |
65 | @@ -316,13 +319,16 @@ int main (int argc, char **argv) | |
66 | #ifdef HAVE_DNSSEC | |
67 | blockdata_init(); | |
68 | #endif | |
69 | + } | |
70 | ||
71 | #ifdef HAVE_LINUX_NETWORK | |
72 | - if (!option_bool(OPT_NO_POLL)) | |
73 | - inotify_dnsmasq_init(); | |
74 | + if ((!option_bool(OPT_NO_POLL) && daemon->port != 0) || | |
75 | + daemon->dhcp || daemon->doing_dhcp6) | |
76 | + inotify_dnsmasq_init(); | |
77 | + else | |
78 | + daemon->inotifyfd = -1; | |
79 | #endif | |
80 | - } | |
81 | - | |
82 | + | |
83 | if (option_bool(OPT_DBUS)) | |
84 | #ifdef HAVE_DBUS | |
85 | { | |
86 | @@ -745,7 +751,7 @@ int main (int argc, char **argv) | |
87 | #endif | |
88 | ||
89 | #ifdef HAVE_TFTP | |
90 | - if (option_bool(OPT_TFTP)) | |
91 | + if (option_bool(OPT_TFTP)) | |
92 | { | |
93 | #ifdef FD_SETSIZE | |
94 | if (FD_SETSIZE < (unsigned)max_fd) | |
95 | @@ -870,7 +876,7 @@ int main (int argc, char **argv) | |
96 | #if defined(HAVE_LINUX_NETWORK) | |
97 | FD_SET(daemon->netlinkfd, &rset); | |
98 | bump_maxfd(daemon->netlinkfd, &maxfd); | |
99 | - if (daemon->port != 0 && !option_bool(OPT_NO_POLL)) | |
100 | + if (daemon->inotifyfd != -1) | |
101 | { | |
102 | FD_SET(daemon->inotifyfd, &rset); | |
103 | bump_maxfd(daemon->inotifyfd, &maxfd); | |
104 | @@ -943,8 +949,11 @@ int main (int argc, char **argv) | |
105 | #endif | |
106 | ||
107 | #ifdef HAVE_LINUX_NETWORK | |
108 | - if (daemon->port != 0 && !option_bool(OPT_NO_POLL) && FD_ISSET(daemon->inotifyfd, &rset) && inotify_check()) | |
109 | - poll_resolv(1, 1, now); | |
110 | + if (daemon->inotifyfd != -1 && FD_ISSET(daemon->inotifyfd, &rset) && inotify_check(now)) | |
111 | + { | |
112 | + if (daemon->port != 0 && !option_bool(OPT_NO_POLL)) | |
113 | + poll_resolv(1, 1, now); | |
114 | + } | |
115 | #else | |
116 | /* Check for changes to resolv files once per second max. */ | |
117 | /* Don't go silent for long periods if the clock goes backwards. */ | |
118 | @@ -1385,6 +1394,9 @@ void clear_cache_and_reload(time_t now) | |
119 | if (option_bool(OPT_ETHERS)) | |
120 | dhcp_read_ethers(); | |
121 | reread_dhcp(); | |
122 | +#ifdef HAVE_LINUX_NETWORK | |
123 | + set_dhcp_inotify(); | |
124 | +#endif | |
125 | dhcp_update_configs(daemon->dhcp_conf); | |
126 | lease_update_from_configs(); | |
127 | lease_update_file(now); | |
128 | diff --git a/src/dnsmasq.h b/src/dnsmasq.h | |
129 | index f8275e3ac479..d841fdc064ad 100644 | |
130 | --- a/src/dnsmasq.h | |
131 | +++ b/src/dnsmasq.h | |
132 | @@ -550,13 +550,17 @@ struct resolvc { | |
133 | #endif | |
134 | }; | |
135 | ||
136 | -/* adn-hosts parms from command-line (also dhcp-hostsfile and dhcp-optsfile */ | |
137 | +/* adn-hosts parms from command-line (also dhcp-hostsfile and dhcp-optsfile and dhcp-hostsdir*/ | |
138 | #define AH_DIR 1 | |
139 | #define AH_INACTIVE 2 | |
140 | +#define AH_WD_DONE 4 | |
141 | struct hostsfile { | |
142 | struct hostsfile *next; | |
143 | int flags; | |
144 | char *fname; | |
145 | +#ifdef HAVE_LINUX_NETWORK | |
146 | + int wd; /* inotify watch descriptor */ | |
147 | +#endif | |
148 | unsigned int index; /* matches to cache entries for logging */ | |
149 | }; | |
150 | ||
151 | @@ -961,7 +965,7 @@ extern struct daemon { | |
152 | int doing_ra, doing_dhcp6; | |
153 | struct dhcp_netid_list *dhcp_ignore, *dhcp_ignore_names, *dhcp_gen_names; | |
154 | struct dhcp_netid_list *force_broadcast, *bootp_dynamic; | |
155 | - struct hostsfile *dhcp_hosts_file, *dhcp_opts_file; | |
156 | + struct hostsfile *dhcp_hosts_file, *dhcp_opts_file, *inotify_hosts; | |
157 | int dhcp_max, tftp_max; | |
158 | int dhcp_server_port, dhcp_client_port; | |
159 | int start_tftp_port, end_tftp_port; | |
160 | @@ -1197,7 +1201,7 @@ void reset_option_bool(unsigned int opt); | |
161 | struct hostsfile *expand_filelist(struct hostsfile *list); | |
162 | char *parse_server(char *arg, union mysockaddr *addr, | |
163 | union mysockaddr *source_addr, char *interface, int *flags); | |
164 | - | |
165 | +int option_read_hostsfile(char *file); | |
166 | /* forward.c */ | |
167 | void reply_query(int fd, int family, time_t now); | |
168 | void receive_query(struct listener *listen, time_t now); | |
169 | @@ -1486,5 +1490,8 @@ int detect_loop(char *query, int type); | |
170 | /* inotify.c */ | |
171 | #ifdef HAVE_LINUX_NETWORK | |
172 | void inotify_dnsmasq_init(); | |
173 | -int inotify_check(void); | |
174 | +int inotify_check(time_t now); | |
175 | +# ifdef HAVE_DHCP | |
176 | +void set_dhcp_inotify(void); | |
177 | +# endif | |
178 | #endif | |
179 | diff --git a/src/inotify.c b/src/inotify.c | |
180 | index 83730008c11b..52a30d7f44db 100644 | |
181 | --- a/src/inotify.c | |
182 | +++ b/src/inotify.c | |
183 | @@ -19,6 +19,11 @@ | |
184 | ||
185 | #include <sys/inotify.h> | |
186 | ||
187 | +#ifdef HAVE_DHCP | |
188 | +static void check_for_dhcp_inotify(struct inotify_event *in, time_t now); | |
189 | +#endif | |
190 | + | |
191 | + | |
192 | /* the strategy is to set a inotify on the directories containing | |
193 | resolv files, for any files in the directory which are close-write | |
194 | or moved into the directory. | |
195 | @@ -40,8 +45,6 @@ void inotify_dnsmasq_init() | |
196 | struct resolvc *res; | |
197 | ||
198 | inotify_buffer = safe_malloc(INOTIFY_SZ); | |
199 | - | |
200 | - | |
201 | daemon->inotifyfd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); | |
202 | ||
203 | if (daemon->inotifyfd == -1) | |
204 | @@ -66,6 +69,7 @@ void inotify_dnsmasq_init() | |
205 | { | |
206 | *d = 0; /* make path just directory */ | |
207 | res->wd = inotify_add_watch(daemon->inotifyfd, path, IN_CLOSE_WRITE | IN_MOVED_TO); | |
208 | + | |
209 | res->file = d+1; /* pointer to filename */ | |
210 | *d = '/'; | |
211 | ||
212 | @@ -78,7 +82,7 @@ void inotify_dnsmasq_init() | |
213 | } | |
214 | } | |
215 | ||
216 | -int inotify_check(void) | |
217 | +int inotify_check(time_t now) | |
218 | { | |
219 | int hit = 0; | |
220 | ||
221 | @@ -101,13 +105,116 @@ int inotify_check(void) | |
222 | for (res = daemon->resolv_files; res; res = res->next) | |
223 | if (res->wd == in->wd && in->len != 0 && strcmp(res->file, in->name) == 0) | |
224 | hit = 1; | |
225 | + | |
226 | +#ifdef HAVE_DHCP | |
227 | + if (daemon->dhcp || daemon->doing_dhcp6) | |
228 | + check_for_dhcp_inotify(in, now); | |
229 | +#endif | |
230 | } | |
231 | } | |
232 | - | |
233 | return hit; | |
234 | } | |
235 | ||
236 | -#endif | |
237 | +#ifdef HAVE_DHCP | |
238 | +/* initialisation for dhcp-hostdir. Set inotify watch for each directory, and read pre-existing files */ | |
239 | +void set_dhcp_inotify(void) | |
240 | +{ | |
241 | + struct hostsfile *ah; | |
242 | ||
243 | - | |
244 | + for (ah = daemon->inotify_hosts; ah; ah = ah->next) | |
245 | + { | |
246 | + DIR *dir_stream = NULL; | |
247 | + struct dirent *ent; | |
248 | + struct stat buf; | |
249 | + | |
250 | + if (stat(ah->fname, &buf) == -1 || !(S_ISDIR(buf.st_mode))) | |
251 | + { | |
252 | + my_syslog(LOG_ERR, _("bad directory in dhcp-hostsdir %s"), ah->fname); | |
253 | + continue; | |
254 | + } | |
255 | + | |
256 | + if (!(ah->flags & AH_WD_DONE)) | |
257 | + { | |
258 | + ah->wd = inotify_add_watch(daemon->inotifyfd, ah->fname, IN_CLOSE_WRITE | IN_MOVED_TO); | |
259 | + ah->flags |= AH_WD_DONE; | |
260 | + } | |
261 | + /* Read contents of dir _after_ calling add_watch, in the ho[e of avoiding | |
262 | + a race which misses files being added as we start */ | |
263 | + if (ah->wd == -1 || !(dir_stream = opendir(ah->fname))) | |
264 | + { | |
265 | + my_syslog(LOG_ERR, _("failed to create inotify for %s"), ah->fname); | |
266 | + continue; | |
267 | + } | |
268 | + | |
269 | + while ((ent = readdir(dir_stream))) | |
270 | + { | |
271 | + size_t lendir = strlen(ah->fname); | |
272 | + size_t lenfile = strlen(ent->d_name); | |
273 | + char *path; | |
274 | + | |
275 | + /* ignore emacs backups and dotfiles */ | |
276 | + if (lenfile == 0 || | |
277 | + ent->d_name[lenfile - 1] == '~' || | |
278 | + (ent->d_name[0] == '#' && ent->d_name[lenfile - 1] == '#') || | |
279 | + ent->d_name[0] == '.') | |
280 | + continue; | |
281 | + | |
282 | + if ((path = whine_malloc(lendir + lenfile + 2))) | |
283 | + { | |
284 | + strcpy(path, ah->fname); | |
285 | + strcat(path, "/"); | |
286 | + strcat(path, ent->d_name); | |
287 | + | |
288 | + /* ignore non-regular files */ | |
289 | + if (stat(path, &buf) != -1 && S_ISREG(buf.st_mode)) | |
290 | + option_read_hostsfile(path); | |
291 | + | |
292 | + free(path); | |
293 | + } | |
294 | + } | |
295 | + } | |
296 | +} | |
297 | + | |
298 | +static void check_for_dhcp_inotify(struct inotify_event *in, time_t now) | |
299 | +{ | |
300 | + struct hostsfile *ah; | |
301 | + | |
302 | + /* ignore emacs backups and dotfiles */ | |
303 | + if (in->len == 0 || | |
304 | + in->name[in->len - 1] == '~' || | |
305 | + (in->name[0] == '#' && in->name[in->len - 1] == '#') || | |
306 | + in->name[0] == '.') | |
307 | + return; | |
308 | + | |
309 | + for (ah = daemon->inotify_hosts; ah; ah = ah->next) | |
310 | + if (ah->wd == in->wd) | |
311 | + { | |
312 | + size_t lendir = strlen(ah->fname); | |
313 | + char *path; | |
314 | + | |
315 | + if ((path = whine_malloc(lendir + in->len + 2))) | |
316 | + { | |
317 | + strcpy(path, ah->fname); | |
318 | + strcat(path, "/"); | |
319 | + strcat(path, in->name); | |
320 | + | |
321 | + if (option_read_hostsfile(path)) | |
322 | + { | |
323 | + /* Propogate the consequences of loading a new dhcp-host */ | |
324 | + dhcp_update_configs(daemon->dhcp_conf); | |
325 | + lease_update_from_configs(); | |
326 | + lease_update_file(now); | |
327 | + lease_update_dns(1); | |
328 | + } | |
329 | + | |
330 | + free(path); | |
331 | + } | |
332 | + | |
333 | + return; | |
334 | + } | |
335 | +} | |
336 | + | |
337 | +#endif /* DHCP */ | |
338 | + | |
339 | +#endif /* LINUX_NETWORK */ | |
340 | ||
341 | diff --git a/src/option.c b/src/option.c | |
342 | index 8b994098cc9f..22e11c37d374 100644 | |
343 | --- a/src/option.c | |
344 | +++ b/src/option.c | |
345 | @@ -149,7 +149,7 @@ struct myoption { | |
346 | #define LOPT_LOOP_DETECT 337 | |
347 | #define LOPT_IGNORE_ADDR 338 | |
348 | #define LOPT_MINCTTL 339 | |
349 | - | |
350 | +#define LOPT_DHCP_INOTIFY 340 | |
351 | ||
352 | #ifdef HAVE_GETOPT_LONG | |
353 | static const struct option opts[] = | |
354 | @@ -248,6 +248,7 @@ static const struct myoption opts[] = | |
355 | { "interface-name", 1, 0, LOPT_INTNAME }, | |
356 | { "dhcp-hostsfile", 1, 0, LOPT_DHCP_HOST }, | |
357 | { "dhcp-optsfile", 1, 0, LOPT_DHCP_OPTS }, | |
358 | + { "dhcp-hostsdir", 1, 0, LOPT_DHCP_INOTIFY }, | |
359 | { "dhcp-no-override", 0, 0, LOPT_OVERRIDE }, | |
360 | { "tftp-port-range", 1, 0, LOPT_TFTPPORTS }, | |
361 | { "stop-dns-rebind", 0, 0, LOPT_REBIND }, | |
362 | @@ -336,6 +337,7 @@ static struct { | |
363 | { 'G', ARG_DUP, "<hostspec>", gettext_noop("Set address or hostname for a specified machine."), NULL }, | |
364 | { LOPT_DHCP_HOST, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from file."), NULL }, | |
365 | { LOPT_DHCP_OPTS, ARG_DUP, "<path>", gettext_noop("Read DHCP option specs from file."), NULL }, | |
366 | + { LOPT_DHCP_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from a directory."), NULL }, | |
367 | { LOPT_TAG_IF, ARG_DUP, "tag-expression", gettext_noop("Evaluate conditional tag expression."), NULL }, | |
368 | { 'h', OPT_NO_HOSTS, NULL, gettext_noop("Do NOT load %s file."), HOSTSFILE }, | |
369 | { 'H', ARG_DUP, "<path>", gettext_noop("Specify a hosts file to be read in addition to %s."), HOSTSFILE }, | |
370 | @@ -1710,8 +1712,9 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma | |
371 | break; | |
372 | #endif /* HAVE_DHCP */ | |
373 | ||
374 | - case LOPT_DHCP_HOST: /* --dhcp-hostfile */ | |
375 | + case LOPT_DHCP_HOST: /* --dhcp-hostsfile */ | |
376 | case LOPT_DHCP_OPTS: /* --dhcp-optsfile */ | |
377 | + case LOPT_DHCP_INOTIFY: /* dhcp-hostsdir */ | |
378 | case 'H': /* --addn-hosts */ | |
379 | { | |
380 | struct hostsfile *new = opt_malloc(sizeof(struct hostsfile)); | |
381 | @@ -1734,6 +1737,12 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma | |
382 | new->next = daemon->dhcp_opts_file; | |
383 | daemon->dhcp_opts_file = new; | |
384 | } | |
385 | + else if (option == LOPT_DHCP_INOTIFY) | |
386 | + { | |
387 | + new->next = daemon->inotify_hosts; | |
388 | + daemon->inotify_hosts = new; | |
389 | + } | |
390 | + | |
391 | break; | |
392 | } | |
393 | ||
394 | @@ -4042,6 +4051,13 @@ static void read_file(char *file, FILE *f, int hard_opt) | |
395 | fclose(f); | |
396 | } | |
397 | ||
398 | +#ifdef HAVE_DHCP | |
399 | +int option_read_hostsfile(char *file) | |
400 | +{ | |
401 | + return one_file(file, LOPT_BANK); | |
402 | +} | |
403 | +#endif | |
404 | + | |
405 | static int one_file(char *file, int hard_opt) | |
406 | { | |
407 | FILE *f; | |
408 | @@ -4139,7 +4155,7 @@ struct hostsfile *expand_filelist(struct hostsfile *list) | |
409 | ||
410 | /* don't read this as a file */ | |
411 | ah->flags |= AH_INACTIVE; | |
412 | - | |
413 | + | |
414 | if (!(dir_stream = opendir(ah->fname))) | |
415 | my_syslog(LOG_ERR, _("cannot access directory %s: %s"), | |
416 | ah->fname, strerror(errno)); | |
417 | -- | |
418 | 2.1.0 | |
419 |