+++ /dev/null
-From 70d1873dd9e70041ed4bb88c69d5b886b7cc634c Mon Sep 17 00:00:00 2001
-From: Simon Kelley <simon@thekelleys.org.uk>
-Date: Sat, 31 Jan 2015 19:59:29 +0000
-Subject: [PATCH 038/113] Expand inotify code to dhcp-hostsdir, dhcp-optsdir
- and hostsdir.
-
----
- src/cache.c | 81 +++++++++++++++++---------
- src/dnsmasq.c | 9 ++-
- src/dnsmasq.h | 14 +++--
- src/inotify.c | 179 +++++++++++++++++++++++++++++-----------------------------
- src/option.c | 37 +++++++++---
- 5 files changed, 187 insertions(+), 133 deletions(-)
-
-diff --git a/src/cache.c b/src/cache.c
-index 09b6dbf8087a..abaf25ec0f18 100644
---- a/src/cache.c
-+++ b/src/cache.c
-@@ -835,27 +835,42 @@ static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrl
- Only insert each unique address once into this hashing structure.
-
- This complexity avoids O(n^2) divergent CPU use whilst reading
-- large (10000 entry) hosts files. */
--
-- /* hash address */
-- for (j = 0, i = 0; i < addrlen; i++)
-- j = (j*2 +((unsigned char *)addr)[i]) % hashsz;
--
-- for (lookup = rhash[j]; lookup; lookup = lookup->next)
-- if ((lookup->flags & cache->flags & (F_IPV4 | F_IPV6)) &&
-- memcmp(&lookup->addr.addr, addr, addrlen) == 0)
-- {
-- cache->flags &= ~F_REVERSE;
-- break;
-- }
-+ large (10000 entry) hosts files.
-+
-+ Note that we only do this process when bulk-reading hosts files,
-+ for incremental reads, rhash is NULL, and we use cache lookups
-+ instead.
-+ */
-
-- /* maintain address hash chain, insert new unique address */
-- if (!lookup)
-+ if (rhash)
- {
-- cache->next = rhash[j];
-- rhash[j] = cache;
-+ /* hash address */
-+ for (j = 0, i = 0; i < addrlen; i++)
-+ j = (j*2 +((unsigned char *)addr)[i]) % hashsz;
-+
-+ for (lookup = rhash[j]; lookup; lookup = lookup->next)
-+ if ((lookup->flags & cache->flags & (F_IPV4 | F_IPV6)) &&
-+ memcmp(&lookup->addr.addr, addr, addrlen) == 0)
-+ {
-+ cache->flags &= ~F_REVERSE;
-+ break;
-+ }
-+
-+ /* maintain address hash chain, insert new unique address */
-+ if (!lookup)
-+ {
-+ cache->next = rhash[j];
-+ rhash[j] = cache;
-+ }
- }
--
-+ else
-+ {
-+ /* incremental read, lookup in cache */
-+ lookup = cache_find_by_addr(NULL, addr, 0, cache->flags & (F_IPV4 | F_IPV6));
-+ if (lookup && lookup->flags & F_HOSTS)
-+ cache->flags &= ~F_REVERSE;
-+ }
-+
- cache->uid = index;
- memcpy(&cache->addr.addr, addr, addrlen);
- cache_hash(cache);
-@@ -912,7 +927,7 @@ static int gettok(FILE *f, char *token)
- }
- }
-
--static int read_hostsfile(char *filename, unsigned int index, int cache_size, struct crec **rhash, int hashsz)
-+int read_hostsfile(char *filename, unsigned int index, int cache_size, struct crec **rhash, int hashsz)
- {
- FILE *f = fopen(filename, "r");
- char *token = daemon->namebuff, *domain_suffix = NULL;
-@@ -958,7 +973,7 @@ static int read_hostsfile(char *filename, unsigned int index, int cache_size, st
- addr_count++;
-
- /* rehash every 1000 names. */
-- if ((name_count - cache_size) > 1000)
-+ if (rhash && ((name_count - cache_size) > 1000))
- {
- rehash(name_count);
- cache_size = name_count;
-@@ -1005,10 +1020,13 @@ static int read_hostsfile(char *filename, unsigned int index, int cache_size, st
- }
-
- fclose(f);
-- rehash(name_count);
--
-- my_syslog(LOG_INFO, _("read %s - %d addresses"), filename, addr_count);
-
-+ if (rhash)
-+ {
-+ rehash(name_count);
-+ my_syslog(LOG_INFO, _("read %s - %d addresses"), filename, addr_count);
-+ }
-+
- return name_count;
- }
-
-@@ -1118,14 +1136,19 @@ void cache_reload(void)
- my_syslog(LOG_INFO, _("cleared cache"));
- return;
- }
--
-+
- if (!option_bool(OPT_NO_HOSTS))
- total_size = read_hostsfile(HOSTSFILE, SRC_HOSTS, total_size, (struct crec **)daemon->packet, revhashsz);
--
-+
- daemon->addn_hosts = expand_filelist(daemon->addn_hosts);
- for (ah = daemon->addn_hosts; ah; ah = ah->next)
- if (!(ah->flags & AH_INACTIVE))
- total_size = read_hostsfile(ah->fname, ah->index, total_size, (struct crec **)daemon->packet, revhashsz);
-+
-+#ifdef HAVE_INOTIFY
-+ set_dynamic_inotify(AH_HOSTS, total_size, (struct crec **)daemon->packet, revhashsz);
-+#endif
-+
- }
-
- #ifdef HAVE_DHCP
-@@ -1505,7 +1528,13 @@ char *record_source(unsigned int index)
- for (ah = daemon->addn_hosts; ah; ah = ah->next)
- if (ah->index == index)
- return ah->fname;
--
-+
-+#ifdef HAVE_INOTIFY
-+ for (ah = daemon->dynamic_dirs; ah; ah = ah->next)
-+ if (ah->index == index)
-+ return ah->fname;
-+#endif
-+
- return "<unknown>";
- }
-
-diff --git a/src/dnsmasq.c b/src/dnsmasq.c
-index bc4f47170705..2c629fe422aa 100644
---- a/src/dnsmasq.c
-+++ b/src/dnsmasq.c
-@@ -145,8 +145,8 @@ int main (int argc, char **argv)
- #endif
-
- #ifndef HAVE_INOTIFY
-- if (daemon->inotify_hosts)
-- die(_("dhcp-hostsdir not supported on this platform"), NULL, EC_BADCONF);
-+ if (daemon->dynamic_dirs)
-+ die(_("dhcp-hostsdir, dhcp-optsdir and hostsdir are not supported on this platform"), NULL, EC_BADCONF);
- #endif
-
- if (option_bool(OPT_DNSSEC_VALID))
-@@ -324,8 +324,7 @@ int main (int argc, char **argv)
- }
-
- #ifdef HAVE_INOTIFY
-- if ((!option_bool(OPT_NO_POLL) && daemon->port != 0) ||
-- daemon->dhcp || daemon->doing_dhcp6)
-+ if (daemon->port != 0 || daemon->dhcp || daemon->doing_dhcp6)
- inotify_dnsmasq_init();
- else
- daemon->inotifyfd = -1;
-@@ -1400,7 +1399,7 @@ void clear_cache_and_reload(time_t now)
- dhcp_read_ethers();
- reread_dhcp();
- #ifdef HAVE_INOTIFY
-- set_dhcp_inotify();
-+ set_dynamic_inotify(AH_DHCP_HST | AH_DHCP_OPT, 0, NULL, 0);
- #endif
- dhcp_update_configs(daemon->dhcp_conf);
- lease_update_from_configs();
-diff --git a/src/dnsmasq.h b/src/dnsmasq.h
-index 8091634f69db..0c322a93993e 100644
---- a/src/dnsmasq.h
-+++ b/src/dnsmasq.h
-@@ -554,6 +554,9 @@ struct resolvc {
- #define AH_DIR 1
- #define AH_INACTIVE 2
- #define AH_WD_DONE 4
-+#define AH_HOSTS 8
-+#define AH_DHCP_HST 16
-+#define AH_DHCP_OPT 32
- struct hostsfile {
- struct hostsfile *next;
- int flags;
-@@ -965,7 +968,7 @@ extern struct daemon {
- int doing_ra, doing_dhcp6;
- struct dhcp_netid_list *dhcp_ignore, *dhcp_ignore_names, *dhcp_gen_names;
- struct dhcp_netid_list *force_broadcast, *bootp_dynamic;
-- struct hostsfile *dhcp_hosts_file, *dhcp_opts_file, *inotify_hosts;
-+ struct hostsfile *dhcp_hosts_file, *dhcp_opts_file, *dynamic_dirs;
- int dhcp_max, tftp_max;
- int dhcp_server_port, dhcp_client_port;
- int start_tftp_port, end_tftp_port;
-@@ -1071,6 +1074,8 @@ int cache_make_stat(struct txt_record *t);
- char *cache_get_name(struct crec *crecp);
- char *cache_get_cname_target(struct crec *crecp);
- struct crec *cache_enumerate(int init);
-+int read_hostsfile(char *filename, unsigned int index, int cache_size,
-+ struct crec **rhash, int hashsz);
-
- /* blockdata.c */
- #ifdef HAVE_DNSSEC
-@@ -1204,7 +1209,8 @@ void reset_option_bool(unsigned int opt);
- struct hostsfile *expand_filelist(struct hostsfile *list);
- char *parse_server(char *arg, union mysockaddr *addr,
- union mysockaddr *source_addr, char *interface, int *flags);
--int option_read_hostsfile(char *file);
-+int option_read_dynfile(char *file, int flags);
-+
- /* forward.c */
- void reply_query(int fd, int family, time_t now);
- void receive_query(struct listener *listen, time_t now);
-@@ -1494,7 +1500,5 @@ int detect_loop(char *query, int type);
- #ifdef HAVE_INOTIFY
- void inotify_dnsmasq_init();
- int inotify_check(time_t now);
--# ifdef HAVE_DHCP
--void set_dhcp_inotify(void);
--# endif
-+void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revhashsz);
- #endif
-diff --git a/src/inotify.c b/src/inotify.c
-index 818fe8eddda4..c537f4c1562a 100644
---- a/src/inotify.c
-+++ b/src/inotify.c
-@@ -19,11 +19,6 @@
-
- #include <sys/inotify.h>
-
--#ifdef HAVE_DHCP
--static void check_for_dhcp_inotify(struct inotify_event *in, time_t now);
--#endif
--
--
- /* the strategy is to set a inotify on the directories containing
- resolv files, for any files in the directory which are close-write
- or moved into the directory.
-@@ -82,57 +77,28 @@ void inotify_dnsmasq_init()
- }
- }
-
--int inotify_check(time_t now)
-+
-+/* initialisation for dynamic-dir. Set inotify watch for each directory, and read pre-existing files */
-+void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revhashsz)
- {
-- int hit = 0;
-+ struct hostsfile *ah;
-
-- while (1)
-+ for (ah = daemon->dynamic_dirs; ah; ah = ah->next)
- {
-- int rc;
-- char *p;
-- struct resolvc *res;
-- struct inotify_event *in;
--
-- while ((rc = read(daemon->inotifyfd, inotify_buffer, INOTIFY_SZ)) == -1 && errno == EINTR);
--
-- if (rc <= 0)
-- break;
--
-- for (p = inotify_buffer; rc - (p - inotify_buffer) >= (int)sizeof(struct inotify_event); p += sizeof(struct inotify_event) + in->len)
-+ DIR *dir_stream = NULL;
-+ struct dirent *ent;
-+ struct stat buf;
-+
-+ if (!(ah->flags & flag))
-+ continue;
-+
-+ if (stat(ah->fname, &buf) == -1 || !(S_ISDIR(buf.st_mode)))
- {
-- in = (struct inotify_event*)p;
--
-- for (res = daemon->resolv_files; res; res = res->next)
-- if (res->wd == in->wd && in->len != 0 && strcmp(res->file, in->name) == 0)
-- hit = 1;
--
--#ifdef HAVE_DHCP
-- if (daemon->dhcp || daemon->doing_dhcp6)
-- check_for_dhcp_inotify(in, now);
--#endif
-+ my_syslog(LOG_ERR, _("bad dynamic directory %s: %s"),
-+ ah->fname, strerror(errno));
-+ continue;
- }
-- }
-- return hit;
--}
--
--#ifdef HAVE_DHCP
--/* initialisation for dhcp-hostdir. Set inotify watch for each directory, and read pre-existing files */
--void set_dhcp_inotify(void)
--{
-- struct hostsfile *ah;
--
-- for (ah = daemon->inotify_hosts; ah; ah = ah->next)
-- {
-- DIR *dir_stream = NULL;
-- struct dirent *ent;
-- struct stat buf;
--
-- if (stat(ah->fname, &buf) == -1 || !(S_ISDIR(buf.st_mode)))
-- {
-- my_syslog(LOG_ERR, _("bad directory in dhcp-hostsdir %s"), ah->fname);
-- continue;
-- }
--
-+
- if (!(ah->flags & AH_WD_DONE))
- {
- ah->wd = inotify_add_watch(daemon->inotifyfd, ah->fname, IN_CLOSE_WRITE | IN_MOVED_TO);
-@@ -142,7 +108,8 @@ void set_dhcp_inotify(void)
- a race which misses files being added as we start */
- if (ah->wd == -1 || !(dir_stream = opendir(ah->fname)))
- {
-- my_syslog(LOG_ERR, _("failed to create inotify for %s"), ah->fname);
-+ my_syslog(LOG_ERR, _("failed to create inotify for %s: %s"),
-+ ah->fname, strerror(errno));
- continue;
- }
-
-@@ -167,54 +134,90 @@ void set_dhcp_inotify(void)
-
- /* ignore non-regular files */
- if (stat(path, &buf) != -1 && S_ISREG(buf.st_mode))
-- option_read_hostsfile(path);
--
-+ {
-+ if (ah->flags & AH_HOSTS)
-+ total_size = read_hostsfile(path, ah->index, total_size, rhash, revhashsz);
-+#ifdef HAVE_DHCP
-+ else if (ah->flags & (AH_DHCP_HST | AH_DHCP_OPT))
-+ option_read_dynfile(path, ah->flags);
-+#endif
-+ }
-+
- free(path);
- }
- }
- }
- }
-
--static void check_for_dhcp_inotify(struct inotify_event *in, time_t now)
-+int inotify_check(time_t now)
- {
-+ int hit = 0;
- struct hostsfile *ah;
-
-- /* ignore emacs backups and dotfiles */
-- if (in->len == 0 ||
-- in->name[in->len - 1] == '~' ||
-- (in->name[0] == '#' && in->name[in->len - 1] == '#') ||
-- in->name[0] == '.')
-- return;
--
-- for (ah = daemon->inotify_hosts; ah; ah = ah->next)
-- if (ah->wd == in->wd)
-- {
-- size_t lendir = strlen(ah->fname);
-- char *path;
--
-- if ((path = whine_malloc(lendir + in->len + 2)))
-- {
-- strcpy(path, ah->fname);
-- strcat(path, "/");
-- strcat(path, in->name);
--
-- if (option_read_hostsfile(path))
-+ while (1)
-+ {
-+ int rc;
-+ char *p;
-+ struct resolvc *res;
-+ struct inotify_event *in;
-+
-+ while ((rc = read(daemon->inotifyfd, inotify_buffer, INOTIFY_SZ)) == -1 && errno == EINTR);
-+
-+ if (rc <= 0)
-+ break;
-+
-+ for (p = inotify_buffer; rc - (p - inotify_buffer) >= (int)sizeof(struct inotify_event); p += sizeof(struct inotify_event) + in->len)
-+ {
-+ in = (struct inotify_event*)p;
-+
-+ for (res = daemon->resolv_files; res; res = res->next)
-+ if (res->wd == in->wd && in->len != 0 && strcmp(res->file, in->name) == 0)
-+ hit = 1;
-+
-+ /* ignore emacs backups and dotfiles */
-+ if (in->len == 0 ||
-+ in->name[in->len - 1] == '~' ||
-+ (in->name[0] == '#' && in->name[in->len - 1] == '#') ||
-+ in->name[0] == '.')
-+ continue;
-+
-+ for (ah = daemon->dynamic_dirs; ah; ah = ah->next)
-+ if (ah->wd == in->wd)
- {
-- /* Propogate the consequences of loading a new dhcp-host */
-- dhcp_update_configs(daemon->dhcp_conf);
-- lease_update_from_configs();
-- lease_update_file(now);
-- lease_update_dns(1);
-+ size_t lendir = strlen(ah->fname);
-+ char *path;
-+
-+ if ((path = whine_malloc(lendir + in->len + 2)))
-+ {
-+ strcpy(path, ah->fname);
-+ strcat(path, "/");
-+ strcat(path, in->name);
-+
-+ if (ah->flags & AH_HOSTS)
-+ read_hostsfile(path, ah->index, 0, NULL, 0);
-+#ifdef HAVE_DHCP
-+ else if (ah->flags & AH_DHCP_HST)
-+ {
-+ if (option_read_dynfile(path, AH_DHCP_HST))
-+ {
-+ /* Propogate the consequences of loading a new dhcp-host */
-+ dhcp_update_configs(daemon->dhcp_conf);
-+ lease_update_from_configs();
-+ lease_update_file(now);
-+ lease_update_dns(1);
-+ }
-+ }
-+ else if (ah->flags & AH_DHCP_OPT)
-+ option_read_dynfile(path, AH_DHCP_OPT);
-+#endif
-+
-+ free(path);
-+ }
- }
--
-- free(path);
-- }
--
-- return;
-- }
-+ }
-+ }
-+ return hit;
- }
-
--#endif /* DHCP */
--
- #endif /* INOTIFY */
-
-diff --git a/src/option.c b/src/option.c
-index 22e11c37d374..6ef80117cc8c 100644
---- a/src/option.c
-+++ b/src/option.c
-@@ -150,6 +150,8 @@ struct myoption {
- #define LOPT_IGNORE_ADDR 338
- #define LOPT_MINCTTL 339
- #define LOPT_DHCP_INOTIFY 340
-+#define LOPT_DHOPT_INOTIFY 341
-+#define LOPT_HOST_INOTIFY 342
-
- #ifdef HAVE_GETOPT_LONG
- static const struct option opts[] =
-@@ -200,6 +202,7 @@ static const struct myoption opts[] =
- { "local-ttl", 1, 0, 'T' },
- { "no-negcache", 0, 0, 'N' },
- { "addn-hosts", 1, 0, 'H' },
-+ { "hostsdir", 1, 0, LOPT_HOST_INOTIFY },
- { "query-port", 1, 0, 'Q' },
- { "except-interface", 1, 0, 'I' },
- { "no-dhcp-interface", 1, 0, '2' },
-@@ -249,6 +252,7 @@ static const struct myoption opts[] =
- { "dhcp-hostsfile", 1, 0, LOPT_DHCP_HOST },
- { "dhcp-optsfile", 1, 0, LOPT_DHCP_OPTS },
- { "dhcp-hostsdir", 1, 0, LOPT_DHCP_INOTIFY },
-+ { "dhcp-optsdir", 1, 0, LOPT_DHOPT_INOTIFY },
- { "dhcp-no-override", 0, 0, LOPT_OVERRIDE },
- { "tftp-port-range", 1, 0, LOPT_TFTPPORTS },
- { "stop-dns-rebind", 0, 0, LOPT_REBIND },
-@@ -338,9 +342,11 @@ static struct {
- { LOPT_DHCP_HOST, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from file."), NULL },
- { LOPT_DHCP_OPTS, ARG_DUP, "<path>", gettext_noop("Read DHCP option specs from file."), NULL },
- { LOPT_DHCP_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from a directory."), NULL },
-+ { LOPT_DHOPT_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read DHCP options from a directory."), NULL },
- { LOPT_TAG_IF, ARG_DUP, "tag-expression", gettext_noop("Evaluate conditional tag expression."), NULL },
- { 'h', OPT_NO_HOSTS, NULL, gettext_noop("Do NOT load %s file."), HOSTSFILE },
- { 'H', ARG_DUP, "<path>", gettext_noop("Specify a hosts file to be read in addition to %s."), HOSTSFILE },
-+ { LOPT_HOST_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read hosts files from a directory."), NULL },
- { 'i', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) to listen on."), NULL },
- { 'I', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) NOT to listen on.") , NULL },
- { 'j', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP user class to tag."), NULL },
-@@ -1712,10 +1718,12 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
- break;
- #endif /* HAVE_DHCP */
-
-- case LOPT_DHCP_HOST: /* --dhcp-hostsfile */
-- case LOPT_DHCP_OPTS: /* --dhcp-optsfile */
-- case LOPT_DHCP_INOTIFY: /* dhcp-hostsdir */
-- case 'H': /* --addn-hosts */
-+ case LOPT_DHCP_HOST: /* --dhcp-hostsfile */
-+ case LOPT_DHCP_OPTS: /* --dhcp-optsfile */
-+ case LOPT_DHCP_INOTIFY: /* --dhcp-hostsdir */
-+ case LOPT_DHOPT_INOTIFY: /* --dhcp-optsdir */
-+ case LOPT_HOST_INOTIFY: /* --hostsdir */
-+ case 'H': /* --addn-hosts */
- {
- struct hostsfile *new = opt_malloc(sizeof(struct hostsfile));
- static unsigned int hosts_index = SRC_AH;
-@@ -1737,10 +1745,16 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
- new->next = daemon->dhcp_opts_file;
- daemon->dhcp_opts_file = new;
- }
-- else if (option == LOPT_DHCP_INOTIFY)
-+ else
- {
-- new->next = daemon->inotify_hosts;
-- daemon->inotify_hosts = new;
-+ new->next = daemon->dynamic_dirs;
-+ daemon->dynamic_dirs = new;
-+ if (option == LOPT_DHCP_INOTIFY)
-+ new->flags |= AH_DHCP_HST;
-+ else if (option == LOPT_DHOPT_INOTIFY)
-+ new->flags |= AH_DHCP_OPT;
-+ else if (option == LOPT_HOST_INOTIFY)
-+ new->flags |= AH_HOSTS;
- }
-
- break;
-@@ -4052,9 +4066,14 @@ static void read_file(char *file, FILE *f, int hard_opt)
- }
-
- #ifdef HAVE_DHCP
--int option_read_hostsfile(char *file)
-+int option_read_dynfile(char *file, int flags)
- {
-- return one_file(file, LOPT_BANK);
-+ if (flags & AH_DHCP_HST)
-+ return one_file(file, LOPT_BANK);
-+ else if (flags & AH_DHCP_OPT)
-+ return one_file(file, LOPT_OPTS);
-+
-+ return 0;
- }
- #endif
-
---
-2.1.0
-