]> git.ipfire.org Git - ipfire-2.x.git/blob - src/patches/dnsmasq/0034-Add-dhcp-hostsdir-config-option.patch
dnsmasq: Import more patches from upstream
[ipfire-2.x.git] / src / patches / dnsmasq / 0034-Add-dhcp-hostsdir-config-option.patch
1 From 5f4dc5c6ca50655ab14f572c7e30815ed74cd51a Mon Sep 17 00:00:00 2001
2 From: Simon Kelley <simon@thekelleys.org.uk>
3 Date: Tue, 20 Jan 2015 20:51:02 +0000
4 Subject: [PATCH 34/78] Add --dhcp-hostsdir config option.
5
6 ---
7 CHANGELOG | 5 +++
8 man/dnsmasq.8 | 9 +++++
9 src/dnsmasq.c | 28 ++++++++++----
10 src/dnsmasq.h | 15 ++++++--
11 src/inotify.c | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
12 src/option.c | 22 +++++++++--
13 6 files changed, 177 insertions(+), 21 deletions(-)
14
15 diff --git a/CHANGELOG b/CHANGELOG
16 index bbd7e6619689..0076b557e95e 100644
17 --- a/CHANGELOG
18 +++ b/CHANGELOG
19 @@ -53,6 +53,11 @@ version 2.73
20 Cope with multiple interfaces with the same link-local
21 address. (IPv6 addresses are scoped, so this is allowed.)
22 Thanks to Cory Benfield for help with this.
23 +
24 + Add --dhcp-hostsdir. This allows addition of new host
25 + configurations to a running dnsmasq instance much more
26 + cheaply than having dnsmasq re-read all its existing
27 + configuration each time.
28
29
30 version 2.72
31 diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
32 index 5cfa355dea4a..005b5cca8d1f 100644
33 --- a/man/dnsmasq.8
34 +++ b/man/dnsmasq.8
35 @@ -977,6 +977,15 @@ is given, then read all the files contained in that directory. The advantage of
36 using this option is the same as for --dhcp-hostsfile: the
37 dhcp-optsfile will be re-read when dnsmasq receives SIGHUP. Note that
38 it is possible to encode the information in a
39 +.TP
40 +.B --dhcp-hostsdir=<path>
41 +This is exactly equivalent to dhcp-hostfile, except for the following. The path MUST be a
42 +directory, and not an individual file. Changed or new files within
43 +the directory are read automatically, without the need to send SIGHUP.
44 +If a file is deleted for changed after it has been read by dnsmasq, then the
45 +host record it contained will remain until dnsmasq recieves a SIGHUP, or
46 +is restarted; ie host records are only added dynamically.
47 +.TP
48 .B --dhcp-boot
49 flag as DHCP options, using the options names bootfile-name,
50 server-ip-address and tftp-server. This allows these to be included
51 diff --git a/src/dnsmasq.c b/src/dnsmasq.c
52 index c0c0589d4ce1..04cc98278f62 100644
53 --- a/src/dnsmasq.c
54 +++ b/src/dnsmasq.c
55 @@ -142,6 +142,9 @@ int main (int argc, char **argv)
56 set_option_bool(OPT_NOWILD);
57 reset_option_bool(OPT_CLEVERBIND);
58 }
59 +
60 + if (daemon->inotify_hosts)
61 + die(_("dhcp-hostsdir not supported on this platform"), NULL, EC_BADCONF);
62 #endif
63
64 if (option_bool(OPT_DNSSEC_VALID))
65 @@ -316,13 +319,16 @@ int main (int argc, char **argv)
66 #ifdef HAVE_DNSSEC
67 blockdata_init();
68 #endif
69 + }
70
71 #ifdef HAVE_LINUX_NETWORK
72 - if (!option_bool(OPT_NO_POLL))
73 - inotify_dnsmasq_init();
74 + if ((!option_bool(OPT_NO_POLL) && daemon->port != 0) ||
75 + daemon->dhcp || daemon->doing_dhcp6)
76 + inotify_dnsmasq_init();
77 + else
78 + daemon->inotifyfd = -1;
79 #endif
80 - }
81 -
82 +
83 if (option_bool(OPT_DBUS))
84 #ifdef HAVE_DBUS
85 {
86 @@ -745,7 +751,7 @@ int main (int argc, char **argv)
87 #endif
88
89 #ifdef HAVE_TFTP
90 - if (option_bool(OPT_TFTP))
91 + if (option_bool(OPT_TFTP))
92 {
93 #ifdef FD_SETSIZE
94 if (FD_SETSIZE < (unsigned)max_fd)
95 @@ -870,7 +876,7 @@ int main (int argc, char **argv)
96 #if defined(HAVE_LINUX_NETWORK)
97 FD_SET(daemon->netlinkfd, &rset);
98 bump_maxfd(daemon->netlinkfd, &maxfd);
99 - if (daemon->port != 0 && !option_bool(OPT_NO_POLL))
100 + if (daemon->inotifyfd != -1)
101 {
102 FD_SET(daemon->inotifyfd, &rset);
103 bump_maxfd(daemon->inotifyfd, &maxfd);
104 @@ -943,8 +949,11 @@ int main (int argc, char **argv)
105 #endif
106
107 #ifdef HAVE_LINUX_NETWORK
108 - if (daemon->port != 0 && !option_bool(OPT_NO_POLL) && FD_ISSET(daemon->inotifyfd, &rset) && inotify_check())
109 - poll_resolv(1, 1, now);
110 + if (daemon->inotifyfd != -1 && FD_ISSET(daemon->inotifyfd, &rset) && inotify_check(now))
111 + {
112 + if (daemon->port != 0 && !option_bool(OPT_NO_POLL))
113 + poll_resolv(1, 1, now);
114 + }
115 #else
116 /* Check for changes to resolv files once per second max. */
117 /* Don't go silent for long periods if the clock goes backwards. */
118 @@ -1385,6 +1394,9 @@ void clear_cache_and_reload(time_t now)
119 if (option_bool(OPT_ETHERS))
120 dhcp_read_ethers();
121 reread_dhcp();
122 +#ifdef HAVE_LINUX_NETWORK
123 + set_dhcp_inotify();
124 +#endif
125 dhcp_update_configs(daemon->dhcp_conf);
126 lease_update_from_configs();
127 lease_update_file(now);
128 diff --git a/src/dnsmasq.h b/src/dnsmasq.h
129 index f8275e3ac479..d841fdc064ad 100644
130 --- a/src/dnsmasq.h
131 +++ b/src/dnsmasq.h
132 @@ -550,13 +550,17 @@ struct resolvc {
133 #endif
134 };
135
136 -/* adn-hosts parms from command-line (also dhcp-hostsfile and dhcp-optsfile */
137 +/* adn-hosts parms from command-line (also dhcp-hostsfile and dhcp-optsfile and dhcp-hostsdir*/
138 #define AH_DIR 1
139 #define AH_INACTIVE 2
140 +#define AH_WD_DONE 4
141 struct hostsfile {
142 struct hostsfile *next;
143 int flags;
144 char *fname;
145 +#ifdef HAVE_LINUX_NETWORK
146 + int wd; /* inotify watch descriptor */
147 +#endif
148 unsigned int index; /* matches to cache entries for logging */
149 };
150
151 @@ -961,7 +965,7 @@ extern struct daemon {
152 int doing_ra, doing_dhcp6;
153 struct dhcp_netid_list *dhcp_ignore, *dhcp_ignore_names, *dhcp_gen_names;
154 struct dhcp_netid_list *force_broadcast, *bootp_dynamic;
155 - struct hostsfile *dhcp_hosts_file, *dhcp_opts_file;
156 + struct hostsfile *dhcp_hosts_file, *dhcp_opts_file, *inotify_hosts;
157 int dhcp_max, tftp_max;
158 int dhcp_server_port, dhcp_client_port;
159 int start_tftp_port, end_tftp_port;
160 @@ -1197,7 +1201,7 @@ void reset_option_bool(unsigned int opt);
161 struct hostsfile *expand_filelist(struct hostsfile *list);
162 char *parse_server(char *arg, union mysockaddr *addr,
163 union mysockaddr *source_addr, char *interface, int *flags);
164 -
165 +int option_read_hostsfile(char *file);
166 /* forward.c */
167 void reply_query(int fd, int family, time_t now);
168 void receive_query(struct listener *listen, time_t now);
169 @@ -1486,5 +1490,8 @@ int detect_loop(char *query, int type);
170 /* inotify.c */
171 #ifdef HAVE_LINUX_NETWORK
172 void inotify_dnsmasq_init();
173 -int inotify_check(void);
174 +int inotify_check(time_t now);
175 +# ifdef HAVE_DHCP
176 +void set_dhcp_inotify(void);
177 +# endif
178 #endif
179 diff --git a/src/inotify.c b/src/inotify.c
180 index 83730008c11b..52a30d7f44db 100644
181 --- a/src/inotify.c
182 +++ b/src/inotify.c
183 @@ -19,6 +19,11 @@
184
185 #include <sys/inotify.h>
186
187 +#ifdef HAVE_DHCP
188 +static void check_for_dhcp_inotify(struct inotify_event *in, time_t now);
189 +#endif
190 +
191 +
192 /* the strategy is to set a inotify on the directories containing
193 resolv files, for any files in the directory which are close-write
194 or moved into the directory.
195 @@ -40,8 +45,6 @@ void inotify_dnsmasq_init()
196 struct resolvc *res;
197
198 inotify_buffer = safe_malloc(INOTIFY_SZ);
199 -
200 -
201 daemon->inotifyfd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
202
203 if (daemon->inotifyfd == -1)
204 @@ -66,6 +69,7 @@ void inotify_dnsmasq_init()
205 {
206 *d = 0; /* make path just directory */
207 res->wd = inotify_add_watch(daemon->inotifyfd, path, IN_CLOSE_WRITE | IN_MOVED_TO);
208 +
209 res->file = d+1; /* pointer to filename */
210 *d = '/';
211
212 @@ -78,7 +82,7 @@ void inotify_dnsmasq_init()
213 }
214 }
215
216 -int inotify_check(void)
217 +int inotify_check(time_t now)
218 {
219 int hit = 0;
220
221 @@ -101,13 +105,116 @@ int inotify_check(void)
222 for (res = daemon->resolv_files; res; res = res->next)
223 if (res->wd == in->wd && in->len != 0 && strcmp(res->file, in->name) == 0)
224 hit = 1;
225 +
226 +#ifdef HAVE_DHCP
227 + if (daemon->dhcp || daemon->doing_dhcp6)
228 + check_for_dhcp_inotify(in, now);
229 +#endif
230 }
231 }
232 -
233 return hit;
234 }
235
236 -#endif
237 +#ifdef HAVE_DHCP
238 +/* initialisation for dhcp-hostdir. Set inotify watch for each directory, and read pre-existing files */
239 +void set_dhcp_inotify(void)
240 +{
241 + struct hostsfile *ah;
242
243 -
244 + for (ah = daemon->inotify_hosts; ah; ah = ah->next)
245 + {
246 + DIR *dir_stream = NULL;
247 + struct dirent *ent;
248 + struct stat buf;
249 +
250 + if (stat(ah->fname, &buf) == -1 || !(S_ISDIR(buf.st_mode)))
251 + {
252 + my_syslog(LOG_ERR, _("bad directory in dhcp-hostsdir %s"), ah->fname);
253 + continue;
254 + }
255 +
256 + if (!(ah->flags & AH_WD_DONE))
257 + {
258 + ah->wd = inotify_add_watch(daemon->inotifyfd, ah->fname, IN_CLOSE_WRITE | IN_MOVED_TO);
259 + ah->flags |= AH_WD_DONE;
260 + }
261 + /* Read contents of dir _after_ calling add_watch, in the ho[e of avoiding
262 + a race which misses files being added as we start */
263 + if (ah->wd == -1 || !(dir_stream = opendir(ah->fname)))
264 + {
265 + my_syslog(LOG_ERR, _("failed to create inotify for %s"), ah->fname);
266 + continue;
267 + }
268 +
269 + while ((ent = readdir(dir_stream)))
270 + {
271 + size_t lendir = strlen(ah->fname);
272 + size_t lenfile = strlen(ent->d_name);
273 + char *path;
274 +
275 + /* ignore emacs backups and dotfiles */
276 + if (lenfile == 0 ||
277 + ent->d_name[lenfile - 1] == '~' ||
278 + (ent->d_name[0] == '#' && ent->d_name[lenfile - 1] == '#') ||
279 + ent->d_name[0] == '.')
280 + continue;
281 +
282 + if ((path = whine_malloc(lendir + lenfile + 2)))
283 + {
284 + strcpy(path, ah->fname);
285 + strcat(path, "/");
286 + strcat(path, ent->d_name);
287 +
288 + /* ignore non-regular files */
289 + if (stat(path, &buf) != -1 && S_ISREG(buf.st_mode))
290 + option_read_hostsfile(path);
291 +
292 + free(path);
293 + }
294 + }
295 + }
296 +}
297 +
298 +static void check_for_dhcp_inotify(struct inotify_event *in, time_t now)
299 +{
300 + struct hostsfile *ah;
301 +
302 + /* ignore emacs backups and dotfiles */
303 + if (in->len == 0 ||
304 + in->name[in->len - 1] == '~' ||
305 + (in->name[0] == '#' && in->name[in->len - 1] == '#') ||
306 + in->name[0] == '.')
307 + return;
308 +
309 + for (ah = daemon->inotify_hosts; ah; ah = ah->next)
310 + if (ah->wd == in->wd)
311 + {
312 + size_t lendir = strlen(ah->fname);
313 + char *path;
314 +
315 + if ((path = whine_malloc(lendir + in->len + 2)))
316 + {
317 + strcpy(path, ah->fname);
318 + strcat(path, "/");
319 + strcat(path, in->name);
320 +
321 + if (option_read_hostsfile(path))
322 + {
323 + /* Propogate the consequences of loading a new dhcp-host */
324 + dhcp_update_configs(daemon->dhcp_conf);
325 + lease_update_from_configs();
326 + lease_update_file(now);
327 + lease_update_dns(1);
328 + }
329 +
330 + free(path);
331 + }
332 +
333 + return;
334 + }
335 +}
336 +
337 +#endif /* DHCP */
338 +
339 +#endif /* LINUX_NETWORK */
340
341 diff --git a/src/option.c b/src/option.c
342 index 8b994098cc9f..22e11c37d374 100644
343 --- a/src/option.c
344 +++ b/src/option.c
345 @@ -149,7 +149,7 @@ struct myoption {
346 #define LOPT_LOOP_DETECT 337
347 #define LOPT_IGNORE_ADDR 338
348 #define LOPT_MINCTTL 339
349 -
350 +#define LOPT_DHCP_INOTIFY 340
351
352 #ifdef HAVE_GETOPT_LONG
353 static const struct option opts[] =
354 @@ -248,6 +248,7 @@ static const struct myoption opts[] =
355 { "interface-name", 1, 0, LOPT_INTNAME },
356 { "dhcp-hostsfile", 1, 0, LOPT_DHCP_HOST },
357 { "dhcp-optsfile", 1, 0, LOPT_DHCP_OPTS },
358 + { "dhcp-hostsdir", 1, 0, LOPT_DHCP_INOTIFY },
359 { "dhcp-no-override", 0, 0, LOPT_OVERRIDE },
360 { "tftp-port-range", 1, 0, LOPT_TFTPPORTS },
361 { "stop-dns-rebind", 0, 0, LOPT_REBIND },
362 @@ -336,6 +337,7 @@ static struct {
363 { 'G', ARG_DUP, "<hostspec>", gettext_noop("Set address or hostname for a specified machine."), NULL },
364 { LOPT_DHCP_HOST, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from file."), NULL },
365 { LOPT_DHCP_OPTS, ARG_DUP, "<path>", gettext_noop("Read DHCP option specs from file."), NULL },
366 + { LOPT_DHCP_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from a directory."), NULL },
367 { LOPT_TAG_IF, ARG_DUP, "tag-expression", gettext_noop("Evaluate conditional tag expression."), NULL },
368 { 'h', OPT_NO_HOSTS, NULL, gettext_noop("Do NOT load %s file."), HOSTSFILE },
369 { 'H', ARG_DUP, "<path>", gettext_noop("Specify a hosts file to be read in addition to %s."), HOSTSFILE },
370 @@ -1710,8 +1712,9 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
371 break;
372 #endif /* HAVE_DHCP */
373
374 - case LOPT_DHCP_HOST: /* --dhcp-hostfile */
375 + case LOPT_DHCP_HOST: /* --dhcp-hostsfile */
376 case LOPT_DHCP_OPTS: /* --dhcp-optsfile */
377 + case LOPT_DHCP_INOTIFY: /* dhcp-hostsdir */
378 case 'H': /* --addn-hosts */
379 {
380 struct hostsfile *new = opt_malloc(sizeof(struct hostsfile));
381 @@ -1734,6 +1737,12 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
382 new->next = daemon->dhcp_opts_file;
383 daemon->dhcp_opts_file = new;
384 }
385 + else if (option == LOPT_DHCP_INOTIFY)
386 + {
387 + new->next = daemon->inotify_hosts;
388 + daemon->inotify_hosts = new;
389 + }
390 +
391 break;
392 }
393
394 @@ -4042,6 +4051,13 @@ static void read_file(char *file, FILE *f, int hard_opt)
395 fclose(f);
396 }
397
398 +#ifdef HAVE_DHCP
399 +int option_read_hostsfile(char *file)
400 +{
401 + return one_file(file, LOPT_BANK);
402 +}
403 +#endif
404 +
405 static int one_file(char *file, int hard_opt)
406 {
407 FILE *f;
408 @@ -4139,7 +4155,7 @@ struct hostsfile *expand_filelist(struct hostsfile *list)
409
410 /* don't read this as a file */
411 ah->flags |= AH_INACTIVE;
412 -
413 +
414 if (!(dir_stream = opendir(ah->fname)))
415 my_syslog(LOG_ERR, _("cannot access directory %s: %s"),
416 ah->fname, strerror(errno));
417 --
418 2.1.0
419