blob: 7107833af41356f58b83f586dd1697793f37d4e8 [file] [log] [blame]
Simon Kelleyd1ced3a2018-01-01 22:18:03 +00001/* dnsmasq is Copyright (c) 2000-2018 Simon Kelley
Simon Kelley193de4a2014-12-10 17:32:16 +00002
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"
Simon Kelley04918052015-01-26 11:23:43 +000018#ifdef HAVE_INOTIFY
Simon Kelley193de4a2014-12-10 17:32:16 +000019
Matthias Andreed310ab72014-12-27 15:36:38 +000020#include <sys/inotify.h>
Simon Kelley362c9302015-07-06 21:48:49 +010021#include <sys/param.h> /* For MAXSYMLINKS */
Matthias Andreed310ab72014-12-27 15:36:38 +000022
Ville Skyttäfaaf3062018-01-14 17:32:52 +000023/* the strategy is to set an inotify on the directories containing
Simon Kelley193de4a2014-12-10 17:32:16 +000024 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
36static char *inotify_buffer;
37#define INOTIFY_SZ (sizeof(struct inotify_event) + NAME_MAX + 1)
38
Simon Kelley362c9302015-07-06 21:48:49 +010039/* 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 */
43static char *my_readlink(char *path)
44{
Simon Kelley45c5cb12015-07-08 22:40:57 +010045 ssize_t rc, size = 64;
Simon Kelley362c9302015-07-06 21:48:49 +010046 char *buf;
47
48 while (1)
49 {
50 buf = safe_malloc(size);
Simon Kelley45c5cb12015-07-08 22:40:57 +010051 rc = readlink(path, buf, (size_t)size);
Simon Kelley362c9302015-07-06 21:48:49 +010052
53 if (rc == -1)
54 {
55 /* Not link or doesn't exist. */
56 if (errno == EINVAL || errno == ENOENT)
Simon Kelleye1abeee2016-03-16 17:22:27 +000057 {
58 free(buf);
59 return NULL;
60 }
Simon Kelley362c9302015-07-06 21:48:49 +010061 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
Simon Kelley193de4a2014-12-10 17:32:16 +000088void inotify_dnsmasq_init()
89{
90 struct resolvc *res;
Simon Kelley193de4a2014-12-10 17:32:16 +000091 inotify_buffer = safe_malloc(INOTIFY_SZ);
Simon Kelley857973e2014-12-15 15:58:13 +000092 daemon->inotifyfd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
93
Simon Kelley193de4a2014-12-10 17:32:16 +000094 if (daemon->inotifyfd == -1)
95 die(_("failed to create inotify: %s"), NULL, EC_MISC);
Simon Kelley77607cb2015-09-10 23:08:43 +010096
97 if (option_bool(OPT_NO_RESOLV))
98 return;
Simon Kelley857973e2014-12-15 15:58:13 +000099
Simon Kelley193de4a2014-12-10 17:32:16 +0000100 for (res = daemon->resolv_files; res; res = res->next)
101 {
Simon Kelley362c9302015-07-06 21:48:49 +0100102 char *d, *new_path, *path = safe_malloc(strlen(res->name) + 1);
103 int links = MAXSYMLINKS;
104
105 strcpy(path, res->name);
106
klemens43517fc2017-02-19 15:53:37 +0000107 /* Follow symlinks until we reach a non-symlink, or a non-existent file. */
Simon Kelley362c9302015-07-06 21:48:49 +0100108 while ((new_path = my_readlink(path)))
Simon Kelley857973e2014-12-15 15:58:13 +0000109 {
Simon Kelley362c9302015-07-06 21:48:49 +0100110 if (links-- == 0)
111 die(_("too many symlinks following %s"), res->name, EC_MISC);
112 free(path);
113 path = new_path;
Simon Kelley857973e2014-12-15 15:58:13 +0000114 }
Simon Kelley362c9302015-07-06 21:48:49 +0100115
116 res->wd = -1;
117
Simon Kelley857973e2014-12-15 15:58:13 +0000118 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);
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000122
Simon Kelley857973e2014-12-15 15:58:13 +0000123 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);
Simon Kelley362c9302015-07-06 21:48:49 +0100128 }
129
130 if (res->wd == -1)
131 die(_("failed to create inotify for %s: %s"), res->name, EC_MISC);
132
Simon Kelley193de4a2014-12-10 17:32:16 +0000133 }
134}
135
Simon Kelley193de4a2014-12-10 17:32:16 +0000136
Simon Kelley70d18732015-01-31 19:59:29 +0000137/* initialisation for dynamic-dir. Set inotify watch for each directory, and read pre-existing files */
138void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revhashsz)
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000139{
140 struct hostsfile *ah;
Simon Kelley70d18732015-01-31 19:59:29 +0000141
142 for (ah = daemon->dynamic_dirs; ah; ah = ah->next)
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000143 {
Simon Kelley70d18732015-01-31 19:59:29 +0000144 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
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000158 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 }
Simon Kelley8ff70de2015-02-14 20:02:37 +0000163
164 /* Read contents of dir _after_ calling add_watch, in the hope of avoiding
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000165 a race which misses files being added as we start */
166 if (ah->wd == -1 || !(dir_stream = opendir(ah->fname)))
167 {
Simon Kelley70d18732015-01-31 19:59:29 +0000168 my_syslog(LOG_ERR, _("failed to create inotify for %s: %s"),
169 ah->fname, strerror(errno));
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000170 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))
Simon Kelley70d18732015-01-31 19:59:29 +0000194 {
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
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000203 free(path);
204 }
205 }
Simon Kelleye1abeee2016-03-16 17:22:27 +0000206
207 closedir(dir_stream);
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000208 }
209}
210
Simon Kelley70d18732015-01-31 19:59:29 +0000211int inotify_check(time_t now)
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000212{
Simon Kelley70d18732015-01-31 19:59:29 +0000213 int hit = 0;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000214 struct hostsfile *ah;
215
Simon Kelley70d18732015-01-31 19:59:29 +0000216 while (1)
217 {
218 int rc;
219 char *p;
220 struct resolvc *res;
221 struct inotify_event *in;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000222
Simon Kelley70d18732015-01-31 19:59:29 +0000223 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 {
Andy Hawkins55ecde72018-02-14 14:24:39 +0000230 size_t namelen;
231
Simon Kelley70d18732015-01-31 19:59:29 +0000232 in = (struct inotify_event*)p;
233
Simon Kelley70d18732015-01-31 19:59:29 +0000234 /* ignore emacs backups and dotfiles */
Andy Hawkins55ecde72018-02-14 14:24:39 +0000235 if (in->len == 0 || (namelen = strlen(in->name)) == 0 ||
236 in->name[namelen - 1] == '~' ||
237 (in->name[0] == '#' && in->name[namelen - 1] == '#') ||
Simon Kelley70d18732015-01-31 19:59:29 +0000238 in->name[0] == '.')
239 continue;
Andy Hawkins55ecde72018-02-14 14:24:39 +0000240
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
Simon Kelley70d18732015-01-31 19:59:29 +0000245 for (ah = daemon->dynamic_dirs; ah; ah = ah->next)
246 if (ah->wd == in->wd)
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000247 {
Simon Kelley70d18732015-01-31 19:59:29 +0000248 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);
Simon Kelleyf9c86372015-02-03 21:52:48 +0000256
257 my_syslog(LOG_INFO, _("inotify, new or changed file %s"), path);
258
Simon Kelley70d18732015-01-31 19:59:29 +0000259 if (ah->flags & AH_HOSTS)
Simon Kelley2941d3a2015-02-02 22:36:42 +0000260 {
261 read_hostsfile(path, ah->index, 0, NULL, 0);
262#ifdef HAVE_DHCP
263 if (daemon->dhcp || daemon->doing_dhcp6)
264 {
Josh Soref730c6742017-02-06 16:14:04 +0000265 /* Propagate the consequences of loading a new dhcp-host */
Simon Kelley2941d3a2015-02-02 22:36:42 +0000266 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 }
Simon Kelley70d18732015-01-31 19:59:29 +0000273#ifdef HAVE_DHCP
274 else if (ah->flags & AH_DHCP_HST)
275 {
276 if (option_read_dynfile(path, AH_DHCP_HST))
277 {
Josh Soref730c6742017-02-06 16:14:04 +0000278 /* Propagate the consequences of loading a new dhcp-host */
Simon Kelley70d18732015-01-31 19:59:29 +0000279 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 }
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000291 }
Simon Kelley70d18732015-01-31 19:59:29 +0000292 }
293 }
294 return hit;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000295}
296
Simon Kelley04918052015-01-26 11:23:43 +0000297#endif /* INOTIFY */
Simon Kelley193de4a2014-12-10 17:32:16 +0000298