File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / dnsmasq / src / inotify.c
Revision 1.1: download - view: text, annotated - select for diffs - revision graph
Wed Nov 2 09:57:01 2016 UTC (7 years, 8 months ago) by misho
CVS tags: MAIN, HEAD
Initial revision

    1: /* dnsmasq is Copyright (c) 2000-2016 Simon Kelley
    2:  
    3:    This program is free software; you can redistribute it and/or modify
    4:    it under the terms of the GNU General Public License as published by
    5:    the Free Software Foundation; version 2 dated June, 1991, or
    6:    (at your option) version 3 dated 29 June, 2007.
    7:  
    8:    This program is distributed in the hope that it will be useful,
    9:    but WITHOUT ANY WARRANTY; without even the implied warranty of
   10:    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   11:    GNU General Public License for more details.
   12:      
   13:    You should have received a copy of the GNU General Public License
   14:    along with this program.  If not, see <http://www.gnu.org/licenses/>.
   15: */
   16: 
   17: #include "dnsmasq.h"
   18: #ifdef HAVE_INOTIFY
   19: 
   20: #include <sys/inotify.h>
   21: #include <sys/param.h> /* For MAXSYMLINKS */
   22: 
   23: /* the strategy is to set a inotify on the directories containing
   24:    resolv files, for any files in the directory which are close-write 
   25:    or moved into the directory.
   26:    
   27:    When either of those happen, we look to see if the file involved
   28:    is actually a resolv-file, and if so, call poll-resolv with
   29:    the "force" argument, to ensure it's read.
   30: 
   31:    This adds one new error condition: the directories containing
   32:    all specified resolv-files must exist at start-up, even if the actual
   33:    files don't. 
   34: */
   35: 
   36: static char *inotify_buffer;
   37: #define INOTIFY_SZ (sizeof(struct inotify_event) + NAME_MAX + 1)
   38: 
   39: /* If path is a symbolic link, return the path it
   40:    points to, made absolute if relative.
   41:    If path doesn't exist or is not a symlink, return NULL.
   42:    Return value is malloc'ed */
   43: static char *my_readlink(char *path)
   44: {
   45:   ssize_t rc, size = 64;
   46:   char *buf;
   47: 
   48:   while (1)
   49:     {
   50:       buf = safe_malloc(size);
   51:       rc = readlink(path, buf, (size_t)size);
   52:       
   53:       if (rc == -1)
   54: 	{
   55: 	  /* Not link or doesn't exist. */
   56: 	  if (errno == EINVAL || errno == ENOENT)
   57: 	    {
   58: 	      free(buf);
   59: 	      return NULL;
   60: 	    }
   61: 	  else
   62: 	    die(_("cannot access path %s: %s"), path, EC_MISC);
   63: 	}
   64:       else if (rc < size-1)
   65: 	{
   66: 	  char *d;
   67: 	  
   68: 	  buf[rc] = 0;
   69: 	  if (buf[0] != '/' && ((d = strrchr(path, '/'))))
   70: 	    {
   71: 	      /* Add path to relative link */
   72: 	      char *new_buf = safe_malloc((d - path) + strlen(buf) + 2);
   73: 	      *(d+1) = 0;
   74: 	      strcpy(new_buf, path);
   75: 	      strcat(new_buf, buf);
   76: 	      free(buf);
   77: 	      buf = new_buf;
   78: 	    }
   79: 	  return buf;
   80: 	}
   81: 
   82:       /* Buffer too small, increase and retry */
   83:       size += 64;
   84:       free(buf);
   85:     }
   86: }
   87: 
   88: void inotify_dnsmasq_init()
   89: {
   90:   struct resolvc *res;
   91:   inotify_buffer = safe_malloc(INOTIFY_SZ);
   92:   daemon->inotifyfd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
   93:   
   94:   if (daemon->inotifyfd == -1)
   95:     die(_("failed to create inotify: %s"), NULL, EC_MISC);
   96: 
   97:   if (option_bool(OPT_NO_RESOLV))
   98:     return;
   99:   
  100:   for (res = daemon->resolv_files; res; res = res->next)
  101:     {
  102:       char *d, *new_path, *path = safe_malloc(strlen(res->name) + 1);
  103:       int links = MAXSYMLINKS;
  104: 
  105:       strcpy(path, res->name);
  106: 
  107:       /* Follow symlinks until we reach a non-symlink, or a non-existant file. */
  108:       while ((new_path = my_readlink(path)))
  109: 	{
  110: 	  if (links-- == 0)
  111: 	    die(_("too many symlinks following %s"), res->name, EC_MISC);
  112: 	  free(path);
  113: 	  path = new_path;
  114: 	}
  115: 
  116:       res->wd = -1;
  117: 
  118:       if ((d = strrchr(path, '/')))
  119: 	{
  120: 	  *d = 0; /* make path just directory */
  121: 	  res->wd = inotify_add_watch(daemon->inotifyfd, path, IN_CLOSE_WRITE | IN_MOVED_TO);
  122: 
  123: 	  res->file = d+1; /* pointer to filename */
  124: 	  *d = '/';
  125: 	  
  126: 	  if (res->wd == -1 && errno == ENOENT)
  127: 	    die(_("directory %s for resolv-file is missing, cannot poll"), res->name, EC_MISC);
  128: 	}	  
  129: 	 
  130:       if (res->wd == -1)
  131: 	die(_("failed to create inotify for %s: %s"), res->name, EC_MISC);
  132: 	
  133:     }
  134: }
  135: 
  136: 
  137: /* initialisation for dynamic-dir. Set inotify watch for each directory, and read pre-existing files */
  138: void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revhashsz)
  139: {
  140:   struct hostsfile *ah;
  141:   
  142:   for (ah = daemon->dynamic_dirs; ah; ah = ah->next)
  143:     {
  144:       DIR *dir_stream = NULL;
  145:       struct dirent *ent;
  146:       struct stat buf;
  147:      
  148:       if (!(ah->flags & flag))
  149: 	continue;
  150:  
  151:       if (stat(ah->fname, &buf) == -1 || !(S_ISDIR(buf.st_mode)))
  152: 	{
  153: 	  my_syslog(LOG_ERR, _("bad dynamic directory %s: %s"), 
  154: 		    ah->fname, strerror(errno));
  155: 	  continue;
  156: 	}
  157:       
  158:        if (!(ah->flags & AH_WD_DONE))
  159: 	 {
  160: 	   ah->wd = inotify_add_watch(daemon->inotifyfd, ah->fname, IN_CLOSE_WRITE | IN_MOVED_TO);
  161: 	   ah->flags |= AH_WD_DONE;
  162: 	 }
  163: 
  164:        /* Read contents of dir _after_ calling add_watch, in the hope of avoiding
  165: 	  a race which misses files being added as we start */
  166:        if (ah->wd == -1 || !(dir_stream = opendir(ah->fname)))
  167: 	 {
  168: 	   my_syslog(LOG_ERR, _("failed to create inotify for %s: %s"),
  169: 		     ah->fname, strerror(errno));
  170: 	   continue;
  171: 	 }
  172: 
  173:        while ((ent = readdir(dir_stream)))
  174: 	 {
  175: 	   size_t lendir = strlen(ah->fname);
  176: 	   size_t lenfile = strlen(ent->d_name);
  177: 	   char *path;
  178: 	   
  179: 	   /* ignore emacs backups and dotfiles */
  180: 	   if (lenfile == 0 || 
  181: 	       ent->d_name[lenfile - 1] == '~' ||
  182: 	       (ent->d_name[0] == '#' && ent->d_name[lenfile - 1] == '#') ||
  183: 	       ent->d_name[0] == '.')
  184: 	     continue;
  185: 	   
  186: 	   if ((path = whine_malloc(lendir + lenfile + 2)))
  187: 	     {
  188: 	       strcpy(path, ah->fname);
  189: 	       strcat(path, "/");
  190: 	       strcat(path, ent->d_name);
  191: 	       
  192: 	       /* ignore non-regular files */
  193: 	       if (stat(path, &buf) != -1 && S_ISREG(buf.st_mode))
  194: 		 {
  195: 		   if (ah->flags & AH_HOSTS)
  196: 		     total_size = read_hostsfile(path, ah->index, total_size, rhash, revhashsz);
  197: #ifdef HAVE_DHCP
  198: 		   else if (ah->flags & (AH_DHCP_HST | AH_DHCP_OPT))
  199: 		     option_read_dynfile(path, ah->flags);
  200: #endif		   
  201: 		 }
  202: 
  203: 	       free(path);
  204: 	     }
  205: 	 }
  206: 
  207:        closedir(dir_stream);
  208:     }
  209: }
  210: 
  211: int inotify_check(time_t now)
  212: {
  213:   int hit = 0;
  214:   struct hostsfile *ah;
  215: 
  216:   while (1)
  217:     {
  218:       int rc;
  219:       char *p;
  220:       struct resolvc *res;
  221:       struct inotify_event *in;
  222: 
  223:       while ((rc = read(daemon->inotifyfd, inotify_buffer, INOTIFY_SZ)) == -1 && errno == EINTR);
  224:       
  225:       if (rc <= 0)
  226: 	break;
  227:       
  228:       for (p = inotify_buffer; rc - (p - inotify_buffer) >= (int)sizeof(struct inotify_event); p += sizeof(struct inotify_event) + in->len) 
  229: 	{
  230: 	  in = (struct inotify_event*)p;
  231: 	  
  232: 	  for (res = daemon->resolv_files; res; res = res->next)
  233: 	    if (res->wd == in->wd && in->len != 0 && strcmp(res->file, in->name) == 0)
  234: 	      hit = 1;
  235: 
  236: 	  /* ignore emacs backups and dotfiles */
  237: 	  if (in->len == 0 || 
  238: 	      in->name[in->len - 1] == '~' ||
  239: 	      (in->name[0] == '#' && in->name[in->len - 1] == '#') ||
  240: 	      in->name[0] == '.')
  241: 	    continue;
  242: 	  
  243: 	  for (ah = daemon->dynamic_dirs; ah; ah = ah->next)
  244: 	    if (ah->wd == in->wd)
  245: 	      {
  246: 		size_t lendir = strlen(ah->fname);
  247: 		char *path;
  248: 		
  249: 		if ((path = whine_malloc(lendir + in->len + 2)))
  250: 		  {
  251: 		    strcpy(path, ah->fname);
  252: 		    strcat(path, "/");
  253: 		    strcat(path, in->name);
  254: 		     
  255: 		    my_syslog(LOG_INFO, _("inotify, new or changed file %s"), path);
  256: 
  257: 		    if (ah->flags & AH_HOSTS)
  258: 		      {
  259: 			read_hostsfile(path, ah->index, 0, NULL, 0);
  260: #ifdef HAVE_DHCP
  261: 			if (daemon->dhcp || daemon->doing_dhcp6) 
  262: 			  {
  263: 			    /* Propogate the consequences of loading a new dhcp-host */
  264: 			    dhcp_update_configs(daemon->dhcp_conf);
  265: 			    lease_update_from_configs(); 
  266: 			    lease_update_file(now); 
  267: 			    lease_update_dns(1);
  268: 			  }
  269: #endif
  270: 		      }
  271: #ifdef HAVE_DHCP
  272: 		    else if (ah->flags & AH_DHCP_HST)
  273: 		      {
  274: 			if (option_read_dynfile(path, AH_DHCP_HST))
  275: 			  {
  276: 			    /* Propogate the consequences of loading a new dhcp-host */
  277: 			    dhcp_update_configs(daemon->dhcp_conf);
  278: 			    lease_update_from_configs(); 
  279: 			    lease_update_file(now); 
  280: 			    lease_update_dns(1);
  281: 			  }
  282: 		      }
  283: 		    else if (ah->flags & AH_DHCP_OPT)
  284: 		      option_read_dynfile(path, AH_DHCP_OPT);
  285: #endif
  286: 		    
  287: 		    free(path);
  288: 		  }
  289: 	      }
  290: 	}
  291:     }
  292:   return hit;
  293: }
  294: 
  295: #endif  /* INOTIFY */
  296:   

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>