dnsmasq: update to 2.73 (23.06.2015)
[tomato.git] / release / src / router / dnsmasq / src / inotify.c
blob9422066257f55afceb13806910b1a083c2ef655f
1 /* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
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.
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.
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/>.
17 #include "dnsmasq.h"
18 #ifdef HAVE_INOTIFY
20 #include <sys/inotify.h>
22 /* the strategy is to set a inotify on the directories containing
23 resolv files, for any files in the directory which are close-write
24 or moved into the directory.
26 When either of those happen, we look to see if the file involved
27 is actually a resolv-file, and if so, call poll-resolv with
28 the "force" argument, to ensure it's read.
30 This adds one new error condition: the directories containing
31 all specified resolv-files must exist at start-up, even if the actual
32 files don't.
35 static char *inotify_buffer;
36 #define INOTIFY_SZ (sizeof(struct inotify_event) + NAME_MAX + 1)
38 void inotify_dnsmasq_init()
40 struct resolvc *res;
42 inotify_buffer = safe_malloc(INOTIFY_SZ);
43 daemon->inotifyfd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
45 if (daemon->inotifyfd == -1)
46 die(_("failed to create inotify: %s"), NULL, EC_MISC);
48 for (res = daemon->resolv_files; res; res = res->next)
50 char *d = NULL, *path;
52 if (!(path = realpath(res->name, NULL)))
54 /* realpath will fail if the file doesn't exist, but
55 dnsmasq copes with missing files, so fall back
56 and assume that symlinks are not in use in that case. */
57 if (errno == ENOENT)
58 path = res->name;
59 else
60 die(_("cannot cannonicalise resolv-file %s: %s"), res->name, EC_MISC);
63 if ((d = strrchr(path, '/')))
65 *d = 0; /* make path just directory */
66 res->wd = inotify_add_watch(daemon->inotifyfd, path, IN_CLOSE_WRITE | IN_MOVED_TO);
68 res->file = d+1; /* pointer to filename */
69 *d = '/';
71 if (res->wd == -1 && errno == ENOENT)
72 die(_("directory %s for resolv-file is missing, cannot poll"), res->name, EC_MISC);
74 if (res->wd == -1)
75 die(_("failed to create inotify for %s: %s"), res->name, EC_MISC);
81 /* initialisation for dynamic-dir. Set inotify watch for each directory, and read pre-existing files */
82 void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revhashsz)
84 struct hostsfile *ah;
86 for (ah = daemon->dynamic_dirs; ah; ah = ah->next)
88 DIR *dir_stream = NULL;
89 struct dirent *ent;
90 struct stat buf;
92 if (!(ah->flags & flag))
93 continue;
95 if (stat(ah->fname, &buf) == -1 || !(S_ISDIR(buf.st_mode)))
97 my_syslog(LOG_ERR, _("bad dynamic directory %s: %s"),
98 ah->fname, strerror(errno));
99 continue;
102 if (!(ah->flags & AH_WD_DONE))
104 ah->wd = inotify_add_watch(daemon->inotifyfd, ah->fname, IN_CLOSE_WRITE | IN_MOVED_TO);
105 ah->flags |= AH_WD_DONE;
108 /* Read contents of dir _after_ calling add_watch, in the hope of avoiding
109 a race which misses files being added as we start */
110 if (ah->wd == -1 || !(dir_stream = opendir(ah->fname)))
112 my_syslog(LOG_ERR, _("failed to create inotify for %s: %s"),
113 ah->fname, strerror(errno));
114 continue;
117 while ((ent = readdir(dir_stream)))
119 size_t lendir = strlen(ah->fname);
120 size_t lenfile = strlen(ent->d_name);
121 char *path;
123 /* ignore emacs backups and dotfiles */
124 if (lenfile == 0 ||
125 ent->d_name[lenfile - 1] == '~' ||
126 (ent->d_name[0] == '#' && ent->d_name[lenfile - 1] == '#') ||
127 ent->d_name[0] == '.')
128 continue;
130 if ((path = whine_malloc(lendir + lenfile + 2)))
132 strcpy(path, ah->fname);
133 strcat(path, "/");
134 strcat(path, ent->d_name);
136 /* ignore non-regular files */
137 if (stat(path, &buf) != -1 && S_ISREG(buf.st_mode))
139 if (ah->flags & AH_HOSTS)
140 total_size = read_hostsfile(path, ah->index, total_size, rhash, revhashsz);
141 #ifdef HAVE_DHCP
142 else if (ah->flags & (AH_DHCP_HST | AH_DHCP_OPT))
143 option_read_dynfile(path, ah->flags);
144 #endif
147 free(path);
153 int inotify_check(time_t now)
155 int hit = 0;
156 struct hostsfile *ah;
158 while (1)
160 int rc;
161 char *p;
162 struct resolvc *res;
163 struct inotify_event *in;
165 while ((rc = read(daemon->inotifyfd, inotify_buffer, INOTIFY_SZ)) == -1 && errno == EINTR);
167 if (rc <= 0)
168 break;
170 for (p = inotify_buffer; rc - (p - inotify_buffer) >= (int)sizeof(struct inotify_event); p += sizeof(struct inotify_event) + in->len)
172 in = (struct inotify_event*)p;
174 for (res = daemon->resolv_files; res; res = res->next)
175 if (res->wd == in->wd && in->len != 0 && strcmp(res->file, in->name) == 0)
176 hit = 1;
178 /* ignore emacs backups and dotfiles */
179 if (in->len == 0 ||
180 in->name[in->len - 1] == '~' ||
181 (in->name[0] == '#' && in->name[in->len - 1] == '#') ||
182 in->name[0] == '.')
183 continue;
185 for (ah = daemon->dynamic_dirs; ah; ah = ah->next)
186 if (ah->wd == in->wd)
188 size_t lendir = strlen(ah->fname);
189 char *path;
191 if ((path = whine_malloc(lendir + in->len + 2)))
193 strcpy(path, ah->fname);
194 strcat(path, "/");
195 strcat(path, in->name);
197 my_syslog(LOG_INFO, _("inotify, new or changed file %s"), path);
199 if (ah->flags & AH_HOSTS)
201 read_hostsfile(path, ah->index, 0, NULL, 0);
202 #ifdef HAVE_DHCP
203 if (daemon->dhcp || daemon->doing_dhcp6)
205 /* Propogate the consequences of loading a new dhcp-host */
206 dhcp_update_configs(daemon->dhcp_conf);
207 lease_update_from_configs();
208 lease_update_file(now);
209 lease_update_dns(1);
211 #endif
213 #ifdef HAVE_DHCP
214 else if (ah->flags & AH_DHCP_HST)
216 if (option_read_dynfile(path, AH_DHCP_HST))
218 /* Propogate the consequences of loading a new dhcp-host */
219 dhcp_update_configs(daemon->dhcp_conf);
220 lease_update_from_configs();
221 lease_update_file(now);
222 lease_update_dns(1);
225 else if (ah->flags & AH_DHCP_OPT)
226 option_read_dynfile(path, AH_DHCP_OPT);
227 #endif
229 free(path);
234 return hit;
237 #endif /* INOTIFY */