Annotation of embedaddon/dnsmasq/src/inotify.c, revision 1.1

1.1     ! misho       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>