]> git.ipfire.org Git - ipfire-2.x.git/blame - 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
CommitLineData
6644c1c7
MT
1From 5f4dc5c6ca50655ab14f572c7e30815ed74cd51a Mon Sep 17 00:00:00 2001
2From: Simon Kelley <simon@thekelleys.org.uk>
3Date: Tue, 20 Jan 2015 20:51:02 +0000
d54a2ce4 4Subject: [PATCH 34/78] Add --dhcp-hostsdir config option.
6644c1c7
MT
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
15diff --git a/CHANGELOG b/CHANGELOG
16index 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
31diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
32index 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
51diff --git a/src/dnsmasq.c b/src/dnsmasq.c
52index 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);
128diff --git a/src/dnsmasq.h b/src/dnsmasq.h
129index 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
179diff --git a/src/inotify.c b/src/inotify.c
180index 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
341diff --git a/src/option.c b/src/option.c
342index 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--
4182.1.0
419