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>