]>
Commit | Line | Data |
---|---|---|
6644c1c7 MT |
1 | From 193de4abf59e49c6b70d54cfe9720fcb95ca2f71 Mon Sep 17 00:00:00 2001 |
2 | From: Simon Kelley <simon@thekelleys.org.uk> | |
3 | Date: Wed, 10 Dec 2014 17:32:16 +0000 | |
efbd3a9a | 4 | Subject: [PATCH 09/98] Use inotify instead of polling on Linux. |
6644c1c7 MT |
5 | |
6 | This should solve problems people are seeing when a file changes | |
7 | twice within a second and thus is missed for polling. | |
8 | --- | |
9 | Makefile | 2 +- | |
10 | bld/Android.mk | 2 +- | |
11 | src/dnsmasq.c | 25 ++++++++++++-- | |
12 | src/dnsmasq.h | 11 ++++++- | |
13 | src/inotify.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | |
14 | 5 files changed, 137 insertions(+), 5 deletions(-) | |
15 | create mode 100644 src/inotify.c | |
16 | ||
17 | diff --git a/Makefile b/Makefile | |
18 | index 58a7975f60b5..c340f1c7b59a 100644 | |
19 | --- a/Makefile | |
20 | +++ b/Makefile | |
21 | @@ -69,7 +69,7 @@ objs = cache.o rfc1035.o util.o option.o forward.o network.o \ | |
22 | dnsmasq.o dhcp.o lease.o rfc2131.o netlink.o dbus.o bpf.o \ | |
23 | helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o \ | |
24 | dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o \ | |
25 | - domain.o dnssec.o blockdata.o tables.o loop.o | |
26 | + domain.o dnssec.o blockdata.o tables.o loop.o inotify.o | |
27 | ||
28 | hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \ | |
29 | dns-protocol.h radv-protocol.h ip6addr.h | |
30 | diff --git a/bld/Android.mk b/bld/Android.mk | |
31 | index d855094eb264..d627796e8edc 100644 | |
32 | --- a/bld/Android.mk | |
33 | +++ b/bld/Android.mk | |
34 | @@ -10,7 +10,7 @@ LOCAL_SRC_FILES := bpf.c cache.c dbus.c dhcp.c dnsmasq.c \ | |
35 | dhcp6.c rfc3315.c dhcp-common.c outpacket.c \ | |
36 | radv.c slaac.c auth.c ipset.c domain.c \ | |
37 | dnssec.c dnssec-openssl.c blockdata.c tables.c \ | |
38 | - loop.c | |
39 | + loop.c inotify.c | |
40 | ||
41 | LOCAL_MODULE := dnsmasq | |
42 | ||
43 | diff --git a/src/dnsmasq.c b/src/dnsmasq.c | |
44 | index f4a89fc38183..bf2e25a55780 100644 | |
45 | --- a/src/dnsmasq.c | |
46 | +++ b/src/dnsmasq.c | |
47 | @@ -315,9 +315,15 @@ int main (int argc, char **argv) | |
48 | if (daemon->port != 0) | |
49 | { | |
50 | cache_init(); | |
51 | + | |
52 | #ifdef HAVE_DNSSEC | |
53 | blockdata_init(); | |
54 | #endif | |
55 | + | |
56 | +#ifdef HAVE_LINUX_NETWORK | |
57 | + if (!option_bool(OPT_NO_POLL)) | |
58 | + inotify_dnsmasq_init(); | |
59 | +#endif | |
60 | } | |
61 | ||
62 | if (option_bool(OPT_DBUS)) | |
63 | @@ -793,6 +799,11 @@ int main (int argc, char **argv) | |
64 | ||
65 | pid = getpid(); | |
66 | ||
67 | +#ifdef HAVE_LINUX_NETWORK | |
68 | + /* Using inotify, have to select a resolv file at startup */ | |
69 | + poll_resolv(1, 0, now); | |
70 | +#endif | |
71 | + | |
72 | while (1) | |
73 | { | |
74 | int maxfd = -1; | |
75 | @@ -862,11 +873,16 @@ int main (int argc, char **argv) | |
76 | #if defined(HAVE_LINUX_NETWORK) | |
77 | FD_SET(daemon->netlinkfd, &rset); | |
78 | bump_maxfd(daemon->netlinkfd, &maxfd); | |
79 | + if (daemon->port != 0 && !option_bool(OPT_NO_POLL)) | |
80 | + { | |
81 | + FD_SET(daemon->inotifyfd, &rset); | |
82 | + bump_maxfd(daemon->inotifyfd, &maxfd); | |
83 | + } | |
84 | #elif defined(HAVE_BSD_NETWORK) | |
85 | FD_SET(daemon->routefd, &rset); | |
86 | bump_maxfd(daemon->routefd, &maxfd); | |
87 | #endif | |
88 | - | |
89 | + | |
90 | FD_SET(piperead, &rset); | |
91 | bump_maxfd(piperead, &maxfd); | |
92 | ||
93 | @@ -929,6 +945,10 @@ int main (int argc, char **argv) | |
94 | route_sock(); | |
95 | #endif | |
96 | ||
97 | +#ifdef HAVE_LINUX_NETWORK | |
98 | + if (daemon->port != 0 && !option_bool(OPT_NO_POLL) && FD_ISSET(daemon->inotifyfd, &rset) && inotify_check()) | |
99 | + poll_resolv(1, 1, now); | |
100 | +#else | |
101 | /* Check for changes to resolv files once per second max. */ | |
102 | /* Don't go silent for long periods if the clock goes backwards. */ | |
103 | if (daemon->last_resolv == 0 || | |
104 | @@ -941,7 +961,8 @@ int main (int argc, char **argv) | |
105 | poll_resolv(0, daemon->last_resolv != 0, now); | |
106 | daemon->last_resolv = now; | |
107 | } | |
108 | - | |
109 | +#endif | |
110 | + | |
111 | if (FD_ISSET(piperead, &rset)) | |
112 | async_event(piperead, now); | |
113 | ||
114 | diff --git a/src/dnsmasq.h b/src/dnsmasq.h | |
115 | index e74b15a5459a..ebb6b957812f 100644 | |
116 | --- a/src/dnsmasq.h | |
117 | +++ b/src/dnsmasq.h | |
118 | @@ -541,6 +541,10 @@ struct resolvc { | |
119 | int is_default, logged; | |
120 | time_t mtime; | |
121 | char *name; | |
122 | +#ifdef HAVE_LINUX_NETWORK | |
123 | + int wd; /* inotify watch descriptor */ | |
124 | + char *file; /* pointer to file part if path */ | |
125 | +#endif | |
126 | }; | |
127 | ||
128 | /* adn-hosts parms from command-line (also dhcp-hostsfile and dhcp-optsfile */ | |
129 | @@ -998,7 +1002,7 @@ extern struct daemon { | |
130 | /* DHCP state */ | |
131 | int dhcpfd, helperfd, pxefd; | |
132 | #if defined(HAVE_LINUX_NETWORK) | |
133 | - int netlinkfd; | |
134 | + int netlinkfd, inotifyfd; | |
135 | #elif defined(HAVE_BSD_NETWORK) | |
136 | int dhcp_raw_fd, dhcp_icmp_fd, routefd; | |
137 | #endif | |
138 | @@ -1469,3 +1473,8 @@ void loop_send_probes(); | |
139 | int detect_loop(char *query, int type); | |
140 | #endif | |
141 | ||
142 | +/* inotify.c */ | |
143 | +#ifdef HAVE_LINUX_NETWORK | |
144 | +void inotify_dnsmasq_init(); | |
145 | +int inotify_check(void); | |
146 | +#endif | |
147 | diff --git a/src/inotify.c b/src/inotify.c | |
148 | new file mode 100644 | |
149 | index 000000000000..a0223443d6b6 | |
150 | --- /dev/null | |
151 | +++ b/src/inotify.c | |
152 | @@ -0,0 +1,102 @@ | |
153 | +/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley | |
154 | + | |
155 | + This program is free software; you can redistribute it and/or modify | |
156 | + it under the terms of the GNU General Public License as published by | |
157 | + the Free Software Foundation; version 2 dated June, 1991, or | |
158 | + (at your option) version 3 dated 29 June, 2007. | |
159 | + | |
160 | + This program is distributed in the hope that it will be useful, | |
161 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
162 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
163 | + GNU General Public License for more details. | |
164 | + | |
165 | + You should have received a copy of the GNU General Public License | |
166 | + along with this program. If not, see <http://www.gnu.org/licenses/>. | |
167 | +*/ | |
168 | + | |
169 | +#include "dnsmasq.h" | |
170 | +#include <sys/inotify.h> | |
171 | + | |
172 | +#ifdef HAVE_LINUX_NETWORK | |
173 | + | |
174 | +/* the strategy is to set a inotify on the directories containing | |
175 | + resolv files, for any files in the directory which are close-write | |
176 | + or moved into the directory. | |
177 | + | |
178 | + When either of those happen, we look to see if the file involved | |
179 | + is actually a resolv-file, and if so, call poll-resolv with | |
180 | + the "force" argument, to ensure it's read. | |
181 | + | |
182 | + This adds one new error condition: the directories containing | |
183 | + all specified resolv-files must exist at start-up, even if the actual | |
184 | + files don't. | |
185 | +*/ | |
186 | + | |
187 | +static char *inotify_buffer; | |
188 | +#define INOTIFY_SZ (sizeof(struct inotify_event) + NAME_MAX + 1) | |
189 | + | |
190 | +void inotify_dnsmasq_init() | |
191 | +{ | |
192 | + struct resolvc *res; | |
193 | + | |
194 | + inotify_buffer = safe_malloc(INOTIFY_SZ); | |
195 | + | |
196 | + daemon->inotifyfd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); | |
197 | + | |
198 | + if (daemon->inotifyfd == -1) | |
199 | + die(_("failed to create inotify: %s"), NULL, EC_MISC); | |
200 | + | |
201 | + for (res = daemon->resolv_files; res; res = res->next) | |
202 | + { | |
203 | + char *d = strrchr(res->name, '/'); | |
204 | + | |
205 | + if (!d) | |
206 | + die(_("resolv-file %s not an absolute path"), res->name, EC_MISC); | |
207 | + | |
208 | + *d = 0; /* make ->name just directory */ | |
209 | + res->wd = inotify_add_watch(daemon->inotifyfd, res->name, IN_CLOSE_WRITE | IN_MOVED_TO); | |
210 | + res->file = d+1; /* pointer to filename */ | |
211 | + | |
212 | + if (res->wd == -1 && errno == ENOENT) | |
213 | + die(_("directory %s for resolv-file is missing, cannot poll"), res->name, EC_MISC); | |
214 | + | |
215 | + *d = '/'; /* restore name */ | |
216 | + | |
217 | + if (res->wd == -1) | |
218 | + die(_("failed to create inotify for %s: %s"), res->name, EC_MISC); | |
219 | + } | |
220 | +} | |
221 | + | |
222 | +int inotify_check(void) | |
223 | +{ | |
224 | + int hit = 0; | |
225 | + | |
226 | + while (1) | |
227 | + { | |
228 | + int rc; | |
229 | + char *p; | |
230 | + struct resolvc *res; | |
231 | + struct inotify_event *in; | |
232 | + | |
233 | + while ((rc = read(daemon->inotifyfd, inotify_buffer, INOTIFY_SZ)) == -1 && errno == EINTR); | |
234 | + | |
235 | + if (rc <= 0) | |
236 | + break; | |
237 | + | |
238 | + for (p = inotify_buffer; rc - (p - inotify_buffer) >= (int)sizeof(struct inotify_event); p += sizeof(struct inotify_event) + in->len) | |
239 | + { | |
240 | + in = (struct inotify_event*)p; | |
241 | + | |
242 | + for (res = daemon->resolv_files; res; res = res->next) | |
243 | + if (res->wd == in->wd && in->len != 0 && strcmp(res->file, in->name) == 0) | |
244 | + hit = 1; | |
245 | + } | |
246 | + } | |
247 | + | |
248 | + return hit; | |
249 | +} | |
250 | + | |
251 | +#endif | |
252 | + | |
253 | + | |
254 | + | |
255 | -- | |
256 | 2.1.0 | |
257 |