]> git.ipfire.org Git - people/ms/dnsmasq.git/commitdiff
Expand inotify code to dhcp-hostsdir, dhcp-optsdir and hostsdir.
authorSimon Kelley <simon@thekelleys.org.uk>
Sat, 31 Jan 2015 19:59:29 +0000 (19:59 +0000)
committerSimon Kelley <simon@thekelleys.org.uk>
Sat, 31 Jan 2015 19:59:29 +0000 (19:59 +0000)
src/cache.c
src/dnsmasq.c
src/dnsmasq.h
src/inotify.c
src/option.c

index 09b6dbf8087a877c43fe858ea2780427533783d9..abaf25ec0f1876a50b2f96b55983cb55f6c42ce9 100644 (file)
@@ -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>";
 }
 
index bc4f47170705d4c08a651d25d744cce566d3dbcf..2c629fe422aa9b4f9f56b85e63aeb1f8e7714847 100644 (file)
@@ -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(); 
index 8091634f69dba1125ce95bf4542dccaf7e78ab48..0c322a93993e778e6c728861a5d09d1049a55cf1 100644 (file)
@@ -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
index 818fe8eddda47bf2b2c7a548a6066a1675de3933..c537f4c1562a90e7df2a449ae2eec2c9540b0ae1 100644 (file)
 
 #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 */
   
index 22e11c37d37496841284e886fc98420c779d0a97..6ef80117cc8cdef68cb52d2f284282b335fb8b7d 100644 (file)
@@ -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