1 From 70d1873dd9e70041ed4bb88c69d5b886b7cc634c Mon Sep 17 00:00:00 2001
2 From: Simon Kelley <simon@thekelleys.org.uk>
3 Date: Sat, 31 Jan 2015 19:59:29 +0000
4 Subject: [PATCH 38/55] Expand inotify code to dhcp-hostsdir, dhcp-optsdir and
8 src/cache.c | 81 +++++++++++++++++---------
10 src/dnsmasq.h | 14 +++--
11 src/inotify.c | 179 +++++++++++++++++++++++++++++-----------------------------
12 src/option.c | 37 +++++++++---
13 5 files changed, 187 insertions(+), 133 deletions(-)
15 diff --git a/src/cache.c b/src/cache.c
16 index 09b6dbf8087a..abaf25ec0f18 100644
19 @@ -835,27 +835,42 @@ static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrl
20 Only insert each unique address once into this hashing structure.
22 This complexity avoids O(n^2) divergent CPU use whilst reading
23 - large (10000 entry) hosts files. */
26 - for (j = 0, i = 0; i < addrlen; i++)
27 - j = (j*2 +((unsigned char *)addr)[i]) % hashsz;
29 - for (lookup = rhash[j]; lookup; lookup = lookup->next)
30 - if ((lookup->flags & cache->flags & (F_IPV4 | F_IPV6)) &&
31 - memcmp(&lookup->addr.addr, addr, addrlen) == 0)
33 - cache->flags &= ~F_REVERSE;
36 + large (10000 entry) hosts files.
38 + Note that we only do this process when bulk-reading hosts files,
39 + for incremental reads, rhash is NULL, and we use cache lookups
43 - /* maintain address hash chain, insert new unique address */
47 - cache->next = rhash[j];
50 + for (j = 0, i = 0; i < addrlen; i++)
51 + j = (j*2 +((unsigned char *)addr)[i]) % hashsz;
53 + for (lookup = rhash[j]; lookup; lookup = lookup->next)
54 + if ((lookup->flags & cache->flags & (F_IPV4 | F_IPV6)) &&
55 + memcmp(&lookup->addr.addr, addr, addrlen) == 0)
57 + cache->flags &= ~F_REVERSE;
61 + /* maintain address hash chain, insert new unique address */
64 + cache->next = rhash[j];
71 + /* incremental read, lookup in cache */
72 + lookup = cache_find_by_addr(NULL, addr, 0, cache->flags & (F_IPV4 | F_IPV6));
73 + if (lookup && lookup->flags & F_HOSTS)
74 + cache->flags &= ~F_REVERSE;
78 memcpy(&cache->addr.addr, addr, addrlen);
80 @@ -912,7 +927,7 @@ static int gettok(FILE *f, char *token)
84 -static int read_hostsfile(char *filename, unsigned int index, int cache_size, struct crec **rhash, int hashsz)
85 +int read_hostsfile(char *filename, unsigned int index, int cache_size, struct crec **rhash, int hashsz)
87 FILE *f = fopen(filename, "r");
88 char *token = daemon->namebuff, *domain_suffix = NULL;
89 @@ -958,7 +973,7 @@ static int read_hostsfile(char *filename, unsigned int index, int cache_size, st
92 /* rehash every 1000 names. */
93 - if ((name_count - cache_size) > 1000)
94 + if (rhash && ((name_count - cache_size) > 1000))
97 cache_size = name_count;
98 @@ -1005,10 +1020,13 @@ static int read_hostsfile(char *filename, unsigned int index, int cache_size, st
102 - rehash(name_count);
104 - my_syslog(LOG_INFO, _("read %s - %d addresses"), filename, addr_count);
108 + rehash(name_count);
109 + my_syslog(LOG_INFO, _("read %s - %d addresses"), filename, addr_count);
115 @@ -1118,14 +1136,19 @@ void cache_reload(void)
116 my_syslog(LOG_INFO, _("cleared cache"));
121 if (!option_bool(OPT_NO_HOSTS))
122 total_size = read_hostsfile(HOSTSFILE, SRC_HOSTS, total_size, (struct crec **)daemon->packet, revhashsz);
125 daemon->addn_hosts = expand_filelist(daemon->addn_hosts);
126 for (ah = daemon->addn_hosts; ah; ah = ah->next)
127 if (!(ah->flags & AH_INACTIVE))
128 total_size = read_hostsfile(ah->fname, ah->index, total_size, (struct crec **)daemon->packet, revhashsz);
131 + set_dynamic_inotify(AH_HOSTS, total_size, (struct crec **)daemon->packet, revhashsz);
137 @@ -1505,7 +1528,13 @@ char *record_source(unsigned int index)
138 for (ah = daemon->addn_hosts; ah; ah = ah->next)
139 if (ah->index == index)
144 + for (ah = daemon->dynamic_dirs; ah; ah = ah->next)
145 + if (ah->index == index)
152 diff --git a/src/dnsmasq.c b/src/dnsmasq.c
153 index bc4f47170705..2c629fe422aa 100644
156 @@ -145,8 +145,8 @@ int main (int argc, char **argv)
160 - if (daemon->inotify_hosts)
161 - die(_("dhcp-hostsdir not supported on this platform"), NULL, EC_BADCONF);
162 + if (daemon->dynamic_dirs)
163 + die(_("dhcp-hostsdir, dhcp-optsdir and hostsdir are not supported on this platform"), NULL, EC_BADCONF);
166 if (option_bool(OPT_DNSSEC_VALID))
167 @@ -324,8 +324,7 @@ int main (int argc, char **argv)
171 - if ((!option_bool(OPT_NO_POLL) && daemon->port != 0) ||
172 - daemon->dhcp || daemon->doing_dhcp6)
173 + if (daemon->port != 0 || daemon->dhcp || daemon->doing_dhcp6)
174 inotify_dnsmasq_init();
176 daemon->inotifyfd = -1;
177 @@ -1400,7 +1399,7 @@ void clear_cache_and_reload(time_t now)
181 - set_dhcp_inotify();
182 + set_dynamic_inotify(AH_DHCP_HST | AH_DHCP_OPT, 0, NULL, 0);
184 dhcp_update_configs(daemon->dhcp_conf);
185 lease_update_from_configs();
186 diff --git a/src/dnsmasq.h b/src/dnsmasq.h
187 index 8091634f69db..0c322a93993e 100644
190 @@ -554,6 +554,9 @@ struct resolvc {
192 #define AH_INACTIVE 2
195 +#define AH_DHCP_HST 16
196 +#define AH_DHCP_OPT 32
198 struct hostsfile *next;
200 @@ -965,7 +968,7 @@ extern struct daemon {
201 int doing_ra, doing_dhcp6;
202 struct dhcp_netid_list *dhcp_ignore, *dhcp_ignore_names, *dhcp_gen_names;
203 struct dhcp_netid_list *force_broadcast, *bootp_dynamic;
204 - struct hostsfile *dhcp_hosts_file, *dhcp_opts_file, *inotify_hosts;
205 + struct hostsfile *dhcp_hosts_file, *dhcp_opts_file, *dynamic_dirs;
206 int dhcp_max, tftp_max;
207 int dhcp_server_port, dhcp_client_port;
208 int start_tftp_port, end_tftp_port;
209 @@ -1071,6 +1074,8 @@ int cache_make_stat(struct txt_record *t);
210 char *cache_get_name(struct crec *crecp);
211 char *cache_get_cname_target(struct crec *crecp);
212 struct crec *cache_enumerate(int init);
213 +int read_hostsfile(char *filename, unsigned int index, int cache_size,
214 + struct crec **rhash, int hashsz);
218 @@ -1204,7 +1209,8 @@ void reset_option_bool(unsigned int opt);
219 struct hostsfile *expand_filelist(struct hostsfile *list);
220 char *parse_server(char *arg, union mysockaddr *addr,
221 union mysockaddr *source_addr, char *interface, int *flags);
222 -int option_read_hostsfile(char *file);
223 +int option_read_dynfile(char *file, int flags);
226 void reply_query(int fd, int family, time_t now);
227 void receive_query(struct listener *listen, time_t now);
228 @@ -1494,7 +1500,5 @@ int detect_loop(char *query, int type);
230 void inotify_dnsmasq_init();
231 int inotify_check(time_t now);
233 -void set_dhcp_inotify(void);
235 +void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revhashsz);
237 diff --git a/src/inotify.c b/src/inotify.c
238 index 818fe8eddda4..c537f4c1562a 100644
243 #include <sys/inotify.h>
246 -static void check_for_dhcp_inotify(struct inotify_event *in, time_t now);
250 /* the strategy is to set a inotify on the directories containing
251 resolv files, for any files in the directory which are close-write
252 or moved into the directory.
253 @@ -82,57 +77,28 @@ void inotify_dnsmasq_init()
257 -int inotify_check(time_t now)
259 +/* initialisation for dynamic-dir. Set inotify watch for each directory, and read pre-existing files */
260 +void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revhashsz)
263 + struct hostsfile *ah;
266 + for (ah = daemon->dynamic_dirs; ah; ah = ah->next)
270 - struct resolvc *res;
271 - struct inotify_event *in;
273 - while ((rc = read(daemon->inotifyfd, inotify_buffer, INOTIFY_SZ)) == -1 && errno == EINTR);
278 - for (p = inotify_buffer; rc - (p - inotify_buffer) >= (int)sizeof(struct inotify_event); p += sizeof(struct inotify_event) + in->len)
279 + DIR *dir_stream = NULL;
280 + struct dirent *ent;
283 + if (!(ah->flags & flag))
286 + if (stat(ah->fname, &buf) == -1 || !(S_ISDIR(buf.st_mode)))
288 - in = (struct inotify_event*)p;
290 - for (res = daemon->resolv_files; res; res = res->next)
291 - if (res->wd == in->wd && in->len != 0 && strcmp(res->file, in->name) == 0)
295 - if (daemon->dhcp || daemon->doing_dhcp6)
296 - check_for_dhcp_inotify(in, now);
298 + my_syslog(LOG_ERR, _("bad dynamic directory %s: %s"),
299 + ah->fname, strerror(errno));
307 -/* initialisation for dhcp-hostdir. Set inotify watch for each directory, and read pre-existing files */
308 -void set_dhcp_inotify(void)
310 - struct hostsfile *ah;
312 - for (ah = daemon->inotify_hosts; ah; ah = ah->next)
314 - DIR *dir_stream = NULL;
315 - struct dirent *ent;
318 - if (stat(ah->fname, &buf) == -1 || !(S_ISDIR(buf.st_mode)))
320 - my_syslog(LOG_ERR, _("bad directory in dhcp-hostsdir %s"), ah->fname);
325 if (!(ah->flags & AH_WD_DONE))
327 ah->wd = inotify_add_watch(daemon->inotifyfd, ah->fname, IN_CLOSE_WRITE | IN_MOVED_TO);
328 @@ -142,7 +108,8 @@ void set_dhcp_inotify(void)
329 a race which misses files being added as we start */
330 if (ah->wd == -1 || !(dir_stream = opendir(ah->fname)))
332 - my_syslog(LOG_ERR, _("failed to create inotify for %s"), ah->fname);
333 + my_syslog(LOG_ERR, _("failed to create inotify for %s: %s"),
334 + ah->fname, strerror(errno));
338 @@ -167,54 +134,90 @@ void set_dhcp_inotify(void)
340 /* ignore non-regular files */
341 if (stat(path, &buf) != -1 && S_ISREG(buf.st_mode))
342 - option_read_hostsfile(path);
345 + if (ah->flags & AH_HOSTS)
346 + total_size = read_hostsfile(path, ah->index, total_size, rhash, revhashsz);
348 + else if (ah->flags & (AH_DHCP_HST | AH_DHCP_OPT))
349 + option_read_dynfile(path, ah->flags);
359 -static void check_for_dhcp_inotify(struct inotify_event *in, time_t now)
360 +int inotify_check(time_t now)
363 struct hostsfile *ah;
365 - /* ignore emacs backups and dotfiles */
366 - if (in->len == 0 ||
367 - in->name[in->len - 1] == '~' ||
368 - (in->name[0] == '#' && in->name[in->len - 1] == '#') ||
369 - in->name[0] == '.')
372 - for (ah = daemon->inotify_hosts; ah; ah = ah->next)
373 - if (ah->wd == in->wd)
375 - size_t lendir = strlen(ah->fname);
378 - if ((path = whine_malloc(lendir + in->len + 2)))
380 - strcpy(path, ah->fname);
382 - strcat(path, in->name);
384 - if (option_read_hostsfile(path))
389 + struct resolvc *res;
390 + struct inotify_event *in;
392 + while ((rc = read(daemon->inotifyfd, inotify_buffer, INOTIFY_SZ)) == -1 && errno == EINTR);
397 + for (p = inotify_buffer; rc - (p - inotify_buffer) >= (int)sizeof(struct inotify_event); p += sizeof(struct inotify_event) + in->len)
399 + in = (struct inotify_event*)p;
401 + for (res = daemon->resolv_files; res; res = res->next)
402 + if (res->wd == in->wd && in->len != 0 && strcmp(res->file, in->name) == 0)
405 + /* ignore emacs backups and dotfiles */
406 + if (in->len == 0 ||
407 + in->name[in->len - 1] == '~' ||
408 + (in->name[0] == '#' && in->name[in->len - 1] == '#') ||
409 + in->name[0] == '.')
412 + for (ah = daemon->dynamic_dirs; ah; ah = ah->next)
413 + if (ah->wd == in->wd)
415 - /* Propogate the consequences of loading a new dhcp-host */
416 - dhcp_update_configs(daemon->dhcp_conf);
417 - lease_update_from_configs();
418 - lease_update_file(now);
419 - lease_update_dns(1);
420 + size_t lendir = strlen(ah->fname);
423 + if ((path = whine_malloc(lendir + in->len + 2)))
425 + strcpy(path, ah->fname);
427 + strcat(path, in->name);
429 + if (ah->flags & AH_HOSTS)
430 + read_hostsfile(path, ah->index, 0, NULL, 0);
432 + else if (ah->flags & AH_DHCP_HST)
434 + if (option_read_dynfile(path, AH_DHCP_HST))
436 + /* Propogate the consequences of loading a new dhcp-host */
437 + dhcp_update_configs(daemon->dhcp_conf);
438 + lease_update_from_configs();
439 + lease_update_file(now);
440 + lease_update_dns(1);
443 + else if (ah->flags & AH_DHCP_OPT)
444 + option_read_dynfile(path, AH_DHCP_OPT);
465 diff --git a/src/option.c b/src/option.c
466 index 22e11c37d374..6ef80117cc8c 100644
469 @@ -150,6 +150,8 @@ struct myoption {
470 #define LOPT_IGNORE_ADDR 338
471 #define LOPT_MINCTTL 339
472 #define LOPT_DHCP_INOTIFY 340
473 +#define LOPT_DHOPT_INOTIFY 341
474 +#define LOPT_HOST_INOTIFY 342
476 #ifdef HAVE_GETOPT_LONG
477 static const struct option opts[] =
478 @@ -200,6 +202,7 @@ static const struct myoption opts[] =
479 { "local-ttl", 1, 0, 'T' },
480 { "no-negcache", 0, 0, 'N' },
481 { "addn-hosts", 1, 0, 'H' },
482 + { "hostsdir", 1, 0, LOPT_HOST_INOTIFY },
483 { "query-port", 1, 0, 'Q' },
484 { "except-interface", 1, 0, 'I' },
485 { "no-dhcp-interface", 1, 0, '2' },
486 @@ -249,6 +252,7 @@ static const struct myoption opts[] =
487 { "dhcp-hostsfile", 1, 0, LOPT_DHCP_HOST },
488 { "dhcp-optsfile", 1, 0, LOPT_DHCP_OPTS },
489 { "dhcp-hostsdir", 1, 0, LOPT_DHCP_INOTIFY },
490 + { "dhcp-optsdir", 1, 0, LOPT_DHOPT_INOTIFY },
491 { "dhcp-no-override", 0, 0, LOPT_OVERRIDE },
492 { "tftp-port-range", 1, 0, LOPT_TFTPPORTS },
493 { "stop-dns-rebind", 0, 0, LOPT_REBIND },
494 @@ -338,9 +342,11 @@ static struct {
495 { LOPT_DHCP_HOST, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from file."), NULL },
496 { LOPT_DHCP_OPTS, ARG_DUP, "<path>", gettext_noop("Read DHCP option specs from file."), NULL },
497 { LOPT_DHCP_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from a directory."), NULL },
498 + { LOPT_DHOPT_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read DHCP options from a directory."), NULL },
499 { LOPT_TAG_IF, ARG_DUP, "tag-expression", gettext_noop("Evaluate conditional tag expression."), NULL },
500 { 'h', OPT_NO_HOSTS, NULL, gettext_noop("Do NOT load %s file."), HOSTSFILE },
501 { 'H', ARG_DUP, "<path>", gettext_noop("Specify a hosts file to be read in addition to %s."), HOSTSFILE },
502 + { LOPT_HOST_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read hosts files from a directory."), NULL },
503 { 'i', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) to listen on."), NULL },
504 { 'I', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) NOT to listen on.") , NULL },
505 { 'j', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP user class to tag."), NULL },
506 @@ -1712,10 +1718,12 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
508 #endif /* HAVE_DHCP */
510 - case LOPT_DHCP_HOST: /* --dhcp-hostsfile */
511 - case LOPT_DHCP_OPTS: /* --dhcp-optsfile */
512 - case LOPT_DHCP_INOTIFY: /* dhcp-hostsdir */
513 - case 'H': /* --addn-hosts */
514 + case LOPT_DHCP_HOST: /* --dhcp-hostsfile */
515 + case LOPT_DHCP_OPTS: /* --dhcp-optsfile */
516 + case LOPT_DHCP_INOTIFY: /* --dhcp-hostsdir */
517 + case LOPT_DHOPT_INOTIFY: /* --dhcp-optsdir */
518 + case LOPT_HOST_INOTIFY: /* --hostsdir */
519 + case 'H': /* --addn-hosts */
521 struct hostsfile *new = opt_malloc(sizeof(struct hostsfile));
522 static unsigned int hosts_index = SRC_AH;
523 @@ -1737,10 +1745,16 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
524 new->next = daemon->dhcp_opts_file;
525 daemon->dhcp_opts_file = new;
527 - else if (option == LOPT_DHCP_INOTIFY)
530 - new->next = daemon->inotify_hosts;
531 - daemon->inotify_hosts = new;
532 + new->next = daemon->dynamic_dirs;
533 + daemon->dynamic_dirs = new;
534 + if (option == LOPT_DHCP_INOTIFY)
535 + new->flags |= AH_DHCP_HST;
536 + else if (option == LOPT_DHOPT_INOTIFY)
537 + new->flags |= AH_DHCP_OPT;
538 + else if (option == LOPT_HOST_INOTIFY)
539 + new->flags |= AH_HOSTS;
543 @@ -4052,9 +4066,14 @@ static void read_file(char *file, FILE *f, int hard_opt)
547 -int option_read_hostsfile(char *file)
548 +int option_read_dynfile(char *file, int flags)
550 - return one_file(file, LOPT_BANK);
551 + if (flags & AH_DHCP_HST)
552 + return one_file(file, LOPT_BANK);
553 + else if (flags & AH_DHCP_OPT)
554 + return one_file(file, LOPT_OPTS);