diff options
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | bld/Android.mk | 2 | ||||
-rw-r--r-- | src/dnsmasq.c | 25 | ||||
-rw-r--r-- | src/dnsmasq.h | 11 | ||||
-rw-r--r-- | src/inotify.c | 102 |
5 files changed, 137 insertions, 5 deletions
@@ -69,7 +69,7 @@ objs = cache.o rfc1035.o util.o option.o forward.o network.o \ dnsmasq.o dhcp.o lease.o rfc2131.o netlink.o dbus.o bpf.o \ helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o \ dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o \ - domain.o dnssec.o blockdata.o tables.o loop.o + domain.o dnssec.o blockdata.o tables.o loop.o inotify.o hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \ dns-protocol.h radv-protocol.h ip6addr.h diff --git a/bld/Android.mk b/bld/Android.mk index d855094..d627796 100644 --- a/bld/Android.mk +++ b/bld/Android.mk @@ -10,7 +10,7 @@ LOCAL_SRC_FILES := bpf.c cache.c dbus.c dhcp.c dnsmasq.c \ dhcp6.c rfc3315.c dhcp-common.c outpacket.c \ radv.c slaac.c auth.c ipset.c domain.c \ dnssec.c dnssec-openssl.c blockdata.c tables.c \ - loop.c + loop.c inotify.c LOCAL_MODULE := dnsmasq diff --git a/src/dnsmasq.c b/src/dnsmasq.c index f4a89fc..bf2e25a 100644 --- a/src/dnsmasq.c +++ b/src/dnsmasq.c @@ -315,9 +315,15 @@ int main (int argc, char **argv) if (daemon->port != 0) { cache_init(); + #ifdef HAVE_DNSSEC blockdata_init(); #endif + +#ifdef HAVE_LINUX_NETWORK + if (!option_bool(OPT_NO_POLL)) + inotify_dnsmasq_init(); +#endif } if (option_bool(OPT_DBUS)) @@ -793,6 +799,11 @@ int main (int argc, char **argv) pid = getpid(); +#ifdef HAVE_LINUX_NETWORK + /* Using inotify, have to select a resolv file at startup */ + poll_resolv(1, 0, now); +#endif + while (1) { int maxfd = -1; @@ -862,11 +873,16 @@ int main (int argc, char **argv) #if defined(HAVE_LINUX_NETWORK) FD_SET(daemon->netlinkfd, &rset); bump_maxfd(daemon->netlinkfd, &maxfd); + if (daemon->port != 0 && !option_bool(OPT_NO_POLL)) + { + FD_SET(daemon->inotifyfd, &rset); + bump_maxfd(daemon->inotifyfd, &maxfd); + } #elif defined(HAVE_BSD_NETWORK) FD_SET(daemon->routefd, &rset); bump_maxfd(daemon->routefd, &maxfd); #endif - + FD_SET(piperead, &rset); bump_maxfd(piperead, &maxfd); @@ -929,6 +945,10 @@ int main (int argc, char **argv) route_sock(); #endif +#ifdef HAVE_LINUX_NETWORK + if (daemon->port != 0 && !option_bool(OPT_NO_POLL) && FD_ISSET(daemon->inotifyfd, &rset) && inotify_check()) + poll_resolv(1, 1, now); +#else /* Check for changes to resolv files once per second max. */ /* Don't go silent for long periods if the clock goes backwards. */ if (daemon->last_resolv == 0 || @@ -941,7 +961,8 @@ int main (int argc, char **argv) poll_resolv(0, daemon->last_resolv != 0, now); daemon->last_resolv = now; } - +#endif + if (FD_ISSET(piperead, &rset)) async_event(piperead, now); diff --git a/src/dnsmasq.h b/src/dnsmasq.h index e74b15a..ebb6b95 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -541,6 +541,10 @@ struct resolvc { int is_default, logged; time_t mtime; char *name; +#ifdef HAVE_LINUX_NETWORK + int wd; /* inotify watch descriptor */ + char *file; /* pointer to file part if path */ +#endif }; /* adn-hosts parms from command-line (also dhcp-hostsfile and dhcp-optsfile */ @@ -998,7 +1002,7 @@ extern struct daemon { /* DHCP state */ int dhcpfd, helperfd, pxefd; #if defined(HAVE_LINUX_NETWORK) - int netlinkfd; + int netlinkfd, inotifyfd; #elif defined(HAVE_BSD_NETWORK) int dhcp_raw_fd, dhcp_icmp_fd, routefd; #endif @@ -1469,3 +1473,8 @@ void loop_send_probes(); int detect_loop(char *query, int type); #endif +/* inotify.c */ +#ifdef HAVE_LINUX_NETWORK +void inotify_dnsmasq_init(); +int inotify_check(void); +#endif diff --git a/src/inotify.c b/src/inotify.c new file mode 100644 index 0000000..a022344 --- /dev/null +++ b/src/inotify.c @@ -0,0 +1,102 @@ +/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 dated June, 1991, or + (at your option) version 3 dated 29 June, 2007. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "dnsmasq.h" +#include <sys/inotify.h> + +#ifdef HAVE_LINUX_NETWORK + +/* the strategy is to set a inotify on the directories containing + resolv files, for any files in the directory which are close-write + or moved into the directory. + + When either of those happen, we look to see if the file involved + is actually a resolv-file, and if so, call poll-resolv with + the "force" argument, to ensure it's read. + + This adds one new error condition: the directories containing + all specified resolv-files must exist at start-up, even if the actual + files don't. +*/ + +static char *inotify_buffer; +#define INOTIFY_SZ (sizeof(struct inotify_event) + NAME_MAX + 1) + +void inotify_dnsmasq_init() +{ + struct resolvc *res; + + inotify_buffer = safe_malloc(INOTIFY_SZ); + + daemon->inotifyfd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); + + if (daemon->inotifyfd == -1) + die(_("failed to create inotify: %s"), NULL, EC_MISC); + + for (res = daemon->resolv_files; res; res = res->next) + { + char *d = strrchr(res->name, '/'); + + if (!d) + die(_("resolv-file %s not an absolute path"), res->name, EC_MISC); + + *d = 0; /* make ->name just directory */ + res->wd = inotify_add_watch(daemon->inotifyfd, res->name, IN_CLOSE_WRITE | IN_MOVED_TO); + res->file = d+1; /* pointer to filename */ + + if (res->wd == -1 && errno == ENOENT) + die(_("directory %s for resolv-file is missing, cannot poll"), res->name, EC_MISC); + + *d = '/'; /* restore name */ + + if (res->wd == -1) + die(_("failed to create inotify for %s: %s"), res->name, EC_MISC); + } +} + +int inotify_check(void) +{ + int hit = 0; + + while (1) + { + int rc; + char *p; + struct resolvc *res; + struct inotify_event *in; + + while ((rc = read(daemon->inotifyfd, inotify_buffer, INOTIFY_SZ)) == -1 && errno == EINTR); + + if (rc <= 0) + break; + + for (p = inotify_buffer; rc - (p - inotify_buffer) >= (int)sizeof(struct inotify_event); p += sizeof(struct inotify_event) + in->len) + { + in = (struct inotify_event*)p; + + for (res = daemon->resolv_files; res; res = res->next) + if (res->wd == in->wd && in->len != 0 && strcmp(res->file, in->name) == 0) + hit = 1; + } + } + + return hit; +} + +#endif + + + |