File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / dnsmasq / src / inotify.c
Revision 1.1.1.3 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Sep 27 11:02:07 2023 UTC (10 months ago) by misho
Branches: dnsmasq, MAIN
CVS tags: v8_2p1, HEAD
Version 8.2p1

    1: /* dnsmasq is Copyright (c) 2000-2022 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 an 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-existent 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: static struct hostsfile *dyndir_addhosts(struct dyndir *dd, char *path)
  137: {
  138:   /* Check if this file is already known in dd->files */
  139:   struct hostsfile *ah = NULL;
  140:   for(ah = dd->files; ah; ah = ah->next)
  141:     if(ah && ah->fname && strcmp(path, ah->fname) == 0)
  142:       return ah;
  143: 
  144:   /* Not known, create new hostsfile record for this dyndir */
  145:   struct hostsfile *newah = NULL;
  146:   if(!(newah = whine_malloc(sizeof(struct hostsfile))))
  147:     return NULL;
  148: 
  149:   /* Add this file to the tip of the linked list */
  150:   newah->next = dd->files;
  151:   dd->files = newah;
  152: 
  153:   /* Copy flags, set index and the full file path */
  154:   newah->flags = dd->flags;
  155:   newah->index = daemon->host_index++;
  156:   newah->fname = path;
  157: 
  158:   return newah;
  159: }
  160: 
  161: 
  162: /* initialisation for dynamic-dir. Set inotify watch for each directory, and read pre-existing files */
  163: void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revhashsz)
  164: {
  165:   struct dyndir *dd;
  166: 
  167:   for (dd = daemon->dynamic_dirs; dd; dd = dd->next)
  168:     {
  169:       DIR *dir_stream = NULL;
  170:       struct dirent *ent;
  171:       struct stat buf;
  172: 
  173:       if (!(dd->flags & flag))
  174: 	continue;
  175: 
  176:       if (stat(dd->dname, &buf) == -1)
  177: 	{
  178: 	  my_syslog(LOG_ERR, _("bad dynamic directory %s: %s"), 
  179: 		    dd->dname, strerror(errno));
  180: 	  continue;
  181: 	}
  182: 
  183:       if (!(S_ISDIR(buf.st_mode)))
  184: 	{
  185: 	  my_syslog(LOG_ERR, _("bad dynamic directory %s: %s"), 
  186: 		    dd->dname, _("not a directory"));
  187: 	  continue;
  188: 	}
  189: 
  190:        if (!(dd->flags & AH_WD_DONE))
  191: 	 {
  192: 	   dd->wd = inotify_add_watch(daemon->inotifyfd, dd->dname, IN_CLOSE_WRITE | IN_MOVED_TO | IN_DELETE);
  193: 	   dd->flags |= AH_WD_DONE;
  194: 	 }
  195: 
  196:        /* Read contents of dir _after_ calling add_watch, in the hope of avoiding
  197: 	  a race which misses files being added as we start */
  198:        if (dd->wd == -1 || !(dir_stream = opendir(dd->dname)))
  199: 	 {
  200: 	   my_syslog(LOG_ERR, _("failed to create inotify for %s: %s"),
  201: 		     dd->dname, strerror(errno));
  202: 	   continue;
  203: 	 }
  204: 
  205:        while ((ent = readdir(dir_stream)))
  206: 	 {
  207: 	   size_t lendir = strlen(dd->dname);
  208: 	   size_t lenfile = strlen(ent->d_name);
  209: 	   char *path;
  210: 
  211: 	   /* ignore emacs backups and dotfiles */
  212: 	   if (lenfile == 0 || 
  213: 	       ent->d_name[lenfile - 1] == '~' ||
  214: 	       (ent->d_name[0] == '#' && ent->d_name[lenfile - 1] == '#') ||
  215: 	       ent->d_name[0] == '.')
  216: 	     continue;
  217: 
  218: 	   if ((path = whine_malloc(lendir + lenfile + 2)))
  219: 	     {
  220: 	       struct hostsfile *ah;
  221: 
  222: 	       strcpy(path, dd->dname);
  223: 	       strcat(path, "/");
  224: 	       strcat(path, ent->d_name);
  225: 
  226: 	       if (!(ah = dyndir_addhosts(dd, path)))
  227: 		 {
  228: 		   free(path);
  229: 		   continue;
  230: 		 }
  231: 	       
  232: 	       /* ignore non-regular files */
  233: 	       if (stat(path, &buf) != -1 && S_ISREG(buf.st_mode))
  234: 		 {
  235: 		   if (dd->flags & AH_HOSTS)
  236: 		     total_size = read_hostsfile(path, ah->index, total_size, rhash, revhashsz);
  237: #ifdef HAVE_DHCP
  238: 		   else if (dd->flags & (AH_DHCP_HST | AH_DHCP_OPT))
  239: 		     option_read_dynfile(path, dd->flags);
  240: #endif		   
  241: 		 }
  242: 	     }
  243: 	 }
  244: 
  245:        closedir(dir_stream);
  246:     }
  247: }
  248: 
  249: int inotify_check(time_t now)
  250: {
  251:   int hit = 0;
  252:   struct dyndir *dd;
  253: 
  254:   while (1)
  255:     {
  256:       int rc;
  257:       char *p;
  258:       struct resolvc *res;
  259:       struct inotify_event *in;
  260: 
  261:       while ((rc = read(daemon->inotifyfd, inotify_buffer, INOTIFY_SZ)) == -1 && errno == EINTR);
  262:       
  263:       if (rc <= 0)
  264: 	break;
  265:       
  266:       for (p = inotify_buffer; rc - (p - inotify_buffer) >= (int)sizeof(struct inotify_event); p += sizeof(struct inotify_event) + in->len) 
  267: 	{
  268: 	  size_t namelen;
  269: 
  270: 	  in = (struct inotify_event*)p;
  271: 	  
  272: 	  /* ignore emacs backups and dotfiles */
  273: 	  if (in->len == 0 || (namelen = strlen(in->name)) == 0 ||
  274: 	      in->name[namelen - 1] == '~' ||
  275: 	      (in->name[0] == '#' && in->name[namelen - 1] == '#') ||
  276: 	      in->name[0] == '.')
  277: 	    continue;
  278: 
  279: 	  for (res = daemon->resolv_files; res; res = res->next)
  280: 	    if (res->wd == in->wd && strcmp(res->file, in->name) == 0)
  281: 	      hit = 1;
  282: 
  283: 	  for (dd = daemon->dynamic_dirs; dd; dd = dd->next)
  284: 	    if (dd->wd == in->wd)
  285: 	      {
  286: 		size_t lendir = strlen(dd->dname);
  287: 		char *path;
  288: 				
  289: 		if ((path = whine_malloc(lendir + in->len + 2)))
  290: 		  {
  291: 		    struct hostsfile *ah = NULL;
  292: 
  293: 		    strcpy(path, dd->dname);
  294: 		    strcat(path, "/");
  295: 		    strcat(path, in->name);
  296: 
  297: 		    /* Is this is a deletion event? */
  298: 		    if (in->mask & IN_DELETE)
  299: 		      my_syslog(LOG_INFO, _("inotify: %s removed"), path);
  300: 		    else 
  301: 		      my_syslog(LOG_INFO, _("inotify: %s new or modified"), path);
  302: 
  303: 		    if (dd->flags & AH_HOSTS)
  304: 		      {
  305: 			if ((ah = dyndir_addhosts(dd, path)))
  306: 			  {
  307: 			    const unsigned int removed = cache_remove_uid(ah->index);
  308: 			    if (removed > 0)
  309: 			      my_syslog(LOG_INFO, _("inotify: flushed %u names read from %s"), removed, path);
  310: 
  311: 			    /* (Re-)load hostsfile only if this event isn't triggered by deletion */
  312: 			    if (!(in->mask & IN_DELETE))
  313: 			      read_hostsfile(path, ah->index, 0, NULL, 0);
  314: #ifdef HAVE_DHCP
  315: 			    if (daemon->dhcp || daemon->doing_dhcp6) 
  316: 			      {
  317: 				/* Propagate the consequences of loading a new dhcp-host */
  318: 				dhcp_update_configs(daemon->dhcp_conf);
  319: 				lease_update_from_configs(); 
  320: 				lease_update_file(now); 
  321: 				lease_update_dns(1);
  322: 			      }
  323: #endif
  324: 			  }
  325: 		      }
  326: #ifdef HAVE_DHCP
  327: 		    else if (dd->flags & AH_DHCP_HST)
  328: 		      {
  329: 			if (option_read_dynfile(path, AH_DHCP_HST))
  330: 			  {
  331: 			    /* Propagate the consequences of loading a new dhcp-host */
  332: 			    dhcp_update_configs(daemon->dhcp_conf);
  333: 			    lease_update_from_configs(); 
  334: 			    lease_update_file(now); 
  335: 			    lease_update_dns(1);
  336: 			  }
  337: 		      }
  338: 		    else if (dd->flags & AH_DHCP_OPT)
  339: 		      option_read_dynfile(path, AH_DHCP_OPT);
  340: #endif
  341: 		    
  342: 		    if (!ah)
  343: 		      free(path);
  344: 		  }
  345: 	      }
  346: 	}
  347:     }
  348:   return hit;
  349: }
  350: 
  351: #endif  /* INOTIFY */

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