]>
Commit | Line | Data |
---|---|---|
bb330e25 AF |
1 | # |
2 | # Based on the following patch: | |
3 | # | |
4 | # Upstream bug: https://sourceware.org/bugzilla/show_bug.cgi?id=14906 | |
5 | # URL: https://sourceware.org/ml/libc-alpha/2015-02/msg00504.html | |
6 | # | |
7 | # 2015-02-17 Carlos O'Donell <carlos@redhat.com> | |
8 | # | |
9 | # [BZ #14906] | |
10 | # * nscd/cache.c (prune_cache): Use TRACED_FILE. Compare and update | |
11 | # traced file mtime. Use consistent log message. | |
12 | # * nscd/connections.c [HAVE_INOTIFY] (install_watches): New function. | |
13 | # (register_traced_file): Call install_watches. Always set mtime. | |
14 | # (invalidate_cache): Iterate over all trace files. Call install_watches. | |
15 | # (inotify_check_files): Don't inline. Handle watching parent | |
16 | # directories and configuration file movement in and out. | |
17 | # (handle_inotify_events): New function. | |
18 | # (main_loop_poll): Call handle_inotify_events. | |
19 | # (main_loop_epoll): Likewise. | |
20 | # * nscd/nscd.h: Define TRACED_FILE, TRACED_DIR, and PATH_MAX. | |
21 | # (struct traced_file): Use array of inotify fds. Add parent directory, | |
22 | # and basename. | |
23 | # (struct database_dyn): Remove unused file_mtime. | |
24 | # (init_traced_file): New inline function. | |
25 | # (define_traced_file): New macro. | |
26 | # * nss/nss_db/db-init.c: Use define_traced_file. | |
27 | # (_nss_db_init): Use init_traced_file. | |
28 | # * nss/nss_files/files-init.c: Use define_traced_file. | |
29 | # (_nss_files_init): Use init_traced_file. | |
30 | # | |
31 | diff -urN glibc-2.12-2-gc4ccff1.orig/misc/sys/cdefs.h glibc-2.12-2-gc4ccff1.mod1/misc/sys/cdefs.h | |
32 | --- glibc-2.12-2-gc4ccff1.orig/misc/sys/cdefs.h 2015-02-18 04:42:12.115187116 -0500 | |
33 | +++ glibc-2.12-2-gc4ccff1.mod1/misc/sys/cdefs.h 2015-02-18 04:02:03.635159090 -0500 | |
34 | @@ -362,6 +362,14 @@ | |
35 | # endif | |
36 | #endif | |
37 | ||
38 | +#if __GNUC__ >= 3 | |
39 | +# define __glibc_unlikely(cond) __builtin_expect ((cond), 0) | |
40 | +# define __glibc_likely(cond) __builtin_expect ((cond), 1) | |
41 | +#else | |
42 | +# define __glibc_unlikely(cond) (cond) | |
43 | +# define __glibc_likely(cond) (cond) | |
44 | +#endif | |
45 | + | |
46 | #include <bits/wordsize.h> | |
47 | ||
48 | #if defined __LONG_DOUBLE_MATH_OPTIONAL && defined __NO_LONG_DOUBLE_MATH | |
49 | diff -urN glibc-2.12-2-gc4ccff1.orig/nscd/cache.c glibc-2.12-2-gc4ccff1.mod1/nscd/cache.c | |
50 | --- glibc-2.12-2-gc4ccff1.orig/nscd/cache.c 2015-02-18 04:42:12.329180362 -0500 | |
51 | +++ glibc-2.12-2-gc4ccff1.mod1/nscd/cache.c 2015-02-18 04:02:03.635159090 -0500 | |
52 | @@ -266,29 +266,52 @@ | |
53 | ||
54 | /* If we check for the modification of the underlying file we invalidate | |
55 | the entries also in this case. */ | |
56 | - if (table->inotify_descr < 0 && table->check_file && now != LONG_MAX) | |
57 | + if (table->check_file && now != LONG_MAX) | |
58 | { | |
59 | - struct stat64 st; | |
60 | + struct traced_file *runp = table->traced_files; | |
61 | ||
62 | - if (stat64 (table->filename, &st) < 0) | |
63 | + while (runp != NULL) | |
64 | { | |
65 | - char buf[128]; | |
66 | - /* We cannot stat() the file, disable file checking if the | |
67 | - file does not exist. */ | |
68 | - dbg_log (_("cannot stat() file `%s': %s"), | |
69 | - table->filename, strerror_r (errno, buf, sizeof (buf))); | |
70 | - if (errno == ENOENT) | |
71 | - table->check_file = 0; | |
72 | - } | |
73 | - else | |
74 | - { | |
75 | - if (st.st_mtime != table->file_mtime) | |
76 | +#ifdef HAVE_INOTIFY | |
77 | + if (runp->inotify_descr[TRACED_FILE] == -1) | |
78 | +#endif | |
79 | { | |
80 | - /* The file changed. Invalidate all entries. */ | |
81 | - now = LONG_MAX; | |
82 | - table->file_mtime = st.st_mtime; | |
83 | + struct stat64 st; | |
84 | + | |
85 | + if (stat64 (runp->fname, &st) < 0) | |
86 | + { | |
87 | + /* Print a diagnostic that the traced file was missing. | |
88 | + We must not disable tracing since the file might return | |
89 | + shortly and we want to reload it at the next pruning. | |
90 | + Disabling tracing here would go against the configuration | |
91 | + as specified by the user via check-files. */ | |
92 | + char buf[128]; | |
93 | + dbg_log (_("checking for monitored file `%s': %s"), | |
94 | + runp->fname, strerror_r (errno, buf, sizeof (buf))); | |
95 | + } | |
96 | + else | |
97 | + { | |
98 | + /* This must be `!=` to catch cases where users turn the | |
99 | + clocks back and we still want to detect any time difference | |
100 | + in mtime. */ | |
101 | + if (st.st_mtime != runp->mtime) | |
102 | + { | |
103 | + dbg_log (_("monitored file `%s` changed (mtime)"), | |
104 | + runp->fname); | |
105 | + /* The file changed. Invalidate all entries. */ | |
106 | + now = LONG_MAX; | |
107 | + runp->mtime = st.st_mtime; | |
108 | +#ifdef HAVE_INOTIFY | |
109 | + /* Attempt to install a watch on the file. */ | |
110 | + install_watches (runp); | |
111 | +#endif | |
112 | + } | |
113 | + } | |
114 | } | |
115 | + | |
116 | + runp = runp->next; | |
117 | } | |
118 | + | |
119 | } | |
120 | ||
121 | /* We run through the table and find values which are not valid anymore. | |
122 | diff -urN glibc-2.12-2-gc4ccff1.orig/nscd/connections.c glibc-2.12-2-gc4ccff1.mod1/nscd/connections.c | |
123 | --- glibc-2.12-2-gc4ccff1.orig/nscd/connections.c 2015-02-18 04:42:12.333180236 -0500 | |
124 | +++ glibc-2.12-2-gc4ccff1.mod1/nscd/connections.c 2015-02-18 04:40:51.674726008 -0500 | |
125 | @@ -74,6 +74,25 @@ | |
126 | static void begin_drop_privileges (void); | |
127 | static void finish_drop_privileges (void); | |
128 | ||
129 | +/* Define the traced files. */ | |
130 | +#define PWD_FILENAME "/etc/passwd" | |
131 | +define_traced_file (pwd, PWD_FILENAME); | |
132 | + | |
133 | +#define GRP_FILENAME "/etc/group" | |
134 | +define_traced_file (grp, GRP_FILENAME); | |
135 | + | |
136 | +#define HST_FILENAME "/etc/hosts" | |
137 | +define_traced_file (hst, HST_FILENAME); | |
138 | + | |
139 | +#define RESOLV_FILENAME "/etc/resolv.conf" | |
140 | +define_traced_file (resolv, RESOLV_FILENAME); | |
141 | + | |
142 | +#define SERV_FILENAME "/etc/services" | |
143 | +define_traced_file (serv, SERV_FILENAME); | |
144 | + | |
145 | +#define NETGR_FILENAME "/etc/netgroup" | |
146 | +define_traced_file (netgr, NETGR_FILENAME); | |
147 | + | |
148 | /* Map request type to a string. */ | |
149 | const char *const serv2str[LASTREQ] = | |
150 | { | |
151 | @@ -115,8 +134,6 @@ | |
152 | .shared = 0, | |
153 | .max_db_size = DEFAULT_MAX_DB_SIZE, | |
154 | .suggested_module = DEFAULT_SUGGESTED_MODULE, | |
155 | - .reset_res = 0, | |
156 | - .filename = "/etc/passwd", | |
157 | .db_filename = _PATH_NSCD_PASSWD_DB, | |
158 | .disabled_iov = &pwd_iov_disabled, | |
159 | .postimeout = 3600, | |
160 | @@ -136,8 +153,6 @@ | |
161 | .shared = 0, | |
162 | .max_db_size = DEFAULT_MAX_DB_SIZE, | |
163 | .suggested_module = DEFAULT_SUGGESTED_MODULE, | |
164 | - .reset_res = 0, | |
165 | - .filename = "/etc/group", | |
166 | .db_filename = _PATH_NSCD_GROUP_DB, | |
167 | .disabled_iov = &grp_iov_disabled, | |
168 | .postimeout = 3600, | |
169 | @@ -157,8 +172,6 @@ | |
170 | .shared = 0, | |
171 | .max_db_size = DEFAULT_MAX_DB_SIZE, | |
172 | .suggested_module = DEFAULT_SUGGESTED_MODULE, | |
173 | - .reset_res = 1, | |
174 | - .filename = "/etc/hosts", | |
175 | .db_filename = _PATH_NSCD_HOSTS_DB, | |
176 | .disabled_iov = &hst_iov_disabled, | |
177 | .postimeout = 3600, | |
178 | @@ -178,8 +191,6 @@ | |
179 | .shared = 0, | |
180 | .max_db_size = DEFAULT_MAX_DB_SIZE, | |
181 | .suggested_module = DEFAULT_SUGGESTED_MODULE, | |
182 | - .reset_res = 0, | |
183 | - .filename = "/etc/services", | |
184 | .db_filename = _PATH_NSCD_SERVICES_DB, | |
185 | .disabled_iov = &serv_iov_disabled, | |
186 | .postimeout = 28800, | |
187 | @@ -199,8 +210,6 @@ | |
188 | .shared = 0, | |
189 | .max_db_size = DEFAULT_MAX_DB_SIZE, | |
190 | .suggested_module = DEFAULT_SUGGESTED_MODULE, | |
191 | - .reset_res = 0, | |
192 | - .filename = "/etc/netgroup", | |
193 | .db_filename = _PATH_NSCD_NETGROUP_DB, | |
194 | .disabled_iov = &netgroup_iov_disabled, | |
195 | .postimeout = 28800, | |
196 | @@ -863,41 +872,26 @@ | |
197 | dbs[cnt].shared = 0; | |
198 | assert (dbs[cnt].ro_fd == -1); | |
199 | } | |
200 | + } | |
201 | ||
202 | - dbs[cnt].inotify_descr = -1; | |
203 | - if (dbs[cnt].check_file) | |
204 | - { | |
205 | -#ifdef HAVE_INOTIFY | |
206 | - if (inotify_fd < 0 | |
207 | - || (dbs[cnt].inotify_descr | |
208 | - = inotify_add_watch (inotify_fd, dbs[cnt].filename, | |
209 | - IN_DELETE_SELF | IN_MODIFY)) < 0) | |
210 | - /* We cannot notice changes in the main thread. */ | |
211 | -#endif | |
212 | - { | |
213 | - /* We need the modification date of the file. */ | |
214 | - struct stat64 st; | |
215 | + /* Initialize and register the traced files. */ | |
216 | + init_traced_file (&pwd_traced_file.file, PWD_FILENAME, 0); | |
217 | + register_traced_file (pwddb, &pwd_traced_file.file); | |
218 | ||
219 | - if (stat64 (dbs[cnt].filename, &st) < 0) | |
220 | - { | |
221 | - /* We cannot stat() the file, disable file checking. */ | |
222 | - dbg_log (_("cannot stat() file `%s': %s"), | |
223 | - dbs[cnt].filename, strerror (errno)); | |
224 | - dbs[cnt].check_file = 0; | |
225 | - } | |
226 | - else | |
227 | - dbs[cnt].file_mtime = st.st_mtime; | |
228 | - } | |
229 | - } | |
230 | + init_traced_file (&grp_traced_file.file, GRP_FILENAME, 0); | |
231 | + register_traced_file (grpdb, &grp_traced_file.file); | |
232 | ||
233 | -#ifdef HAVE_INOTIFY | |
234 | - if (cnt == hstdb && inotify_fd >= -1) | |
235 | - /* We also monitor the resolver configuration file. */ | |
236 | - resolv_conf_descr = inotify_add_watch (inotify_fd, | |
237 | - _PATH_RESCONF, | |
238 | - IN_DELETE_SELF | IN_MODIFY); | |
239 | -#endif | |
240 | - } | |
241 | + init_traced_file (&hst_traced_file.file, HST_FILENAME, 0); | |
242 | + register_traced_file (hstdb, &hst_traced_file.file); | |
243 | + | |
244 | + init_traced_file (&resolv_traced_file.file, RESOLV_FILENAME, 1); | |
245 | + register_traced_file (hstdb, &resolv_traced_file.file); | |
246 | + | |
247 | + init_traced_file (&serv_traced_file.file, SERV_FILENAME, 0); | |
248 | + register_traced_file (servdb, &serv_traced_file.file); | |
249 | + | |
250 | + init_traced_file (&netgr_traced_file.file, NETGR_FILENAME, 0); | |
251 | + register_traced_file (netgrdb, &netgr_traced_file.file); | |
252 | ||
253 | /* Create the socket. */ | |
254 | #ifndef __ASSUME_SOCK_CLOEXEC | |
255 | @@ -968,6 +962,92 @@ | |
256 | finish_drop_privileges (); | |
257 | } | |
258 | ||
259 | +#ifdef HAVE_INOTIFY | |
260 | +#define TRACED_FILE_MASK (IN_DELETE_SELF | IN_CLOSE_WRITE | IN_MOVE_SELF) | |
261 | +#define TRACED_DIR_MASK (IN_DELETE_SELF | IN_CREATE | IN_MOVED_TO | IN_MOVE_SELF) | |
262 | +void | |
263 | +install_watches (struct traced_file *finfo) | |
264 | +{ | |
265 | + /* If we have inotify support use it exclusively with no fallback | |
266 | + to stat. This is a design decision to make the implementation | |
267 | + sipmler. Either we use fstat for the file name or we use inotify | |
268 | + for both the file and parent directory. */ | |
269 | + if (finfo->inotify_descr[TRACED_FILE] < 0) | |
270 | + finfo->inotify_descr[TRACED_FILE] = inotify_add_watch (inotify_fd, | |
271 | + finfo->fname, | |
272 | + TRACED_FILE_MASK); | |
273 | + if (finfo->inotify_descr[TRACED_FILE] < 0) | |
274 | + { | |
275 | + dbg_log (_("disabled inotify-based monitoring for file `%s': %s"), | |
276 | + finfo->fname, strerror (errno)); | |
277 | + return; | |
278 | + } | |
279 | + dbg_log (_("monitoring file `%s` (%d)"), | |
280 | + finfo->fname, finfo->inotify_descr[TRACED_FILE]); | |
281 | + /* Additionally listen for IN_CREATE events in the files parent | |
282 | + directory. We do this because the file to be watched might be | |
283 | + deleted and then added back again. When it is added back again | |
284 | + we must re-add the watch. We must also cover IN_MOVED_TO to | |
285 | + detect a file being moved into the directory. */ | |
286 | + if (finfo->inotify_descr[TRACED_DIR] < 0) | |
287 | + finfo->inotify_descr[TRACED_DIR] = inotify_add_watch (inotify_fd, | |
288 | + finfo->dname, | |
289 | + TRACED_DIR_MASK); | |
290 | + if (finfo->inotify_descr[TRACED_DIR] < 0) | |
291 | + { | |
292 | + dbg_log (_("disabled inotify-based monitoring for directory `%s': %s"), | |
293 | + finfo->fname, strerror (errno)); | |
294 | + return; | |
295 | + } | |
296 | + dbg_log (_("monitoring directory `%s` (%d)"), | |
297 | + finfo->dname, finfo->inotify_descr[TRACED_DIR]); | |
298 | +} | |
299 | +#endif | |
300 | + | |
301 | +/* Register the file in FINFO as a traced file for the database DBS[DBIX]. | |
302 | + | |
303 | + We support registering multiple files per database. Each call to | |
304 | + register_traced_file adds to the list of registered files. | |
305 | + | |
306 | + When we prune the database, either through timeout or a request to | |
307 | + invalidate, we will check to see if any of the registered files has changed. | |
308 | + When we accept new connections to handle a cache request we will also | |
309 | + check to see if any of the registered files has changed. | |
310 | + | |
311 | + If we have inotify support then we install an inotify fd to notify us of | |
312 | + file deletion or modification, both of which will require we invalidate | |
313 | + the cache for the database. Without inotify support we stat the file and | |
314 | + store st_mtime to determine if the file has been modified. */ | |
315 | +void | |
316 | +register_traced_file (size_t dbidx, struct traced_file *finfo) | |
317 | +{ | |
318 | + /* If the database is disabled or file checking is disabled | |
319 | + then ignore the registration. */ | |
320 | + if (! dbs[dbidx].enabled || ! dbs[dbidx].check_file) | |
321 | + return; | |
322 | + | |
323 | + if (__glibc_unlikely (debug_level > 0)) | |
324 | + dbg_log (_("monitoring file %s for database %s"), | |
325 | + finfo->fname, dbnames[dbidx]); | |
326 | + | |
327 | +#ifdef HAVE_INOTIFY | |
328 | + install_watches (finfo); | |
329 | +#endif | |
330 | + struct stat64 st; | |
331 | + if (stat64 (finfo->fname, &st) < 0) | |
332 | + { | |
333 | + /* We cannot stat() the file, disable file checking. */ | |
334 | + dbg_log (_("disabled monitoring for file `%s': %s"), | |
335 | + finfo->fname, strerror (errno)); | |
336 | + finfo->mtime = 0; | |
337 | + } | |
338 | + else | |
339 | + finfo->mtime = st.st_mtime; | |
340 | + | |
341 | + /* Queue up the file name. */ | |
342 | + finfo->next = dbs[dbidx].traced_files; | |
343 | + dbs[dbidx].traced_files = finfo; | |
344 | +} | |
345 | ||
346 | /* Close the connections. */ | |
347 | void | |
348 | @@ -986,9 +1066,25 @@ | |
349 | for (number = pwddb; number < lastdb; ++number) | |
350 | if (strcmp (key, dbnames[number]) == 0) | |
351 | { | |
352 | - if (dbs[number].reset_res) | |
353 | - res_init (); | |
354 | - | |
355 | + struct traced_file *runp = dbs[number].traced_files; | |
356 | + while (runp != NULL) | |
357 | + { | |
358 | + /* Make sure we reload from file when checking mtime. */ | |
359 | + runp->mtime = 0; | |
360 | +#ifdef HAVE_INOTIFY | |
361 | + /* During an invalidation we try to reload the traced | |
362 | + file watches. This allows the user to re-sync if | |
363 | + inotify events were lost. Similar to what we do during | |
364 | + pruning. */ | |
365 | + install_watches (runp); | |
366 | +#endif | |
367 | + if (runp->call_res_init) | |
368 | + { | |
369 | + res_init (); | |
370 | + break; | |
371 | + } | |
372 | + runp = runp->next; | |
373 | + } | |
374 | break; | |
375 | } | |
376 | ||
377 | @@ -1817,6 +1913,231 @@ | |
378 | /* Array for times a connection was accepted. */ | |
379 | static time_t *starttime; | |
380 | ||
381 | +#ifdef HAVE_INOTIFY | |
382 | +/* Inotify event for changed file. */ | |
383 | +union __inev | |
384 | +{ | |
385 | + struct inotify_event i; | |
386 | +# ifndef PATH_MAX | |
387 | +# define PATH_MAX 1024 | |
388 | +# endif | |
389 | + char buf[sizeof (struct inotify_event) + PATH_MAX]; | |
390 | +}; | |
391 | + | |
392 | +/* Returns 0 if the file is there and matches the last mtime | |
393 | + on record, otherwise -1. */ | |
394 | +int | |
395 | +check_file (struct traced_file *finfo) | |
396 | +{ | |
397 | + struct stat64 st; | |
398 | + if (stat64 (finfo->fname, &st) < 0) | |
399 | + return -1; | |
400 | + return 0; | |
401 | +} | |
402 | + | |
403 | +/* Process the inotify event in INEV. If the event matches any of the files | |
404 | + registered with a database then mark that database as requiring its cache | |
405 | + to be cleared. We indicate the cache needs clearing by setting | |
406 | + TO_CLEAR[DBCNT] to true for the matching database. */ | |
407 | +static void | |
408 | +inotify_check_files (bool *to_clear, union __inev *inev) | |
409 | +{ | |
410 | + /* Check which of the files changed. */ | |
411 | + for (size_t dbcnt = 0; dbcnt < lastdb; ++dbcnt) | |
412 | + { | |
413 | + struct traced_file *finfo = dbs[dbcnt].traced_files; | |
414 | + | |
415 | + while (finfo != NULL) | |
416 | + { | |
417 | + /* The configuration file was moved or deleted. | |
418 | + We stop watching it at that point, and reinitialize. */ | |
419 | + if (finfo->inotify_descr[TRACED_FILE] == inev->i.wd | |
420 | + && ((inev->i.mask & IN_MOVE_SELF) | |
421 | + || (inev->i.mask & IN_DELETE_SELF) | |
422 | + || (inev->i.mask & IN_IGNORED))) | |
423 | + { | |
424 | + int ret; | |
425 | + bool moved = (inev->i.mask & IN_MOVE_SELF) != 0; | |
426 | + | |
427 | + if (check_file (finfo) == 0) | |
428 | + { | |
429 | + dbg_log (_("ignored out of order inotify event for `%s`"), | |
430 | + finfo->fname); | |
431 | + return; | |
432 | + } | |
433 | + | |
434 | + dbg_log (_("monitored file `%s` was %s, removing watch"), | |
435 | + finfo->fname, moved ? "moved" : "deleted"); | |
436 | + /* File was moved out, remove the watch. Watches are | |
437 | + automatically removed when the file is deleted. */ | |
438 | + if (moved) | |
439 | + { | |
440 | + ret = inotify_rm_watch (inotify_fd, inev->i.wd); | |
441 | + if (ret < 0) | |
442 | + dbg_log (_("failed to remove file watch `%s`: %s"), | |
443 | + finfo->fname, strerror (errno)); | |
444 | + } | |
445 | + finfo->inotify_descr[TRACED_FILE] = -1; | |
446 | + to_clear[dbcnt] = true; | |
447 | + if (finfo->call_res_init) | |
448 | + res_init (); | |
449 | + return; | |
450 | + } | |
451 | + /* The configuration file was open for writing and has just closed. | |
452 | + We reset the cache and reinitialize. */ | |
453 | + if (finfo->inotify_descr[TRACED_FILE] == inev->i.wd | |
454 | + && inev->i.mask & IN_CLOSE_WRITE) | |
455 | + { | |
456 | + /* Mark cache as needing to be cleared and reinitialize. */ | |
457 | + dbg_log (_("monitored file `%s` was written to"), finfo->fname); | |
458 | + to_clear[dbcnt] = true; | |
459 | + if (finfo->call_res_init) | |
460 | + res_init (); | |
461 | + return; | |
462 | + } | |
463 | + /* The parent directory was moved or deleted. There is no coming | |
464 | + back from this. We do not track the parent of the parent, and | |
465 | + once this happens we trigger one last invalidation. You must | |
466 | + restart nscd to track subsequent changes. We track this to | |
467 | + do one last robust re-initialization and then we're done. */ | |
468 | + if (finfo->inotify_descr[TRACED_DIR] == inev->i.wd | |
469 | + && ((inev->i.mask & IN_DELETE_SELF) | |
470 | + || (inev->i.mask & IN_MOVE_SELF) | |
471 | + || (inev->i.mask & IN_IGNORED))) | |
472 | + { | |
473 | + bool moved = (inev->i.mask & IN_MOVE_SELF) != 0; | |
474 | + /* The directory watch may have already been removed | |
475 | + but we don't know so we just remove it again and | |
476 | + ignore the error. Then we remove the file watch. | |
477 | + Note: watches are automatically removed for deleted | |
478 | + files. */ | |
479 | + if (moved) | |
480 | + inotify_rm_watch (inotify_fd, inev->i.wd); | |
481 | + if (finfo->inotify_descr[TRACED_FILE] != -1) | |
482 | + { | |
483 | + dbg_log (_("monitored parent directory `%s` was %s, removing watch on `%s`"), | |
484 | + finfo->dname, moved ? "moved" : "deleted", finfo->fname); | |
485 | + if (inotify_rm_watch (inotify_fd, finfo->inotify_descr[TRACED_FILE]) < 0) | |
486 | + dbg_log (_("failed to remove file watch `%s`: %s"), | |
487 | + finfo->dname, strerror (errno)); | |
488 | + } | |
489 | + finfo->inotify_descr[TRACED_FILE] = -1; | |
490 | + finfo->inotify_descr[TRACED_DIR] = -1; | |
491 | + to_clear[dbcnt] = true; | |
492 | + if (finfo->call_res_init) | |
493 | + res_init (); | |
494 | + /* Continue to the next entry since this might be the | |
495 | + parent directory for multiple registered files and | |
496 | + we want to remove watches for all registered files. */ | |
497 | + continue; | |
498 | + } | |
499 | + /* The parent directory had a create or moved to event. */ | |
500 | + if (finfo->inotify_descr[TRACED_DIR] == inev->i.wd | |
501 | + && ((inev->i.mask & IN_MOVED_TO) | |
502 | + || (inev->i.mask & IN_CREATE)) | |
503 | + && strcmp (inev->i.name, finfo->sfname) == 0) | |
504 | + { | |
505 | + /* We detected a directory change. We look for the creation | |
506 | + of the file we are tracking or the move of the same file | |
507 | + into the directory. */ | |
508 | + int ret; | |
509 | + dbg_log (_("monitored file `%s` was %s, adding watch"), | |
510 | + finfo->fname, | |
511 | + inev->i.mask & IN_CREATE ? "created" : "moved into place"); | |
512 | + /* File was moved in or created. Regenerate the watch. */ | |
513 | + if (finfo->inotify_descr[TRACED_FILE] != -1) | |
514 | + inotify_rm_watch (inotify_fd, | |
515 | + finfo->inotify_descr[TRACED_FILE]); | |
516 | + | |
517 | + ret = inotify_add_watch (inotify_fd, | |
518 | + finfo->fname, | |
519 | + TRACED_FILE_MASK); | |
520 | + if (ret < 0) | |
521 | + dbg_log (_("failed to add file watch `%s`: %s"), | |
522 | + finfo->fname, strerror (errno)); | |
523 | + | |
524 | + finfo->inotify_descr[TRACED_FILE] = ret; | |
525 | + | |
526 | + /* The file is new or moved so mark cache as needing to | |
527 | + be cleared and reinitialize. */ | |
528 | + to_clear[dbcnt] = true; | |
529 | + if (finfo->call_res_init) | |
530 | + res_init (); | |
531 | + | |
532 | + /* Done re-adding the watch. Don't return, we may still | |
533 | + have other files in this same directory, same watch | |
534 | + descriptor, and need to process them. */ | |
535 | + } | |
536 | + /* Other events are ignored, and we move on to the next file. */ | |
537 | + finfo = finfo->next; | |
538 | + } | |
539 | + } | |
540 | +} | |
541 | + | |
542 | +/* If an entry in the array of booleans TO_CLEAR is TRUE then clear the cache | |
543 | + for the associated database, otherwise do nothing. The TO_CLEAR array must | |
544 | + have LASTDB entries. */ | |
545 | +static inline void | |
546 | +clear_db_cache (bool *to_clear) | |
547 | +{ | |
548 | + for (size_t dbcnt = 0; dbcnt < lastdb; ++dbcnt) | |
549 | + if (to_clear[dbcnt]) | |
550 | + { | |
551 | + pthread_mutex_lock (&dbs[dbcnt].prune_lock); | |
552 | + dbs[dbcnt].clear_cache = 1; | |
553 | + pthread_mutex_unlock (&dbs[dbcnt].prune_lock); | |
554 | + pthread_cond_signal (&dbs[dbcnt].prune_cond); | |
555 | + } | |
556 | +} | |
557 | + | |
558 | +int | |
559 | +handle_inotify_events (void) | |
560 | +{ | |
561 | + bool to_clear[lastdb] = { false, }; | |
562 | + union __inev inev; | |
563 | + | |
564 | + /* Read all inotify events for files registered via | |
565 | + register_traced_file(). */ | |
566 | + while (1) | |
567 | + { | |
568 | + /* Potentially read multiple events into buf. */ | |
569 | + ssize_t nb = TEMP_FAILURE_RETRY (read (inotify_fd, | |
570 | + &inev.buf, | |
571 | + sizeof (inev))); | |
572 | + if (nb < (ssize_t) sizeof (struct inotify_event)) | |
573 | + { | |
574 | + /* Not even 1 event. */ | |
575 | + if (__glibc_unlikely (nb == -1 && errno != EAGAIN)) | |
576 | + return -1; | |
577 | + /* Done reading events that are ready. */ | |
578 | + break; | |
579 | + } | |
580 | + /* Process all events. The normal inotify interface delivers | |
581 | + complete events on a read and never a partial event. */ | |
582 | + char *eptr = &inev.buf[0]; | |
583 | + ssize_t count; | |
584 | + while (1) | |
585 | + { | |
586 | + /* Check which of the files changed. */ | |
587 | + inotify_check_files (to_clear, &inev); | |
588 | + count = sizeof (struct inotify_event) + inev.i.len; | |
589 | + eptr += count; | |
590 | + nb -= count; | |
591 | + if (nb >= (ssize_t) sizeof (struct inotify_event)) | |
592 | + memcpy (&inev, eptr, nb); | |
593 | + else | |
594 | + break; | |
595 | + } | |
596 | + continue; | |
597 | + } | |
598 | + /* Actually perform the cache clearing. */ | |
599 | + clear_db_cache (to_clear); | |
600 | + return 0; | |
601 | +} | |
602 | + | |
603 | + | |
604 | +#endif | |
605 | + | |
606 | ||
607 | static void | |
608 | __attribute__ ((__noreturn__)) | |
609 | @@ -1910,66 +2231,21 @@ | |
610 | { | |
611 | if (conns[1].revents != 0) | |
612 | { | |
613 | - bool to_clear[lastdb] = { false, }; | |
614 | - union | |
615 | - { | |
616 | -# ifndef PATH_MAX | |
617 | -# define PATH_MAX 1024 | |
618 | -# endif | |
619 | - struct inotify_event i; | |
620 | - char buf[sizeof (struct inotify_event) + PATH_MAX]; | |
621 | - } inev; | |
622 | + int ret; | |
623 | + ret = handle_inotify_events (); | |
624 | ||
625 | - while (1) | |
626 | + if (ret == -1) | |
627 | { | |
628 | - ssize_t nb = TEMP_FAILURE_RETRY (read (inotify_fd, &inev, | |
629 | - sizeof (inev))); | |
630 | - if (nb < (ssize_t) sizeof (struct inotify_event)) | |
631 | - { | |
632 | - if (__builtin_expect (nb == -1 && errno != EAGAIN, | |
633 | - 0)) | |
634 | - { | |
635 | - /* Something went wrong when reading the inotify | |
636 | - data. Better disable inotify. */ | |
637 | - dbg_log (_("\ | |
638 | -disabled inotify after read error %d"), | |
639 | - errno); | |
640 | - conns[1].fd = -1; | |
641 | - firstfree = 1; | |
642 | - if (nused == 2) | |
643 | - nused = 1; | |
644 | - close (inotify_fd); | |
645 | - inotify_fd = -1; | |
646 | - } | |
647 | - break; | |
648 | - } | |
649 | - | |
650 | - /* Check which of the files changed. */ | |
651 | - for (size_t dbcnt = 0; dbcnt < lastdb; ++dbcnt) | |
652 | - if (inev.i.wd == dbs[dbcnt].inotify_descr) | |
653 | - { | |
654 | - to_clear[dbcnt] = true; | |
655 | - goto next; | |
656 | - } | |
657 | - | |
658 | - if (inev.i.wd == resolv_conf_descr) | |
659 | - { | |
660 | - res_init (); | |
661 | - to_clear[hstdb] = true; | |
662 | - } | |
663 | - next:; | |
664 | + /* Something went wrong when reading the inotify | |
665 | + data. Better disable inotify. */ | |
666 | + dbg_log (_("disabled inotify-based monitoring after read error %d"), errno); | |
667 | + conns[1].fd = -1; | |
668 | + firstfree = 1; | |
669 | + if (nused == 2) | |
670 | + nused = 1; | |
671 | + close (inotify_fd); | |
672 | + inotify_fd = -1; | |
673 | } | |
674 | - | |
675 | - /* Actually perform the cache clearing. */ | |
676 | - for (size_t dbcnt = 0; dbcnt < lastdb; ++dbcnt) | |
677 | - if (to_clear[dbcnt]) | |
678 | - { | |
679 | - pthread_mutex_lock (&dbs[dbcnt].prune_lock); | |
680 | - dbs[dbcnt].clear_cache = 1; | |
681 | - pthread_mutex_unlock (&dbs[dbcnt].prune_lock); | |
682 | - pthread_cond_signal (&dbs[dbcnt].prune_cond); | |
683 | - } | |
684 | - | |
685 | --n; | |
686 | } | |
687 | ||
688 | @@ -2112,58 +2388,18 @@ | |
689 | # ifdef HAVE_INOTIFY | |
690 | else if (revs[cnt].data.fd == inotify_fd) | |
691 | { | |
692 | - bool to_clear[lastdb] = { false, }; | |
693 | - union | |
694 | - { | |
695 | - struct inotify_event i; | |
696 | - char buf[sizeof (struct inotify_event) + PATH_MAX]; | |
697 | - } inev; | |
698 | - | |
699 | - while (1) | |
700 | + int ret; | |
701 | + ret = handle_inotify_events (); | |
702 | + if (ret == -1) | |
703 | { | |
704 | - ssize_t nb = TEMP_FAILURE_RETRY (read (inotify_fd, &inev, | |
705 | - sizeof (inev))); | |
706 | - if (nb < (ssize_t) sizeof (struct inotify_event)) | |
707 | - { | |
708 | - if (__builtin_expect (nb == -1 && errno != EAGAIN, 0)) | |
709 | - { | |
710 | - /* Something went wrong when reading the inotify | |
711 | - data. Better disable inotify. */ | |
712 | - dbg_log (_("disabled inotify after read error %d"), | |
713 | - errno); | |
714 | - (void) epoll_ctl (efd, EPOLL_CTL_DEL, inotify_fd, | |
715 | - NULL); | |
716 | - close (inotify_fd); | |
717 | - inotify_fd = -1; | |
718 | - } | |
719 | - break; | |
720 | - } | |
721 | - | |
722 | - /* Check which of the files changed. */ | |
723 | - for (size_t dbcnt = 0; dbcnt < lastdb; ++dbcnt) | |
724 | - if (inev.i.wd == dbs[dbcnt].inotify_descr) | |
725 | - { | |
726 | - to_clear[dbcnt] = true; | |
727 | - goto next; | |
728 | - } | |
729 | - | |
730 | - if (inev.i.wd == resolv_conf_descr) | |
731 | - { | |
732 | - res_init (); | |
733 | - to_clear[hstdb] = true; | |
734 | - } | |
735 | - next:; | |
736 | + /* Something went wrong when reading the inotify | |
737 | + data. Better disable inotify. */ | |
738 | + dbg_log (_("disabled inotify-based monitoring after read error %d"), errno); | |
739 | + (void) epoll_ctl (efd, EPOLL_CTL_DEL, inotify_fd, NULL); | |
740 | + close (inotify_fd); | |
741 | + inotify_fd = -1; | |
742 | + break; | |
743 | } | |
744 | - | |
745 | - /* Actually perform the cache clearing. */ | |
746 | - for (size_t dbcnt = 0; dbcnt < lastdb; ++dbcnt) | |
747 | - if (to_clear[dbcnt]) | |
748 | - { | |
749 | - pthread_mutex_lock (&dbs[dbcnt].prune_lock); | |
750 | - dbs[dbcnt].clear_cache = 1; | |
751 | - pthread_mutex_unlock (&dbs[dbcnt].prune_lock); | |
752 | - pthread_cond_signal (&dbs[dbcnt].prune_cond); | |
753 | - } | |
754 | } | |
755 | # endif | |
756 | else | |
757 | diff -urN glibc-2.12-2-gc4ccff1.orig/nscd/nscd.h glibc-2.12-2-gc4ccff1.mod1/nscd/nscd.h | |
758 | --- glibc-2.12-2-gc4ccff1.orig/nscd/nscd.h 2015-02-18 04:42:12.329180362 -0500 | |
759 | +++ glibc-2.12-2-gc4ccff1.mod1/nscd/nscd.h 2015-02-18 04:02:03.636159059 -0500 | |
760 | @@ -62,6 +62,67 @@ | |
761 | 80% of the thread stack size. */ | |
762 | #define MAX_STACK_USE ((8 * NSCD_THREAD_STACKSIZE) / 10) | |
763 | ||
764 | +/* Records the file registered per database that when changed | |
765 | + or modified requires invalidating the database. */ | |
766 | +struct traced_file | |
767 | +{ | |
768 | + /* Tracks the last modified time of the traced file. */ | |
769 | + time_t mtime; | |
770 | + /* Support multiple registered files per database. */ | |
771 | + struct traced_file *next; | |
772 | + int call_res_init; | |
773 | + /* Requires Inotify support to do anything useful. */ | |
774 | +#define TRACED_FILE 0 | |
775 | +#define TRACED_DIR 1 | |
776 | + int inotify_descr[2]; | |
777 | +# ifndef PATH_MAX | |
778 | +# define PATH_MAX 1024 | |
779 | +# endif | |
780 | + /* The parent directory is used to scan for creation/deletion. */ | |
781 | + char dname[PATH_MAX]; | |
782 | + /* Just the name of the file with no directory component. */ | |
783 | + char *sfname; | |
784 | + /* The full-path name of the registered file. */ | |
785 | + char fname[]; | |
786 | +}; | |
787 | + | |
788 | +/* Initialize a `struct traced_file`. As input we need the name | |
789 | + of the file, and if invalidation requires calling res_init. | |
790 | + If CRINIT is 1 then res_init will be called after invalidation | |
791 | + or if the traced file is changed in any way, otherwise it will | |
792 | + not. */ | |
793 | +static inline void | |
794 | +init_traced_file(struct traced_file *file, const char *fname, int crinit) | |
795 | +{ | |
796 | + char *dname; | |
797 | + file->mtime = 0; | |
798 | + file->inotify_descr[TRACED_FILE] = -1; | |
799 | + file->inotify_descr[TRACED_DIR] = -1; | |
800 | + strcpy (file->fname, fname); | |
801 | + /* Compute the parent directory name and store a copy. The copy makes | |
802 | + it much faster to add/remove watches while nscd is running instead | |
803 | + of computing this over and over again in a temp buffer. */ | |
804 | + file->dname[0] = '\0'; | |
805 | + dname = strrchr (fname, '/'); | |
806 | + if (dname != NULL) | |
807 | + { | |
808 | + size_t len = (size_t)(dname - fname); | |
809 | + if (len > sizeof (file->dname)) | |
810 | + abort (); | |
811 | + strncpy (file->dname, file->fname, len); | |
812 | + file->dname[len] = '\0'; | |
813 | + } | |
814 | + /* The basename is the name just after the last forward slash. */ | |
815 | + file->sfname = &dname[1]; | |
816 | + file->call_res_init = crinit; | |
817 | +} | |
818 | + | |
819 | +#define define_traced_file(id, filename) \ | |
820 | +static union \ | |
821 | +{ \ | |
822 | + struct traced_file file; \ | |
823 | + char buf[sizeof (struct traced_file) + sizeof (filename)]; \ | |
824 | +} id##_traced_file; | |
825 | ||
826 | /* Structure describing dynamic part of one database. */ | |
827 | struct database_dyn | |
828 | @@ -74,15 +135,12 @@ | |
829 | ||
830 | int enabled; | |
831 | int check_file; | |
832 | - int inotify_descr; | |
833 | int clear_cache; | |
834 | int persistent; | |
835 | int shared; | |
836 | int propagate; | |
837 | - int reset_res; | |
838 | - const char filename[16]; | |
839 | + struct traced_file *traced_files; | |
840 | const char *db_filename; | |
841 | - time_t file_mtime; | |
842 | size_t suggested_module; | |
843 | size_t max_db_size; | |
844 | ||
845 | @@ -199,6 +257,10 @@ | |
846 | ||
847 | /* connections.c */ | |
848 | extern void nscd_init (void); | |
849 | +extern void register_traced_file (size_t dbidx, struct traced_file *finfo); | |
850 | +#ifdef HAVE_INOTIFY | |
851 | +extern void install_watches (struct traced_file *finfo); | |
852 | +#endif | |
853 | extern void close_sockets (void); | |
854 | extern void start_threads (void) __attribute__ ((__noreturn__)); |