summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Kelley <simon@thekelleys.org.uk>2015-01-31 19:59:29 +0000
committerSimon Kelley <simon@thekelleys.org.uk>2015-01-31 19:59:29 +0000
commit70d1873dd9e70041ed4bb88c69d5b886b7cc634c (patch)
tree21ee489cf4319d52aef79474e19103c4ac6cd2a9
parent0491805d2ff6e7727f0272c94fd97d9897d1e22c (diff)
downloaddnsmasq-70d1873dd9e70041ed4bb88c69d5b886b7cc634c.tar.gz
Expand inotify code to dhcp-hostsdir, dhcp-optsdir and hostsdir.
-rw-r--r--src/cache.c81
-rw-r--r--src/dnsmasq.c9
-rw-r--r--src/dnsmasq.h14
-rw-r--r--src/inotify.c179
-rw-r--r--src/option.c37
5 files changed, 187 insertions, 133 deletions
diff --git a/src/cache.c b/src/cache.c
index 09b6dbf..abaf25e 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 bc4f471..2c629fe 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 8091634..0c322a9 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 818fe8e..c537f4c 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 22e11c3..6ef8011 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