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