]>
git.ipfire.org Git - people/ms/dnsmasq.git/blob - src/inotify.c
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>
21 #include <sys/param.h> /* For MAXSYMLINKS */
23 /* the strategy is to set a inotify on the directories containing
24 resolv files, for any files in the directory which are close-write
25 or moved into the directory.
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.
31 This adds one new error condition: the directories containing
32 all specified resolv-files must exist at start-up, even if the actual
36 static char *inotify_buffer
;
37 #define INOTIFY_SZ (sizeof(struct inotify_event) + NAME_MAX + 1)
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
)
51 buf
= safe_malloc(size
);
52 rc
= readlink(path
, buf
, size
);
56 /* Not link or doesn't exist. */
57 if (errno
== EINVAL
|| errno
== ENOENT
)
60 die(_("cannot access path %s: %s"), path
, EC_MISC
);
67 if (buf
[0] != '/' && ((d
= strrchr(path
, '/'))))
69 /* Add path to relative link */
70 char *new_buf
= safe_malloc((d
- path
) + strlen(buf
) + 2);
72 strcpy(new_buf
, path
);
80 /* Buffer too small, increase and retry */
86 void inotify_dnsmasq_init()
89 inotify_buffer
= safe_malloc(INOTIFY_SZ
);
90 daemon
->inotifyfd
= inotify_init1(IN_NONBLOCK
| IN_CLOEXEC
);
92 if (daemon
->inotifyfd
== -1)
93 die(_("failed to create inotify: %s"), NULL
, EC_MISC
);
95 for (res
= daemon
->resolv_files
; res
; res
= res
->next
)
97 char *d
, *new_path
, *path
= safe_malloc(strlen(res
->name
) + 1);
98 int links
= MAXSYMLINKS
;
100 strcpy(path
, res
->name
);
102 /* Follow symlinks until we reach a non-symlink, or a non-existant file. */
103 while ((new_path
= my_readlink(path
)))
106 die(_("too many symlinks following %s"), res
->name
, EC_MISC
);
113 if ((d
= strrchr(path
, '/')))
115 *d
= 0; /* make path just directory */
116 res
->wd
= inotify_add_watch(daemon
->inotifyfd
, path
, IN_CLOSE_WRITE
| IN_MOVED_TO
);
118 res
->file
= d
+1; /* pointer to filename */
121 if (res
->wd
== -1 && errno
== ENOENT
)
122 die(_("directory %s for resolv-file is missing, cannot poll"), res
->name
, EC_MISC
);
126 die(_("failed to create inotify for %s: %s"), res
->name
, EC_MISC
);
132 /* initialisation for dynamic-dir. Set inotify watch for each directory, and read pre-existing files */
133 void set_dynamic_inotify(int flag
, int total_size
, struct crec
**rhash
, int revhashsz
)
135 struct hostsfile
*ah
;
137 for (ah
= daemon
->dynamic_dirs
; ah
; ah
= ah
->next
)
139 DIR *dir_stream
= NULL
;
143 if (!(ah
->flags
& flag
))
146 if (stat(ah
->fname
, &buf
) == -1 || !(S_ISDIR(buf
.st_mode
)))
148 my_syslog(LOG_ERR
, _("bad dynamic directory %s: %s"),
149 ah
->fname
, strerror(errno
));
153 if (!(ah
->flags
& AH_WD_DONE
))
155 ah
->wd
= inotify_add_watch(daemon
->inotifyfd
, ah
->fname
, IN_CLOSE_WRITE
| IN_MOVED_TO
);
156 ah
->flags
|= AH_WD_DONE
;
159 /* Read contents of dir _after_ calling add_watch, in the hope of avoiding
160 a race which misses files being added as we start */
161 if (ah
->wd
== -1 || !(dir_stream
= opendir(ah
->fname
)))
163 my_syslog(LOG_ERR
, _("failed to create inotify for %s: %s"),
164 ah
->fname
, strerror(errno
));
168 while ((ent
= readdir(dir_stream
)))
170 size_t lendir
= strlen(ah
->fname
);
171 size_t lenfile
= strlen(ent
->d_name
);
174 /* ignore emacs backups and dotfiles */
176 ent
->d_name
[lenfile
- 1] == '~' ||
177 (ent
->d_name
[0] == '#' && ent
->d_name
[lenfile
- 1] == '#') ||
178 ent
->d_name
[0] == '.')
181 if ((path
= whine_malloc(lendir
+ lenfile
+ 2)))
183 strcpy(path
, ah
->fname
);
185 strcat(path
, ent
->d_name
);
187 /* ignore non-regular files */
188 if (stat(path
, &buf
) != -1 && S_ISREG(buf
.st_mode
))
190 if (ah
->flags
& AH_HOSTS
)
191 total_size
= read_hostsfile(path
, ah
->index
, total_size
, rhash
, revhashsz
);
193 else if (ah
->flags
& (AH_DHCP_HST
| AH_DHCP_OPT
))
194 option_read_dynfile(path
, ah
->flags
);
204 int inotify_check(time_t now
)
207 struct hostsfile
*ah
;
214 struct inotify_event
*in
;
216 while ((rc
= read(daemon
->inotifyfd
, inotify_buffer
, INOTIFY_SZ
)) == -1 && errno
== EINTR
);
221 for (p
= inotify_buffer
; rc
- (p
- inotify_buffer
) >= (int)sizeof(struct inotify_event
); p
+= sizeof(struct inotify_event
) + in
->len
)
223 in
= (struct inotify_event
*)p
;
225 for (res
= daemon
->resolv_files
; res
; res
= res
->next
)
226 if (res
->wd
== in
->wd
&& in
->len
!= 0 && strcmp(res
->file
, in
->name
) == 0)
229 /* ignore emacs backups and dotfiles */
231 in
->name
[in
->len
- 1] == '~' ||
232 (in
->name
[0] == '#' && in
->name
[in
->len
- 1] == '#') ||
236 for (ah
= daemon
->dynamic_dirs
; ah
; ah
= ah
->next
)
237 if (ah
->wd
== in
->wd
)
239 size_t lendir
= strlen(ah
->fname
);
242 if ((path
= whine_malloc(lendir
+ in
->len
+ 2)))
244 strcpy(path
, ah
->fname
);
246 strcat(path
, in
->name
);
248 my_syslog(LOG_INFO
, _("inotify, new or changed file %s"), path
);
250 if (ah
->flags
& AH_HOSTS
)
252 read_hostsfile(path
, ah
->index
, 0, NULL
, 0);
254 if (daemon
->dhcp
|| daemon
->doing_dhcp6
)
256 /* Propogate the consequences of loading a new dhcp-host */
257 dhcp_update_configs(daemon
->dhcp_conf
);
258 lease_update_from_configs();
259 lease_update_file(now
);
265 else if (ah
->flags
& AH_DHCP_HST
)
267 if (option_read_dynfile(path
, AH_DHCP_HST
))
269 /* Propogate the consequences of loading a new dhcp-host */
270 dhcp_update_configs(daemon
->dhcp_conf
);
271 lease_update_from_configs();
272 lease_update_file(now
);
276 else if (ah
->flags
& AH_DHCP_OPT
)
277 option_read_dynfile(path
, AH_DHCP_OPT
);