diff options
-rw-r--r-- | src/cache.c | 81 | ||||
-rw-r--r-- | src/dnsmasq.c | 9 | ||||
-rw-r--r-- | src/dnsmasq.h | 14 | ||||
-rw-r--r-- | src/inotify.c | 179 | ||||
-rw-r--r-- | src/option.c | 37 |
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 |