blob: 0c775de861ffe02b4d4b77c14b2b9a95f606f93d [file] [log] [blame]
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001/* dnsmasq is Copyright (c) 2000-2024 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
Tarun Kundu12e3b2e2024-08-15 16:16:53 -070097 if (daemon->port == 0 || option_bool(OPT_NO_RESOLV))
Simon Kelley77607cb2015-09-10 23:08:43 +010098 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
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700136static 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
Simon Kelley193de4a2014-12-10 17:32:16 +0000161
Simon Kelley70d18732015-01-31 19:59:29 +0000162/* initialisation for dynamic-dir. Set inotify watch for each directory, and read pre-existing files */
163void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revhashsz)
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000164{
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700165 struct dyndir *dd;
166
167 for (dd = daemon->dynamic_dirs; dd; dd = dd->next)
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000168 {
Simon Kelley70d18732015-01-31 19:59:29 +0000169 DIR *dir_stream = NULL;
170 struct dirent *ent;
171 struct stat buf;
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700172
173 if (!(dd->flags & flag))
Simon Kelley70d18732015-01-31 19:59:29 +0000174 continue;
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700175
176 if (stat(dd->dname, &buf) == -1)
Simon Kelley70d18732015-01-31 19:59:29 +0000177 {
178 my_syslog(LOG_ERR, _("bad dynamic directory %s: %s"),
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700179 dd->dname, strerror(errno));
Simon Kelley70d18732015-01-31 19:59:29 +0000180 continue;
181 }
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700182
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))
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000191 {
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700192 dd->wd = inotify_add_watch(daemon->inotifyfd, dd->dname, IN_CLOSE_WRITE | IN_MOVED_TO | IN_DELETE);
193 dd->flags |= AH_WD_DONE;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000194 }
Simon Kelley8ff70de2015-02-14 20:02:37 +0000195
196 /* Read contents of dir _after_ calling add_watch, in the hope of avoiding
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000197 a race which misses files being added as we start */
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700198 if (dd->wd == -1 || !(dir_stream = opendir(dd->dname)))
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000199 {
Simon Kelley70d18732015-01-31 19:59:29 +0000200 my_syslog(LOG_ERR, _("failed to create inotify for %s: %s"),
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700201 dd->dname, strerror(errno));
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000202 continue;
203 }
204
205 while ((ent = readdir(dir_stream)))
206 {
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700207 size_t lendir = strlen(dd->dname);
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000208 size_t lenfile = strlen(ent->d_name);
209 char *path;
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700210
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000211 /* 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;
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700217
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000218 if ((path = whine_malloc(lendir + lenfile + 2)))
219 {
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700220 struct hostsfile *ah;
221
222 strcpy(path, dd->dname);
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000223 strcat(path, "/");
224 strcat(path, ent->d_name);
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700225
226 if (!(ah = dyndir_addhosts(dd, path)))
227 {
228 free(path);
229 continue;
230 }
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000231
232 /* ignore non-regular files */
233 if (stat(path, &buf) != -1 && S_ISREG(buf.st_mode))
Simon Kelley70d18732015-01-31 19:59:29 +0000234 {
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700235 if (dd->flags & AH_HOSTS)
Simon Kelley70d18732015-01-31 19:59:29 +0000236 total_size = read_hostsfile(path, ah->index, total_size, rhash, revhashsz);
237#ifdef HAVE_DHCP
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700238 else if (dd->flags & (AH_DHCP_HST | AH_DHCP_OPT))
239 option_read_dynfile(path, dd->flags);
Simon Kelley70d18732015-01-31 19:59:29 +0000240#endif
241 }
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000242 }
243 }
Simon Kelleye1abeee2016-03-16 17:22:27 +0000244
245 closedir(dir_stream);
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000246 }
247}
248
Simon Kelley70d18732015-01-31 19:59:29 +0000249int inotify_check(time_t now)
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000250{
Simon Kelley70d18732015-01-31 19:59:29 +0000251 int hit = 0;
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700252 struct dyndir *dd;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000253
Simon Kelley70d18732015-01-31 19:59:29 +0000254 while (1)
255 {
256 int rc;
257 char *p;
258 struct resolvc *res;
259 struct inotify_event *in;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000260
Simon Kelley70d18732015-01-31 19:59:29 +0000261 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 {
Andy Hawkins55ecde72018-02-14 14:24:39 +0000268 size_t namelen;
269
Simon Kelley70d18732015-01-31 19:59:29 +0000270 in = (struct inotify_event*)p;
271
Simon Kelley70d18732015-01-31 19:59:29 +0000272 /* ignore emacs backups and dotfiles */
Andy Hawkins55ecde72018-02-14 14:24:39 +0000273 if (in->len == 0 || (namelen = strlen(in->name)) == 0 ||
274 in->name[namelen - 1] == '~' ||
275 (in->name[0] == '#' && in->name[namelen - 1] == '#') ||
Simon Kelley70d18732015-01-31 19:59:29 +0000276 in->name[0] == '.')
277 continue;
Andy Hawkins55ecde72018-02-14 14:24:39 +0000278
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
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700283 for (dd = daemon->dynamic_dirs; dd; dd = dd->next)
284 if (dd->wd == in->wd)
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000285 {
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700286 size_t lendir = strlen(dd->dname);
Simon Kelley70d18732015-01-31 19:59:29 +0000287 char *path;
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700288
Simon Kelley70d18732015-01-31 19:59:29 +0000289 if ((path = whine_malloc(lendir + in->len + 2)))
290 {
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700291 struct hostsfile *ah = NULL;
292
293 strcpy(path, dd->dname);
Simon Kelley70d18732015-01-31 19:59:29 +0000294 strcat(path, "/");
295 strcat(path, in->name);
Simon Kelleyf9c86372015-02-03 21:52:48 +0000296
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700297 /* 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)
Simon Kelley2941d3a2015-02-02 22:36:42 +0000304 {
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700305 if ((ah = dyndir_addhosts(dd, path)))
Simon Kelley2941d3a2015-02-02 22:36:42 +0000306 {
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700307 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 }
Simon Kelley2941d3a2015-02-02 22:36:42 +0000323#endif
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700324 }
Simon Kelley2941d3a2015-02-02 22:36:42 +0000325 }
Simon Kelley70d18732015-01-31 19:59:29 +0000326#ifdef HAVE_DHCP
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700327 else if (dd->flags & AH_DHCP_HST)
Simon Kelley70d18732015-01-31 19:59:29 +0000328 {
329 if (option_read_dynfile(path, AH_DHCP_HST))
330 {
Josh Soref730c6742017-02-06 16:14:04 +0000331 /* Propagate the consequences of loading a new dhcp-host */
Simon Kelley70d18732015-01-31 19:59:29 +0000332 dhcp_update_configs(daemon->dhcp_conf);
333 lease_update_from_configs();
334 lease_update_file(now);
335 lease_update_dns(1);
336 }
337 }
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700338 else if (dd->flags & AH_DHCP_OPT)
Simon Kelley70d18732015-01-31 19:59:29 +0000339 option_read_dynfile(path, AH_DHCP_OPT);
340#endif
341
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700342 if (!ah)
343 free(path);
Simon Kelley70d18732015-01-31 19:59:29 +0000344 }
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000345 }
Simon Kelley70d18732015-01-31 19:59:29 +0000346 }
347 }
348 return hit;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000349}
350
Simon Kelley04918052015-01-26 11:23:43 +0000351#endif /* INOTIFY */