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

1.1.1.2 ! misho       1: /* dnsmasq is Copyright (c) 2000-2021 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: 
                    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:        {
1.1.1.2 ! misho     230:          size_t namelen;
        !           231: 
1.1       misho     232:          in = (struct inotify_event*)p;
                    233:          
                    234:          /* ignore emacs backups and dotfiles */
1.1.1.2 ! misho     235:          if (in->len == 0 || (namelen = strlen(in->name)) == 0 ||
        !           236:              in->name[namelen - 1] == '~' ||
        !           237:              (in->name[0] == '#' && in->name[namelen - 1] == '#') ||
1.1       misho     238:              in->name[0] == '.')
                    239:            continue;
1.1.1.2 ! misho     240: 
        !           241:          for (res = daemon->resolv_files; res; res = res->next)
        !           242:            if (res->wd == in->wd && strcmp(res->file, in->name) == 0)
        !           243:              hit = 1;
        !           244: 
1.1       misho     245:          for (ah = daemon->dynamic_dirs; ah; ah = ah->next)
                    246:            if (ah->wd == in->wd)
                    247:              {
                    248:                size_t lendir = strlen(ah->fname);
                    249:                char *path;
                    250:                
                    251:                if ((path = whine_malloc(lendir + in->len + 2)))
                    252:                  {
                    253:                    strcpy(path, ah->fname);
                    254:                    strcat(path, "/");
                    255:                    strcat(path, in->name);
                    256:                     
                    257:                    my_syslog(LOG_INFO, _("inotify, new or changed file %s"), path);
                    258: 
                    259:                    if (ah->flags & AH_HOSTS)
                    260:                      {
                    261:                        read_hostsfile(path, ah->index, 0, NULL, 0);
                    262: #ifdef HAVE_DHCP
                    263:                        if (daemon->dhcp || daemon->doing_dhcp6) 
                    264:                          {
1.1.1.2 ! misho     265:                            /* Propagate the consequences of loading a new dhcp-host */
1.1       misho     266:                            dhcp_update_configs(daemon->dhcp_conf);
                    267:                            lease_update_from_configs(); 
                    268:                            lease_update_file(now); 
                    269:                            lease_update_dns(1);
                    270:                          }
                    271: #endif
                    272:                      }
                    273: #ifdef HAVE_DHCP
                    274:                    else if (ah->flags & AH_DHCP_HST)
                    275:                      {
                    276:                        if (option_read_dynfile(path, AH_DHCP_HST))
                    277:                          {
1.1.1.2 ! misho     278:                            /* Propagate the consequences of loading a new dhcp-host */
1.1       misho     279:                            dhcp_update_configs(daemon->dhcp_conf);
                    280:                            lease_update_from_configs(); 
                    281:                            lease_update_file(now); 
                    282:                            lease_update_dns(1);
                    283:                          }
                    284:                      }
                    285:                    else if (ah->flags & AH_DHCP_OPT)
                    286:                      option_read_dynfile(path, AH_DHCP_OPT);
                    287: #endif
                    288:                    
                    289:                    free(path);
                    290:                  }
                    291:              }
                    292:        }
                    293:     }
                    294:   return hit;
                    295: }
                    296: 
                    297: #endif  /* INOTIFY */
                    298:   

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