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>