]> git.ipfire.org Git - ipfire-2.x.git/blame - src/patches/glibc/glibc-rh859965.patch
dhcpcd: fix delay after dhcp down.
[ipfire-2.x.git] / src / patches / glibc / glibc-rh859965.patch
CommitLineData
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#
31diff -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
49diff -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.
122diff -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
757diff -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__));