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

1.1.1.3 ! misho       1: /* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
1.1       misho       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: 
1.1.1.2   misho      23: /* the strategy is to set an inotify on the directories containing
1.1       misho      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: 
1.1.1.2   misho     107:       /* Follow symlinks until we reach a non-symlink, or a non-existent file. */
1.1       misho     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: 
1.1.1.3 ! misho     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: 
1.1       misho     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: {
1.1.1.3 ! misho     165:   struct dyndir *dd;
        !           166: 
        !           167:   for (dd = daemon->dynamic_dirs; dd; dd = dd->next)
1.1       misho     168:     {
                    169:       DIR *dir_stream = NULL;
                    170:       struct dirent *ent;
                    171:       struct stat buf;
1.1.1.3 ! misho     172: 
        !           173:       if (!(dd->flags & flag))
1.1       misho     174:        continue;
1.1.1.3 ! misho     175: 
        !           176:       if (stat(dd->dname, &buf) == -1)
1.1       misho     177:        {
                    178:          my_syslog(LOG_ERR, _("bad dynamic directory %s: %s"), 
1.1.1.3 ! misho     179:                    dd->dname, strerror(errno));
1.1       misho     180:          continue;
                    181:        }
1.1.1.3 ! misho     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))
1.1       misho     191:         {
1.1.1.3 ! misho     192:           dd->wd = inotify_add_watch(daemon->inotifyfd, dd->dname, IN_CLOSE_WRITE | IN_MOVED_TO | IN_DELETE);
        !           193:           dd->flags |= AH_WD_DONE;
1.1       misho     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 */
1.1.1.3 ! misho     198:        if (dd->wd == -1 || !(dir_stream = opendir(dd->dname)))
1.1       misho     199:         {
                    200:           my_syslog(LOG_ERR, _("failed to create inotify for %s: %s"),
1.1.1.3 ! misho     201:                     dd->dname, strerror(errno));
1.1       misho     202:           continue;
                    203:         }
                    204: 
                    205:        while ((ent = readdir(dir_stream)))
                    206:         {
1.1.1.3 ! misho     207:           size_t lendir = strlen(dd->dname);
1.1       misho     208:           size_t lenfile = strlen(ent->d_name);
                    209:           char *path;
1.1.1.3 ! misho     210: 
1.1       misho     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;
1.1.1.3 ! misho     217: 
1.1       misho     218:           if ((path = whine_malloc(lendir + lenfile + 2)))
                    219:             {
1.1.1.3 ! misho     220:               struct hostsfile *ah;
        !           221: 
        !           222:               strcpy(path, dd->dname);
1.1       misho     223:               strcat(path, "/");
                    224:               strcat(path, ent->d_name);
1.1.1.3 ! misho     225: 
        !           226:               if (!(ah = dyndir_addhosts(dd, path)))
        !           227:                 {
        !           228:                   free(path);
        !           229:                   continue;
        !           230:                 }
1.1       misho     231:               
                    232:               /* ignore non-regular files */
                    233:               if (stat(path, &buf) != -1 && S_ISREG(buf.st_mode))
                    234:                 {
1.1.1.3 ! misho     235:                   if (dd->flags & AH_HOSTS)
1.1       misho     236:                     total_size = read_hostsfile(path, ah->index, total_size, rhash, revhashsz);
                    237: #ifdef HAVE_DHCP
1.1.1.3 ! misho     238:                   else if (dd->flags & (AH_DHCP_HST | AH_DHCP_OPT))
        !           239:                     option_read_dynfile(path, dd->flags);
1.1       misho     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;
1.1.1.3 ! misho     252:   struct dyndir *dd;
1.1       misho     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:        {
1.1.1.2   misho     268:          size_t namelen;
                    269: 
1.1       misho     270:          in = (struct inotify_event*)p;
                    271:          
                    272:          /* ignore emacs backups and dotfiles */
1.1.1.2   misho     273:          if (in->len == 0 || (namelen = strlen(in->name)) == 0 ||
                    274:              in->name[namelen - 1] == '~' ||
                    275:              (in->name[0] == '#' && in->name[namelen - 1] == '#') ||
1.1       misho     276:              in->name[0] == '.')
                    277:            continue;
1.1.1.2   misho     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: 
1.1.1.3 ! misho     283:          for (dd = daemon->dynamic_dirs; dd; dd = dd->next)
        !           284:            if (dd->wd == in->wd)
1.1       misho     285:              {
1.1.1.3 ! misho     286:                size_t lendir = strlen(dd->dname);
1.1       misho     287:                char *path;
1.1.1.3 ! misho     288:                                
1.1       misho     289:                if ((path = whine_malloc(lendir + in->len + 2)))
                    290:                  {
1.1.1.3 ! misho     291:                    struct hostsfile *ah = NULL;
        !           292: 
        !           293:                    strcpy(path, dd->dname);
1.1       misho     294:                    strcat(path, "/");
                    295:                    strcat(path, in->name);
                    296: 
1.1.1.3 ! misho     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)
1.1       misho     304:                      {
1.1.1.3 ! misho     305:                        if ((ah = dyndir_addhosts(dd, path)))
1.1       misho     306:                          {
1.1.1.3 ! misho     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:                              }
1.1       misho     323: #endif
1.1.1.3 ! misho     324:                          }
1.1       misho     325:                      }
                    326: #ifdef HAVE_DHCP
1.1.1.3 ! misho     327:                    else if (dd->flags & AH_DHCP_HST)
1.1       misho     328:                      {
                    329:                        if (option_read_dynfile(path, AH_DHCP_HST))
                    330:                          {
1.1.1.2   misho     331:                            /* Propagate the consequences of loading a new dhcp-host */
1.1       misho     332:                            dhcp_update_configs(daemon->dhcp_conf);
                    333:                            lease_update_from_configs(); 
                    334:                            lease_update_file(now); 
                    335:                            lease_update_dns(1);
                    336:                          }
                    337:                      }
1.1.1.3 ! misho     338:                    else if (dd->flags & AH_DHCP_OPT)
1.1       misho     339:                      option_read_dynfile(path, AH_DHCP_OPT);
                    340: #endif
                    341:                    
1.1.1.3 ! misho     342:                    if (!ah)
        !           343:                      free(path);
1.1       misho     344:                  }
                    345:              }
                    346:        }
                    347:     }
                    348:   return hit;
                    349: }
                    350: 
                    351: #endif  /* INOTIFY */

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