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>