Blame nscd/cache.c

Packit 6c4009
/* Copyright (c) 1998-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
Packit 6c4009
Packit 6c4009
   This program is free software; you can redistribute it and/or modify
Packit 6c4009
   it under the terms of the GNU General Public License as published
Packit 6c4009
   by the Free Software Foundation; version 2 of the License, or
Packit 6c4009
   (at your option) any later version.
Packit 6c4009
Packit 6c4009
   This program is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 6c4009
   GNU General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU General Public License
Packit 6c4009
   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <assert.h>
Packit 6c4009
#include <atomic.h>
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <error.h>
Packit 6c4009
#include <inttypes.h>
Packit 6c4009
#include <limits.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <libintl.h>
Packit 6c4009
#include <arpa/inet.h>
Packit 6c4009
#include <sys/mman.h>
Packit 6c4009
#include <sys/param.h>
Packit 6c4009
#include <sys/stat.h>
Packit 6c4009
#include <sys/uio.h>
Packit 6c4009
#include <nss.h>
Packit 6c4009
Packit 6c4009
#include "nscd.h"
Packit 6c4009
#include "dbg_log.h"
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Wrapper functions with error checking for standard functions.  */
Packit 6c4009
extern void *xcalloc (size_t n, size_t s);
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Number of times a value is reloaded without being used.  UINT_MAX
Packit 6c4009
   means unlimited.  */
Packit 6c4009
unsigned int reload_count = DEFAULT_RELOAD_LIMIT;
Packit 6c4009
Packit 6c4009
Packit 6c4009
static time_t (*const readdfcts[LASTREQ]) (struct database_dyn *,
Packit 6c4009
					   struct hashentry *,
Packit 6c4009
					   struct datahead *) =
Packit 6c4009
{
Packit 6c4009
  [GETPWBYNAME] = readdpwbyname,
Packit 6c4009
  [GETPWBYUID] = readdpwbyuid,
Packit 6c4009
  [GETGRBYNAME] = readdgrbyname,
Packit 6c4009
  [GETGRBYGID] = readdgrbygid,
Packit 6c4009
  [GETHOSTBYNAME] = readdhstbyname,
Packit 6c4009
  [GETHOSTBYNAMEv6] = readdhstbynamev6,
Packit 6c4009
  [GETHOSTBYADDR] = readdhstbyaddr,
Packit 6c4009
  [GETHOSTBYADDRv6] = readdhstbyaddrv6,
Packit 6c4009
  [GETAI] = readdhstai,
Packit 6c4009
  [INITGROUPS] = readdinitgroups,
Packit 6c4009
  [GETSERVBYNAME] = readdservbyname,
Packit 6c4009
  [GETSERVBYPORT] = readdservbyport,
Packit 6c4009
  [GETNETGRENT] = readdgetnetgrent,
Packit 6c4009
  [INNETGR] = readdinnetgr
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Search the cache for a matching entry and return it when found.  If
Packit 6c4009
   this fails search the negative cache and return (void *) -1 if this
Packit 6c4009
   search was successful.  Otherwise return NULL.
Packit 6c4009
Packit 6c4009
   This function must be called with the read-lock held.  */
Packit 6c4009
struct datahead *
Packit 6c4009
cache_search (request_type type, const void *key, size_t len,
Packit 6c4009
	      struct database_dyn *table, uid_t owner)
Packit 6c4009
{
Packit 6c4009
  unsigned long int hash = __nss_hash (key, len) % table->head->module;
Packit 6c4009
Packit 6c4009
  unsigned long int nsearched = 0;
Packit 6c4009
  struct datahead *result = NULL;
Packit 6c4009
Packit 6c4009
  ref_t work = table->head->array[hash];
Packit 6c4009
  while (work != ENDREF)
Packit 6c4009
    {
Packit 6c4009
      ++nsearched;
Packit 6c4009
Packit 6c4009
      struct hashentry *here = (struct hashentry *) (table->data + work);
Packit 6c4009
Packit 6c4009
      if (type == here->type && len == here->len
Packit 6c4009
	  && memcmp (key, table->data + here->key, len) == 0
Packit 6c4009
	  && here->owner == owner)
Packit 6c4009
	{
Packit 6c4009
	  /* We found the entry.  Increment the appropriate counter.  */
Packit 6c4009
	  struct datahead *dh
Packit 6c4009
	    = (struct datahead *) (table->data + here->packet);
Packit 6c4009
Packit 6c4009
	  /* See whether we must ignore the entry.  */
Packit 6c4009
	  if (dh->usable)
Packit 6c4009
	    {
Packit 6c4009
	      /* We do not synchronize the memory here.  The statistics
Packit 6c4009
		 data is not crucial, we synchronize only once in a while
Packit 6c4009
		 in the cleanup threads.  */
Packit 6c4009
	      if (dh->notfound)
Packit 6c4009
		++table->head->neghit;
Packit 6c4009
	      else
Packit 6c4009
		{
Packit 6c4009
		  ++table->head->poshit;
Packit 6c4009
Packit 6c4009
		  if (dh->nreloads != 0)
Packit 6c4009
		    dh->nreloads = 0;
Packit 6c4009
		}
Packit 6c4009
Packit 6c4009
	      result = dh;
Packit 6c4009
	      break;
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      work = here->next;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (nsearched > table->head->maxnsearched)
Packit 6c4009
    table->head->maxnsearched = nsearched;
Packit 6c4009
Packit 6c4009
  return result;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Add a new entry to the cache.  The return value is zero if the function
Packit 6c4009
   call was successful.
Packit 6c4009
Packit 6c4009
   This function must be called with the read-lock held.
Packit 6c4009
Packit 6c4009
   We modify the table but we nevertheless only acquire a read-lock.
Packit 6c4009
   This is ok since we use operations which would be safe even without
Packit 6c4009
   locking, given that the `prune_cache' function never runs.  Using
Packit 6c4009
   the readlock reduces the chance of conflicts.  */
Packit 6c4009
int
Packit 6c4009
cache_add (int type, const void *key, size_t len, struct datahead *packet,
Packit 6c4009
	   bool first, struct database_dyn *table,
Packit 6c4009
	   uid_t owner, bool prune_wakeup)
Packit 6c4009
{
Packit 6c4009
  if (__glibc_unlikely (debug_level >= 2))
Packit 6c4009
    {
Packit 6c4009
      const char *str;
Packit 6c4009
      char buf[INET6_ADDRSTRLEN + 1];
Packit 6c4009
      if (type == GETHOSTBYADDR || type == GETHOSTBYADDRv6)
Packit 6c4009
	str = inet_ntop (type == GETHOSTBYADDR ? AF_INET : AF_INET6,
Packit 6c4009
			 key, buf, sizeof (buf));
Packit 6c4009
      else
Packit 6c4009
	str = key;
Packit 6c4009
Packit 6c4009
      dbg_log (_("add new entry \"%s\" of type %s for %s to cache%s"),
Packit 6c4009
	       str, serv2str[type], dbnames[table - dbs],
Packit 6c4009
	       first ? _(" (first)") : "");
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  unsigned long int hash = __nss_hash (key, len) % table->head->module;
Packit 6c4009
  struct hashentry *newp;
Packit 6c4009
Packit 6c4009
  newp = mempool_alloc (table, sizeof (struct hashentry), 0);
Packit 6c4009
  /* If we cannot allocate memory, just do not do anything.  */
Packit 6c4009
  if (newp == NULL)
Packit 6c4009
    {
Packit 6c4009
      /* If necessary mark the entry as unusable so that lookups will
Packit 6c4009
	 not use it.  */
Packit 6c4009
      if (first)
Packit 6c4009
	packet->usable = false;
Packit 6c4009
Packit 6c4009
      return -1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  newp->type = type;
Packit 6c4009
  newp->first = first;
Packit 6c4009
  newp->len = len;
Packit 6c4009
  newp->key = (char *) key - table->data;
Packit 6c4009
  assert (newp->key + newp->len <= table->head->first_free);
Packit 6c4009
  newp->owner = owner;
Packit 6c4009
  newp->packet = (char *) packet - table->data;
Packit 6c4009
  assert ((newp->packet & BLOCK_ALIGN_M1) == 0);
Packit 6c4009
Packit 6c4009
  /* Put the new entry in the first position.  */
Packit 6c4009
  /* TODO Review concurrency.  Use atomic_exchange_release.  */
Packit 6c4009
  newp->next = atomic_load_relaxed (&table->head->array[hash]);
Packit 6c4009
  while (!atomic_compare_exchange_weak_release (&table->head->array[hash],
Packit 6c4009
						(ref_t *) &newp->next,
Packit 6c4009
						(ref_t) ((char *) newp
Packit 6c4009
							 - table->data)));
Packit 6c4009
Packit 6c4009
  /* Update the statistics.  */
Packit 6c4009
  if (packet->notfound)
Packit 6c4009
    ++table->head->negmiss;
Packit 6c4009
  else if (first)
Packit 6c4009
    ++table->head->posmiss;
Packit 6c4009
Packit 6c4009
  /* We depend on this value being correct and at least as high as the
Packit 6c4009
     real number of entries.  */
Packit 6c4009
  atomic_increment (&table->head->nentries);
Packit 6c4009
Packit 6c4009
  /* It does not matter that we are not loading the just increment
Packit 6c4009
     value, this is just for statistics.  */
Packit 6c4009
  unsigned long int nentries = table->head->nentries;
Packit 6c4009
  if (nentries > table->head->maxnentries)
Packit 6c4009
    table->head->maxnentries = nentries;
Packit 6c4009
Packit 6c4009
  if (table->persistent)
Packit 6c4009
    // XXX async OK?
Packit 6c4009
    msync ((void *) table->head,
Packit 6c4009
	   (char *) &table->head->array[hash] - (char *) table->head
Packit 6c4009
	   + sizeof (ref_t), MS_ASYNC);
Packit 6c4009
Packit 6c4009
  /* We do not have to worry about the pruning thread if we are
Packit 6c4009
     re-adding the data since this is done by the pruning thread.  We
Packit 6c4009
     also do not have to do anything in case this is not the first
Packit 6c4009
     time the data is entered since different data heads all have the
Packit 6c4009
     same timeout.  */
Packit 6c4009
  if (first && prune_wakeup)
Packit 6c4009
    {
Packit 6c4009
      /* Perhaps the prune thread for the table is not running in a long
Packit 6c4009
	 time.  Wake it if necessary.  */
Packit 6c4009
      pthread_mutex_lock (&table->prune_lock);
Packit 6c4009
      time_t next_wakeup = table->wakeup_time;
Packit 6c4009
      bool do_wakeup = false;
Packit 6c4009
      if (next_wakeup > packet->timeout + CACHE_PRUNE_INTERVAL)
Packit 6c4009
	{
Packit 6c4009
	  table->wakeup_time = packet->timeout;
Packit 6c4009
	  do_wakeup = true;
Packit 6c4009
	}
Packit 6c4009
      pthread_mutex_unlock (&table->prune_lock);
Packit 6c4009
      if (do_wakeup)
Packit 6c4009
	pthread_cond_signal (&table->prune_cond);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Walk through the table and remove all entries which lifetime ended.
Packit 6c4009
Packit 6c4009
   We have a problem here.  To actually remove the entries we must get
Packit 6c4009
   the write-lock.  But since we want to keep the time we have the
Packit 6c4009
   lock as short as possible we cannot simply acquire the lock when we
Packit 6c4009
   start looking for timedout entries.
Packit 6c4009
Packit 6c4009
   Therefore we do it in two stages: first we look for entries which
Packit 6c4009
   must be invalidated and remember them.  Then we get the lock and
Packit 6c4009
   actually remove them.  This is complicated by the way we have to
Packit 6c4009
   free the data structures since some hash table entries share the same
Packit 6c4009
   data.  */
Packit 6c4009
time_t
Packit 6c4009
prune_cache (struct database_dyn *table, time_t now, int fd)
Packit 6c4009
{
Packit 6c4009
  size_t cnt = table->head->module;
Packit 6c4009
Packit 6c4009
  /* If this table is not actually used don't do anything.  */
Packit 6c4009
  if (cnt == 0)
Packit 6c4009
    {
Packit 6c4009
      if (fd != -1)
Packit 6c4009
	{
Packit 6c4009
	  /* Reply to the INVALIDATE initiator.  */
Packit 6c4009
	  int32_t resp = 0;
Packit 6c4009
	  writeall (fd, &resp, sizeof (resp));
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      /* No need to do this again anytime soon.  */
Packit 6c4009
      return 24 * 60 * 60;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* If we check for the modification of the underlying file we invalidate
Packit 6c4009
     the entries also in this case.  */
Packit 6c4009
  if (table->check_file && now != LONG_MAX)
Packit 6c4009
    {
Packit 6c4009
      struct traced_file *runp = table->traced_files;
Packit 6c4009
Packit 6c4009
      while (runp != NULL)
Packit 6c4009
	{
Packit 6c4009
#ifdef HAVE_INOTIFY
Packit 6c4009
	  if (runp->inotify_descr[TRACED_FILE] == -1)
Packit 6c4009
#endif
Packit 6c4009
	    {
Packit 6c4009
	      struct stat64 st;
Packit 6c4009
Packit 6c4009
	      if (stat64 (runp->fname, &st) < 0)
Packit 6c4009
		{
Packit 6c4009
		  /* Print a diagnostic that the traced file was missing.
Packit 6c4009
		     We must not disable tracing since the file might return
Packit 6c4009
		     shortly and we want to reload it at the next pruning.
Packit 6c4009
		     Disabling tracing here would go against the configuration
Packit 6c4009
		     as specified by the user via check-files.  */
Packit 6c4009
		  char buf[128];
Packit 6c4009
		  dbg_log (_("checking for monitored file `%s': %s"),
Packit 6c4009
			   runp->fname, strerror_r (errno, buf, sizeof (buf)));
Packit 6c4009
		}
Packit 6c4009
	      else
Packit 6c4009
		{
Packit 6c4009
		  /* This must be `!=` to catch cases where users turn the
Packit 6c4009
		     clocks back and we still want to detect any time difference
Packit 6c4009
		     in mtime.  */
Packit 6c4009
		  if (st.st_mtime != runp->mtime)
Packit 6c4009
		    {
Packit 6c4009
		      dbg_log (_("monitored file `%s` changed (mtime)"),
Packit 6c4009
			       runp->fname);
Packit 6c4009
		      /* The file changed. Invalidate all entries.  */
Packit 6c4009
		      now = LONG_MAX;
Packit 6c4009
		      runp->mtime = st.st_mtime;
Packit 6c4009
#ifdef HAVE_INOTIFY
Packit 6c4009
		      /* Attempt to install a watch on the file.  */
Packit 6c4009
		      install_watches (runp);
Packit 6c4009
#endif
Packit 6c4009
		    }
Packit 6c4009
		}
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  runp = runp->next;
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* We run through the table and find values which are not valid anymore.
Packit 6c4009
Packit 6c4009
     Note that for the initial step, finding the entries to be removed,
Packit 6c4009
     we don't need to get any lock.  It is at all timed assured that the
Packit 6c4009
     linked lists are set up correctly and that no second thread prunes
Packit 6c4009
     the cache.  */
Packit 6c4009
  bool *mark;
Packit 6c4009
  size_t memory_needed = cnt * sizeof (bool);
Packit 6c4009
  bool mark_use_alloca;
Packit 6c4009
  if (__glibc_likely (memory_needed <= MAX_STACK_USE))
Packit 6c4009
    {
Packit 6c4009
      mark = alloca (cnt * sizeof (bool));
Packit 6c4009
      memset (mark, '\0', memory_needed);
Packit 6c4009
      mark_use_alloca = true;
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      mark = xcalloc (1, memory_needed);
Packit 6c4009
      mark_use_alloca = false;
Packit 6c4009
    }
Packit 6c4009
  size_t first = cnt + 1;
Packit 6c4009
  size_t last = 0;
Packit 6c4009
  char *const data = table->data;
Packit 6c4009
  bool any = false;
Packit 6c4009
Packit 6c4009
  if (__glibc_unlikely (debug_level > 2))
Packit 6c4009
    dbg_log (_("pruning %s cache; time %ld"),
Packit 6c4009
	     dbnames[table - dbs], (long int) now);
Packit 6c4009
Packit 6c4009
#define NO_TIMEOUT LONG_MAX
Packit 6c4009
  time_t next_timeout = NO_TIMEOUT;
Packit 6c4009
  do
Packit 6c4009
    {
Packit 6c4009
      ref_t run = table->head->array[--cnt];
Packit 6c4009
Packit 6c4009
      while (run != ENDREF)
Packit 6c4009
	{
Packit 6c4009
	  struct hashentry *runp = (struct hashentry *) (data + run);
Packit 6c4009
	  struct datahead *dh = (struct datahead *) (data + runp->packet);
Packit 6c4009
Packit 6c4009
	  /* Some debug support.  */
Packit 6c4009
	  if (__glibc_unlikely (debug_level > 2))
Packit 6c4009
	    {
Packit 6c4009
	      char buf[INET6_ADDRSTRLEN];
Packit 6c4009
	      const char *str;
Packit 6c4009
Packit 6c4009
	      if (runp->type == GETHOSTBYADDR || runp->type == GETHOSTBYADDRv6)
Packit 6c4009
		{
Packit 6c4009
		  inet_ntop (runp->type == GETHOSTBYADDR ? AF_INET : AF_INET6,
Packit 6c4009
			     data + runp->key, buf, sizeof (buf));
Packit 6c4009
		  str = buf;
Packit 6c4009
		}
Packit 6c4009
	      else
Packit 6c4009
		str = data + runp->key;
Packit 6c4009
Packit 6c4009
	      dbg_log (_("considering %s entry \"%s\", timeout %" PRIu64),
Packit 6c4009
		       serv2str[runp->type], str, dh->timeout);
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  /* Check whether the entry timed out.  */
Packit 6c4009
	  if (dh->timeout < now)
Packit 6c4009
	    {
Packit 6c4009
	      /* This hash bucket could contain entries which need to
Packit 6c4009
		 be looked at.  */
Packit 6c4009
	      mark[cnt] = true;
Packit 6c4009
Packit 6c4009
	      first = MIN (first, cnt);
Packit 6c4009
	      last = MAX (last, cnt);
Packit 6c4009
Packit 6c4009
	      /* We only have to look at the data of the first entries
Packit 6c4009
		 since the count information is kept in the data part
Packit 6c4009
		 which is shared.  */
Packit 6c4009
	      if (runp->first)
Packit 6c4009
		{
Packit 6c4009
Packit 6c4009
		  /* At this point there are two choices: we reload the
Packit 6c4009
		     value or we discard it.  Do not change NRELOADS if
Packit 6c4009
		     we never not reload the record.  */
Packit 6c4009
		  if ((reload_count != UINT_MAX
Packit 6c4009
		       && __builtin_expect (dh->nreloads >= reload_count, 0))
Packit 6c4009
		      /* We always remove negative entries.  */
Packit 6c4009
		      || dh->notfound
Packit 6c4009
		      /* Discard everything if the user explicitly
Packit 6c4009
			 requests it.  */
Packit 6c4009
		      || now == LONG_MAX)
Packit 6c4009
		    {
Packit 6c4009
		      /* Remove the value.  */
Packit 6c4009
		      dh->usable = false;
Packit 6c4009
Packit 6c4009
		      /* We definitely have some garbage entries now.  */
Packit 6c4009
		      any = true;
Packit 6c4009
		    }
Packit 6c4009
		  else
Packit 6c4009
		    {
Packit 6c4009
		      /* Reload the value.  We do this only for the
Packit 6c4009
			 initially used key, not the additionally
Packit 6c4009
			 added derived value.  */
Packit 6c4009
		      assert (runp->type < LASTREQ
Packit 6c4009
			      && readdfcts[runp->type] != NULL);
Packit 6c4009
Packit 6c4009
		      time_t timeout = readdfcts[runp->type] (table, runp, dh);
Packit 6c4009
		      next_timeout = MIN (next_timeout, timeout);
Packit 6c4009
Packit 6c4009
		      /* If the entry has been replaced, we might need
Packit 6c4009
			 cleanup.  */
Packit 6c4009
		      any |= !dh->usable;
Packit 6c4009
		    }
Packit 6c4009
		}
Packit 6c4009
	    }
Packit 6c4009
	  else
Packit 6c4009
	    {
Packit 6c4009
	      assert (dh->usable);
Packit 6c4009
	      next_timeout = MIN (next_timeout, dh->timeout);
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  run = runp->next;
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
  while (cnt > 0);
Packit 6c4009
Packit 6c4009
  if (__glibc_unlikely (fd != -1))
Packit 6c4009
    {
Packit 6c4009
      /* Reply to the INVALIDATE initiator that the cache has been
Packit 6c4009
	 invalidated.  */
Packit 6c4009
      int32_t resp = 0;
Packit 6c4009
      writeall (fd, &resp, sizeof (resp));
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (first <= last)
Packit 6c4009
    {
Packit 6c4009
      struct hashentry *head = NULL;
Packit 6c4009
Packit 6c4009
      /* Now we have to get the write lock since we are about to modify
Packit 6c4009
	 the table.  */
Packit 6c4009
      if (__glibc_unlikely (pthread_rwlock_trywrlock (&table->lock) != 0))
Packit 6c4009
	{
Packit 6c4009
	  ++table->head->wrlockdelayed;
Packit 6c4009
	  pthread_rwlock_wrlock (&table->lock);
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      while (first <= last)
Packit 6c4009
	{
Packit 6c4009
	  if (mark[first])
Packit 6c4009
	    {
Packit 6c4009
	      ref_t *old = &table->head->array[first];
Packit 6c4009
	      ref_t run = table->head->array[first];
Packit 6c4009
Packit 6c4009
	      assert (run != ENDREF);
Packit 6c4009
	      do
Packit 6c4009
		{
Packit 6c4009
		  struct hashentry *runp = (struct hashentry *) (data + run);
Packit 6c4009
		  struct datahead *dh
Packit 6c4009
		    = (struct datahead *) (data + runp->packet);
Packit 6c4009
Packit 6c4009
		  if (! dh->usable)
Packit 6c4009
		    {
Packit 6c4009
		      /* We need the list only for debugging but it is
Packit 6c4009
			 more costly to avoid creating the list than
Packit 6c4009
			 doing it.  */
Packit 6c4009
		      runp->dellist = head;
Packit 6c4009
		      head = runp;
Packit 6c4009
Packit 6c4009
		      /* No need for an atomic operation, we have the
Packit 6c4009
			 write lock.  */
Packit 6c4009
		      --table->head->nentries;
Packit 6c4009
Packit 6c4009
		      run = *old = runp->next;
Packit 6c4009
		    }
Packit 6c4009
		  else
Packit 6c4009
		    {
Packit 6c4009
		      old = &runp->next;
Packit 6c4009
		      run = runp->next;
Packit 6c4009
		    }
Packit 6c4009
		}
Packit 6c4009
	      while (run != ENDREF);
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  ++first;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      /* It's all done.  */
Packit 6c4009
      pthread_rwlock_unlock (&table->lock);
Packit 6c4009
Packit 6c4009
      /* Make sure the data is saved to disk.  */
Packit 6c4009
      if (table->persistent)
Packit 6c4009
	msync (table->head,
Packit 6c4009
	       data + table->head->first_free - (char *) table->head,
Packit 6c4009
	       MS_ASYNC);
Packit 6c4009
Packit 6c4009
      /* One extra pass if we do debugging.  */
Packit 6c4009
      if (__glibc_unlikely (debug_level > 0))
Packit 6c4009
	{
Packit 6c4009
	  struct hashentry *runp = head;
Packit 6c4009
Packit 6c4009
	  while (runp != NULL)
Packit 6c4009
	    {
Packit 6c4009
	      char buf[INET6_ADDRSTRLEN];
Packit 6c4009
	      const char *str;
Packit 6c4009
Packit 6c4009
	      if (runp->type == GETHOSTBYADDR || runp->type == GETHOSTBYADDRv6)
Packit 6c4009
		{
Packit 6c4009
		  inet_ntop (runp->type == GETHOSTBYADDR ? AF_INET : AF_INET6,
Packit 6c4009
			     data + runp->key, buf, sizeof (buf));
Packit 6c4009
		  str = buf;
Packit 6c4009
		}
Packit 6c4009
	      else
Packit 6c4009
		str = data + runp->key;
Packit 6c4009
Packit 6c4009
	      dbg_log ("remove %s entry \"%s\"", serv2str[runp->type], str);
Packit 6c4009
Packit 6c4009
	      runp = runp->dellist;
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (__glibc_unlikely (! mark_use_alloca))
Packit 6c4009
    free (mark);
Packit 6c4009
Packit 6c4009
  /* Run garbage collection if any entry has been removed or replaced.  */
Packit 6c4009
  if (any)
Packit 6c4009
    gc (table);
Packit 6c4009
Packit 6c4009
  /* If there is no entry in the database and we therefore have no new
Packit 6c4009
     timeout value, tell the caller to wake up in 24 hours.  */
Packit 6c4009
  return next_timeout == NO_TIMEOUT ? 24 * 60 * 60 : next_timeout - now;
Packit 6c4009
}