/* Inner loops of cache daemon.
- Copyright (C) 1998-2012 Free Software Foundation, Inc.
+ Copyright (C) 1998-2016 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
+#include <stdint.h>
#include <arpa/inet.h>
#ifdef HAVE_NETLINK
# include <linux/netlink.h>
#include <resolv/resolv.h>
#include <kernel-features.h>
+#include <libc-internal.h>
/* Support to run nscd as an unprivileged user */
static int nl_status_fd = -1;
#endif
-#ifndef __ASSUME_SOCK_CLOEXEC
-/* Negative if SOCK_CLOEXEC is not supported, positive if it is, zero
- before be know the result. */
-static int have_sock_cloexec;
-#endif
#ifndef __ASSUME_ACCEPT4
static int have_accept4;
#endif
use_he = 1,
use_he_begin = use_he | use_begin,
use_he_end = use_he | use_end,
-#if SEPARATE_KEY
- use_key = 2,
- use_key_begin = use_key | use_begin,
- use_key_end = use_key | use_end,
- use_key_first = use_key_begin | use_first,
-#endif
use_data = 3,
use_data_begin = use_data | use_begin,
use_data_end = use_data | use_end,
if (here->key < here->packet + sizeof (struct datahead)
|| here->key > here->packet + dh->allocsize
|| here->key + here->len > here->packet + dh->allocsize)
- {
-#if SEPARATE_KEY
- /* If keys can appear outside of data, this should be done
- instead. But gc doesn't mark the data in that case. */
- if (! check_use (data, head->first_free, usemap,
- use_key | (here->first ? use_first : 0),
- here->key, here->len))
-#endif
- goto fail;
- }
+ goto fail;
work = here->next;
he->first == true hashentry. */
for (ref_t idx = 0; idx < head->first_free; ++idx)
{
-#if SEPARATE_KEY
- if (usemap[idx] == use_key_begin)
- goto fail;
-#endif
if (usemap[idx] == use_data_begin)
goto fail;
}
close (fd);
}
else if (errno == EACCES)
- error (EXIT_FAILURE, 0, _("cannot access '%s'"),
- dbs[cnt].db_filename);
+ do_exit (EXIT_FAILURE, 0, _("cannot access '%s'"),
+ dbs[cnt].db_filename);
}
if (dbs[cnt].head == NULL)
{
dbg_log (_("database for %s corrupted or simultaneously used; remove %s manually if necessary and restart"),
dbnames[cnt], dbs[cnt].db_filename);
- // XXX Correct way to terminate?
- exit (1);
+ do_exit (1, 0, NULL);
}
if (dbs[cnt].persistent)
cannot create read-only descriptor for \"%s\"; no mmap"),
dbs[cnt].db_filename);
- /* Before we create the header, initialiye the hash
- table. So that if we get interrupted if writing
+ /* Before we create the header, initialize the hash
+ table. That way if we get interrupted while writing
the header we can recognize a partially initialized
database. */
size_t ps = sysconf (_SC_PAGESIZE);
}
/* Create the socket. */
-#ifndef __ASSUME_SOCK_CLOEXEC
- sock = -1;
- if (have_sock_cloexec >= 0)
-#endif
- {
- sock = socket (AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
-#ifndef __ASSUME_SOCK_CLOEXEC
- if (have_sock_cloexec == 0)
- have_sock_cloexec = sock != -1 || errno != EINVAL ? 1 : -1;
-#endif
- }
-#ifndef __ASSUME_SOCK_CLOEXEC
- if (have_sock_cloexec < 0)
- sock = socket (AF_UNIX, SOCK_STREAM, 0);
-#endif
+ sock = socket (AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
if (sock < 0)
{
dbg_log (_("cannot open socket: %s"), strerror (errno));
- exit (errno == EACCES ? 4 : 1);
+ do_exit (errno == EACCES ? 4 : 1, 0, NULL);
}
/* Bind a name to the socket. */
struct sockaddr_un sock_addr;
if (bind (sock, (struct sockaddr *) &sock_addr, sizeof (sock_addr)) < 0)
{
dbg_log ("%s: %s", _PATH_NSCDSOCKET, strerror (errno));
- exit (errno == EACCES ? 4 : 1);
- }
-
-#ifndef __ASSUME_SOCK_CLOEXEC
- if (have_sock_cloexec < 0)
- {
- /* We don't want to get stuck on accept. */
- int fl = fcntl (sock, F_GETFL);
- if (fl == -1 || fcntl (sock, F_SETFL, fl | O_NONBLOCK) == -1)
- {
- dbg_log (_("cannot change socket to nonblocking mode: %s"),
- strerror (errno));
- exit (1);
- }
-
- /* The descriptor needs to be closed on exec. */
- if (paranoia && fcntl (sock, F_SETFD, FD_CLOEXEC) == -1)
- {
- dbg_log (_("cannot set socket to close on exec: %s"),
- strerror (errno));
- exit (1);
- }
+ do_exit (errno == EACCES ? 4 : 1, 0, NULL);
}
-#endif
/* Set permissions for the socket. */
chmod (_PATH_NSCDSOCKET, DEFFILEMODE);
{
dbg_log (_("cannot enable socket to accept connections: %s"),
strerror (errno));
- exit (1);
+ do_exit (1, 0, NULL);
}
#ifdef HAVE_NETLINK
/* Start the timestamp process. */
dbs[hstdb].head->extra_data[NSCD_HST_IDX_CONF_TIMESTAMP]
= __bump_nl_timestamp ();
-
-# ifndef __ASSUME_SOCK_CLOEXEC
- if (have_sock_cloexec < 0)
- {
- /* We don't want to get stuck on accept. */
- int fl = fcntl (nl_status_fd, F_GETFL);
- if (fl == -1
- || fcntl (nl_status_fd, F_SETFL, fl | O_NONBLOCK) == -1)
- {
- dbg_log (_("\
-cannot change socket to nonblocking mode: %s"),
- strerror (errno));
- exit (1);
- }
-
- /* The descriptor needs to be closed on exec. */
- if (paranoia
- && fcntl (nl_status_fd, F_SETFD, FD_CLOEXEC) == -1)
- {
- dbg_log (_("cannot set socket to close on exec: %s"),
- strerror (errno));
- exit (1);
- }
- }
-# endif
}
}
}
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
+
+/* Register the file in FINFO as a traced file for the database DBS[DBIX].
+ We support registering multiple files per database. Each call to
+ register_traced_file adds to the list of registered files.
+
+ When we prune the database, either through timeout or a request to
+ invalidate, we will check to see if any of the registered files has changed.
+ When we accept new connections to handle a cache request we will also
+ check to see if any of the registered files has changed.
+
+ If we have inotify support then we install an inotify fd to notify us of
+ file deletion or modification, both of which will require we invalidate
+ the cache for the database. Without inotify support we stat the file and
+ store st_mtime to determine if the file has been modified. */
void
register_traced_file (size_t dbidx, struct traced_file *finfo)
{
+ /* If the database is disabled or file checking is disabled
+ then ignore the registration. */
if (! dbs[dbidx].enabled || ! dbs[dbidx].check_file)
return;
- if (__builtin_expect (debug_level > 0, 0))
- dbg_log (_("register trace file %s for database %s"),
+ if (__glibc_unlikely (debug_level > 0))
+ dbg_log (_("monitoring file %s for database %s"),
finfo->fname, dbnames[dbidx]);
#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
+ struct stat64 st;
+ if (stat64 (finfo->fname, &st) < 0)
{
- /* 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;
+ /* 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;
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)
{
#endif
(void) TEMP_FAILURE_RETRY (sendmsg (fd, &msg, MSG_NOSIGNAL));
- if (__builtin_expect (debug_level > 0, 0))
+ if (__glibc_unlikely (debug_level > 0))
dbg_log (_("provide access to FD %d, for %s"), db->ro_fd, key);
}
#endif /* SCM_RIGHTS */
}
/* Is this service enabled? */
- if (__builtin_expect (!db->enabled, 0))
+ if (__glibc_unlikely (!db->enabled))
{
/* No, sent the prepared record. */
if (TEMP_FAILURE_RETRY (send (fd, db->disabled_iov->iov_base,
}
/* Be sure we can read the data. */
- if (__builtin_expect (pthread_rwlock_tryrdlock (&db->lock) != 0, 0))
+ if (__glibc_unlikely (pthread_rwlock_tryrdlock (&db->lock) != 0))
{
++db->head->rdlockdelayed;
pthread_rwlock_rdlock (&db->lock);
ssize_t nwritten;
#ifdef HAVE_SENDFILE
- if (__builtin_expect (db->mmap_used, 1))
+ if (__glibc_likely (db->mmap_used))
{
assert (db->wr_fd != -1);
assert ((char *) cached->data > (char *) db->data);
cannot change to old GID: %s; disabling paranoia mode"),
strerror (errno));
- setuid (server_uid);
+ ignore_value (setuid (server_uid));
paranoia = 0;
return;
}
if (server_user != NULL)
{
- setuid (server_uid);
- setgid (server_gid);
+ ignore_value (setuid (server_uid));
+ ignore_value (setgid (server_gid));
}
paranoia = 0;
return;
if (server_user != NULL)
{
- setuid (server_uid);
- setgid (server_gid);
+ ignore_value (setuid (server_uid));
+ ignore_value (setgid (server_gid));
}
if (chdir ("/") != 0)
dbg_log (_("cannot change current working directory to \"/\": %s"),
dbs[my_number].head->timestamp = now;
struct timespec prune_ts;
- if (__builtin_expect (clock_gettime (timeout_clock, &prune_ts) == -1, 0))
+ if (__glibc_unlikely (clock_gettime (timeout_clock, &prune_ts) == -1))
/* Should never happen. */
abort ();
we need to wake up occasionally to update the timestamp.
Wait 90% of the update period. */
#define UPDATE_MAPPING_TIMEOUT (MAPPING_TIMEOUT * 9 / 10)
- if (__builtin_expect (! dont_need_update, 0))
+ if (__glibc_unlikely (! dont_need_update))
{
next_wait = MIN (UPDATE_MAPPING_TIMEOUT, next_wait);
dbs[my_number].head->timestamp = now;
#ifdef SO_PEERCRED
pid_t pid = 0;
- if (__builtin_expect (debug_level > 0, 0))
+ if (__glibc_unlikely (debug_level > 0))
{
struct ucred caller;
socklen_t optlen = sizeof (caller);
else
{
/* Get the key. */
- char keybuf[MAXKEYLEN];
+ char keybuf[MAXKEYLEN + 1];
if (__builtin_expect (TEMP_FAILURE_RETRY (read (fd, keybuf,
req.key_len))
strerror_r (errno, buf, sizeof (buf)));
goto close_and_out;
}
+ keybuf[req.key_len] = '\0';
if (__builtin_expect (debug_level, 0) > 0)
{
}
bool do_signal = true;
- if (__builtin_expect (nready == 0, 0))
+ if (__glibc_unlikely (nready == 0))
{
++client_queued;
do_signal = false;
/* Check whether restarting should happen. */
-static inline int
+static bool
restart_p (time_t now)
{
return (paranoia && readylist == NULL && nready == nthreads
/* Array for times a connection was accepted. */
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__))
{
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)
+ 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);
- 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:;
+ /* 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;
}
-
- /* 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;
}
# 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:;
+ /* Something went wrong when reading the inotify
+ data. Better disable inotify. */
+ dbg_log (_("disabled inotify-based monitoring after read error %d"), errno);
+ (void) epoll_ctl (efd, EPOLL_CTL_DEL, inotify_fd, NULL);
+ close (inotify_fd);
+ inotify_fd = -1;
+ break;
}
-
- /* 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);
- }
}
# endif
# ifdef HAVE_NETLINK
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)
if (pthread_cond_init (&dbs[i].prune_cond, &condattr) != 0)
{
dbg_log (_("could not initialize conditional variable"));
- exit (1);
+ do_exit (1, 0, NULL);
}
pthread_t th;
&& pthread_create (&th, &attr, nscd_run_prune, (void *) i) != 0)
{
dbg_log (_("could not start clean-up thread; terminating"));
- exit (1);
+ do_exit (1, 0, NULL);
}
}
if (i == 0)
{
dbg_log (_("could not start any worker thread; terminating"));
- exit (1);
+ do_exit (1, 0, NULL);
}
break;
}
}
+ /* Now it is safe to let the parent know that we're doing fine and it can
+ exit. */
+ notify_parent (0);
+
/* Determine how much room for descriptors we should initially
allocate. This might need to change later if we cap the number
with MAXCONN. */
if (pwd == NULL)
{
dbg_log (_("Failed to run nscd as user '%s'"), server_user);
- error (EXIT_FAILURE, 0, _("Failed to run nscd as user '%s'"),
- server_user);
+ do_exit (EXIT_FAILURE, 0,
+ _("Failed to run nscd as user '%s'"), server_user);
}
server_uid = pwd->pw_uid;
{
/* This really must never happen. */
dbg_log (_("Failed to run nscd as user '%s'"), server_user);
- error (EXIT_FAILURE, errno, _("initial getgrouplist failed"));
+ do_exit (EXIT_FAILURE, errno,
+ _("initial getgrouplist failed"));
}
server_groups = (gid_t *) xmalloc (server_ngroups * sizeof (gid_t));
== -1)
{
dbg_log (_("Failed to run nscd as user '%s'"), server_user);
- error (EXIT_FAILURE, errno, _("getgrouplist failed"));
+ do_exit (EXIT_FAILURE, errno, _("getgrouplist failed"));
}
}
if (setgroups (server_ngroups, server_groups) == -1)
{
dbg_log (_("Failed to run nscd as user '%s'"), server_user);
- error (EXIT_FAILURE, errno, _("setgroups failed"));
+ do_exit (EXIT_FAILURE, errno, _("setgroups failed"));
}
int res;
if (res == -1)
{
dbg_log (_("Failed to run nscd as user '%s'"), server_user);
- perror ("setgid");
- exit (4);
+ do_exit (4, errno, "setgid");
}
if (paranoia)
if (res == -1)
{
dbg_log (_("Failed to run nscd as user '%s'"), server_user);
- perror ("setuid");
- exit (4);
+ do_exit (4, errno, "setuid");
}
#if defined HAVE_LIBAUDIT && defined HAVE_LIBCAP