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/>.
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
35 static char *inotify_buffer
;
36 #define INOTIFY_SZ (sizeof(struct inotify_event) + NAME_MAX + 1)
38 void inotify_dnsmasq_init()
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. */
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 */
71 if (res
->wd
== -1 && errno
== ENOENT
)
72 die(_("directory %s for resolv-file is missing, cannot poll"), res
->name
, EC_MISC
);
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
)
86 for (ah
= daemon
->dynamic_dirs
; ah
; ah
= ah
->next
)
88 DIR *dir_stream
= NULL
;
92 if (!(ah
->flags
& flag
))
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
));
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
));
117 while ((ent
= readdir(dir_stream
)))
119 size_t lendir
= strlen(ah
->fname
);
120 size_t lenfile
= strlen(ent
->d_name
);
123 /* ignore emacs backups and dotfiles */
125 ent
->d_name
[lenfile
- 1] == '~' ||
126 (ent
->d_name
[0] == '#' && ent
->d_name
[lenfile
- 1] == '#') ||
127 ent
->d_name
[0] == '.')
130 if ((path
= whine_malloc(lendir
+ lenfile
+ 2)))
132 strcpy(path
, ah
->fname
);
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
);
142 else if (ah
->flags
& (AH_DHCP_HST
| AH_DHCP_OPT
))
143 option_read_dynfile(path
, ah
->flags
);
153 int inotify_check(time_t now
)
156 struct hostsfile
*ah
;
163 struct inotify_event
*in
;
165 while ((rc
= read(daemon
->inotifyfd
, inotify_buffer
, INOTIFY_SZ
)) == -1 && errno
== EINTR
);
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)
178 /* ignore emacs backups and dotfiles */
180 in
->name
[in
->len
- 1] == '~' ||
181 (in
->name
[0] == '#' && in
->name
[in
->len
- 1] == '#') ||
185 for (ah
= daemon
->dynamic_dirs
; ah
; ah
= ah
->next
)
186 if (ah
->wd
== in
->wd
)
188 size_t lendir
= strlen(ah
->fname
);
191 if ((path
= whine_malloc(lendir
+ in
->len
+ 2)))
193 strcpy(path
, ah
->fname
);
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);
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
);
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
);
225 else if (ah
->flags
& AH_DHCP_OPT
)
226 option_read_dynfile(path
, AH_DHCP_OPT
);