]> git.ipfire.org Git - people/ms/dnsmasq.git/blame - src/inotify.c
Expand inotify code to dhcp-hostsdir, dhcp-optsdir and hostsdir.
[people/ms/dnsmasq.git] / src / inotify.c
CommitLineData
193de4ab
SK
1/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
2
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"
0491805d 18#ifdef HAVE_INOTIFY
193de4ab 19
d310ab7e
MA
20#include <sys/inotify.h>
21
193de4ab
SK
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.
25
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.
29
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.
33*/
34
35static char *inotify_buffer;
36#define INOTIFY_SZ (sizeof(struct inotify_event) + NAME_MAX + 1)
37
38void inotify_dnsmasq_init()
39{
40 struct resolvc *res;
41
42 inotify_buffer = safe_malloc(INOTIFY_SZ);
857973e6
SK
43 daemon->inotifyfd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
44
193de4ab
SK
45 if (daemon->inotifyfd == -1)
46 die(_("failed to create inotify: %s"), NULL, EC_MISC);
857973e6 47
193de4ab
SK
48 for (res = daemon->resolv_files; res; res = res->next)
49 {
857973e6 50 char *d = NULL, *path;
193de4ab 51
857973e6
SK
52 if (!(path = realpath(res->name, NULL)))
53 {
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);
61 }
193de4ab 62
857973e6
SK
63 if ((d = strrchr(path, '/')))
64 {
65 *d = 0; /* make path just directory */
66 res->wd = inotify_add_watch(daemon->inotifyfd, path, IN_CLOSE_WRITE | IN_MOVED_TO);
5f4dc5c6 67
857973e6
SK
68 res->file = d+1; /* pointer to filename */
69 *d = '/';
70
71 if (res->wd == -1 && errno == ENOENT)
72 die(_("directory %s for resolv-file is missing, cannot poll"), res->name, EC_MISC);
73
74 if (res->wd == -1)
75 die(_("failed to create inotify for %s: %s"), res->name, EC_MISC);
76 }
193de4ab
SK
77 }
78}
79
70d1873d
SK
80
81/* initialisation for dynamic-dir. Set inotify watch for each directory, and read pre-existing files */
82void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revhashsz)
193de4ab 83{
70d1873d 84 struct hostsfile *ah;
193de4ab 85
70d1873d 86 for (ah = daemon->dynamic_dirs; ah; ah = ah->next)
193de4ab 87 {
70d1873d
SK
88 DIR *dir_stream = NULL;
89 struct dirent *ent;
90 struct stat buf;
91
92 if (!(ah->flags & flag))
93 continue;
94
95 if (stat(ah->fname, &buf) == -1 || !(S_ISDIR(buf.st_mode)))
193de4ab 96 {
70d1873d
SK
97 my_syslog(LOG_ERR, _("bad dynamic directory %s: %s"),
98 ah->fname, strerror(errno));
99 continue;
193de4ab 100 }
70d1873d 101
5f4dc5c6
SK
102 if (!(ah->flags & AH_WD_DONE))
103 {
104 ah->wd = inotify_add_watch(daemon->inotifyfd, ah->fname, IN_CLOSE_WRITE | IN_MOVED_TO);
105 ah->flags |= AH_WD_DONE;
106 }
107 /* Read contents of dir _after_ calling add_watch, in the ho[e of avoiding
108 a race which misses files being added as we start */
109 if (ah->wd == -1 || !(dir_stream = opendir(ah->fname)))
110 {
70d1873d
SK
111 my_syslog(LOG_ERR, _("failed to create inotify for %s: %s"),
112 ah->fname, strerror(errno));
5f4dc5c6
SK
113 continue;
114 }
115
116 while ((ent = readdir(dir_stream)))
117 {
118 size_t lendir = strlen(ah->fname);
119 size_t lenfile = strlen(ent->d_name);
120 char *path;
121
122 /* ignore emacs backups and dotfiles */
123 if (lenfile == 0 ||
124 ent->d_name[lenfile - 1] == '~' ||
125 (ent->d_name[0] == '#' && ent->d_name[lenfile - 1] == '#') ||
126 ent->d_name[0] == '.')
127 continue;
128
129 if ((path = whine_malloc(lendir + lenfile + 2)))
130 {
131 strcpy(path, ah->fname);
132 strcat(path, "/");
133 strcat(path, ent->d_name);
134
135 /* ignore non-regular files */
136 if (stat(path, &buf) != -1 && S_ISREG(buf.st_mode))
70d1873d
SK
137 {
138 if (ah->flags & AH_HOSTS)
139 total_size = read_hostsfile(path, ah->index, total_size, rhash, revhashsz);
140#ifdef HAVE_DHCP
141 else if (ah->flags & (AH_DHCP_HST | AH_DHCP_OPT))
142 option_read_dynfile(path, ah->flags);
143#endif
144 }
145
5f4dc5c6
SK
146 free(path);
147 }
148 }
149 }
150}
151
70d1873d 152int inotify_check(time_t now)
5f4dc5c6 153{
70d1873d 154 int hit = 0;
5f4dc5c6
SK
155 struct hostsfile *ah;
156
70d1873d
SK
157 while (1)
158 {
159 int rc;
160 char *p;
161 struct resolvc *res;
162 struct inotify_event *in;
163
164 while ((rc = read(daemon->inotifyfd, inotify_buffer, INOTIFY_SZ)) == -1 && errno == EINTR);
165
166 if (rc <= 0)
167 break;
168
169 for (p = inotify_buffer; rc - (p - inotify_buffer) >= (int)sizeof(struct inotify_event); p += sizeof(struct inotify_event) + in->len)
170 {
171 in = (struct inotify_event*)p;
172
173 for (res = daemon->resolv_files; res; res = res->next)
174 if (res->wd == in->wd && in->len != 0 && strcmp(res->file, in->name) == 0)
175 hit = 1;
176
177 /* ignore emacs backups and dotfiles */
178 if (in->len == 0 ||
179 in->name[in->len - 1] == '~' ||
180 (in->name[0] == '#' && in->name[in->len - 1] == '#') ||
181 in->name[0] == '.')
182 continue;
183
184 for (ah = daemon->dynamic_dirs; ah; ah = ah->next)
185 if (ah->wd == in->wd)
5f4dc5c6 186 {
70d1873d
SK
187 size_t lendir = strlen(ah->fname);
188 char *path;
189
190 if ((path = whine_malloc(lendir + in->len + 2)))
191 {
192 strcpy(path, ah->fname);
193 strcat(path, "/");
194 strcat(path, in->name);
195
196 if (ah->flags & AH_HOSTS)
197 read_hostsfile(path, ah->index, 0, NULL, 0);
198#ifdef HAVE_DHCP
199 else if (ah->flags & AH_DHCP_HST)
200 {
201 if (option_read_dynfile(path, AH_DHCP_HST))
202 {
203 /* Propogate the consequences of loading a new dhcp-host */
204 dhcp_update_configs(daemon->dhcp_conf);
205 lease_update_from_configs();
206 lease_update_file(now);
207 lease_update_dns(1);
208 }
209 }
210 else if (ah->flags & AH_DHCP_OPT)
211 option_read_dynfile(path, AH_DHCP_OPT);
212#endif
213
214 free(path);
215 }
5f4dc5c6 216 }
70d1873d
SK
217 }
218 }
219 return hit;
5f4dc5c6
SK
220}
221
0491805d 222#endif /* INOTIFY */
193de4ab 223