Blame SOURCES/glibc-rh1193797.patch

147e83
commit cf9313e7d1dd42addd6cf8c9277f0f18a62cdeff
147e83
Author: Carlos O'Donell <carlos@systemhalted.org>
147e83
Date:   Fri Mar 13 09:49:24 2015 -0400
147e83
147e83
    Enhance nscd's inotify support (Bug 14906).
147e83
147e83
--- glibc-2.17-c758a686/nscd/cache.c	2012-12-24 22:02:13.000000000 -0500
147e83
+++ glibc-2.17-c758a686/nscd/cache.c	2015-05-13 13:45:57.259958374 -0400
147e83
@@ -272,28 +272,38 @@
147e83
       while (runp != NULL)
147e83
 	{
147e83
 #ifdef HAVE_INOTIFY
147e83
-	  if (runp->inotify_descr == -1)
147e83
+	  if (runp->inotify_descr[TRACED_FILE] == -1)
147e83
 #endif
147e83
 	    {
147e83
 	      struct stat64 st;
147e83
 
147e83
 	      if (stat64 (runp->fname, &st) < 0)
147e83
 		{
147e83
+		  /* Print a diagnostic that the traced file was missing.
147e83
+		     We must not disable tracing since the file might return
147e83
+		     shortly and we want to reload it at the next pruning.
147e83
+		     Disabling tracing here would go against the configuration
147e83
+		     as specified by the user via check-files.  */
147e83
 		  char buf[128];
147e83
-		  /* We cannot stat() the file, disable file checking if the
147e83
-		     file does not exist.  */
147e83
-		  dbg_log (_("cannot stat() file `%s': %s"),
147e83
+		  dbg_log (_("checking for monitored file `%s': %s"),
147e83
 			   runp->fname, strerror_r (errno, buf, sizeof (buf)));
147e83
-		  if (errno == ENOENT)
147e83
-		    table->check_file = 0;
147e83
 		}
147e83
 	      else
147e83
 		{
147e83
-		  if (st.st_mtime != table->file_mtime)
147e83
+		  /* This must be `!=` to catch cases where users turn the
147e83
+		     clocks back and we still want to detect any time difference
147e83
+		     in mtime.  */
147e83
+		  if (st.st_mtime != runp->mtime)
147e83
 		    {
147e83
-		      /* The file changed.  Invalidate all entries.  */
147e83
+		      dbg_log (_("monitored file `%s` changed (mtime)"),
147e83
+			       runp->fname);
147e83
+		      /* The file changed. Invalidate all entries.  */
147e83
 		      now = LONG_MAX;
147e83
-		      table->file_mtime = st.st_mtime;
147e83
+		      runp->mtime = st.st_mtime;
147e83
+#ifdef HAVE_INOTIFY
147e83
+		      /* Attempt to install a watch on the file.  */
147e83
+		      install_watches (runp);
147e83
+#endif
147e83
 		    }
147e83
 		}
147e83
 	    }
147e83
--- glibc-2.17-c758a686/nscd/connections.c	2015-05-12 15:03:02.870274443 -0400
147e83
+++ glibc-2.17-c758a686/nscd/connections.c	2015-05-13 13:45:57.259958374 -0400
147e83
@@ -974,6 +974,44 @@
147e83
     finish_drop_privileges ();
147e83
 }
147e83
 
147e83
+#ifdef HAVE_INOTIFY
147e83
+#define TRACED_FILE_MASK (IN_DELETE_SELF | IN_CLOSE_WRITE | IN_MOVE_SELF)
147e83
+#define TRACED_DIR_MASK (IN_DELETE_SELF | IN_CREATE | IN_MOVED_TO | IN_MOVE_SELF)
147e83
+void
147e83
+install_watches (struct traced_file *finfo)
147e83
+{
147e83
+  /* Use inotify support if we have it.  */
147e83
+  if (finfo->inotify_descr[TRACED_FILE] < 0)
147e83
+    finfo->inotify_descr[TRACED_FILE] = inotify_add_watch (inotify_fd,
147e83
+							   finfo->fname,
147e83
+			 				   TRACED_FILE_MASK);
147e83
+  if (finfo->inotify_descr[TRACED_FILE] < 0)
147e83
+    {
147e83
+      dbg_log (_("disabled inotify-based monitoring for file `%s': %s"),
147e83
+		 finfo->fname, strerror (errno));
147e83
+      return;
147e83
+    }
147e83
+  dbg_log (_("monitoring file `%s` (%d)"),
147e83
+	   finfo->fname, finfo->inotify_descr[TRACED_FILE]);
147e83
+  /* Additionally listen for events in the file's parent directory.
147e83
+     We do this because the file to be watched might be
147e83
+     deleted and then added back again.  When it is added back again
147e83
+     we must re-add the watch.  We must also cover IN_MOVED_TO to
147e83
+     detect a file being moved into the directory.  */
147e83
+  if (finfo->inotify_descr[TRACED_DIR] < 0)
147e83
+    finfo->inotify_descr[TRACED_DIR] = inotify_add_watch (inotify_fd,
147e83
+							  finfo->dname,
147e83
+							  TRACED_DIR_MASK);
147e83
+  if (finfo->inotify_descr[TRACED_DIR] < 0)
147e83
+    {
147e83
+      dbg_log (_("disabled inotify-based monitoring for directory `%s': %s"),
147e83
+		 finfo->fname, strerror (errno));
147e83
+      return;
147e83
+    }
147e83
+  dbg_log (_("monitoring directory `%s` (%d)"),
147e83
+	   finfo->dname, finfo->inotify_descr[TRACED_DIR]);
147e83
+}
147e83
+#endif
147e83
 
147e83
 void
147e83
 register_traced_file (size_t dbidx, struct traced_file *finfo)
147e83
@@ -982,30 +1020,24 @@
147e83
     return;
147e83
 
147e83
   if (__builtin_expect (debug_level > 0, 0))
147e83
-    dbg_log (_("register trace file %s for database %s"),
147e83
-	     finfo->fname, dbnames[dbidx]);
147e83
+    dbg_log (_("monitoring file `%s' for database `%s' (%d)"),
147e83
+	     finfo->fname, dbnames[dbidx],
147e83
+             finfo->inotify_descr[TRACED_DIR]);
147e83
 
147e83
 #ifdef HAVE_INOTIFY
147e83
-  if (inotify_fd < 0
147e83
-      || (finfo->inotify_descr = inotify_add_watch (inotify_fd, finfo->fname,
147e83
-						    IN_DELETE_SELF
147e83
-						    | IN_MODIFY)) < 0)
147e83
+  install_watches (finfo);
147e83
 #endif
147e83
-    {
147e83
-      /* We need the modification date of the file.  */
147e83
-      struct stat64 st;
147e83
-
147e83
-      if (stat64 (finfo->fname, &st) < 0)
147e83
-	{
147e83
-	  /* We cannot stat() the file, disable file checking.  */
147e83
-	  dbg_log (_("cannot stat() file `%s': %s"),
147e83
-		   finfo->fname, strerror (errno));
147e83
-	  return;
147e83
-	}
147e83
 
147e83
-      finfo->inotify_descr = -1;
147e83
-      finfo->mtime = st.st_mtime;
147e83
+  struct stat64 st;
147e83
+  if (stat64 (finfo->fname, &st) < 0)
147e83
+    {
147e83
+      /* We cannot stat() the file. Set mtime to zero and try again later.  */
147e83
+      dbg_log (_("stat failed for file `%s'; will try again later: %s"),
147e83
+               finfo->fname, strerror (errno));
147e83
+      finfo->mtime = 0;
147e83
     }
147e83
+  else
147e83
+    finfo->mtime = st.st_mtime;
147e83
 
147e83
   /* Queue up the file name.  */
147e83
   finfo->next = dbs[dbidx].traced_files;
147e83
@@ -1030,20 +1062,27 @@
147e83
   for (number = pwddb; number < lastdb; ++number)
147e83
     if (strcmp (key, dbnames[number]) == 0)
147e83
       {
147e83
-	if (number == hstdb)
147e83
+	struct traced_file *runp = dbs[number].traced_files;
147e83
+	while (runp != NULL)
147e83
 	  {
147e83
-	    struct traced_file *runp = dbs[hstdb].traced_files;
147e83
-	    while (runp != NULL)
147e83
-	      if (runp->call_res_init)
147e83
-		{
147e83
-		  res_init ();
147e83
-		  break;
147e83
-		}
147e83
-	      else
147e83
-		runp = runp->next;
147e83
+	    /* Make sure we reload from file when checking mtime.  */
147e83
+	    runp->mtime = 0;
147e83
+#ifdef HAVE_INOTIFY
147e83
+	    /* During an invalidation we try to reload the traced
147e83
+	       file watches.  This allows the user to re-sync if
147e83
+	       inotify events were lost.  Similar to what we do during
147e83
+	       pruning.  */
147e83
+	    install_watches (runp);
147e83
+#endif
147e83
+	    if (runp->call_res_init)
147e83
+	      {
147e83
+		res_init ();
147e83
+		break;
147e83
+	      }
147e83
+	    runp = runp->next;
147e83
 	  }
147e83
 	break;
147e83
-    }
147e83
+      }
147e83
 
147e83
   if (number == lastdb)
147e83
     {
147e83
@@ -1871,6 +1910,234 @@
147e83
 static time_t *starttime;
147e83
 
147e83
 
147e83
+#ifdef HAVE_INOTIFY
147e83
+/* Inotify event for changed file.  */
147e83
+union __inev
147e83
+{
147e83
+  struct inotify_event i;
147e83
+# ifndef PATH_MAX
147e83
+#  define PATH_MAX 1024
147e83
+# endif
147e83
+  char buf[sizeof (struct inotify_event) + PATH_MAX];
147e83
+};
147e83
+
147e83
+/* Returns 0 if the file is there otherwise -1.  */
147e83
+int
147e83
+check_file (struct traced_file *finfo)
147e83
+{
147e83
+  struct stat64 st;
147e83
+  /* We could check mtime and if different re-add
147e83
+     the watches, and invalidate the database, but we
147e83
+     don't because we are called from inotify_check_files
147e83
+     which should be doing that work.  If sufficient inotify
147e83
+     events were lost then the next pruning or invalidation
147e83
+     will do the stat and mtime check.  We don't do it here to
147e83
+     keep the logic simple.  */
147e83
+  if (stat64 (finfo->fname, &st) < 0)
147e83
+    return -1;
147e83
+  return 0;
147e83
+}
147e83
+
147e83
+/* Process the inotify event in INEV. If the event matches any of the files
147e83
+   registered with a database then mark that database as requiring its cache
147e83
+   to be cleared. We indicate the cache needs clearing by setting
147e83
+   TO_CLEAR[DBCNT] to true for the matching database.  */
147e83
+static void
147e83
+inotify_check_files (bool *to_clear, union __inev *inev)
147e83
+{
147e83
+  /* Check which of the files changed.  */
147e83
+  for (size_t dbcnt = 0; dbcnt < lastdb; ++dbcnt)
147e83
+    {
147e83
+      struct traced_file *finfo = dbs[dbcnt].traced_files;
147e83
+
147e83
+      while (finfo != NULL)
147e83
+	{
147e83
+	  /* The configuration file was moved or deleted.
147e83
+	     We stop watching it at that point, and reinitialize.  */
147e83
+	  if (finfo->inotify_descr[TRACED_FILE] == inev->i.wd
147e83
+	      && ((inev->i.mask & IN_MOVE_SELF)
147e83
+		  || (inev->i.mask & IN_DELETE_SELF)
147e83
+		  || (inev->i.mask & IN_IGNORED)))
147e83
+	    {
147e83
+	      int ret;
147e83
+	      bool moved = (inev->i.mask & IN_MOVE_SELF) != 0;
147e83
+
147e83
+	      if (check_file (finfo) == 0)
147e83
+	        {
147e83
+		  dbg_log (_("ignored inotify event for `%s` (file exists)"),
147e83
+			   finfo->fname);
147e83
+		  return;
147e83
+		}
147e83
+
147e83
+	      dbg_log (_("monitored file `%s` was %s, removing watch"),
147e83
+		       finfo->fname, moved ? "moved" : "deleted");
147e83
+	      /* File was moved out, remove the watch.  Watches are
147e83
+		 automatically removed when the file is deleted.  */
147e83
+	      if (moved)
147e83
+		{
147e83
+		  ret = inotify_rm_watch (inotify_fd, inev->i.wd);
147e83
+		  if (ret < 0)
147e83
+		    dbg_log (_("failed to remove file watch `%s`: %s"),
147e83
+			     finfo->fname, strerror (errno));
147e83
+		}
147e83
+	      finfo->inotify_descr[TRACED_FILE] = -1;
147e83
+	      to_clear[dbcnt] = true;
147e83
+	      if (finfo->call_res_init)
147e83
+	        res_init ();
147e83
+	      return;
147e83
+	    }
147e83
+	  /* The configuration file was open for writing and has just closed.
147e83
+	     We reset the cache and reinitialize.  */
147e83
+	  if (finfo->inotify_descr[TRACED_FILE] == inev->i.wd
147e83
+	      && inev->i.mask & IN_CLOSE_WRITE)
147e83
+	    {
147e83
+	      /* Mark cache as needing to be cleared and reinitialize.  */
147e83
+	      dbg_log (_("monitored file `%s` was written to"), finfo->fname);
147e83
+	      to_clear[dbcnt] = true;
147e83
+	      if (finfo->call_res_init)
147e83
+	        res_init ();
147e83
+	      return;
147e83
+	    }
147e83
+	  /* The parent directory was moved or deleted.  We trigger one last
147e83
+	     invalidation.  At the next pruning or invalidation we may add
147e83
+	     this watch back if the file is present again.  */
147e83
+	  if (finfo->inotify_descr[TRACED_DIR] == inev->i.wd
147e83
+	      && ((inev->i.mask & IN_DELETE_SELF)
147e83
+		  || (inev->i.mask & IN_MOVE_SELF)
147e83
+		  || (inev->i.mask & IN_IGNORED)))
147e83
+	    {
147e83
+	      bool moved = (inev->i.mask & IN_MOVE_SELF) != 0;
147e83
+	      /* The directory watch may have already been removed
147e83
+		 but we don't know so we just remove it again and
147e83
+		 ignore the error.  Then we remove the file watch.
147e83
+		 Note: watches are automatically removed for deleted
147e83
+		 files.  */
147e83
+	      if (moved)
147e83
+		inotify_rm_watch (inotify_fd, inev->i.wd);
147e83
+	      if (finfo->inotify_descr[TRACED_FILE] != -1)
147e83
+		{
147e83
+		  dbg_log (_("monitored parent directory `%s` was %s, removing watch on `%s`"),
147e83
+			   finfo->dname, moved ? "moved" : "deleted", finfo->fname);
147e83
+		  if (inotify_rm_watch (inotify_fd, finfo->inotify_descr[TRACED_FILE]) < 0)
147e83
+		    dbg_log (_("failed to remove file watch `%s`: %s"),
147e83
+			     finfo->dname, strerror (errno));
147e83
+		}
147e83
+	      finfo->inotify_descr[TRACED_FILE] = -1;
147e83
+	      finfo->inotify_descr[TRACED_DIR] = -1;
147e83
+	      to_clear[dbcnt] = true;
147e83
+	      if (finfo->call_res_init)
147e83
+	        res_init ();
147e83
+	      /* Continue to the next entry since this might be the
147e83
+		 parent directory for multiple registered files and
147e83
+		 we want to remove watches for all registered files.  */
147e83
+	      continue;
147e83
+	    }
147e83
+	  /* The parent directory had a create or moved to event.  */
147e83
+	  if (finfo->inotify_descr[TRACED_DIR] == inev->i.wd
147e83
+	      && ((inev->i.mask & IN_MOVED_TO)
147e83
+		  || (inev->i.mask & IN_CREATE))
147e83
+	      && strcmp (inev->i.name, finfo->sfname) == 0)
147e83
+	    {
147e83
+	      /* We detected a directory change.  We look for the creation
147e83
+		 of the file we are tracking or the move of the same file
147e83
+		 into the directory.  */
147e83
+	      int ret;
147e83
+	      dbg_log (_("monitored file `%s` was %s, adding watch"),
147e83
+		       finfo->fname,
147e83
+		       inev->i.mask & IN_CREATE ? "created" : "moved into place");
147e83
+	      /* File was moved in or created.  Regenerate the watch.  */
147e83
+	      if (finfo->inotify_descr[TRACED_FILE] != -1)
147e83
+		inotify_rm_watch (inotify_fd,
147e83
+				  finfo->inotify_descr[TRACED_FILE]);
147e83
+
147e83
+	      ret = inotify_add_watch (inotify_fd,
147e83
+				       finfo->fname,
147e83
+				       TRACED_FILE_MASK);
147e83
+	      if (ret < 0)
147e83
+		dbg_log (_("failed to add file watch `%s`: %s"),
147e83
+			 finfo->fname, strerror (errno));
147e83
+
147e83
+	      finfo->inotify_descr[TRACED_FILE] = ret;
147e83
+
147e83
+	      /* The file is new or moved so mark cache as needing to
147e83
+		 be cleared and reinitialize.  */
147e83
+	      to_clear[dbcnt] = true;
147e83
+	      if (finfo->call_res_init)
147e83
+		res_init ();
147e83
+
147e83
+	      /* Done re-adding the watch.  Don't return, we may still
147e83
+		 have other files in this same directory, same watch
147e83
+		 descriptor, and need to process them.  */
147e83
+	    }
147e83
+	  /* Other events are ignored, and we move on to the next file.  */
147e83
+	  finfo = finfo->next;
147e83
+        }
147e83
+    }
147e83
+}
147e83
+
147e83
+/* If an entry in the array of booleans TO_CLEAR is TRUE then clear the cache
147e83
+   for the associated database, otherwise do nothing. The TO_CLEAR array must
147e83
+   have LASTDB entries.  */
147e83
+static inline void
147e83
+clear_db_cache (bool *to_clear)
147e83
+{
147e83
+  for (size_t dbcnt = 0; dbcnt < lastdb; ++dbcnt)
147e83
+    if (to_clear[dbcnt])
147e83
+      {
147e83
+	pthread_mutex_lock (&dbs[dbcnt].prune_lock);
147e83
+	dbs[dbcnt].clear_cache = 1;
147e83
+	pthread_mutex_unlock (&dbs[dbcnt].prune_lock);
147e83
+	pthread_cond_signal (&dbs[dbcnt].prune_cond);
147e83
+      }
147e83
+}
147e83
+
147e83
+int
147e83
+handle_inotify_events (void)
147e83
+{
147e83
+  bool to_clear[lastdb] = { false, };
147e83
+  union __inev inev;
147e83
+
147e83
+  /* Read all inotify events for files registered via
147e83
+     register_traced_file().  */
147e83
+  while (1)
147e83
+    {
147e83
+      /* Potentially read multiple events into buf.  */
147e83
+      ssize_t nb = TEMP_FAILURE_RETRY (read (inotify_fd,
147e83
+					     &inev.buf,
147e83
+					     sizeof (inev)));
147e83
+      if (nb < (ssize_t) sizeof (struct inotify_event))
147e83
+	{
147e83
+	  /* Not even 1 event.  */
147e83
+	  if (__glibc_unlikely (nb == -1 && errno != EAGAIN))
147e83
+	    return -1;
147e83
+	  /* Done reading events that are ready.  */
147e83
+	  break;
147e83
+	}
147e83
+      /* Process all events.  The normal inotify interface delivers
147e83
+	 complete events on a read and never a partial event.  */
147e83
+      char *eptr = &inev.buf[0];
147e83
+      ssize_t count;
147e83
+      while (1)
147e83
+	{
147e83
+	  /* Check which of the files changed.  */
147e83
+	  inotify_check_files (to_clear, &inev);
147e83
+	  count = sizeof (struct inotify_event) + inev.i.len;
147e83
+	  eptr += count;
147e83
+	  nb -= count;
147e83
+	  if (nb >= (ssize_t) sizeof (struct inotify_event))
147e83
+	    memcpy (&inev, eptr, nb);
147e83
+	  else
147e83
+	    break;
147e83
+	}
147e83
+      continue;
147e83
+    }
147e83
+  /* Actually perform the cache clearing.  */
147e83
+  clear_db_cache (to_clear);
147e83
+  return 0;
147e83
+}
147e83
+
147e83
+#endif
147e83
+
147e83
 static void
147e83
 __attribute__ ((__noreturn__))
147e83
 main_loop_poll (void)
147e83
@@ -1975,72 +2242,21 @@
147e83
 	    {
147e83
 	      if (conns[1].revents != 0)
147e83
 		{
147e83
-		  bool to_clear[lastdb] = { false, };
147e83
-		  union
147e83
-		  {
147e83
-# ifndef PATH_MAX
147e83
-#  define PATH_MAX 1024
147e83
-# endif
147e83
-		    struct inotify_event i;
147e83
-		    char buf[sizeof (struct inotify_event) + PATH_MAX];
147e83
-		  } inev;
147e83
-
147e83
-		  while (1)
147e83
-		    {
147e83
-		      ssize_t nb = TEMP_FAILURE_RETRY (read (inotify_fd, &inev,
147e83
-							     sizeof (inev)));
147e83
-		      if (nb < (ssize_t) sizeof (struct inotify_event))
147e83
-			{
147e83
-			  if (__builtin_expect (nb == -1 && errno != EAGAIN,
147e83
-						0))
147e83
-			    {
147e83
-			      /* Something went wrong when reading the inotify
147e83
-				 data.  Better disable inotify.  */
147e83
-			      dbg_log (_("\
147e83
-disabled inotify after read error %d"),
147e83
-				       errno);
147e83
-			      conns[1].fd = -1;
147e83
-			      firstfree = 1;
147e83
-			      if (nused == 2)
147e83
-				nused = 1;
147e83
-			      close (inotify_fd);
147e83
-			      inotify_fd = -1;
147e83
-			    }
147e83
-			  break;
147e83
-			}
147e83
-
147e83
-		      /* Check which of the files changed.  */
147e83
-		      for (size_t dbcnt = 0; dbcnt < lastdb; ++dbcnt)
147e83
-			{
147e83
-			  struct traced_file *finfo = dbs[dbcnt].traced_files;
147e83
-
147e83
-			  while (finfo != NULL)
147e83
-			    {
147e83
-			      if (finfo->inotify_descr == inev.i.wd)
147e83
-				{
147e83
-				  to_clear[dbcnt] = true;
147e83
-				  if (finfo->call_res_init)
147e83
-				    res_init ();
147e83
-				  goto next;
147e83
-				}
147e83
-
147e83
-			      finfo = finfo->next;
147e83
-			    }
147e83
-			}
147e83
-		    next:;
147e83
-		    }
147e83
-
147e83
-		  /* Actually perform the cache clearing.  */
147e83
-		  for (size_t dbcnt = 0; dbcnt < lastdb; ++dbcnt)
147e83
-		    if (to_clear[dbcnt])
147e83
-		      {
147e83
-			pthread_mutex_lock (&dbs[dbcnt].prune_lock);
147e83
-			dbs[dbcnt].clear_cache = 1;
147e83
-			pthread_mutex_unlock (&dbs[dbcnt].prune_lock);
147e83
-			pthread_cond_signal (&dbs[dbcnt].prune_cond);
147e83
-		      }
147e83
-
147e83
-		  --n;
147e83
+                  int ret;
147e83
+                  ret = handle_inotify_events ();
147e83
+                  if (ret == -1)
147e83
+                    {
147e83
+                      /* Something went wrong when reading the inotify
147e83
+                         data.  Better disable inotify.  */
147e83
+                      dbg_log (_("disabled inotify-based monitoring after read error %d"), errno);
147e83
+                      conns[1].fd = -1;
147e83
+                      firstfree = 1;
147e83
+                      if (nused == 2)
147e83
+                        nused = 1;
147e83
+                      close (inotify_fd);
147e83
+                      inotify_fd = -1;
147e83
+                    }
147e83
+                  --n;
147e83
 		}
147e83
 
147e83
 	      first = 2;
147e83
@@ -2207,64 +2423,18 @@
147e83
 # ifdef HAVE_INOTIFY
147e83
 	else if (revs[cnt].data.fd == inotify_fd)
147e83
 	  {
147e83
-	    bool to_clear[lastdb] = { false, };
147e83
-	    union
147e83
-	    {
147e83
-	      struct inotify_event i;
147e83
-	      char buf[sizeof (struct inotify_event) + PATH_MAX];
147e83
-	    } inev;
147e83
-
147e83
-	    while (1)
147e83
+	    int ret;
147e83
+	    ret = handle_inotify_events ();
147e83
+	    if (ret == -1)
147e83
 	      {
147e83
-		ssize_t nb = TEMP_FAILURE_RETRY (read (inotify_fd, &inev,
147e83
-						 sizeof (inev)));
147e83
-		if (nb < (ssize_t) sizeof (struct inotify_event))
147e83
-		  {
147e83
-		    if (__builtin_expect (nb == -1 && errno != EAGAIN, 0))
147e83
-		      {
147e83
-			/* Something went wrong when reading the inotify
147e83
-			   data.  Better disable inotify.  */
147e83
-			dbg_log (_("disabled inotify after read error %d"),
147e83
-				 errno);
147e83
-			(void) epoll_ctl (efd, EPOLL_CTL_DEL, inotify_fd,
147e83
-					  NULL);
147e83
-			close (inotify_fd);
147e83
-			inotify_fd = -1;
147e83
-		      }
147e83
-		    break;
147e83
-		  }
147e83
-
147e83
-		/* Check which of the files changed.  */
147e83
-		for (size_t dbcnt = 0; dbcnt < lastdb; ++dbcnt)
147e83
-		  {
147e83
-		    struct traced_file *finfo = dbs[dbcnt].traced_files;
147e83
-
147e83
-		    while (finfo != NULL)
147e83
-		      {
147e83
-			if (finfo->inotify_descr == inev.i.wd)
147e83
-			  {
147e83
-			    to_clear[dbcnt] = true;
147e83
-			    if (finfo->call_res_init)
147e83
-			      res_init ();
147e83
-			    goto next;
147e83
-			  }
147e83
-
147e83
-			finfo = finfo->next;
147e83
-		      }
147e83
-		  }
147e83
-	      next:;
147e83
-	      }
147e83
-
147e83
-	    /* Actually perform the cache clearing.  */
147e83
-	    for (size_t dbcnt = 0; dbcnt < lastdb; ++dbcnt)
147e83
-	      if (to_clear[dbcnt])
147e83
-		{
147e83
-		  pthread_mutex_lock (&dbs[dbcnt].prune_lock);
147e83
-		  dbs[dbcnt].clear_cache = 1;
147e83
-		  pthread_mutex_unlock (&dbs[dbcnt].prune_lock);
147e83
-		  pthread_cond_signal (&dbs[dbcnt].prune_cond);
147e83
-		}
147e83
-	  }
147e83
+		/* Something went wrong when reading the inotify
147e83
+		   data.  Better disable inotify.  */
147e83
+		dbg_log (_("disabled inotify-based monitoring after read error %d"), errno);
147e83
+                close (inotify_fd);
147e83
+                inotify_fd = -1;
147e83
+                break;
147e83
+              }
147e83
+          }
147e83
 # endif
147e83
 # ifdef HAVE_NETLINK
147e83
 	else if (revs[cnt].data.fd == nl_status_fd)
147e83
@@ -2300,7 +2470,9 @@
147e83
 	  no reply in too long of a time.  */
147e83
       time_t laststart = now - ACCEPT_TIMEOUT;
147e83
       assert (starttime[sock] == 0);
147e83
+# ifdef HAVE_INOTIFY
147e83
       assert (inotify_fd == -1 || starttime[inotify_fd] == 0);
147e83
+# endif
147e83
       assert (nl_status_fd == -1 || starttime[nl_status_fd] == 0);
147e83
       for (int cnt = highest; cnt > STDERR_FILENO; --cnt)
147e83
 	if (starttime[cnt] != 0 && starttime[cnt] < laststart)
147e83
--- glibc-2.17-c758a686/nscd/nscd.h	2015-05-12 15:03:02.870274443 -0400
147e83
+++ glibc-2.17-c758a686/nscd/nscd.h	2015-05-13 13:45:57.259958374 -0400
147e83
@@ -61,17 +61,67 @@
147e83
    80% of the thread stack size.  */
147e83
 #define MAX_STACK_USE ((8 * NSCD_THREAD_STACKSIZE) / 10)
147e83
 
147e83
-
147e83
-/* Registered filename used to fill database.  */
147e83
+/* Records the file registered per database that when changed
147e83
+   or modified requires invalidating the database.  */
147e83
 struct traced_file
147e83
 {
147e83
+  /* Tracks the last modified time of the traced file.  */
147e83
   time_t mtime;
147e83
+  /* Support multiple registered files per database.  */
147e83
   struct traced_file *next;
147e83
   int call_res_init;
147e83
-  int inotify_descr;
147e83
+  /* Requires Inotify support to do anything useful.  */
147e83
+#define TRACED_FILE	0
147e83
+#define TRACED_DIR	1
147e83
+  int inotify_descr[2];
147e83
+# ifndef PATH_MAX
147e83
+#  define PATH_MAX 1024
147e83
+# endif
147e83
+  /* The parent directory is used to scan for creation/deletion.  */
147e83
+  char dname[PATH_MAX];
147e83
+  /* Just the name of the file with no directory component.  */
147e83
+  char *sfname;
147e83
+  /* The full-path name of the registered file.  */
147e83
   char fname[];
147e83
 };
147e83
 
147e83
+/* Initialize a `struct traced_file`.  As input we need the name
147e83
+   of the file, and if invalidation requires calling res_init.
147e83
+   If CRINIT is 1 then res_init will be called after invalidation
147e83
+   or if the traced file is changed in any way, otherwise it will
147e83
+   not.  */
147e83
+static inline void
147e83
+init_traced_file(struct traced_file *file, const char *fname, int crinit)
147e83
+{
147e83
+   char *dname;
147e83
+   file->mtime = 0;
147e83
+   file->inotify_descr[TRACED_FILE] = -1;
147e83
+   file->inotify_descr[TRACED_DIR] = -1;
147e83
+   strcpy (file->fname, fname);
147e83
+   /* Compute the parent directory name and store a copy.  The copy makes
147e83
+      it much faster to add/remove watches while nscd is running instead
147e83
+      of computing this over and over again in a temp buffer.  */
147e83
+   file->dname[0] = '\0';
147e83
+   dname = strrchr (fname, '/');
147e83
+   if (dname != NULL)
147e83
+     {
147e83
+       size_t len = (size_t)(dname - fname);
147e83
+       if (len > sizeof (file->dname))
147e83
+	 abort ();
147e83
+       strncpy (file->dname, file->fname, len);
147e83
+       file->dname[len] = '\0';
147e83
+     }
147e83
+   /* The basename is the name just after the last forward slash.  */
147e83
+   file->sfname = &dname[1];
147e83
+   file->call_res_init = crinit;
147e83
+}
147e83
+
147e83
+#define define_traced_file(id, filename) 			\
147e83
+static union							\
147e83
+{								\
147e83
+  struct traced_file file;					\
147e83
+  char buf[sizeof (struct traced_file) + sizeof (filename)];	\
147e83
+} id##_traced_file;
147e83
 
147e83
 /* Structure describing dynamic part of one database.  */
147e83
 struct database_dyn
147e83
@@ -90,7 +140,6 @@
147e83
   int propagate;
147e83
   struct traced_file *traced_files;
147e83
   const char *db_filename;
147e83
-  time_t file_mtime;
147e83
   size_t suggested_module;
147e83
   size_t max_db_size;
147e83
 
147e83
@@ -216,6 +265,9 @@
147e83
 /* connections.c */
147e83
 extern void nscd_init (void);
147e83
 extern void register_traced_file (size_t dbidx, struct traced_file *finfo);
147e83
+#ifdef HAVE_INOTIFY
147e83
+extern void install_watches (struct traced_file *finfo);
147e83
+#endif
147e83
 extern void close_sockets (void);
147e83
 extern void start_threads (void) __attribute__ ((__noreturn__));
147e83
 
147e83
--- glibc-2.17-c758a686/nss/nss_db/db-init.c	2012-12-24 22:02:13.000000000 -0500
147e83
+++ glibc-2.17-c758a686/nss/nss_db/db-init.c	2015-05-13 13:45:57.269958504 -0400
147e83
@@ -22,35 +22,25 @@
147e83
 #include <nscd/nscd.h>
147e83
 #include <string.h>
147e83
 
147e83
-static union
147e83
-{
147e83
-  struct traced_file file;
147e83
-  char buf[sizeof (struct traced_file) + sizeof (_PATH_VARDB "passwd.db")];
147e83
-} pwd_traced_file;
147e83
-
147e83
-static union
147e83
-{
147e83
-  struct traced_file file;
147e83
-  char buf[sizeof (struct traced_file) + sizeof (_PATH_VARDB "group.db")];
147e83
-} grp_traced_file;
147e83
+#define PWD_FILENAME (_PATH_VARDB "passwd.db")
147e83
+define_traced_file (pwd, PWD_FILENAME);
147e83
 
147e83
-static union
147e83
-{
147e83
-  struct traced_file file;
147e83
-  char buf[sizeof (struct traced_file) + sizeof (_PATH_VARDB "services.db")];
147e83
-} serv_traced_file;
147e83
+#define GRP_FILENAME (_PATH_VARDB "group.db")
147e83
+define_traced_file (grp, GRP_FILENAME);
147e83
 
147e83
+#define SERV_FILENAME (_PATH_VARDB "services.db")
147e83
+define_traced_file (serv, SERV_FILENAME);
147e83
 
147e83
 void
147e83
 _nss_db_init (void (*cb) (size_t, struct traced_file *))
147e83
 {
147e83
-  strcpy (pwd_traced_file.file.fname,_PATH_VARDB  "passwd.db");
147e83
+  init_traced_file (&pwd_traced_file.file, PWD_FILENAME, 0);
147e83
   cb (pwddb, &pwd_traced_file.file);
147e83
 
147e83
-  strcpy (grp_traced_file.file.fname, _PATH_VARDB "group.db");
147e83
+  init_traced_file (&grp_traced_file.file, GRP_FILENAME, 0);
147e83
   cb (grpdb, &grp_traced_file.file);
147e83
 
147e83
-  strcpy (serv_traced_file.file.fname, _PATH_VARDB "services.db");
147e83
+  init_traced_file (&serv_traced_file.file, SERV_FILENAME, 0);
147e83
   cb (servdb, &serv_traced_file.file);
147e83
 }
147e83
 
147e83
--- glibc-2.17-c758a686/nss/nss_files/files-init.c	2012-12-24 22:02:13.000000000 -0500
147e83
+++ glibc-2.17-c758a686/nss/nss_files/files-init.c	2015-05-13 13:45:57.269958504 -0400
147e83
@@ -18,43 +18,46 @@
147e83
 
147e83
 #ifdef USE_NSCD
147e83
 
147e83
+#include <string.h>
147e83
 #include <nscd/nscd.h>
147e83
 
147e83
+#define PWD_FILENAME "/etc/passwd"
147e83
+define_traced_file (pwd, PWD_FILENAME);
147e83
 
147e83
-#define TF(id, filename, ...)					\
147e83
-static union							\
147e83
-{								\
147e83
-  struct traced_file file;					\
147e83
-  char buf[sizeof (struct traced_file) + sizeof (filename)];	\
147e83
-} id##_traced_file =						\
147e83
-  {								\
147e83
-    .file =							\
147e83
-    {								\
147e83
-      .fname = filename, ## __VA_ARGS__				\
147e83
-    }								\
147e83
-  }
147e83
-
147e83
-TF (pwd, "/etc/passwd");
147e83
-TF (grp, "/etc/group");
147e83
-TF (hst, "/etc/hosts");
147e83
-TF (resolv, "/etc/resolv.conf", .call_res_init = 1);
147e83
-TF (serv, "/etc/services");
147e83
-TF (netgr, "/etc/netgroup");
147e83
-
147e83
-
147e83
+#define GRP_FILENAME "/etc/group"
147e83
+define_traced_file (grp, GRP_FILENAME);
147e83
+ 
147e83
+#define HST_FILENAME "/etc/hosts"
147e83
+define_traced_file (hst, HST_FILENAME);
147e83
+
147e83
+#define RESOLV_FILENAME "/etc/resolv.conf"
147e83
+define_traced_file (resolv, RESOLV_FILENAME);
147e83
+
147e83
+#define SERV_FILENAME "/etc/services"
147e83
+define_traced_file (serv, SERV_FILENAME);
147e83
+
147e83
+#define NETGR_FILENAME "/etc/netgroup"
147e83
+define_traced_file (netgr, NETGR_FILENAME);
147e83
+ 
147e83
 void
147e83
 _nss_files_init (void (*cb) (size_t, struct traced_file *))
147e83
 {
147e83
+  init_traced_file (&pwd_traced_file.file, PWD_FILENAME, 0);
147e83
   cb (pwddb, &pwd_traced_file.file);
147e83
 
147e83
+  init_traced_file (&grp_traced_file.file, GRP_FILENAME, 0);
147e83
   cb (grpdb, &grp_traced_file.file);
147e83
 
147e83
+  init_traced_file (&hst_traced_file.file, HST_FILENAME, 0);
147e83
   cb (hstdb, &hst_traced_file.file);
147e83
 
147e83
+  init_traced_file (&resolv_traced_file.file, RESOLV_FILENAME, 1);
147e83
   cb (hstdb, &resolv_traced_file.file);
147e83
 
147e83
+  init_traced_file (&serv_traced_file.file, SERV_FILENAME, 0);
147e83
   cb (servdb, &serv_traced_file.file);
147e83
-
147e83
+ 
147e83
+  init_traced_file (&netgr_traced_file.file, NETGR_FILENAME, 0);
147e83
   cb (netgrdb, &netgr_traced_file.file);
147e83
 }
147e83