about summary refs log tree commit diff
path: root/REORG.TODO/nscd
diff options
context:
space:
mode:
Diffstat (limited to 'REORG.TODO/nscd')
-rw-r--r--REORG.TODO/nscd/Depend1
-rw-r--r--REORG.TODO/nscd/Makefile103
-rw-r--r--REORG.TODO/nscd/aicache.c585
-rw-r--r--REORG.TODO/nscd/cache.c540
-rw-r--r--REORG.TODO/nscd/connections.c2558
-rw-r--r--REORG.TODO/nscd/dbg_log.c85
-rw-r--r--REORG.TODO/nscd/dbg_log.h30
-rw-r--r--REORG.TODO/nscd/gai.c48
-rw-r--r--REORG.TODO/nscd/getgrgid_r.c35
-rw-r--r--REORG.TODO/nscd/getgrnam_r.c34
-rw-r--r--REORG.TODO/nscd/gethstbyad_r.c46
-rw-r--r--REORG.TODO/nscd/gethstbynm3_r.c55
-rw-r--r--REORG.TODO/nscd/getpwnam_r.c31
-rw-r--r--REORG.TODO/nscd/getpwuid_r.c31
-rw-r--r--REORG.TODO/nscd/getsrvbynm_r.c30
-rw-r--r--REORG.TODO/nscd/getsrvbypt_r.c30
-rw-r--r--REORG.TODO/nscd/grpcache.c572
-rw-r--r--REORG.TODO/nscd/hstcache.c619
-rw-r--r--REORG.TODO/nscd/initgrcache.c438
-rw-r--r--REORG.TODO/nscd/mem.c589
-rw-r--r--REORG.TODO/nscd/netgroupcache.c699
-rw-r--r--REORG.TODO/nscd/nscd-client.h452
-rw-r--r--REORG.TODO/nscd/nscd.c700
-rw-r--r--REORG.TODO/nscd/nscd.conf88
-rw-r--r--REORG.TODO/nscd/nscd.h377
-rw-r--r--REORG.TODO/nscd/nscd.init116
-rw-r--r--REORG.TODO/nscd/nscd.service19
-rw-r--r--REORG.TODO/nscd/nscd.tmpfiles4
-rw-r--r--REORG.TODO/nscd/nscd_conf.c315
-rw-r--r--REORG.TODO/nscd/nscd_getai.c216
-rw-r--r--REORG.TODO/nscd/nscd_getgr_r.c330
-rw-r--r--REORG.TODO/nscd/nscd_gethst_r.c459
-rw-r--r--REORG.TODO/nscd/nscd_getpw_r.c241
-rw-r--r--REORG.TODO/nscd/nscd_getserv_r.c388
-rw-r--r--REORG.TODO/nscd/nscd_helper.c564
-rw-r--r--REORG.TODO/nscd/nscd_initgroups.c180
-rw-r--r--REORG.TODO/nscd/nscd_netgroup.c289
-rw-r--r--REORG.TODO/nscd/nscd_proto.h79
-rw-r--r--REORG.TODO/nscd/nscd_setup_thread.c27
-rw-r--r--REORG.TODO/nscd/nscd_stat.c318
-rw-r--r--REORG.TODO/nscd/pwdcache.c552
-rw-r--r--REORG.TODO/nscd/res_hconf.c13
-rw-r--r--REORG.TODO/nscd/selinux.c453
-rw-r--r--REORG.TODO/nscd/selinux.h61
-rw-r--r--REORG.TODO/nscd/servicescache.c474
45 files changed, 13874 insertions, 0 deletions
diff --git a/REORG.TODO/nscd/Depend b/REORG.TODO/nscd/Depend
new file mode 100644
index 0000000000..6c1aa44e6e
--- /dev/null
+++ b/REORG.TODO/nscd/Depend
@@ -0,0 +1 @@
+nptl
diff --git a/REORG.TODO/nscd/Makefile b/REORG.TODO/nscd/Makefile
new file mode 100644
index 0000000000..4126996887
--- /dev/null
+++ b/REORG.TODO/nscd/Makefile
@@ -0,0 +1,103 @@
+# Copyright (C) 1998-2017 Free Software Foundation, Inc.
+# This file is part of the GNU C Library.
+
+# The GNU C Library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+
+# The GNU C Library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+
+# You should have received a copy of the GNU Lesser General Public
+# License along with the GNU C Library; if not, see
+# <http://www.gnu.org/licenses/>.
+
+#
+#	Sub-makefile for nscd portion of the library.
+#
+subdir	:= nscd
+
+include ../Makeconfig
+
+ifneq ($(use-nscd),no)
+routines := nscd_getpw_r nscd_getgr_r nscd_gethst_r nscd_getai \
+	    nscd_initgroups nscd_getserv_r nscd_netgroup
+aux	:= nscd_helper
+endif
+
+# To find xmalloc.c
+vpath %.c ../locale/programs
+
+nscd-modules := nscd connections pwdcache getpwnam_r getpwuid_r grpcache \
+		getgrnam_r getgrgid_r hstcache gethstbyad_r gethstbynm3_r \
+		getsrvbynm_r getsrvbypt_r servicescache \
+		dbg_log nscd_conf nscd_stat cache mem nscd_setup_thread \
+		xmalloc xstrdup aicache initgrcache gai res_hconf \
+		netgroupcache
+
+ifeq ($(build-nscd)$(have-thread-library),yesyes)
+
+others += nscd
+others-pie += nscd
+install-sbin := nscd
+
+extra-objs = $(nscd-modules:=.o)
+
+endif
+
+all-nscd-modules := $(nscd-modules) selinux
+ifeq (yes,$(have-selinux))
+ifeq (yes,$(have-libaudit))
+libaudit = -laudit
+ifeq (yes,$(have-libcap))
+libcap = -lcap
+endif
+endif
+
+nscd-modules += selinux
+selinux-LIBS := -lselinux $(libaudit) $(libcap)
+
+# The configure.ac check for libselinux and its headers did not use
+# $SYSINCLUDES.  The directory specified by --with-headers usually
+# contains only the basic kernel interface headers, not something like
+# libselinux.  So the simplest thing is to presume that the standard
+# system headers will be ok for this file.
+$(objpfx)nscd_stat.o: sysincludes = # nothing
+$(objpfx)selinux.o: sysincludes = # nothing
+endif
+
+LDLIBS-nscd = $(selinux-LIBS)
+
+include ../Rules
+
+CFLAGS-nscd_getpw_r.c = -fexceptions
+CFLAGS-nscd_getgr_r.c = -fexceptions
+CFLAGS-nscd_gethst_r.c = -fexceptions
+CFLAGS-nscd_getai.c = -fexceptions
+CFLAGS-nscd_initgroups.c = -fexceptions
+
+CPPFLAGS-nscd += -D_FORTIFY_SOURCE=2
+
+ifeq (yesyes,$(have-fpie)$(build-shared))
+CFLAGS-nscd += $(pie-ccflag)
+endif
+
+ifeq (yesyes,$(have-fpie)$(build-shared))
+LDFLAGS-nscd = -Wl,-z,now
+endif
+
+# Set libof-nscd.
+cpp-srcs-left := $(nscd-modules)
+lib := nscd
+include $(patsubst %,$(..)libof-iterator.mk,$(cpp-srcs-left))
+
+$(objpfx)nscd: $(nscd-modules:%=$(objpfx)%.o)
+
+ifeq ($(build-shared),yes)
+$(objpfx)nscd: $(shared-thread-library) $(common-objpfx)nis/libnsl.so
+else
+$(objpfx)nscd: $(static-thread-library) $(common-objpfx)nis/libnsl.a
+endif
diff --git a/REORG.TODO/nscd/aicache.c b/REORG.TODO/nscd/aicache.c
new file mode 100644
index 0000000000..7bf4131979
--- /dev/null
+++ b/REORG.TODO/nscd/aicache.c
@@ -0,0 +1,585 @@
+/* Cache handling for host lookup.
+   Copyright (C) 2004-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@redhat.com>, 2004.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published
+   by the Free Software Foundation; version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
+
+#include <assert.h>
+#include <errno.h>
+#include <libintl.h>
+#include <netdb.h>
+#include <nss.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <resolv/resolv-internal.h>
+#include <resolv/res_hconf.h>
+
+#include "dbg_log.h"
+#include "nscd.h"
+#ifdef HAVE_SENDFILE
+# include <kernel-features.h>
+#endif
+
+
+typedef enum nss_status (*nss_gethostbyname4_r)
+  (const char *name, struct gaih_addrtuple **pat,
+   char *buffer, size_t buflen, int *errnop,
+   int *h_errnop, int32_t *ttlp);
+typedef enum nss_status (*nss_gethostbyname3_r)
+  (const char *name, int af, struct hostent *host,
+   char *buffer, size_t buflen, int *errnop,
+   int *h_errnop, int32_t *, char **);
+typedef enum nss_status (*nss_getcanonname_r)
+  (const char *name, char *buffer, size_t buflen, char **result,
+   int *errnop, int *h_errnop);
+
+
+static const ai_response_header notfound =
+{
+  .version = NSCD_VERSION,
+  .found = 0,
+  .naddrs = 0,
+  .addrslen = 0,
+  .canonlen = 0,
+  .error = 0
+};
+
+
+static time_t
+addhstaiX (struct database_dyn *db, int fd, request_header *req,
+	   void *key, uid_t uid, struct hashentry *const he,
+	   struct datahead *dh)
+{
+  /* Search for the entry matching the key.  Please note that we don't
+     look again in the table whether the dataset is now available.  We
+     simply insert it.  It does not matter if it is in there twice.  The
+     pruning function only will look at the timestamp.  */
+
+  /* We allocate all data in one memory block: the iov vector,
+     the response header and the dataset itself.  */
+  struct dataset
+  {
+    struct datahead head;
+    ai_response_header resp;
+    char strdata[0];
+  } *dataset = NULL;
+
+  if (__glibc_unlikely (debug_level > 0))
+    {
+      if (he == NULL)
+	dbg_log (_("Haven't found \"%s\" in hosts cache!"), (char *) key);
+      else
+	dbg_log (_("Reloading \"%s\" in hosts cache!"), (char *) key);
+    }
+
+  static service_user *hosts_database;
+  service_user *nip;
+  int no_more;
+  int rc6 = 0;
+  int rc4 = 0;
+  int herrno = 0;
+
+  if (hosts_database == NULL)
+    no_more = __nss_database_lookup ("hosts", NULL,
+				     "dns [!UNAVAIL=return] files",
+				     &hosts_database);
+  else
+    no_more = 0;
+  nip = hosts_database;
+
+  /* Initialize configurations.  */
+  _res_hconf_init ();
+  if (__res_maybe_init (&_res, 0) == -1)
+    no_more = 1;
+
+  /* If we are looking for both IPv4 and IPv6 address we don't want
+     the lookup functions to automatically promote IPv4 addresses to
+     IPv6 addresses.  Currently this is decided by setting the
+     RES_USE_INET6 bit in _res.options.  */
+  int old_res_options = _res.options;
+  _res.options &= ~DEPRECATED_RES_USE_INET6;
+
+  size_t tmpbuf6len = 1024;
+  char *tmpbuf6 = alloca (tmpbuf6len);
+  size_t tmpbuf4len = 0;
+  char *tmpbuf4 = NULL;
+  int32_t ttl = INT32_MAX;
+  ssize_t total = 0;
+  char *key_copy = NULL;
+  bool alloca_used = false;
+  time_t timeout = MAX_TIMEOUT_VALUE;
+
+  while (!no_more)
+    {
+      void *cp;
+      int status[2] = { NSS_STATUS_UNAVAIL, NSS_STATUS_UNAVAIL };
+      int naddrs = 0;
+      size_t addrslen = 0;
+      char *canon = NULL;
+      size_t canonlen;
+
+      nss_gethostbyname4_r fct4 = __nss_lookup_function (nip,
+							 "gethostbyname4_r");
+      if (fct4 != NULL)
+	{
+	  struct gaih_addrtuple atmem;
+	  struct gaih_addrtuple *at;
+	  while (1)
+	    {
+	      at = &atmem;
+	      rc6 = 0;
+	      herrno = 0;
+	      status[1] = DL_CALL_FCT (fct4, (key, &at, tmpbuf6, tmpbuf6len,
+					      &rc6, &herrno, &ttl));
+	      if (rc6 != ERANGE || (herrno != NETDB_INTERNAL
+				    && herrno != TRY_AGAIN))
+		break;
+	      tmpbuf6 = extend_alloca (tmpbuf6, tmpbuf6len, 2 * tmpbuf6len);
+	    }
+
+	  if (rc6 != 0 && herrno == NETDB_INTERNAL)
+	    goto out;
+
+	  if (status[1] != NSS_STATUS_SUCCESS)
+	    goto next_nip;
+
+	  /* We found the data.  Count the addresses and the size.  */
+	  for (const struct gaih_addrtuple *at2 = at = &atmem; at2 != NULL;
+	       at2 = at2->next)
+	    {
+	      ++naddrs;
+	      /* We do not handle anything other than IPv4 and IPv6
+		 addresses.  The getaddrinfo implementation does not
+		 either so it is not worth trying to do more.  */
+	      if (at2->family == AF_INET)
+		addrslen += INADDRSZ;
+	      else if (at2->family == AF_INET6)
+		addrslen += IN6ADDRSZ;
+	    }
+	  canon = at->name;
+	  canonlen = strlen (canon) + 1;
+
+	  total = sizeof (*dataset) + naddrs + addrslen + canonlen;
+
+	  /* Now we can allocate the data structure.  If the TTL of the
+	     entry is reported as zero do not cache the entry at all.  */
+	  if (ttl != 0 && he == NULL)
+	    dataset = (struct dataset *) mempool_alloc (db, total
+							+ req->key_len, 1);
+
+	  if (dataset == NULL)
+	    {
+	      /* We cannot permanently add the result in the moment.  But
+		 we can provide the result as is.  Store the data in some
+		 temporary memory.  */
+	      dataset = (struct dataset *) alloca (total + req->key_len);
+
+	      /* We cannot add this record to the permanent database.  */
+	      alloca_used = true;
+	    }
+
+	  /* Fill in the address and address families.  */
+	  char *addrs = dataset->strdata;
+	  uint8_t *family = (uint8_t *) (addrs + addrslen);
+
+	  for (const struct gaih_addrtuple *at2 = at; at2 != NULL;
+	       at2 = at2->next)
+	    {
+	      *family++ = at2->family;
+	      if (at2->family == AF_INET)
+		addrs = mempcpy (addrs, at2->addr, INADDRSZ);
+	      else if (at2->family == AF_INET6)
+		addrs = mempcpy (addrs, at2->addr, IN6ADDRSZ);
+	    }
+
+	  cp = family;
+	}
+      else
+	{
+	  /* Prefer the function which also returns the TTL and
+	     canonical name.  */
+	  nss_gethostbyname3_r fct = __nss_lookup_function (nip,
+							    "gethostbyname3_r");
+	  if (fct == NULL)
+	    fct = __nss_lookup_function (nip, "gethostbyname2_r");
+
+	  if (fct == NULL)
+	    goto next_nip;
+
+	  struct hostent th[2];
+
+	  /* Collect IPv6 information first.  */
+	  while (1)
+	    {
+	      rc6 = 0;
+	      status[0] = DL_CALL_FCT (fct, (key, AF_INET6, &th[0], tmpbuf6,
+					     tmpbuf6len, &rc6, &herrno, &ttl,
+					     &canon));
+	      if (rc6 != ERANGE || herrno != NETDB_INTERNAL)
+		break;
+	      tmpbuf6 = extend_alloca (tmpbuf6, tmpbuf6len, 2 * tmpbuf6len);
+	    }
+
+	  if (rc6 != 0 && herrno == NETDB_INTERNAL)
+	    goto out;
+
+	  /* If the IPv6 lookup has been successful do not use the
+	     buffer used in that lookup, use a new one.  */
+	  if (status[0] == NSS_STATUS_SUCCESS && rc6 == 0)
+	    {
+	      tmpbuf4len = 512;
+	      tmpbuf4 = alloca (tmpbuf4len);
+	    }
+	  else
+	    {
+	      tmpbuf4len = tmpbuf6len;
+	      tmpbuf4 = tmpbuf6;
+	    }
+
+	  /* Next collect IPv4 information.  */
+	  while (1)
+	    {
+	      rc4 = 0;
+	      status[1] = DL_CALL_FCT (fct, (key, AF_INET, &th[1], tmpbuf4,
+					     tmpbuf4len, &rc4, &herrno,
+					     ttl == INT32_MAX ? &ttl : NULL,
+					     canon == NULL ? &canon : NULL));
+	      if (rc4 != ERANGE || herrno != NETDB_INTERNAL)
+		break;
+	      tmpbuf4 = extend_alloca (tmpbuf4, tmpbuf4len, 2 * tmpbuf4len);
+	    }
+
+	  if (rc4 != 0 && herrno == NETDB_INTERNAL)
+	    goto out;
+
+	  if (status[0] != NSS_STATUS_SUCCESS
+	      && status[1] != NSS_STATUS_SUCCESS)
+	    goto next_nip;
+
+	  /* We found the data.  Count the addresses and the size.  */
+	  for (int j = 0; j < 2; ++j)
+	    if (status[j] == NSS_STATUS_SUCCESS)
+	      for (int i = 0; th[j].h_addr_list[i] != NULL; ++i)
+		{
+		  ++naddrs;
+		  addrslen += th[j].h_length;
+		}
+
+	  if (canon == NULL)
+	    {
+	      /* Determine the canonical name.  */
+	      nss_getcanonname_r cfct;
+	      cfct = __nss_lookup_function (nip, "getcanonname_r");
+	      if (cfct != NULL)
+		{
+		  const size_t max_fqdn_len = 256;
+		  char *buf = alloca (max_fqdn_len);
+		  char *s;
+		  int rc;
+
+		  if (DL_CALL_FCT (cfct, (key, buf, max_fqdn_len, &s,
+					  &rc, &herrno))
+		      == NSS_STATUS_SUCCESS)
+		    canon = s;
+		  else
+		    /* Set to name now to avoid using gethostbyaddr.  */
+		    canon = key;
+		}
+	      else
+		{
+		  struct hostent *hstent = NULL;
+		  int herrno;
+		  struct hostent hstent_mem;
+		  void *addr;
+		  size_t addrlen;
+		  int addrfamily;
+
+		  if (status[1] == NSS_STATUS_SUCCESS)
+		    {
+		      addr = th[1].h_addr_list[0];
+		      addrlen = sizeof (struct in_addr);
+		      addrfamily = AF_INET;
+		    }
+		  else
+		    {
+		      addr = th[0].h_addr_list[0];
+		      addrlen = sizeof (struct in6_addr);
+		      addrfamily = AF_INET6;
+		    }
+
+		  size_t tmpbuflen = 512;
+		  char *tmpbuf = alloca (tmpbuflen);
+		  int rc;
+		  while (1)
+		    {
+		      rc = __gethostbyaddr2_r (addr, addrlen, addrfamily,
+					       &hstent_mem, tmpbuf, tmpbuflen,
+					       &hstent, &herrno, NULL);
+		      if (rc != ERANGE || herrno != NETDB_INTERNAL)
+			break;
+		      tmpbuf = extend_alloca (tmpbuf, tmpbuflen,
+					      tmpbuflen * 2);
+		    }
+
+		  if (rc == 0)
+		    {
+		      if (hstent != NULL)
+			canon = hstent->h_name;
+		      else
+			canon = key;
+		    }
+		}
+	    }
+
+	  canonlen = canon == NULL ? 0 : (strlen (canon) + 1);
+
+	  total = sizeof (*dataset) + naddrs + addrslen + canonlen;
+
+
+	  /* Now we can allocate the data structure.  If the TTL of the
+	     entry is reported as zero do not cache the entry at all.  */
+	  if (ttl != 0 && he == NULL)
+	    dataset = (struct dataset *) mempool_alloc (db, total
+							+ req->key_len, 1);
+
+	  if (dataset == NULL)
+	    {
+	      /* We cannot permanently add the result in the moment.  But
+		 we can provide the result as is.  Store the data in some
+		 temporary memory.  */
+	      dataset = (struct dataset *) alloca (total + req->key_len);
+
+	      /* We cannot add this record to the permanent database.  */
+	      alloca_used = true;
+	    }
+
+	  /* Fill in the address and address families.  */
+	  char *addrs = dataset->strdata;
+	  uint8_t *family = (uint8_t *) (addrs + addrslen);
+
+	  for (int j = 0; j < 2; ++j)
+	    if (status[j] == NSS_STATUS_SUCCESS)
+	      for (int i = 0; th[j].h_addr_list[i] != NULL; ++i)
+		{
+		  addrs = mempcpy (addrs, th[j].h_addr_list[i],
+				   th[j].h_length);
+		  *family++ = th[j].h_addrtype;
+		}
+
+	  cp = family;
+	}
+
+      timeout = datahead_init_pos (&dataset->head, total + req->key_len,
+				   total - offsetof (struct dataset, resp),
+				   he == NULL ? 0 : dh->nreloads + 1,
+				   ttl == INT32_MAX ? db->postimeout : ttl);
+
+      /* Fill in the rest of the dataset.  */
+      dataset->resp.version = NSCD_VERSION;
+      dataset->resp.found = 1;
+      dataset->resp.naddrs = naddrs;
+      dataset->resp.addrslen = addrslen;
+      dataset->resp.canonlen = canonlen;
+      dataset->resp.error = NETDB_SUCCESS;
+
+      if (canon != NULL)
+	cp = mempcpy (cp, canon, canonlen);
+
+      key_copy = memcpy (cp, key, req->key_len);
+
+      assert (cp == (char *) dataset + total);
+
+      /* Now we can determine whether on refill we have to create a
+	 new record or not.  */
+      if (he != NULL)
+	{
+	  assert (fd == -1);
+
+	  if (total + req->key_len == dh->allocsize
+	      && total - offsetof (struct dataset, resp) == dh->recsize
+	      && memcmp (&dataset->resp, dh->data,
+			 dh->allocsize - offsetof (struct dataset,
+						   resp)) == 0)
+	    {
+	      /* The data has not changed.  We will just bump the
+		 timeout value.  Note that the new record has been
+		 allocated on the stack and need not be freed.  */
+	      dh->timeout = dataset->head.timeout;
+	      dh->ttl = dataset->head.ttl;
+	      ++dh->nreloads;
+	    }
+	  else
+	    {
+	      /* We have to create a new record.  Just allocate
+		 appropriate memory and copy it.  */
+	      struct dataset *newp
+		= (struct dataset *) mempool_alloc (db, total + req->key_len,
+						    1);
+	      if (__glibc_likely (newp != NULL))
+		{
+		  /* Adjust pointer into the memory block.  */
+		  key_copy = (char *) newp + (key_copy - (char *) dataset);
+
+		  dataset = memcpy (newp, dataset, total + req->key_len);
+		  alloca_used = false;
+		}
+
+	      /* Mark the old record as obsolete.  */
+	      dh->usable = false;
+	    }
+	}
+      else
+	{
+	  /* We write the dataset before inserting it to the database
+	     since while inserting this thread might block and so
+	     would unnecessarily let the receiver wait.  */
+	  assert (fd != -1);
+
+#ifdef HAVE_SENDFILE
+	  if (__builtin_expect (db->mmap_used, 1) && !alloca_used)
+	    {
+	      assert (db->wr_fd != -1);
+	      assert ((char *) &dataset->resp > (char *) db->data);
+	      assert ((char *) dataset - (char *) db->head + total
+		      <= (sizeof (struct database_pers_head)
+			  + db->head->module * sizeof (ref_t)
+			  + db->head->data_size));
+# ifndef __ASSUME_SENDFILE
+	      ssize_t written;
+	      written =
+# endif
+		sendfileall (fd, db->wr_fd, (char *) &dataset->resp
+			     - (char *) db->head, dataset->head.recsize);
+# ifndef __ASSUME_SENDFILE
+	      if (written == -1 && errno == ENOSYS)
+		goto use_write;
+# endif
+	    }
+	  else
+# ifndef __ASSUME_SENDFILE
+	  use_write:
+# endif
+#endif
+	    writeall (fd, &dataset->resp, dataset->head.recsize);
+	}
+
+      goto out;
+
+next_nip:
+      if (nss_next_action (nip, status[1]) == NSS_ACTION_RETURN)
+	break;
+
+      if (nip->next == NULL)
+	no_more = -1;
+      else
+	nip = nip->next;
+    }
+
+  /* No result found.  Create a negative result record.  */
+  if (he != NULL && rc4 == EAGAIN)
+    {
+      /* If we have an old record available but cannot find one now
+	 because the service is not available we keep the old record
+	 and make sure it does not get removed.  */
+      if (reload_count != UINT_MAX && dh->nreloads == reload_count)
+	/* Do not reset the value if we never not reload the record.  */
+	dh->nreloads = reload_count - 1;
+
+      /* Reload with the same time-to-live value.  */
+      timeout = dh->timeout = time (NULL) + dh->ttl;
+    }
+  else
+    {
+      /* We have no data.  This means we send the standard reply for
+	 this case.  */
+      total = sizeof (notfound);
+
+      if (fd != -1)
+	TEMP_FAILURE_RETRY (send (fd, &notfound, total, MSG_NOSIGNAL));
+
+      /* If we have a transient error or cannot permanently store the
+	 result, so be it.  */
+      if (rc4 == EAGAIN || __builtin_expect (db->negtimeout == 0, 0))
+	{
+	  /* Mark the old entry as obsolete.  */
+	  if (dh != NULL)
+	    dh->usable = false;
+	  dataset = NULL;
+	}
+      else if ((dataset = mempool_alloc (db, (sizeof (struct dataset)
+					      + req->key_len), 1)) != NULL)
+	{
+	  timeout = datahead_init_neg (&dataset->head,
+				       sizeof (struct dataset) + req->key_len,
+				       total, db->negtimeout);
+
+	  /* This is the reply.  */
+	  memcpy (&dataset->resp, &notfound, total);
+
+	  /* Copy the key data.  */
+	  key_copy = memcpy (dataset->strdata, key, req->key_len);
+	}
+   }
+
+ out:
+  _res.options |= old_res_options & DEPRECATED_RES_USE_INET6;
+
+  if (dataset != NULL && !alloca_used)
+    {
+      /* If necessary, we also propagate the data to disk.  */
+      if (db->persistent)
+	{
+	  // XXX async OK?
+	  uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
+	  msync ((void *) pval,
+		 ((uintptr_t) dataset & pagesize_m1) + total + req->key_len,
+		 MS_ASYNC);
+	}
+
+      (void) cache_add (req->type, key_copy, req->key_len, &dataset->head,
+			true, db, uid, he == NULL);
+
+      pthread_rwlock_unlock (&db->lock);
+
+      /* Mark the old entry as obsolete.  */
+      if (dh != NULL)
+	dh->usable = false;
+    }
+
+  return timeout;
+}
+
+
+void
+addhstai (struct database_dyn *db, int fd, request_header *req, void *key,
+	  uid_t uid)
+{
+  addhstaiX (db, fd, req, key, uid, NULL, NULL);
+}
+
+
+time_t
+readdhstai (struct database_dyn *db, struct hashentry *he, struct datahead *dh)
+{
+  request_header req =
+    {
+      .type = GETAI,
+      .key_len = he->len
+    };
+
+  return addhstaiX (db, -1, &req, db->data + he->key, he->owner, he, dh);
+}
diff --git a/REORG.TODO/nscd/cache.c b/REORG.TODO/nscd/cache.c
new file mode 100644
index 0000000000..b9dbc7a0bd
--- /dev/null
+++ b/REORG.TODO/nscd/cache.c
@@ -0,0 +1,540 @@
+/* Copyright (c) 1998-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published
+   by the Free Software Foundation; version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
+
+#include <assert.h>
+#include <atomic.h>
+#include <errno.h>
+#include <error.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libintl.h>
+#include <arpa/inet.h>
+#include <rpcsvc/nis.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+
+#include "nscd.h"
+#include "dbg_log.h"
+
+
+/* Wrapper functions with error checking for standard functions.  */
+extern void *xcalloc (size_t n, size_t s);
+
+
+/* Number of times a value is reloaded without being used.  UINT_MAX
+   means unlimited.  */
+unsigned int reload_count = DEFAULT_RELOAD_LIMIT;
+
+
+static time_t (*const readdfcts[LASTREQ]) (struct database_dyn *,
+					   struct hashentry *,
+					   struct datahead *) =
+{
+  [GETPWBYNAME] = readdpwbyname,
+  [GETPWBYUID] = readdpwbyuid,
+  [GETGRBYNAME] = readdgrbyname,
+  [GETGRBYGID] = readdgrbygid,
+  [GETHOSTBYNAME] = readdhstbyname,
+  [GETHOSTBYNAMEv6] = readdhstbynamev6,
+  [GETHOSTBYADDR] = readdhstbyaddr,
+  [GETHOSTBYADDRv6] = readdhstbyaddrv6,
+  [GETAI] = readdhstai,
+  [INITGROUPS] = readdinitgroups,
+  [GETSERVBYNAME] = readdservbyname,
+  [GETSERVBYPORT] = readdservbyport,
+  [GETNETGRENT] = readdgetnetgrent,
+  [INNETGR] = readdinnetgr
+};
+
+
+/* Search the cache for a matching entry and return it when found.  If
+   this fails search the negative cache and return (void *) -1 if this
+   search was successful.  Otherwise return NULL.
+
+   This function must be called with the read-lock held.  */
+struct datahead *
+cache_search (request_type type, const void *key, size_t len,
+	      struct database_dyn *table, uid_t owner)
+{
+  unsigned long int hash = __nis_hash (key, len) % table->head->module;
+
+  unsigned long int nsearched = 0;
+  struct datahead *result = NULL;
+
+  ref_t work = table->head->array[hash];
+  while (work != ENDREF)
+    {
+      ++nsearched;
+
+      struct hashentry *here = (struct hashentry *) (table->data + work);
+
+      if (type == here->type && len == here->len
+	  && memcmp (key, table->data + here->key, len) == 0
+	  && here->owner == owner)
+	{
+	  /* We found the entry.  Increment the appropriate counter.  */
+	  struct datahead *dh
+	    = (struct datahead *) (table->data + here->packet);
+
+	  /* See whether we must ignore the entry.  */
+	  if (dh->usable)
+	    {
+	      /* We do not synchronize the memory here.  The statistics
+		 data is not crucial, we synchronize only once in a while
+		 in the cleanup threads.  */
+	      if (dh->notfound)
+		++table->head->neghit;
+	      else
+		{
+		  ++table->head->poshit;
+
+		  if (dh->nreloads != 0)
+		    dh->nreloads = 0;
+		}
+
+	      result = dh;
+	      break;
+	    }
+	}
+
+      work = here->next;
+    }
+
+  if (nsearched > table->head->maxnsearched)
+    table->head->maxnsearched = nsearched;
+
+  return result;
+}
+
+/* Add a new entry to the cache.  The return value is zero if the function
+   call was successful.
+
+   This function must be called with the read-lock held.
+
+   We modify the table but we nevertheless only acquire a read-lock.
+   This is ok since we use operations which would be safe even without
+   locking, given that the `prune_cache' function never runs.  Using
+   the readlock reduces the chance of conflicts.  */
+int
+cache_add (int type, const void *key, size_t len, struct datahead *packet,
+	   bool first, struct database_dyn *table,
+	   uid_t owner, bool prune_wakeup)
+{
+  if (__glibc_unlikely (debug_level >= 2))
+    {
+      const char *str;
+      char buf[INET6_ADDRSTRLEN + 1];
+      if (type == GETHOSTBYADDR || type == GETHOSTBYADDRv6)
+	str = inet_ntop (type == GETHOSTBYADDR ? AF_INET : AF_INET6,
+			 key, buf, sizeof (buf));
+      else
+	str = key;
+
+      dbg_log (_("add new entry \"%s\" of type %s for %s to cache%s"),
+	       str, serv2str[type], dbnames[table - dbs],
+	       first ? _(" (first)") : "");
+    }
+
+  unsigned long int hash = __nis_hash (key, len) % table->head->module;
+  struct hashentry *newp;
+
+  newp = mempool_alloc (table, sizeof (struct hashentry), 0);
+  /* If we cannot allocate memory, just do not do anything.  */
+  if (newp == NULL)
+    {
+      /* If necessary mark the entry as unusable so that lookups will
+	 not use it.  */
+      if (first)
+	packet->usable = false;
+
+      return -1;
+    }
+
+  newp->type = type;
+  newp->first = first;
+  newp->len = len;
+  newp->key = (char *) key - table->data;
+  assert (newp->key + newp->len <= table->head->first_free);
+  newp->owner = owner;
+  newp->packet = (char *) packet - table->data;
+  assert ((newp->packet & BLOCK_ALIGN_M1) == 0);
+
+  /* Put the new entry in the first position.  */
+  /* TODO Review concurrency.  Use atomic_exchange_release.  */
+  newp->next = atomic_load_relaxed (&table->head->array[hash]);
+  while (!atomic_compare_exchange_weak_release (&table->head->array[hash],
+						(ref_t *) &newp->next,
+						(ref_t) ((char *) newp
+							 - table->data)));
+
+  /* Update the statistics.  */
+  if (packet->notfound)
+    ++table->head->negmiss;
+  else if (first)
+    ++table->head->posmiss;
+
+  /* We depend on this value being correct and at least as high as the
+     real number of entries.  */
+  atomic_increment (&table->head->nentries);
+
+  /* It does not matter that we are not loading the just increment
+     value, this is just for statistics.  */
+  unsigned long int nentries = table->head->nentries;
+  if (nentries > table->head->maxnentries)
+    table->head->maxnentries = nentries;
+
+  if (table->persistent)
+    // XXX async OK?
+    msync ((void *) table->head,
+	   (char *) &table->head->array[hash] - (char *) table->head
+	   + sizeof (ref_t), MS_ASYNC);
+
+  /* We do not have to worry about the pruning thread if we are
+     re-adding the data since this is done by the pruning thread.  We
+     also do not have to do anything in case this is not the first
+     time the data is entered since different data heads all have the
+     same timeout.  */
+  if (first && prune_wakeup)
+    {
+      /* Perhaps the prune thread for the table is not running in a long
+	 time.  Wake it if necessary.  */
+      pthread_mutex_lock (&table->prune_lock);
+      time_t next_wakeup = table->wakeup_time;
+      bool do_wakeup = false;
+      if (next_wakeup > packet->timeout + CACHE_PRUNE_INTERVAL)
+	{
+	  table->wakeup_time = packet->timeout;
+	  do_wakeup = true;
+	}
+      pthread_mutex_unlock (&table->prune_lock);
+      if (do_wakeup)
+	pthread_cond_signal (&table->prune_cond);
+    }
+
+  return 0;
+}
+
+/* Walk through the table and remove all entries which lifetime ended.
+
+   We have a problem here.  To actually remove the entries we must get
+   the write-lock.  But since we want to keep the time we have the
+   lock as short as possible we cannot simply acquire the lock when we
+   start looking for timedout entries.
+
+   Therefore we do it in two stages: first we look for entries which
+   must be invalidated and remember them.  Then we get the lock and
+   actually remove them.  This is complicated by the way we have to
+   free the data structures since some hash table entries share the same
+   data.  */
+time_t
+prune_cache (struct database_dyn *table, time_t now, int fd)
+{
+  size_t cnt = table->head->module;
+
+  /* If this table is not actually used don't do anything.  */
+  if (cnt == 0)
+    {
+      if (fd != -1)
+	{
+	  /* Reply to the INVALIDATE initiator.  */
+	  int32_t resp = 0;
+	  writeall (fd, &resp, sizeof (resp));
+	}
+
+      /* No need to do this again anytime soon.  */
+      return 24 * 60 * 60;
+    }
+
+  /* If we check for the modification of the underlying file we invalidate
+     the entries also in this case.  */
+  if (table->check_file && now != LONG_MAX)
+    {
+      struct traced_file *runp = table->traced_files;
+
+      while (runp != NULL)
+	{
+#ifdef HAVE_INOTIFY
+	  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];
+		  dbg_log (_("checking for monitored file `%s': %s"),
+			   runp->fname, strerror_r (errno, buf, sizeof (buf)));
+		}
+	      else
+		{
+		  /* 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)
+		    {
+		      dbg_log (_("monitored file `%s` changed (mtime)"),
+			       runp->fname);
+		      /* The file changed. Invalidate all entries.  */
+		      now = LONG_MAX;
+		      runp->mtime = st.st_mtime;
+#ifdef HAVE_INOTIFY
+		      /* Attempt to install a watch on the file.  */
+		      install_watches (runp);
+#endif
+		    }
+		}
+	    }
+
+	  runp = runp->next;
+	}
+    }
+
+  /* We run through the table and find values which are not valid anymore.
+
+     Note that for the initial step, finding the entries to be removed,
+     we don't need to get any lock.  It is at all timed assured that the
+     linked lists are set up correctly and that no second thread prunes
+     the cache.  */
+  bool *mark;
+  size_t memory_needed = cnt * sizeof (bool);
+  bool mark_use_alloca;
+  if (__glibc_likely (memory_needed <= MAX_STACK_USE))
+    {
+      mark = alloca (cnt * sizeof (bool));
+      memset (mark, '\0', memory_needed);
+      mark_use_alloca = true;
+    }
+  else
+    {
+      mark = xcalloc (1, memory_needed);
+      mark_use_alloca = false;
+    }
+  size_t first = cnt + 1;
+  size_t last = 0;
+  char *const data = table->data;
+  bool any = false;
+
+  if (__glibc_unlikely (debug_level > 2))
+    dbg_log (_("pruning %s cache; time %ld"),
+	     dbnames[table - dbs], (long int) now);
+
+#define NO_TIMEOUT LONG_MAX
+  time_t next_timeout = NO_TIMEOUT;
+  do
+    {
+      ref_t run = table->head->array[--cnt];
+
+      while (run != ENDREF)
+	{
+	  struct hashentry *runp = (struct hashentry *) (data + run);
+	  struct datahead *dh = (struct datahead *) (data + runp->packet);
+
+	  /* Some debug support.  */
+	  if (__glibc_unlikely (debug_level > 2))
+	    {
+	      char buf[INET6_ADDRSTRLEN];
+	      const char *str;
+
+	      if (runp->type == GETHOSTBYADDR || runp->type == GETHOSTBYADDRv6)
+		{
+		  inet_ntop (runp->type == GETHOSTBYADDR ? AF_INET : AF_INET6,
+			     data + runp->key, buf, sizeof (buf));
+		  str = buf;
+		}
+	      else
+		str = data + runp->key;
+
+	      dbg_log (_("considering %s entry \"%s\", timeout %" PRIu64),
+		       serv2str[runp->type], str, dh->timeout);
+	    }
+
+	  /* Check whether the entry timed out.  */
+	  if (dh->timeout < now)
+	    {
+	      /* This hash bucket could contain entries which need to
+		 be looked at.  */
+	      mark[cnt] = true;
+
+	      first = MIN (first, cnt);
+	      last = MAX (last, cnt);
+
+	      /* We only have to look at the data of the first entries
+		 since the count information is kept in the data part
+		 which is shared.  */
+	      if (runp->first)
+		{
+
+		  /* At this point there are two choices: we reload the
+		     value or we discard it.  Do not change NRELOADS if
+		     we never not reload the record.  */
+		  if ((reload_count != UINT_MAX
+		       && __builtin_expect (dh->nreloads >= reload_count, 0))
+		      /* We always remove negative entries.  */
+		      || dh->notfound
+		      /* Discard everything if the user explicitly
+			 requests it.  */
+		      || now == LONG_MAX)
+		    {
+		      /* Remove the value.  */
+		      dh->usable = false;
+
+		      /* We definitely have some garbage entries now.  */
+		      any = true;
+		    }
+		  else
+		    {
+		      /* Reload the value.  We do this only for the
+			 initially used key, not the additionally
+			 added derived value.  */
+		      assert (runp->type < LASTREQ
+			      && readdfcts[runp->type] != NULL);
+
+		      time_t timeout = readdfcts[runp->type] (table, runp, dh);
+		      next_timeout = MIN (next_timeout, timeout);
+
+		      /* If the entry has been replaced, we might need
+			 cleanup.  */
+		      any |= !dh->usable;
+		    }
+		}
+	    }
+	  else
+	    {
+	      assert (dh->usable);
+	      next_timeout = MIN (next_timeout, dh->timeout);
+	    }
+
+	  run = runp->next;
+	}
+    }
+  while (cnt > 0);
+
+  if (__glibc_unlikely (fd != -1))
+    {
+      /* Reply to the INVALIDATE initiator that the cache has been
+	 invalidated.  */
+      int32_t resp = 0;
+      writeall (fd, &resp, sizeof (resp));
+    }
+
+  if (first <= last)
+    {
+      struct hashentry *head = NULL;
+
+      /* Now we have to get the write lock since we are about to modify
+	 the table.  */
+      if (__glibc_unlikely (pthread_rwlock_trywrlock (&table->lock) != 0))
+	{
+	  ++table->head->wrlockdelayed;
+	  pthread_rwlock_wrlock (&table->lock);
+	}
+
+      while (first <= last)
+	{
+	  if (mark[first])
+	    {
+	      ref_t *old = &table->head->array[first];
+	      ref_t run = table->head->array[first];
+
+	      assert (run != ENDREF);
+	      do
+		{
+		  struct hashentry *runp = (struct hashentry *) (data + run);
+		  struct datahead *dh
+		    = (struct datahead *) (data + runp->packet);
+
+		  if (! dh->usable)
+		    {
+		      /* We need the list only for debugging but it is
+			 more costly to avoid creating the list than
+			 doing it.  */
+		      runp->dellist = head;
+		      head = runp;
+
+		      /* No need for an atomic operation, we have the
+			 write lock.  */
+		      --table->head->nentries;
+
+		      run = *old = runp->next;
+		    }
+		  else
+		    {
+		      old = &runp->next;
+		      run = runp->next;
+		    }
+		}
+	      while (run != ENDREF);
+	    }
+
+	  ++first;
+	}
+
+      /* It's all done.  */
+      pthread_rwlock_unlock (&table->lock);
+
+      /* Make sure the data is saved to disk.  */
+      if (table->persistent)
+	msync (table->head,
+	       data + table->head->first_free - (char *) table->head,
+	       MS_ASYNC);
+
+      /* One extra pass if we do debugging.  */
+      if (__glibc_unlikely (debug_level > 0))
+	{
+	  struct hashentry *runp = head;
+
+	  while (runp != NULL)
+	    {
+	      char buf[INET6_ADDRSTRLEN];
+	      const char *str;
+
+	      if (runp->type == GETHOSTBYADDR || runp->type == GETHOSTBYADDRv6)
+		{
+		  inet_ntop (runp->type == GETHOSTBYADDR ? AF_INET : AF_INET6,
+			     data + runp->key, buf, sizeof (buf));
+		  str = buf;
+		}
+	      else
+		str = data + runp->key;
+
+	      dbg_log ("remove %s entry \"%s\"", serv2str[runp->type], str);
+
+	      runp = runp->dellist;
+	    }
+	}
+    }
+
+  if (__glibc_unlikely (! mark_use_alloca))
+    free (mark);
+
+  /* Run garbage collection if any entry has been removed or replaced.  */
+  if (any)
+    gc (table);
+
+  /* If there is no entry in the database and we therefore have no new
+     timeout value, tell the caller to wake up in 24 hours.  */
+  return next_timeout == NO_TIMEOUT ? 24 * 60 * 60 : next_timeout - now;
+}
diff --git a/REORG.TODO/nscd/connections.c b/REORG.TODO/nscd/connections.c
new file mode 100644
index 0000000000..cc1ed72077
--- /dev/null
+++ b/REORG.TODO/nscd/connections.c
@@ -0,0 +1,2558 @@
+/* Inner loops of cache daemon.
+   Copyright (C) 1998-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published
+   by the Free Software Foundation; version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
+
+#include <alloca.h>
+#include <assert.h>
+#include <atomic.h>
+#include <error.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <ifaddrs.h>
+#include <libintl.h>
+#include <pthread.h>
+#include <pwd.h>
+#include <resolv.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+#ifdef HAVE_NETLINK
+# include <linux/netlink.h>
+# include <linux/rtnetlink.h>
+#endif
+#ifdef HAVE_EPOLL
+# include <sys/epoll.h>
+#endif
+#ifdef HAVE_INOTIFY
+# include <sys/inotify.h>
+#endif
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <sys/poll.h>
+#ifdef HAVE_SENDFILE
+# include <sys/sendfile.h>
+#endif
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+
+#include "nscd.h"
+#include "dbg_log.h"
+#include "selinux.h"
+#include <resolv/resolv.h>
+
+#include <kernel-features.h>
+#include <libc-diag.h>
+
+
+/* Support to run nscd as an unprivileged user */
+const char *server_user;
+static uid_t server_uid;
+static gid_t server_gid;
+const char *stat_user;
+uid_t stat_uid;
+static gid_t *server_groups;
+#ifndef NGROUPS
+# define NGROUPS 32
+#endif
+static int server_ngroups;
+
+static pthread_attr_t attr;
+
+static void begin_drop_privileges (void);
+static void finish_drop_privileges (void);
+
+/* Map request type to a string.  */
+const char *const serv2str[LASTREQ] =
+{
+  [GETPWBYNAME] = "GETPWBYNAME",
+  [GETPWBYUID] = "GETPWBYUID",
+  [GETGRBYNAME] = "GETGRBYNAME",
+  [GETGRBYGID] = "GETGRBYGID",
+  [GETHOSTBYNAME] = "GETHOSTBYNAME",
+  [GETHOSTBYNAMEv6] = "GETHOSTBYNAMEv6",
+  [GETHOSTBYADDR] = "GETHOSTBYADDR",
+  [GETHOSTBYADDRv6] = "GETHOSTBYADDRv6",
+  [SHUTDOWN] = "SHUTDOWN",
+  [GETSTAT] = "GETSTAT",
+  [INVALIDATE] = "INVALIDATE",
+  [GETFDPW] = "GETFDPW",
+  [GETFDGR] = "GETFDGR",
+  [GETFDHST] = "GETFDHST",
+  [GETAI] = "GETAI",
+  [INITGROUPS] = "INITGROUPS",
+  [GETSERVBYNAME] = "GETSERVBYNAME",
+  [GETSERVBYPORT] = "GETSERVBYPORT",
+  [GETFDSERV] = "GETFDSERV",
+  [GETNETGRENT] = "GETNETGRENT",
+  [INNETGR] = "INNETGR",
+  [GETFDNETGR] = "GETFDNETGR"
+};
+
+/* The control data structures for the services.  */
+struct database_dyn dbs[lastdb] =
+{
+  [pwddb] = {
+    .lock = PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP,
+    .prune_lock = PTHREAD_MUTEX_INITIALIZER,
+    .prune_run_lock = PTHREAD_MUTEX_INITIALIZER,
+    .enabled = 0,
+    .check_file = 1,
+    .persistent = 0,
+    .propagate = 1,
+    .shared = 0,
+    .max_db_size = DEFAULT_MAX_DB_SIZE,
+    .suggested_module = DEFAULT_SUGGESTED_MODULE,
+    .db_filename = _PATH_NSCD_PASSWD_DB,
+    .disabled_iov = &pwd_iov_disabled,
+    .postimeout = 3600,
+    .negtimeout = 20,
+    .wr_fd = -1,
+    .ro_fd = -1,
+    .mmap_used = false
+  },
+  [grpdb] = {
+    .lock = PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP,
+    .prune_lock = PTHREAD_MUTEX_INITIALIZER,
+    .prune_run_lock = PTHREAD_MUTEX_INITIALIZER,
+    .enabled = 0,
+    .check_file = 1,
+    .persistent = 0,
+    .propagate = 1,
+    .shared = 0,
+    .max_db_size = DEFAULT_MAX_DB_SIZE,
+    .suggested_module = DEFAULT_SUGGESTED_MODULE,
+    .db_filename = _PATH_NSCD_GROUP_DB,
+    .disabled_iov = &grp_iov_disabled,
+    .postimeout = 3600,
+    .negtimeout = 60,
+    .wr_fd = -1,
+    .ro_fd = -1,
+    .mmap_used = false
+  },
+  [hstdb] = {
+    .lock = PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP,
+    .prune_lock = PTHREAD_MUTEX_INITIALIZER,
+    .prune_run_lock = PTHREAD_MUTEX_INITIALIZER,
+    .enabled = 0,
+    .check_file = 1,
+    .persistent = 0,
+    .propagate = 0,		/* Not used.  */
+    .shared = 0,
+    .max_db_size = DEFAULT_MAX_DB_SIZE,
+    .suggested_module = DEFAULT_SUGGESTED_MODULE,
+    .db_filename = _PATH_NSCD_HOSTS_DB,
+    .disabled_iov = &hst_iov_disabled,
+    .postimeout = 3600,
+    .negtimeout = 20,
+    .wr_fd = -1,
+    .ro_fd = -1,
+    .mmap_used = false
+  },
+  [servdb] = {
+    .lock = PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP,
+    .prune_lock = PTHREAD_MUTEX_INITIALIZER,
+    .prune_run_lock = PTHREAD_MUTEX_INITIALIZER,
+    .enabled = 0,
+    .check_file = 1,
+    .persistent = 0,
+    .propagate = 0,		/* Not used.  */
+    .shared = 0,
+    .max_db_size = DEFAULT_MAX_DB_SIZE,
+    .suggested_module = DEFAULT_SUGGESTED_MODULE,
+    .db_filename = _PATH_NSCD_SERVICES_DB,
+    .disabled_iov = &serv_iov_disabled,
+    .postimeout = 28800,
+    .negtimeout = 20,
+    .wr_fd = -1,
+    .ro_fd = -1,
+    .mmap_used = false
+  },
+  [netgrdb] = {
+    .lock = PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP,
+    .prune_lock = PTHREAD_MUTEX_INITIALIZER,
+    .prune_run_lock = PTHREAD_MUTEX_INITIALIZER,
+    .enabled = 0,
+    .check_file = 1,
+    .persistent = 0,
+    .propagate = 0,		/* Not used.  */
+    .shared = 0,
+    .max_db_size = DEFAULT_MAX_DB_SIZE,
+    .suggested_module = DEFAULT_SUGGESTED_MODULE,
+    .db_filename = _PATH_NSCD_NETGROUP_DB,
+    .disabled_iov = &netgroup_iov_disabled,
+    .postimeout = 28800,
+    .negtimeout = 20,
+    .wr_fd = -1,
+    .ro_fd = -1,
+    .mmap_used = false
+  }
+};
+
+
+/* Mapping of request type to database.  */
+static struct
+{
+  bool data_request;
+  struct database_dyn *db;
+} const reqinfo[LASTREQ] =
+{
+  [GETPWBYNAME] = { true, &dbs[pwddb] },
+  [GETPWBYUID] = { true, &dbs[pwddb] },
+  [GETGRBYNAME] = { true, &dbs[grpdb] },
+  [GETGRBYGID] = { true, &dbs[grpdb] },
+  [GETHOSTBYNAME] = { true, &dbs[hstdb] },
+  [GETHOSTBYNAMEv6] = { true, &dbs[hstdb] },
+  [GETHOSTBYADDR] = { true, &dbs[hstdb] },
+  [GETHOSTBYADDRv6] = { true, &dbs[hstdb] },
+  [SHUTDOWN] = { false, NULL },
+  [GETSTAT] = { false, NULL },
+  [SHUTDOWN] = { false, NULL },
+  [GETFDPW] = { false, &dbs[pwddb] },
+  [GETFDGR] = { false, &dbs[grpdb] },
+  [GETFDHST] = { false, &dbs[hstdb] },
+  [GETAI] = { true, &dbs[hstdb] },
+  [INITGROUPS] = { true, &dbs[grpdb] },
+  [GETSERVBYNAME] = { true, &dbs[servdb] },
+  [GETSERVBYPORT] = { true, &dbs[servdb] },
+  [GETFDSERV] = { false, &dbs[servdb] },
+  [GETNETGRENT] = { true, &dbs[netgrdb] },
+  [INNETGR] = { true, &dbs[netgrdb] },
+  [GETFDNETGR] = { false, &dbs[netgrdb] }
+};
+
+
+/* Initial number of threads to use.  */
+int nthreads = -1;
+/* Maximum number of threads to use.  */
+int max_nthreads = 32;
+
+/* Socket for incoming connections.  */
+static int sock;
+
+#ifdef HAVE_INOTIFY
+/* Inotify descriptor.  */
+int inotify_fd = -1;
+#endif
+
+#ifdef HAVE_NETLINK
+/* Descriptor for netlink status updates.  */
+static int nl_status_fd = -1;
+#endif
+
+/* Number of times clients had to wait.  */
+unsigned long int client_queued;
+
+
+ssize_t
+writeall (int fd, const void *buf, size_t len)
+{
+  size_t n = len;
+  ssize_t ret;
+  do
+    {
+      ret = TEMP_FAILURE_RETRY (send (fd, buf, n, MSG_NOSIGNAL));
+      if (ret <= 0)
+	break;
+      buf = (const char *) buf + ret;
+      n -= ret;
+    }
+  while (n > 0);
+  return ret < 0 ? ret : len - n;
+}
+
+
+#ifdef HAVE_SENDFILE
+ssize_t
+sendfileall (int tofd, int fromfd, off_t off, size_t len)
+{
+  ssize_t n = len;
+  ssize_t ret;
+
+  do
+    {
+      ret = TEMP_FAILURE_RETRY (sendfile (tofd, fromfd, &off, n));
+      if (ret <= 0)
+	break;
+      n -= ret;
+    }
+  while (n > 0);
+  return ret < 0 ? ret : len - n;
+}
+#endif
+
+
+enum usekey
+  {
+    use_not = 0,
+    /* The following three are not really used, they are symbolic constants.  */
+    use_first = 16,
+    use_begin = 32,
+    use_end = 64,
+
+    use_he = 1,
+    use_he_begin = use_he | use_begin,
+    use_he_end = use_he | use_end,
+    use_data = 3,
+    use_data_begin = use_data | use_begin,
+    use_data_end = use_data | use_end,
+    use_data_first = use_data_begin | use_first
+  };
+
+
+static int
+check_use (const char *data, nscd_ssize_t first_free, uint8_t *usemap,
+	   enum usekey use, ref_t start, size_t len)
+{
+  assert (len >= 2);
+
+  if (start > first_free || start + len > first_free
+      || (start & BLOCK_ALIGN_M1))
+    return 0;
+
+  if (usemap[start] == use_not)
+    {
+      /* Add the start marker.  */
+      usemap[start] = use | use_begin;
+      use &= ~use_first;
+
+      while (--len > 0)
+	if (usemap[++start] != use_not)
+	  return 0;
+	else
+	  usemap[start] = use;
+
+      /* Add the end marker.  */
+      usemap[start] = use | use_end;
+    }
+  else if ((usemap[start] & ~use_first) == ((use | use_begin) & ~use_first))
+    {
+      /* Hash entries can't be shared.  */
+      if (use == use_he)
+	return 0;
+
+      usemap[start] |= (use & use_first);
+      use &= ~use_first;
+
+      while (--len > 1)
+	if (usemap[++start] != use)
+	  return 0;
+
+      if (usemap[++start] != (use | use_end))
+	return 0;
+    }
+  else
+    /* Points to a wrong object or somewhere in the middle.  */
+    return 0;
+
+  return 1;
+}
+
+
+/* Verify data in persistent database.  */
+static int
+verify_persistent_db (void *mem, struct database_pers_head *readhead, int dbnr)
+{
+  assert (dbnr == pwddb || dbnr == grpdb || dbnr == hstdb || dbnr == servdb
+	  || dbnr == netgrdb);
+
+  time_t now = time (NULL);
+
+  struct database_pers_head *head = mem;
+  struct database_pers_head head_copy = *head;
+
+  /* Check that the header that was read matches the head in the database.  */
+  if (memcmp (head, readhead, sizeof (*head)) != 0)
+    return 0;
+
+  /* First some easy tests: make sure the database header is sane.  */
+  if (head->version != DB_VERSION
+      || head->header_size != sizeof (*head)
+      /* We allow a timestamp to be one hour ahead of the current time.
+	 This should cover daylight saving time changes.  */
+      || head->timestamp > now + 60 * 60 + 60
+      || (head->gc_cycle & 1)
+      || head->module == 0
+      || (size_t) head->module > INT32_MAX / sizeof (ref_t)
+      || (size_t) head->data_size > INT32_MAX - head->module * sizeof (ref_t)
+      || head->first_free < 0
+      || head->first_free > head->data_size
+      || (head->first_free & BLOCK_ALIGN_M1) != 0
+      || head->maxnentries < 0
+      || head->maxnsearched < 0)
+    return 0;
+
+  uint8_t *usemap = calloc (head->first_free, 1);
+  if (usemap == NULL)
+    return 0;
+
+  const char *data = (char *) &head->array[roundup (head->module,
+						    ALIGN / sizeof (ref_t))];
+
+  nscd_ssize_t he_cnt = 0;
+  for (nscd_ssize_t cnt = 0; cnt < head->module; ++cnt)
+    {
+      ref_t trail = head->array[cnt];
+      ref_t work = trail;
+      int tick = 0;
+
+      while (work != ENDREF)
+	{
+	  if (! check_use (data, head->first_free, usemap, use_he, work,
+			   sizeof (struct hashentry)))
+	    goto fail;
+
+	  /* Now we know we can dereference the record.  */
+	  struct hashentry *here = (struct hashentry *) (data + work);
+
+	  ++he_cnt;
+
+	  /* Make sure the record is for this type of service.  */
+	  if (here->type >= LASTREQ
+	      || reqinfo[here->type].db != &dbs[dbnr])
+	    goto fail;
+
+	  /* Validate boolean field value.  */
+	  if (here->first != false && here->first != true)
+	    goto fail;
+
+	  if (here->len < 0)
+	    goto fail;
+
+	  /* Now the data.  */
+	  if (here->packet < 0
+	      || here->packet > head->first_free
+	      || here->packet + sizeof (struct datahead) > head->first_free)
+	    goto fail;
+
+	  struct datahead *dh = (struct datahead *) (data + here->packet);
+
+	  if (! check_use (data, head->first_free, usemap,
+			   use_data | (here->first ? use_first : 0),
+			   here->packet, dh->allocsize))
+	    goto fail;
+
+	  if (dh->allocsize < sizeof (struct datahead)
+	      || dh->recsize > dh->allocsize
+	      || (dh->notfound != false && dh->notfound != true)
+	      || (dh->usable != false && dh->usable != true))
+	    goto fail;
+
+	  if (here->key < here->packet + sizeof (struct datahead)
+	      || here->key > here->packet + dh->allocsize
+	      || here->key + here->len > here->packet + dh->allocsize)
+	    goto fail;
+
+	  work = here->next;
+
+	  if (work == trail)
+	    /* A circular list, this must not happen.  */
+	    goto fail;
+	  if (tick)
+	    trail = ((struct hashentry *) (data + trail))->next;
+	  tick = 1 - tick;
+	}
+    }
+
+  if (he_cnt != head->nentries)
+    goto fail;
+
+  /* See if all data and keys had at least one reference from
+     he->first == true hashentry.  */
+  for (ref_t idx = 0; idx < head->first_free; ++idx)
+    {
+      if (usemap[idx] == use_data_begin)
+	goto fail;
+    }
+
+  /* Finally, make sure the database hasn't changed since the first test.  */
+  if (memcmp (mem, &head_copy, sizeof (*head)) != 0)
+    goto fail;
+
+  free (usemap);
+  return 1;
+
+fail:
+  free (usemap);
+  return 0;
+}
+
+
+/* Initialize database information structures.  */
+void
+nscd_init (void)
+{
+  /* Look up unprivileged uid/gid/groups before we start listening on the
+     socket  */
+  if (server_user != NULL)
+    begin_drop_privileges ();
+
+  if (nthreads == -1)
+    /* No configuration for this value, assume a default.  */
+    nthreads = 4;
+
+  for (size_t cnt = 0; cnt < lastdb; ++cnt)
+    if (dbs[cnt].enabled)
+      {
+	pthread_rwlock_init (&dbs[cnt].lock, NULL);
+	pthread_mutex_init (&dbs[cnt].memlock, NULL);
+
+	if (dbs[cnt].persistent)
+	  {
+	    /* Try to open the appropriate file on disk.  */
+	    int fd = open (dbs[cnt].db_filename, O_RDWR | O_CLOEXEC);
+	    if (fd != -1)
+	      {
+		char *msg = NULL;
+		struct stat64 st;
+		void *mem;
+		size_t total;
+		struct database_pers_head head;
+		ssize_t n = TEMP_FAILURE_RETRY (read (fd, &head,
+						      sizeof (head)));
+		if (n != sizeof (head) || fstat64 (fd, &st) != 0)
+		  {
+		  fail_db_errno:
+		    /* The code is single-threaded at this point so
+		       using strerror is just fine.  */
+		    msg = strerror (errno);
+		  fail_db:
+		    dbg_log (_("invalid persistent database file \"%s\": %s"),
+			     dbs[cnt].db_filename, msg);
+		    unlink (dbs[cnt].db_filename);
+		  }
+		else if (head.module == 0 && head.data_size == 0)
+		  {
+		    /* The file has been created, but the head has not
+		       been initialized yet.  */
+		    msg = _("uninitialized header");
+		    goto fail_db;
+		  }
+		else if (head.header_size != (int) sizeof (head))
+		  {
+		    msg = _("header size does not match");
+		    goto fail_db;
+		  }
+		else if ((total = (sizeof (head)
+				   + roundup (head.module * sizeof (ref_t),
+					      ALIGN)
+				   + head.data_size))
+			 > st.st_size
+			 || total < sizeof (head))
+		  {
+		    msg = _("file size does not match");
+		    goto fail_db;
+		  }
+		/* Note we map with the maximum size allowed for the
+		   database.  This is likely much larger than the
+		   actual file size.  This is OK on most OSes since
+		   extensions of the underlying file will
+		   automatically translate more pages available for
+		   memory access.  */
+		else if ((mem = mmap (NULL, dbs[cnt].max_db_size,
+				      PROT_READ | PROT_WRITE,
+				      MAP_SHARED, fd, 0))
+			 == MAP_FAILED)
+		  goto fail_db_errno;
+		else if (!verify_persistent_db (mem, &head, cnt))
+		  {
+		    munmap (mem, total);
+		    msg = _("verification failed");
+		    goto fail_db;
+		  }
+		else
+		  {
+		    /* Success.  We have the database.  */
+		    dbs[cnt].head = mem;
+		    dbs[cnt].memsize = total;
+		    dbs[cnt].data = (char *)
+		      &dbs[cnt].head->array[roundup (dbs[cnt].head->module,
+						     ALIGN / sizeof (ref_t))];
+		    dbs[cnt].mmap_used = true;
+
+		    if (dbs[cnt].suggested_module > head.module)
+		      dbg_log (_("suggested size of table for database %s larger than the persistent database's table"),
+			       dbnames[cnt]);
+
+		    dbs[cnt].wr_fd = fd;
+		    fd = -1;
+		    /* We also need a read-only descriptor.  */
+		    if (dbs[cnt].shared)
+		      {
+			dbs[cnt].ro_fd = open (dbs[cnt].db_filename,
+					       O_RDONLY | O_CLOEXEC);
+			if (dbs[cnt].ro_fd == -1)
+			  dbg_log (_("\
+cannot create read-only descriptor for \"%s\"; no mmap"),
+				   dbs[cnt].db_filename);
+		      }
+
+		    // XXX Shall we test whether the descriptors actually
+		    // XXX point to the same file?
+		  }
+
+		/* Close the file descriptors in case something went
+		   wrong in which case the variable have not been
+		   assigned -1.  */
+		if (fd != -1)
+		  close (fd);
+	      }
+	    else if (errno == EACCES)
+	      do_exit (EXIT_FAILURE, 0, _("cannot access '%s'"),
+		       dbs[cnt].db_filename);
+	  }
+
+	if (dbs[cnt].head == NULL)
+	  {
+	    /* No database loaded.  Allocate the data structure,
+	       possibly on disk.  */
+	    struct database_pers_head head;
+	    size_t total = (sizeof (head)
+			    + roundup (dbs[cnt].suggested_module
+				       * sizeof (ref_t), ALIGN)
+			    + (dbs[cnt].suggested_module
+			       * DEFAULT_DATASIZE_PER_BUCKET));
+
+	    /* Try to create the database.  If we do not need a
+	       persistent database create a temporary file.  */
+	    int fd;
+	    int ro_fd = -1;
+	    if (dbs[cnt].persistent)
+	      {
+		fd = open (dbs[cnt].db_filename,
+			   O_RDWR | O_CREAT | O_EXCL | O_TRUNC | O_CLOEXEC,
+			   S_IRUSR | S_IWUSR);
+		if (fd != -1 && dbs[cnt].shared)
+		  ro_fd = open (dbs[cnt].db_filename,
+				O_RDONLY | O_CLOEXEC);
+	      }
+	    else
+	      {
+		char fname[] = _PATH_NSCD_XYZ_DB_TMP;
+		fd = mkostemp (fname, O_CLOEXEC);
+
+		/* We do not need the file name anymore after we
+		   opened another file descriptor in read-only mode.  */
+		if (fd != -1)
+		  {
+		    if (dbs[cnt].shared)
+		      ro_fd = open (fname, O_RDONLY | O_CLOEXEC);
+
+		    unlink (fname);
+		  }
+	      }
+
+	    if (fd == -1)
+	      {
+		if (errno == EEXIST)
+		  {
+		    dbg_log (_("database for %s corrupted or simultaneously used; remove %s manually if necessary and restart"),
+			     dbnames[cnt], dbs[cnt].db_filename);
+		    do_exit (1, 0, NULL);
+		  }
+
+		if  (dbs[cnt].persistent)
+		  dbg_log (_("cannot create %s; no persistent database used"),
+			   dbs[cnt].db_filename);
+		else
+		  dbg_log (_("cannot create %s; no sharing possible"),
+			   dbs[cnt].db_filename);
+
+		dbs[cnt].persistent = 0;
+		// XXX remember: no mmap
+	      }
+	    else
+	      {
+		/* Tell the user if we could not create the read-only
+		   descriptor.  */
+		if (ro_fd == -1 && dbs[cnt].shared)
+		  dbg_log (_("\
+cannot create read-only descriptor for \"%s\"; no mmap"),
+			   dbs[cnt].db_filename);
+
+		/* 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);
+		char tmpbuf[ps];
+		assert (~ENDREF == 0);
+		memset (tmpbuf, '\xff', ps);
+
+		size_t remaining = dbs[cnt].suggested_module * sizeof (ref_t);
+		off_t offset = sizeof (head);
+
+		size_t towrite;
+		if (offset % ps != 0)
+		  {
+		    towrite = MIN (remaining, ps - (offset % ps));
+		    if (pwrite (fd, tmpbuf, towrite, offset) != towrite)
+		      goto write_fail;
+		    offset += towrite;
+		    remaining -= towrite;
+		  }
+
+		while (remaining > ps)
+		  {
+		    if (pwrite (fd, tmpbuf, ps, offset) == -1)
+		      goto write_fail;
+		    offset += ps;
+		    remaining -= ps;
+		  }
+
+		if (remaining > 0
+		    && pwrite (fd, tmpbuf, remaining, offset) != remaining)
+		  goto write_fail;
+
+		/* Create the header of the file.  */
+		struct database_pers_head head =
+		  {
+		    .version = DB_VERSION,
+		    .header_size = sizeof (head),
+		    .module = dbs[cnt].suggested_module,
+		    .data_size = (dbs[cnt].suggested_module
+				  * DEFAULT_DATASIZE_PER_BUCKET),
+		    .first_free = 0
+		  };
+		void *mem;
+
+		if ((TEMP_FAILURE_RETRY (write (fd, &head, sizeof (head)))
+		     != sizeof (head))
+		    || (TEMP_FAILURE_RETRY_VAL (posix_fallocate (fd, 0, total))
+			!= 0)
+		    || (mem = mmap (NULL, dbs[cnt].max_db_size,
+				    PROT_READ | PROT_WRITE,
+				    MAP_SHARED, fd, 0)) == MAP_FAILED)
+		  {
+		  write_fail:
+		    unlink (dbs[cnt].db_filename);
+		    dbg_log (_("cannot write to database file %s: %s"),
+			     dbs[cnt].db_filename, strerror (errno));
+		    dbs[cnt].persistent = 0;
+		  }
+		else
+		  {
+		    /* Success.  */
+		    dbs[cnt].head = mem;
+		    dbs[cnt].data = (char *)
+		      &dbs[cnt].head->array[roundup (dbs[cnt].head->module,
+						     ALIGN / sizeof (ref_t))];
+		    dbs[cnt].memsize = total;
+		    dbs[cnt].mmap_used = true;
+
+		    /* Remember the descriptors.  */
+		    dbs[cnt].wr_fd = fd;
+		    dbs[cnt].ro_fd = ro_fd;
+		    fd = -1;
+		    ro_fd = -1;
+		  }
+
+		if (fd != -1)
+		  close (fd);
+		if (ro_fd != -1)
+		  close (ro_fd);
+	      }
+	  }
+
+	if (dbs[cnt].head == NULL)
+	  {
+	    /* We do not use the persistent database.  Just
+	       create an in-memory data structure.  */
+	    assert (! dbs[cnt].persistent);
+
+	    dbs[cnt].head = xmalloc (sizeof (struct database_pers_head)
+				     + (dbs[cnt].suggested_module
+					* sizeof (ref_t)));
+	    memset (dbs[cnt].head, '\0', sizeof (struct database_pers_head));
+	    assert (~ENDREF == 0);
+	    memset (dbs[cnt].head->array, '\xff',
+		    dbs[cnt].suggested_module * sizeof (ref_t));
+	    dbs[cnt].head->module = dbs[cnt].suggested_module;
+	    dbs[cnt].head->data_size = (DEFAULT_DATASIZE_PER_BUCKET
+					* dbs[cnt].head->module);
+	    dbs[cnt].data = xmalloc (dbs[cnt].head->data_size);
+	    dbs[cnt].head->first_free = 0;
+
+	    dbs[cnt].shared = 0;
+	    assert (dbs[cnt].ro_fd == -1);
+	  }
+      }
+
+  /* Create the socket.  */
+  sock = socket (AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
+  if (sock < 0)
+    {
+      dbg_log (_("cannot open socket: %s"), strerror (errno));
+      do_exit (errno == EACCES ? 4 : 1, 0, NULL);
+    }
+  /* Bind a name to the socket.  */
+  struct sockaddr_un sock_addr;
+  sock_addr.sun_family = AF_UNIX;
+  strcpy (sock_addr.sun_path, _PATH_NSCDSOCKET);
+  if (bind (sock, (struct sockaddr *) &sock_addr, sizeof (sock_addr)) < 0)
+    {
+      dbg_log ("%s: %s", _PATH_NSCDSOCKET, strerror (errno));
+      do_exit (errno == EACCES ? 4 : 1, 0, NULL);
+    }
+
+  /* Set permissions for the socket.  */
+  chmod (_PATH_NSCDSOCKET, DEFFILEMODE);
+
+  /* Set the socket up to accept connections.  */
+  if (listen (sock, SOMAXCONN) < 0)
+    {
+      dbg_log (_("cannot enable socket to accept connections: %s"),
+	       strerror (errno));
+      do_exit (1, 0, NULL);
+    }
+
+#ifdef HAVE_NETLINK
+  if (dbs[hstdb].enabled)
+    {
+      /* Try to open netlink socket to monitor network setting changes.  */
+      nl_status_fd = socket (AF_NETLINK,
+			     SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK,
+			     NETLINK_ROUTE);
+      if (nl_status_fd != -1)
+	{
+	  struct sockaddr_nl snl;
+	  memset (&snl, '\0', sizeof (snl));
+	  snl.nl_family = AF_NETLINK;
+	  /* XXX Is this the best set to use?  */
+	  snl.nl_groups = (RTMGRP_IPV4_IFADDR | RTMGRP_TC | RTMGRP_IPV4_MROUTE
+			   | RTMGRP_IPV4_ROUTE | RTMGRP_IPV4_RULE
+			   | RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_MROUTE
+			   | RTMGRP_IPV6_ROUTE | RTMGRP_IPV6_IFINFO
+			   | RTMGRP_IPV6_PREFIX);
+
+	  if (bind (nl_status_fd, (struct sockaddr *) &snl, sizeof (snl)) != 0)
+	    {
+	      close (nl_status_fd);
+	      nl_status_fd = -1;
+	    }
+	  else
+	    {
+	      /* Start the timestamp process.  */
+	      dbs[hstdb].head->extra_data[NSCD_HST_IDX_CONF_TIMESTAMP]
+		= __bump_nl_timestamp ();
+	    }
+	}
+    }
+#endif
+
+  /* Change to unprivileged uid/gid/groups if specified in config file */
+  if (server_user != NULL)
+    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 (__glibc_unlikely (debug_level > 0))
+    dbg_log (_("monitoring file %s for database %s"),
+	     finfo->fname, dbnames[dbidx]);
+
+#ifdef HAVE_INOTIFY
+  install_watches (finfo);
+#endif
+  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;
+  dbs[dbidx].traced_files = finfo;
+}
+
+
+/* Close the connections.  */
+void
+close_sockets (void)
+{
+  close (sock);
+}
+
+
+static void
+invalidate_cache (char *key, int fd)
+{
+  dbtype number;
+  int32_t resp;
+
+  for (number = pwddb; number < lastdb; ++number)
+    if (strcmp (key, dbnames[number]) == 0)
+      {
+	struct traced_file *runp = dbs[number].traced_files;
+	while (runp != NULL)
+	  {
+	    /* 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)
+    {
+      resp = EINVAL;
+      writeall (fd, &resp, sizeof (resp));
+      return;
+    }
+
+  if (dbs[number].enabled)
+    {
+      pthread_mutex_lock (&dbs[number].prune_run_lock);
+      prune_cache (&dbs[number], LONG_MAX, fd);
+      pthread_mutex_unlock (&dbs[number].prune_run_lock);
+    }
+  else
+    {
+      resp = 0;
+      writeall (fd, &resp, sizeof (resp));
+    }
+}
+
+
+#ifdef SCM_RIGHTS
+static void
+send_ro_fd (struct database_dyn *db, char *key, int fd)
+{
+  /* If we do not have an read-only file descriptor do nothing.  */
+  if (db->ro_fd == -1)
+    return;
+
+  /* We need to send some data along with the descriptor.  */
+  uint64_t mapsize = (db->head->data_size
+		      + roundup (db->head->module * sizeof (ref_t), ALIGN)
+		      + sizeof (struct database_pers_head));
+  struct iovec iov[2];
+  iov[0].iov_base = key;
+  iov[0].iov_len = strlen (key) + 1;
+  iov[1].iov_base = &mapsize;
+  iov[1].iov_len = sizeof (mapsize);
+
+  /* Prepare the control message to transfer the descriptor.  */
+  union
+  {
+    struct cmsghdr hdr;
+    char bytes[CMSG_SPACE (sizeof (int))];
+  } buf;
+  struct msghdr msg = { .msg_iov = iov, .msg_iovlen = 2,
+			.msg_control = buf.bytes,
+			.msg_controllen = sizeof (buf) };
+  struct cmsghdr *cmsg = CMSG_FIRSTHDR (&msg);
+
+  cmsg->cmsg_level = SOL_SOCKET;
+  cmsg->cmsg_type = SCM_RIGHTS;
+  cmsg->cmsg_len = CMSG_LEN (sizeof (int));
+
+  int *ip = (int *) CMSG_DATA (cmsg);
+  *ip = db->ro_fd;
+
+  msg.msg_controllen = cmsg->cmsg_len;
+
+  /* Send the control message.  We repeat when we are interrupted but
+     everything else is ignored.  */
+#ifndef MSG_NOSIGNAL
+# define MSG_NOSIGNAL 0
+#endif
+  (void) TEMP_FAILURE_RETRY (sendmsg (fd, &msg, MSG_NOSIGNAL));
+
+  if (__glibc_unlikely (debug_level > 0))
+    dbg_log (_("provide access to FD %d, for %s"), db->ro_fd, key);
+}
+#endif	/* SCM_RIGHTS */
+
+
+/* Handle new request.  */
+static void
+handle_request (int fd, request_header *req, void *key, uid_t uid, pid_t pid)
+{
+  if (__builtin_expect (req->version, NSCD_VERSION) != NSCD_VERSION)
+    {
+      if (debug_level > 0)
+	dbg_log (_("\
+cannot handle old request version %d; current version is %d"),
+		 req->version, NSCD_VERSION);
+      return;
+    }
+
+  /* Perform the SELinux check before we go on to the standard checks.  */
+  if (selinux_enabled && nscd_request_avc_has_perm (fd, req->type) != 0)
+    {
+      if (debug_level > 0)
+	{
+#ifdef SO_PEERCRED
+# ifdef PATH_MAX
+	  char buf[PATH_MAX];
+# else
+	  char buf[4096];
+# endif
+
+	  snprintf (buf, sizeof (buf), "/proc/%ld/exe", (long int) pid);
+	  ssize_t n = readlink (buf, buf, sizeof (buf) - 1);
+
+	  if (n <= 0)
+	    dbg_log (_("\
+request from %ld not handled due to missing permission"), (long int) pid);
+	  else
+	    {
+	      buf[n] = '\0';
+	      dbg_log (_("\
+request from '%s' [%ld] not handled due to missing permission"),
+		       buf, (long int) pid);
+	    }
+#else
+	  dbg_log (_("request not handled due to missing permission"));
+#endif
+	}
+      return;
+    }
+
+  struct database_dyn *db = reqinfo[req->type].db;
+
+  /* See whether we can service the request from the cache.  */
+  if (__builtin_expect (reqinfo[req->type].data_request, true))
+    {
+      if (__builtin_expect (debug_level, 0) > 0)
+	{
+	  if (req->type == GETHOSTBYADDR || req->type == GETHOSTBYADDRv6)
+	    {
+	      char buf[INET6_ADDRSTRLEN];
+
+	      dbg_log ("\t%s (%s)", serv2str[req->type],
+		       inet_ntop (req->type == GETHOSTBYADDR
+				  ? AF_INET : AF_INET6,
+				  key, buf, sizeof (buf)));
+	    }
+	  else
+	    dbg_log ("\t%s (%s)", serv2str[req->type], (char *) key);
+	}
+
+      /* Is this service enabled?  */
+      if (__glibc_unlikely (!db->enabled))
+	{
+	  /* No, sent the prepared record.  */
+	  if (TEMP_FAILURE_RETRY (send (fd, db->disabled_iov->iov_base,
+					db->disabled_iov->iov_len,
+					MSG_NOSIGNAL))
+	      != (ssize_t) db->disabled_iov->iov_len
+	      && __builtin_expect (debug_level, 0) > 0)
+	    {
+	      /* We have problems sending the result.  */
+	      char buf[256];
+	      dbg_log (_("cannot write result: %s"),
+		       strerror_r (errno, buf, sizeof (buf)));
+	    }
+
+	  return;
+	}
+
+      /* Be sure we can read the data.  */
+      if (__glibc_unlikely (pthread_rwlock_tryrdlock (&db->lock) != 0))
+	{
+	  ++db->head->rdlockdelayed;
+	  pthread_rwlock_rdlock (&db->lock);
+	}
+
+      /* See whether we can handle it from the cache.  */
+      struct datahead *cached;
+      cached = (struct datahead *) cache_search (req->type, key, req->key_len,
+						 db, uid);
+      if (cached != NULL)
+	{
+	  /* Hurray it's in the cache.  */
+	  ssize_t nwritten;
+
+#ifdef HAVE_SENDFILE
+	  if (__glibc_likely (db->mmap_used))
+	    {
+	      assert (db->wr_fd != -1);
+	      assert ((char *) cached->data > (char *) db->data);
+	      assert ((char *) cached->data - (char *) db->head
+		      + cached->recsize
+		      <= (sizeof (struct database_pers_head)
+			  + db->head->module * sizeof (ref_t)
+			  + db->head->data_size));
+	      nwritten = sendfileall (fd, db->wr_fd,
+				      (char *) cached->data
+				      - (char *) db->head, cached->recsize);
+# ifndef __ASSUME_SENDFILE
+	      if (nwritten == -1 && errno == ENOSYS)
+		goto use_write;
+# endif
+	    }
+	  else
+# ifndef __ASSUME_SENDFILE
+	  use_write:
+# endif
+#endif
+	    nwritten = writeall (fd, cached->data, cached->recsize);
+
+	  if (nwritten != cached->recsize
+	      && __builtin_expect (debug_level, 0) > 0)
+	    {
+	      /* We have problems sending the result.  */
+	      char buf[256];
+	      dbg_log (_("cannot write result: %s"),
+		       strerror_r (errno, buf, sizeof (buf)));
+	    }
+
+	  pthread_rwlock_unlock (&db->lock);
+
+	  return;
+	}
+
+      pthread_rwlock_unlock (&db->lock);
+    }
+  else if (__builtin_expect (debug_level, 0) > 0)
+    {
+      if (req->type == INVALIDATE)
+	dbg_log ("\t%s (%s)", serv2str[req->type], (char *) key);
+      else
+	dbg_log ("\t%s", serv2str[req->type]);
+    }
+
+  /* Handle the request.  */
+  switch (req->type)
+    {
+    case GETPWBYNAME:
+      addpwbyname (db, fd, req, key, uid);
+      break;
+
+    case GETPWBYUID:
+      addpwbyuid (db, fd, req, key, uid);
+      break;
+
+    case GETGRBYNAME:
+      addgrbyname (db, fd, req, key, uid);
+      break;
+
+    case GETGRBYGID:
+      addgrbygid (db, fd, req, key, uid);
+      break;
+
+    case GETHOSTBYNAME:
+      addhstbyname (db, fd, req, key, uid);
+      break;
+
+    case GETHOSTBYNAMEv6:
+      addhstbynamev6 (db, fd, req, key, uid);
+      break;
+
+    case GETHOSTBYADDR:
+      addhstbyaddr (db, fd, req, key, uid);
+      break;
+
+    case GETHOSTBYADDRv6:
+      addhstbyaddrv6 (db, fd, req, key, uid);
+      break;
+
+    case GETAI:
+      addhstai (db, fd, req, key, uid);
+      break;
+
+    case INITGROUPS:
+      addinitgroups (db, fd, req, key, uid);
+      break;
+
+    case GETSERVBYNAME:
+      addservbyname (db, fd, req, key, uid);
+      break;
+
+    case GETSERVBYPORT:
+      addservbyport (db, fd, req, key, uid);
+      break;
+
+    case GETNETGRENT:
+      addgetnetgrent (db, fd, req, key, uid);
+      break;
+
+    case INNETGR:
+      addinnetgr (db, fd, req, key, uid);
+      break;
+
+    case GETSTAT:
+    case SHUTDOWN:
+    case INVALIDATE:
+      {
+	/* Get the callers credentials.  */
+#ifdef SO_PEERCRED
+	struct ucred caller;
+	socklen_t optlen = sizeof (caller);
+
+	if (getsockopt (fd, SOL_SOCKET, SO_PEERCRED, &caller, &optlen) < 0)
+	  {
+	    char buf[256];
+
+	    dbg_log (_("error getting caller's id: %s"),
+		     strerror_r (errno, buf, sizeof (buf)));
+	    break;
+	  }
+
+	uid = caller.uid;
+#else
+	/* Some systems have no SO_PEERCRED implementation.  They don't
+	   care about security so we don't as well.  */
+	uid = 0;
+#endif
+      }
+
+      /* Accept shutdown, getstat and invalidate only from root.  For
+	 the stat call also allow the user specified in the config file.  */
+      if (req->type == GETSTAT)
+	{
+	  if (uid == 0 || uid == stat_uid)
+	    send_stats (fd, dbs);
+	}
+      else if (uid == 0)
+	{
+	  if (req->type == INVALIDATE)
+	    invalidate_cache (key, fd);
+	  else
+	    termination_handler (0);
+	}
+      break;
+
+    case GETFDPW:
+    case GETFDGR:
+    case GETFDHST:
+    case GETFDSERV:
+    case GETFDNETGR:
+#ifdef SCM_RIGHTS
+      send_ro_fd (reqinfo[req->type].db, key, fd);
+#endif
+      break;
+
+    default:
+      /* Ignore the command, it's nothing we know.  */
+      break;
+    }
+}
+
+
+/* Restart the process.  */
+static void
+restart (void)
+{
+  /* First determine the parameters.  We do not use the parameters
+     passed to main() since in case nscd is started by running the
+     dynamic linker this will not work.  Yes, this is not the usual
+     case but nscd is part of glibc and we occasionally do this.  */
+  size_t buflen = 1024;
+  char *buf = alloca (buflen);
+  size_t readlen = 0;
+  int fd = open ("/proc/self/cmdline", O_RDONLY);
+  if (fd == -1)
+    {
+      dbg_log (_("\
+cannot open /proc/self/cmdline: %s; disabling paranoia mode"),
+	       strerror (errno));
+
+      paranoia = 0;
+      return;
+    }
+
+  while (1)
+    {
+      ssize_t n = TEMP_FAILURE_RETRY (read (fd, buf + readlen,
+					    buflen - readlen));
+      if (n == -1)
+	{
+	  dbg_log (_("\
+cannot read /proc/self/cmdline: %s; disabling paranoia mode"),
+		   strerror (errno));
+
+	  close (fd);
+	  paranoia = 0;
+	  return;
+	}
+
+      readlen += n;
+
+      if (readlen < buflen)
+	break;
+
+      /* We might have to extend the buffer.  */
+      size_t old_buflen = buflen;
+      char *newp = extend_alloca (buf, buflen, 2 * buflen);
+      buf = memmove (newp, buf, old_buflen);
+    }
+
+  close (fd);
+
+  /* Parse the command line.  Worst case scenario: every two
+     characters form one parameter (one character plus NUL).  */
+  char **argv = alloca ((readlen / 2 + 1) * sizeof (argv[0]));
+  int argc = 0;
+
+  char *cp = buf;
+  while (cp < buf + readlen)
+    {
+      argv[argc++] = cp;
+      cp = (char *) rawmemchr (cp, '\0') + 1;
+    }
+  argv[argc] = NULL;
+
+  /* Second, change back to the old user if we changed it.  */
+  if (server_user != NULL)
+    {
+      if (setresuid (old_uid, old_uid, old_uid) != 0)
+	{
+	  dbg_log (_("\
+cannot change to old UID: %s; disabling paranoia mode"),
+		   strerror (errno));
+
+	  paranoia = 0;
+	  return;
+	}
+
+      if (setresgid (old_gid, old_gid, old_gid) != 0)
+	{
+	  dbg_log (_("\
+cannot change to old GID: %s; disabling paranoia mode"),
+		   strerror (errno));
+
+	  ignore_value (setuid (server_uid));
+	  paranoia = 0;
+	  return;
+	}
+    }
+
+  /* Next change back to the old working directory.  */
+  if (chdir (oldcwd) == -1)
+    {
+      dbg_log (_("\
+cannot change to old working directory: %s; disabling paranoia mode"),
+	       strerror (errno));
+
+      if (server_user != NULL)
+	{
+	  ignore_value (setuid (server_uid));
+	  ignore_value (setgid (server_gid));
+	}
+      paranoia = 0;
+      return;
+    }
+
+  /* Synchronize memory.  */
+  int32_t certainly[lastdb];
+  for (int cnt = 0; cnt < lastdb; ++cnt)
+    if (dbs[cnt].enabled)
+      {
+	/* Make sure nobody keeps using the database.  */
+	dbs[cnt].head->timestamp = 0;
+	certainly[cnt] = dbs[cnt].head->nscd_certainly_running;
+	dbs[cnt].head->nscd_certainly_running = 0;
+
+	if (dbs[cnt].persistent)
+	  // XXX async OK?
+	  msync (dbs[cnt].head, dbs[cnt].memsize, MS_ASYNC);
+      }
+
+  /* The preparations are done.  */
+#ifdef PATH_MAX
+  char pathbuf[PATH_MAX];
+#else
+  char pathbuf[256];
+#endif
+  /* Try to exec the real nscd program so the process name (as reported
+     in /proc/PID/status) will be 'nscd', but fall back to /proc/self/exe
+     if readlink or the exec with the result of the readlink call fails.  */
+  ssize_t n = readlink ("/proc/self/exe", pathbuf, sizeof (pathbuf) - 1);
+  if (n != -1)
+    {
+      pathbuf[n] = '\0';
+      execv (pathbuf, argv);
+    }
+  execv ("/proc/self/exe", argv);
+
+  /* If we come here, we will never be able to re-exec.  */
+  dbg_log (_("re-exec failed: %s; disabling paranoia mode"),
+	   strerror (errno));
+
+  if (server_user != NULL)
+    {
+      ignore_value (setuid (server_uid));
+      ignore_value (setgid (server_gid));
+    }
+  if (chdir ("/") != 0)
+    dbg_log (_("cannot change current working directory to \"/\": %s"),
+	     strerror (errno));
+  paranoia = 0;
+
+  /* Reenable the databases.  */
+  time_t now = time (NULL);
+  for (int cnt = 0; cnt < lastdb; ++cnt)
+    if (dbs[cnt].enabled)
+      {
+	dbs[cnt].head->timestamp = now;
+	dbs[cnt].head->nscd_certainly_running = certainly[cnt];
+      }
+}
+
+
+/* List of file descriptors.  */
+struct fdlist
+{
+  int fd;
+  struct fdlist *next;
+};
+/* Memory allocated for the list.  */
+static struct fdlist *fdlist;
+/* List of currently ready-to-read file descriptors.  */
+static struct fdlist *readylist;
+
+/* Conditional variable and mutex to signal availability of entries in
+   READYLIST.  The condvar is initialized dynamically since we might
+   use a different clock depending on availability.  */
+static pthread_cond_t readylist_cond = PTHREAD_COND_INITIALIZER;
+static pthread_mutex_t readylist_lock = PTHREAD_MUTEX_INITIALIZER;
+
+/* The clock to use with the condvar.  */
+static clockid_t timeout_clock = CLOCK_REALTIME;
+
+/* Number of threads ready to handle the READYLIST.  */
+static unsigned long int nready;
+
+
+/* Function for the clean-up threads.  */
+static void *
+__attribute__ ((__noreturn__))
+nscd_run_prune (void *p)
+{
+  const long int my_number = (long int) p;
+  assert (dbs[my_number].enabled);
+
+  int dont_need_update = setup_thread (&dbs[my_number]);
+
+  time_t now = time (NULL);
+
+  /* We are running.  */
+  dbs[my_number].head->timestamp = now;
+
+  struct timespec prune_ts;
+  if (__glibc_unlikely (clock_gettime (timeout_clock, &prune_ts) == -1))
+    /* Should never happen.  */
+    abort ();
+
+  /* Compute the initial timeout time.  Prevent all the timers to go
+     off at the same time by adding a db-based value.  */
+  prune_ts.tv_sec += CACHE_PRUNE_INTERVAL + my_number;
+  dbs[my_number].wakeup_time = now + CACHE_PRUNE_INTERVAL + my_number;
+
+  pthread_mutex_t *prune_lock = &dbs[my_number].prune_lock;
+  pthread_mutex_t *prune_run_lock = &dbs[my_number].prune_run_lock;
+  pthread_cond_t *prune_cond = &dbs[my_number].prune_cond;
+
+  pthread_mutex_lock (prune_lock);
+  while (1)
+    {
+      /* Wait, but not forever.  */
+      int e = 0;
+      if (! dbs[my_number].clear_cache)
+	e = pthread_cond_timedwait (prune_cond, prune_lock, &prune_ts);
+      assert (__builtin_expect (e == 0 || e == ETIMEDOUT, 1));
+
+      time_t next_wait;
+      now = time (NULL);
+      if (e == ETIMEDOUT || now >= dbs[my_number].wakeup_time
+	  || dbs[my_number].clear_cache)
+	{
+	  /* We will determine the new timout values based on the
+	     cache content.  Should there be concurrent additions to
+	     the cache which are not accounted for in the cache
+	     pruning we want to know about it.  Therefore set the
+	     timeout to the maximum.  It will be descreased when adding
+	     new entries to the cache, if necessary.  */
+	  dbs[my_number].wakeup_time = MAX_TIMEOUT_VALUE;
+
+	  /* Unconditionally reset the flag.  */
+	  time_t prune_now = dbs[my_number].clear_cache ? LONG_MAX : now;
+	  dbs[my_number].clear_cache = 0;
+
+	  pthread_mutex_unlock (prune_lock);
+
+	  /* We use a separate lock for running the prune function (instead
+	     of keeping prune_lock locked) because this enables concurrent
+	     invocations of cache_add which might modify the timeout value.  */
+	  pthread_mutex_lock (prune_run_lock);
+	  next_wait = prune_cache (&dbs[my_number], prune_now, -1);
+	  pthread_mutex_unlock (prune_run_lock);
+
+	  next_wait = MAX (next_wait, CACHE_PRUNE_INTERVAL);
+	  /* If clients cannot determine for sure whether nscd is running
+	     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 (__glibc_unlikely (! dont_need_update))
+	    {
+	      next_wait = MIN (UPDATE_MAPPING_TIMEOUT, next_wait);
+	      dbs[my_number].head->timestamp = now;
+	    }
+
+	  pthread_mutex_lock (prune_lock);
+
+	  /* Make it known when we will wake up again.  */
+	  if (now + next_wait < dbs[my_number].wakeup_time)
+	    dbs[my_number].wakeup_time = now + next_wait;
+	  else
+	    next_wait = dbs[my_number].wakeup_time - now;
+	}
+      else
+	/* The cache was just pruned.  Do not do it again now.  Just
+	   use the new timeout value.  */
+	next_wait = dbs[my_number].wakeup_time - now;
+
+      if (clock_gettime (timeout_clock, &prune_ts) == -1)
+	/* Should never happen.  */
+	abort ();
+
+      /* Compute next timeout time.  */
+      prune_ts.tv_sec += next_wait;
+    }
+}
+
+
+/* This is the main loop.  It is replicated in different threads but
+   the use of the ready list makes sure only one thread handles an
+   incoming connection.  */
+static void *
+__attribute__ ((__noreturn__))
+nscd_run_worker (void *p)
+{
+  char buf[256];
+
+  /* Initial locking.  */
+  pthread_mutex_lock (&readylist_lock);
+
+  /* One more thread available.  */
+  ++nready;
+
+  while (1)
+    {
+      while (readylist == NULL)
+	pthread_cond_wait (&readylist_cond, &readylist_lock);
+
+      struct fdlist *it = readylist->next;
+      if (readylist->next == readylist)
+	/* Just one entry on the list.  */
+	readylist = NULL;
+      else
+	readylist->next = it->next;
+
+      /* Extract the information and mark the record ready to be used
+	 again.  */
+      int fd = it->fd;
+      it->next = NULL;
+
+      /* One more thread available.  */
+      --nready;
+
+      /* We are done with the list.  */
+      pthread_mutex_unlock (&readylist_lock);
+
+      /* Now read the request.  */
+      request_header req;
+      if (__builtin_expect (TEMP_FAILURE_RETRY (read (fd, &req, sizeof (req)))
+			    != sizeof (req), 0))
+	{
+	  /* We failed to read data.  Note that this also might mean we
+	     failed because we would have blocked.  */
+	  if (debug_level > 0)
+	    dbg_log (_("short read while reading request: %s"),
+		     strerror_r (errno, buf, sizeof (buf)));
+	  goto close_and_out;
+	}
+
+      /* Check whether this is a valid request type.  */
+      if (req.type < GETPWBYNAME || req.type >= LASTREQ)
+	goto close_and_out;
+
+      /* Some systems have no SO_PEERCRED implementation.  They don't
+	 care about security so we don't as well.  */
+      uid_t uid = -1;
+#ifdef SO_PEERCRED
+      pid_t pid = 0;
+
+      if (__glibc_unlikely (debug_level > 0))
+	{
+	  struct ucred caller;
+	  socklen_t optlen = sizeof (caller);
+
+	  if (getsockopt (fd, SOL_SOCKET, SO_PEERCRED, &caller, &optlen) == 0)
+	    pid = caller.pid;
+	}
+#else
+      const pid_t pid = 0;
+#endif
+
+      /* It should not be possible to crash the nscd with a silly
+	 request (i.e., a terribly large key).  We limit the size to 1kb.  */
+      if (__builtin_expect (req.key_len, 1) < 0
+	  || __builtin_expect (req.key_len, 1) > MAXKEYLEN)
+	{
+	  if (debug_level > 0)
+	    dbg_log (_("key length in request too long: %d"), req.key_len);
+	}
+      else
+	{
+	  /* Get the key.  */
+	  char keybuf[MAXKEYLEN + 1];
+
+	  if (__builtin_expect (TEMP_FAILURE_RETRY (read (fd, keybuf,
+							  req.key_len))
+				!= req.key_len, 0))
+	    {
+	      /* Again, this can also mean we would have blocked.  */
+	      if (debug_level > 0)
+		dbg_log (_("short read while reading request key: %s"),
+			 strerror_r (errno, buf, sizeof (buf)));
+	      goto close_and_out;
+	    }
+	  keybuf[req.key_len] = '\0';
+
+	  if (__builtin_expect (debug_level, 0) > 0)
+	    {
+#ifdef SO_PEERCRED
+	      if (pid != 0)
+		dbg_log (_("\
+handle_request: request received (Version = %d) from PID %ld"),
+			 req.version, (long int) pid);
+	      else
+#endif
+		dbg_log (_("\
+handle_request: request received (Version = %d)"), req.version);
+	    }
+
+	  /* Phew, we got all the data, now process it.  */
+	  handle_request (fd, &req, keybuf, uid, pid);
+	}
+
+    close_and_out:
+      /* We are done.  */
+      close (fd);
+
+      /* Re-locking.  */
+      pthread_mutex_lock (&readylist_lock);
+
+      /* One more thread available.  */
+      ++nready;
+    }
+  /* NOTREACHED */
+}
+
+
+static unsigned int nconns;
+
+static void
+fd_ready (int fd)
+{
+  pthread_mutex_lock (&readylist_lock);
+
+  /* Find an empty entry in FDLIST.  */
+  size_t inner;
+  for (inner = 0; inner < nconns; ++inner)
+    if (fdlist[inner].next == NULL)
+      break;
+  assert (inner < nconns);
+
+  fdlist[inner].fd = fd;
+
+  if (readylist == NULL)
+    readylist = fdlist[inner].next = &fdlist[inner];
+  else
+    {
+      fdlist[inner].next = readylist->next;
+      readylist = readylist->next = &fdlist[inner];
+    }
+
+  bool do_signal = true;
+  if (__glibc_unlikely (nready == 0))
+    {
+      ++client_queued;
+      do_signal = false;
+
+      /* Try to start another thread to help out.  */
+      pthread_t th;
+      if (nthreads < max_nthreads
+	  && pthread_create (&th, &attr, nscd_run_worker,
+			     (void *) (long int) nthreads) == 0)
+	{
+	  /* We got another thread.  */
+	  ++nthreads;
+	  /* The new thread might need a kick.  */
+	  do_signal = true;
+	}
+
+    }
+
+  pthread_mutex_unlock (&readylist_lock);
+
+  /* Tell one of the worker threads there is work to do.  */
+  if (do_signal)
+    pthread_cond_signal (&readylist_cond);
+}
+
+
+/* Check whether restarting should happen.  */
+static bool
+restart_p (time_t now)
+{
+  return (paranoia && readylist == NULL && nready == nthreads
+	  && now >= restart_time);
+}
+
+
+/* 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__))
+main_loop_poll (void)
+{
+  struct pollfd *conns = (struct pollfd *) xmalloc (nconns
+						    * sizeof (conns[0]));
+
+  conns[0].fd = sock;
+  conns[0].events = POLLRDNORM;
+  size_t nused = 1;
+  size_t firstfree = 1;
+
+#ifdef HAVE_INOTIFY
+  if (inotify_fd != -1)
+    {
+      conns[1].fd = inotify_fd;
+      conns[1].events = POLLRDNORM;
+      nused = 2;
+      firstfree = 2;
+    }
+#endif
+
+#ifdef HAVE_NETLINK
+  size_t idx_nl_status_fd = 0;
+  if (nl_status_fd != -1)
+    {
+      idx_nl_status_fd = nused;
+      conns[nused].fd = nl_status_fd;
+      conns[nused].events = POLLRDNORM;
+      ++nused;
+      firstfree = nused;
+    }
+#endif
+
+  while (1)
+    {
+      /* Wait for any event.  We wait at most a couple of seconds so
+	 that we can check whether we should close any of the accepted
+	 connections since we have not received a request.  */
+#define MAX_ACCEPT_TIMEOUT 30
+#define MIN_ACCEPT_TIMEOUT 5
+#define MAIN_THREAD_TIMEOUT \
+  (MAX_ACCEPT_TIMEOUT * 1000						      \
+   - ((MAX_ACCEPT_TIMEOUT - MIN_ACCEPT_TIMEOUT) * 1000 * nused) / (2 * nconns))
+
+      int n = poll (conns, nused, MAIN_THREAD_TIMEOUT);
+
+      time_t now = time (NULL);
+
+      /* If there is a descriptor ready for reading or there is a new
+	 connection, process this now.  */
+      if (n > 0)
+	{
+	  if (conns[0].revents != 0)
+	    {
+	      /* We have a new incoming connection.  Accept the connection.  */
+	      int fd = TEMP_FAILURE_RETRY (accept4 (sock, NULL, NULL,
+						    SOCK_NONBLOCK));
+
+	      /* Use the descriptor if we have not reached the limit.  */
+	      if (fd >= 0)
+		{
+		  if (firstfree < nconns)
+		    {
+		      conns[firstfree].fd = fd;
+		      conns[firstfree].events = POLLRDNORM;
+		      starttime[firstfree] = now;
+		      if (firstfree >= nused)
+			nused = firstfree + 1;
+
+		      do
+			++firstfree;
+		      while (firstfree < nused && conns[firstfree].fd != -1);
+		    }
+		  else
+		    /* We cannot use the connection so close it.  */
+		    close (fd);
+		}
+
+	      --n;
+	    }
+
+	  size_t first = 1;
+#ifdef HAVE_INOTIFY
+	  if (inotify_fd != -1 && conns[1].fd == inotify_fd)
+	    {
+	      if (conns[1].revents != 0)
+		{
+		  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;
+	    }
+#endif
+
+#ifdef HAVE_NETLINK
+	  if (idx_nl_status_fd != 0 && conns[idx_nl_status_fd].revents != 0)
+	    {
+	      char buf[4096];
+	      /* Read all the data.  We do not interpret it here.  */
+	      while (TEMP_FAILURE_RETRY (read (nl_status_fd, buf,
+					       sizeof (buf))) != -1)
+		;
+
+	      dbs[hstdb].head->extra_data[NSCD_HST_IDX_CONF_TIMESTAMP]
+		= __bump_nl_timestamp ();
+	    }
+#endif
+
+	  for (size_t cnt = first; cnt < nused && n > 0; ++cnt)
+	    if (conns[cnt].revents != 0)
+	      {
+		fd_ready (conns[cnt].fd);
+
+		/* Clean up the CONNS array.  */
+		conns[cnt].fd = -1;
+		if (cnt < firstfree)
+		  firstfree = cnt;
+		if (cnt == nused - 1)
+		  do
+		    --nused;
+		  while (conns[nused - 1].fd == -1);
+
+		--n;
+	      }
+	}
+
+      /* Now find entries which have timed out.  */
+      assert (nused > 0);
+
+      /* We make the timeout length depend on the number of file
+	 descriptors currently used.  */
+#define ACCEPT_TIMEOUT \
+  (MAX_ACCEPT_TIMEOUT							      \
+   - ((MAX_ACCEPT_TIMEOUT - MIN_ACCEPT_TIMEOUT) * nused) / nconns)
+      time_t laststart = now - ACCEPT_TIMEOUT;
+
+      for (size_t cnt = nused - 1; cnt > 0; --cnt)
+	{
+	  if (conns[cnt].fd != -1 && starttime[cnt] < laststart)
+	    {
+	      /* Remove the entry, it timed out.  */
+	      (void) close (conns[cnt].fd);
+	      conns[cnt].fd = -1;
+
+	      if (cnt < firstfree)
+		firstfree = cnt;
+	      if (cnt == nused - 1)
+		do
+		  --nused;
+		while (conns[nused - 1].fd == -1);
+	    }
+	}
+
+      if (restart_p (now))
+	restart ();
+    }
+}
+
+
+#ifdef HAVE_EPOLL
+static void
+main_loop_epoll (int efd)
+{
+  struct epoll_event ev = { 0, };
+  int nused = 1;
+  size_t highest = 0;
+
+  /* Add the socket.  */
+  ev.events = EPOLLRDNORM;
+  ev.data.fd = sock;
+  if (epoll_ctl (efd, EPOLL_CTL_ADD, sock, &ev) == -1)
+    /* We cannot use epoll.  */
+    return;
+
+# ifdef HAVE_INOTIFY
+  if (inotify_fd != -1)
+    {
+      ev.events = EPOLLRDNORM;
+      ev.data.fd = inotify_fd;
+      if (epoll_ctl (efd, EPOLL_CTL_ADD, inotify_fd, &ev) == -1)
+	/* We cannot use epoll.  */
+	return;
+      nused = 2;
+    }
+# endif
+
+# ifdef HAVE_NETLINK
+  if (nl_status_fd != -1)
+    {
+      ev.events = EPOLLRDNORM;
+      ev.data.fd = nl_status_fd;
+      if (epoll_ctl (efd, EPOLL_CTL_ADD, nl_status_fd, &ev) == -1)
+	/* We cannot use epoll.  */
+	return;
+    }
+# endif
+
+  while (1)
+    {
+      struct epoll_event revs[100];
+# define nrevs (sizeof (revs) / sizeof (revs[0]))
+
+      int n = epoll_wait (efd, revs, nrevs, MAIN_THREAD_TIMEOUT);
+
+      time_t now = time (NULL);
+
+      for (int cnt = 0; cnt < n; ++cnt)
+	if (revs[cnt].data.fd == sock)
+	  {
+	    /* A new connection.  */
+	    int fd = TEMP_FAILURE_RETRY (accept4 (sock, NULL, NULL,
+						  SOCK_NONBLOCK));
+
+	    /* Use the descriptor if we have not reached the limit.  */
+	    if (fd >= 0)
+	      {
+		/* Try to add the  new descriptor.  */
+		ev.data.fd = fd;
+		if (fd >= nconns
+		    || epoll_ctl (efd, EPOLL_CTL_ADD, fd, &ev) == -1)
+		  /* The descriptor is too large or something went
+		     wrong.  Close the descriptor.  */
+		  close (fd);
+		else
+		  {
+		    /* Remember when we accepted the connection.  */
+		    starttime[fd] = now;
+
+		    if (fd > highest)
+		      highest = fd;
+
+		    ++nused;
+		  }
+	      }
+	  }
+# ifdef HAVE_INOTIFY
+	else if (revs[cnt].data.fd == inotify_fd)
+	  {
+	    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);
+		(void) epoll_ctl (efd, EPOLL_CTL_DEL, inotify_fd, NULL);
+		close (inotify_fd);
+		inotify_fd = -1;
+		break;
+	      }
+	  }
+# endif
+# ifdef HAVE_NETLINK
+	else if (revs[cnt].data.fd == nl_status_fd)
+	  {
+	    char buf[4096];
+	    /* Read all the data.  We do not interpret it here.  */
+	    while (TEMP_FAILURE_RETRY (read (nl_status_fd, buf,
+					     sizeof (buf))) != -1)
+	      ;
+
+	    __bump_nl_timestamp ();
+	  }
+# endif
+	else
+	  {
+	    /* Remove the descriptor from the epoll descriptor.  */
+	    (void) epoll_ctl (efd, EPOLL_CTL_DEL, revs[cnt].data.fd, NULL);
+
+	    /* Get a worker to handle the request.  */
+	    fd_ready (revs[cnt].data.fd);
+
+	    /* Reset the time.  */
+	    starttime[revs[cnt].data.fd] = 0;
+	    if (revs[cnt].data.fd == highest)
+	      do
+		--highest;
+	      while (highest > 0 && starttime[highest] == 0);
+
+	    --nused;
+	  }
+
+      /*  Now look for descriptors for accepted connections which have
+	  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)
+	  {
+	    /* We are waiting for this one for too long.  Close it.  */
+	    (void) epoll_ctl (efd, EPOLL_CTL_DEL, cnt, NULL);
+
+	    (void) close (cnt);
+
+	    starttime[cnt] = 0;
+	    if (cnt == highest)
+	      --highest;
+	  }
+	else if (cnt != sock && starttime[cnt] == 0 && cnt == highest)
+	  --highest;
+
+      if (restart_p (now))
+	restart ();
+    }
+}
+#endif
+
+
+/* Start all the threads we want.  The initial process is thread no. 1.  */
+void
+start_threads (void)
+{
+  /* Initialize the conditional variable we will use.  The only
+     non-standard attribute we might use is the clock selection.  */
+  pthread_condattr_t condattr;
+  pthread_condattr_init (&condattr);
+
+#if defined _POSIX_CLOCK_SELECTION && _POSIX_CLOCK_SELECTION >= 0 \
+    && defined _POSIX_MONOTONIC_CLOCK && _POSIX_MONOTONIC_CLOCK >= 0
+  /* Determine whether the monotonous clock is available.  */
+  struct timespec dummy;
+# if _POSIX_MONOTONIC_CLOCK == 0
+  if (sysconf (_SC_MONOTONIC_CLOCK) > 0)
+# endif
+# if _POSIX_CLOCK_SELECTION == 0
+    if (sysconf (_SC_CLOCK_SELECTION) > 0)
+# endif
+      if (clock_getres (CLOCK_MONOTONIC, &dummy) == 0
+	  && pthread_condattr_setclock (&condattr, CLOCK_MONOTONIC) == 0)
+	timeout_clock = CLOCK_MONOTONIC;
+#endif
+
+  /* Create the attribute for the threads.  They are all created
+     detached.  */
+  pthread_attr_init (&attr);
+  pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
+  /* Use 1MB stacks, twice as much for 64-bit architectures.  */
+  pthread_attr_setstacksize (&attr, NSCD_THREAD_STACKSIZE);
+
+  /* We allow less than LASTDB threads only for debugging.  */
+  if (debug_level == 0)
+    nthreads = MAX (nthreads, lastdb);
+
+  /* Create the threads which prune the databases.  */
+  // XXX Ideally this work would be done by some of the worker threads.
+  // XXX But this is problematic since we would need to be able to wake
+  // XXX them up explicitly as well as part of the group handling the
+  // XXX ready-list.  This requires an operation where we can wait on
+  // XXX two conditional variables at the same time.  This operation
+  // XXX does not exist (yet).
+  for (long int i = 0; i < lastdb; ++i)
+    {
+      /* Initialize the conditional variable.  */
+      if (pthread_cond_init (&dbs[i].prune_cond, &condattr) != 0)
+	{
+	  dbg_log (_("could not initialize conditional variable"));
+	  do_exit (1, 0, NULL);
+	}
+
+      pthread_t th;
+      if (dbs[i].enabled
+	  && pthread_create (&th, &attr, nscd_run_prune, (void *) i) != 0)
+	{
+	  dbg_log (_("could not start clean-up thread; terminating"));
+	  do_exit (1, 0, NULL);
+	}
+    }
+
+  pthread_condattr_destroy (&condattr);
+
+  for (long int i = 0; i < nthreads; ++i)
+    {
+      pthread_t th;
+      if (pthread_create (&th, &attr, nscd_run_worker, NULL) != 0)
+	{
+	  if (i == 0)
+	    {
+	      dbg_log (_("could not start any worker thread; terminating"));
+	      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.  */
+  const long int nfds = sysconf (_SC_OPEN_MAX);
+#define MINCONN 32
+#define MAXCONN 16384
+  if (nfds == -1 || nfds > MAXCONN)
+    nconns = MAXCONN;
+  else if (nfds < MINCONN)
+    nconns = MINCONN;
+  else
+    nconns = nfds;
+
+  /* We need memory to pass descriptors on to the worker threads.  */
+  fdlist = (struct fdlist *) xcalloc (nconns, sizeof (fdlist[0]));
+  /* Array to keep track when connection was accepted.  */
+  starttime = (time_t *) xcalloc (nconns, sizeof (starttime[0]));
+
+  /* In the main thread we execute the loop which handles incoming
+     connections.  */
+#ifdef HAVE_EPOLL
+  int efd = epoll_create (100);
+  if (efd != -1)
+    {
+      main_loop_epoll (efd);
+      close (efd);
+    }
+#endif
+
+  main_loop_poll ();
+}
+
+
+/* Look up the uid, gid, and supplementary groups to run nscd as. When
+   this function is called, we are not listening on the nscd socket yet so
+   we can just use the ordinary lookup functions without causing a lockup  */
+static void
+begin_drop_privileges (void)
+{
+  struct passwd *pwd = getpwnam (server_user);
+
+  if (pwd == NULL)
+    {
+      dbg_log (_("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;
+  server_gid = pwd->pw_gid;
+
+  /* Save the old UID/GID if we have to change back.  */
+  if (paranoia)
+    {
+      old_uid = getuid ();
+      old_gid = getgid ();
+    }
+
+  if (getgrouplist (server_user, server_gid, NULL, &server_ngroups) == 0)
+    {
+      /* This really must never happen.  */
+      dbg_log (_("Failed to run nscd as user '%s'"), server_user);
+      do_exit (EXIT_FAILURE, errno,
+	       _("initial getgrouplist failed"));
+    }
+
+  server_groups = (gid_t *) xmalloc (server_ngroups * sizeof (gid_t));
+
+  if (getgrouplist (server_user, server_gid, server_groups, &server_ngroups)
+      == -1)
+    {
+      dbg_log (_("Failed to run nscd as user '%s'"), server_user);
+      do_exit (EXIT_FAILURE, errno, _("getgrouplist failed"));
+    }
+}
+
+
+/* Call setgroups(), setgid(), and setuid() to drop root privileges and
+   run nscd as the user specified in the configuration file.  */
+static void
+finish_drop_privileges (void)
+{
+#if defined HAVE_LIBAUDIT && defined HAVE_LIBCAP
+  /* We need to preserve the capabilities to connect to the audit daemon.  */
+  cap_t new_caps = preserve_capabilities ();
+#endif
+
+  if (setgroups (server_ngroups, server_groups) == -1)
+    {
+      dbg_log (_("Failed to run nscd as user '%s'"), server_user);
+      do_exit (EXIT_FAILURE, errno, _("setgroups failed"));
+    }
+
+  int res;
+  if (paranoia)
+    res = setresgid (server_gid, server_gid, old_gid);
+  else
+    res = setgid (server_gid);
+  if (res == -1)
+    {
+      dbg_log (_("Failed to run nscd as user '%s'"), server_user);
+      do_exit (4, errno, "setgid");
+    }
+
+  if (paranoia)
+    res = setresuid (server_uid, server_uid, old_uid);
+  else
+    res = setuid (server_uid);
+  if (res == -1)
+    {
+      dbg_log (_("Failed to run nscd as user '%s'"), server_user);
+      do_exit (4, errno, "setuid");
+    }
+
+#if defined HAVE_LIBAUDIT && defined HAVE_LIBCAP
+  /* Remove the temporary capabilities.  */
+  install_real_capabilities (new_caps);
+#endif
+}
diff --git a/REORG.TODO/nscd/dbg_log.c b/REORG.TODO/nscd/dbg_log.c
new file mode 100644
index 0000000000..d4b19acc0c
--- /dev/null
+++ b/REORG.TODO/nscd/dbg_log.c
@@ -0,0 +1,85 @@
+/* Copyright (c) 1998-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1998.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published
+   by the Free Software Foundation; version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include "dbg_log.h"
+#include "nscd.h"
+
+/* if in debug mode and we have a debug file, we write the messages to it,
+   if in debug mode and no debug file, we write the messages to stderr,
+   else to syslog.  */
+
+static char *logfilename;
+FILE *dbgout;
+int debug_level;
+
+void
+set_logfile (const char *logfile)
+{
+  logfilename = strdup (logfile);
+}
+
+int
+init_logfile (void)
+{
+  if (logfilename)
+    {
+      dbgout = fopen64 (logfilename, "a");
+      return dbgout == NULL ? 0 : 1;
+    }
+  return 1;
+}
+
+void
+dbg_log (const char *fmt,...)
+{
+  va_list ap;
+  char msg2[512];
+
+  va_start (ap, fmt);
+  vsnprintf (msg2, sizeof (msg2), fmt, ap);
+
+  if (debug_level > 0)
+    {
+      time_t t = time (NULL);
+
+      struct tm now;
+      localtime_r (&t, &now);
+
+      char buf[256];
+      strftime (buf, sizeof (buf), "%c", &now);
+
+      char msg[512];
+      snprintf (msg, sizeof (msg), "%s - %d: %s%s", buf, getpid (), msg2,
+		msg2[strlen (msg2) - 1] == '\n' ? "" : "\n");
+      if (dbgout)
+	{
+	  fputs (msg, dbgout);
+	  fflush (dbgout);
+	}
+      else
+	fputs (msg, stderr);
+    }
+  else
+    syslog (LOG_NOTICE, "%d %s", getpid (), msg2);
+
+  va_end (ap);
+}
diff --git a/REORG.TODO/nscd/dbg_log.h b/REORG.TODO/nscd/dbg_log.h
new file mode 100644
index 0000000000..158bfb39aa
--- /dev/null
+++ b/REORG.TODO/nscd/dbg_log.h
@@ -0,0 +1,30 @@
+/* Copyright (c) 1998-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1998.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef _DBG_LOG_H
+#define _DBG_LOG_H	1
+
+extern int debug_level;
+
+extern void dbg_log (const char *str, ...)
+     __attribute__ ((__format__ (__printf__, 1, 2)));;
+
+extern void set_logfile (const char *logfile);
+extern int init_logfile (void);
+
+#endif
diff --git a/REORG.TODO/nscd/gai.c b/REORG.TODO/nscd/gai.c
new file mode 100644
index 0000000000..a1aeadadc3
--- /dev/null
+++ b/REORG.TODO/nscd/gai.c
@@ -0,0 +1,48 @@
+/* Copyright (C) 2004-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 2004.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published
+   by the Free Software Foundation; version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
+
+#include <alloca.h>
+
+/* This file uses the getaddrinfo code but it compiles it without NSCD
+   support.  We just need a few symbol renames.  */
+#define __inet_aton inet_aton
+#define __ioctl ioctl
+#define __getsockname getsockname
+#define __socket socket
+#define __recvmsg recvmsg
+#define __bind bind
+#define __sendto sendto
+#define __strchrnul strchrnul
+#define __getline getline
+#define __qsort_r qsort_r
+/* nscd uses 1MB or 2MB thread stacks.  */
+#define __libc_use_alloca(size) (size <= __MAX_ALLOCA_CUTOFF)
+
+/* We are nscd, so we don't want to be talking to ourselves.  */
+#undef  USE_NSCD
+
+#include <getaddrinfo.c>
+
+/* Support code.  */
+#include <check_pf.c>
+#include <check_native.c>
+#ifdef HAVE_LIBIDN
+# include <libidn/idn-stub.c>
+#endif
+
+/* Some variables normally defined in libc.  */
+service_user *__nss_hosts_database;
diff --git a/REORG.TODO/nscd/getgrgid_r.c b/REORG.TODO/nscd/getgrgid_r.c
new file mode 100644
index 0000000000..0e7fd21be7
--- /dev/null
+++ b/REORG.TODO/nscd/getgrgid_r.c
@@ -0,0 +1,35 @@
+/* Copyright (C) 1996-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published
+   by the Free Software Foundation; version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
+
+#include <grp.h>
+
+#include <grp-merge.h>
+
+#define LOOKUP_TYPE	struct group
+#define FUNCTION_NAME	getgrgid
+#define DATABASE_NAME	group
+#define ADD_PARAMS	gid_t gid
+#define ADD_VARIABLES	gid
+#define BUFLEN		NSS_BUFLEN_GROUP
+
+#define DEEPCOPY_FN	__copy_grp
+#define MERGE_FN	__merge_grp
+
+/* We are nscd, so we don't want to be talking to ourselves.  */
+#undef	USE_NSCD
+
+#include <nss/getXXbyYY_r.c>
diff --git a/REORG.TODO/nscd/getgrnam_r.c b/REORG.TODO/nscd/getgrnam_r.c
new file mode 100644
index 0000000000..80cb441888
--- /dev/null
+++ b/REORG.TODO/nscd/getgrnam_r.c
@@ -0,0 +1,34 @@
+/* Copyright (C) 1996-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published
+   by the Free Software Foundation; version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
+
+#include <grp.h>
+
+#include <grp-merge.h>
+
+#define LOOKUP_TYPE	struct group
+#define FUNCTION_NAME	getgrnam
+#define DATABASE_NAME	group
+#define ADD_PARAMS	const char *name
+#define ADD_VARIABLES	name
+
+#define DEEPCOPY_FN	__copy_grp
+#define MERGE_FN	__merge_grp
+
+/* We are nscd, so we don't want to be talking to ourselves.  */
+#undef	USE_NSCD
+
+#include <nss/getXXbyYY_r.c>
diff --git a/REORG.TODO/nscd/gethstbyad_r.c b/REORG.TODO/nscd/gethstbyad_r.c
new file mode 100644
index 0000000000..b17f0d2b51
--- /dev/null
+++ b/REORG.TODO/nscd/gethstbyad_r.c
@@ -0,0 +1,46 @@
+/* Copyright (C) 1996-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published
+   by the Free Software Foundation; version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
+
+#include <netdb.h>
+
+
+#define LOOKUP_TYPE	struct hostent
+#define FUNCTION_NAME	gethostbyaddr2
+#define FUNCTION2_NAME	gethostbyaddr
+#define DATABASE_NAME	hosts
+#define ADD_PARAMS	const void *addr, socklen_t len, int type
+#define EXTRA_PARAMS	, int32_t *ttlp
+#define ADD_VARIABLES	addr, len, type
+#define EXTRA_VARIABLES , ttlp
+#define NEED_H_ERRNO	1
+#define NEED__RES	1
+#define NEED__RES_HCONF 1
+
+/* We are nscd, so we don't want to be talking to ourselves.  */
+#undef	USE_NSCD
+
+#include "../nss/getXXbyYY_r.c"
+
+
+int
+__gethostbyaddr_r (const void *addr, socklen_t len, int type,
+		   struct hostent *result_buf, char *buf, size_t buflen,
+		   struct hostent **result, int *h_errnop)
+{
+  return __gethostbyaddr2_r (addr, len, type, result_buf, buf, buflen,
+			     result, h_errnop, NULL);
+}
diff --git a/REORG.TODO/nscd/gethstbynm3_r.c b/REORG.TODO/nscd/gethstbynm3_r.c
new file mode 100644
index 0000000000..41bb26845d
--- /dev/null
+++ b/REORG.TODO/nscd/gethstbynm3_r.c
@@ -0,0 +1,55 @@
+/* Copyright (C) 1996-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published
+   by the Free Software Foundation; version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
+
+#include <ctype.h>
+#include <errno.h>
+#include <netdb.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+
+#define LOOKUP_TYPE	struct hostent
+#define FUNCTION_NAME	gethostbyname3
+#define FUNCTION2_NAME	gethostbyname2
+#define DATABASE_NAME	hosts
+#define ADD_PARAMS	const char *name, int af
+#define EXTRA_PARAMS	, int32_t *ttlp, char **canonp
+#define ADD_VARIABLES	name, af
+#define EXTRA_VARIABLES	, ttlp, canonp
+#define NEED_H_ERRNO	1
+#define NEED__RES_HCONF 1
+
+#define HANDLE_DIGITS_DOTS	1
+#define HAVE_LOOKUP_BUFFER	1
+#define HAVE_AF			1
+
+#define __inet_aton inet_aton
+
+/* We are nscd, so we don't want to be talking to ourselves.  */
+#undef	USE_NSCD
+
+#include "../nss/getXXbyYY_r.c"
+
+
+int
+__gethostbyname2_r (const char *name, int af, struct hostent *ret, char *buf,
+		    size_t buflen, struct hostent **result, int *h_errnop)
+{
+  return __gethostbyname3_r (name, af, ret, buf, buflen, result, h_errnop,
+			     NULL, NULL);
+}
diff --git a/REORG.TODO/nscd/getpwnam_r.c b/REORG.TODO/nscd/getpwnam_r.c
new file mode 100644
index 0000000000..9af95b6c01
--- /dev/null
+++ b/REORG.TODO/nscd/getpwnam_r.c
@@ -0,0 +1,31 @@
+/* Copyright (C) 1996-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published
+   by the Free Software Foundation; version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
+
+#include <pwd.h>
+
+
+#define LOOKUP_TYPE	struct passwd
+#define FUNCTION_NAME	getpwnam
+#define DATABASE_NAME	passwd
+#define ADD_PARAMS	const char *name
+#define ADD_VARIABLES	name
+#define BUFLEN		NSS_BUFLEN_PASSWD
+
+/* We are nscd, so we don't want to be talking to ourselves.  */
+#undef	USE_NSCD
+
+#include <nss/getXXbyYY_r.c>
diff --git a/REORG.TODO/nscd/getpwuid_r.c b/REORG.TODO/nscd/getpwuid_r.c
new file mode 100644
index 0000000000..fae2a141be
--- /dev/null
+++ b/REORG.TODO/nscd/getpwuid_r.c
@@ -0,0 +1,31 @@
+/* Copyright (C) 1996-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published
+   by the Free Software Foundation; version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
+
+#include <pwd.h>
+
+
+#define LOOKUP_TYPE	struct passwd
+#define FUNCTION_NAME	getpwuid
+#define DATABASE_NAME	passwd
+#define ADD_PARAMS	uid_t uid
+#define ADD_VARIABLES	uid
+#define BUFLEN		NSS_BUFLEN_PASSWD
+
+/* We are nscd, so we don't want to be talking to ourselves.  */
+#undef	USE_NSCD
+
+#include <nss/getXXbyYY_r.c>
diff --git a/REORG.TODO/nscd/getsrvbynm_r.c b/REORG.TODO/nscd/getsrvbynm_r.c
new file mode 100644
index 0000000000..ef6eac0358
--- /dev/null
+++ b/REORG.TODO/nscd/getsrvbynm_r.c
@@ -0,0 +1,30 @@
+/* Copyright (C) 1996-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published
+   by the Free Software Foundation; version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
+
+#include <netdb.h>
+
+
+#define LOOKUP_TYPE		struct servent
+#define FUNCTION_NAME		getservbyname
+#define DATABASE_NAME		services
+#define ADD_PARAMS		const char *name, const char *proto
+#define ADD_VARIABLES		name, proto
+
+/* We are nscd, so we don't want to be talking to ourselves.  */
+#undef	USE_NSCD
+
+#include "../nss/getXXbyYY_r.c"
diff --git a/REORG.TODO/nscd/getsrvbypt_r.c b/REORG.TODO/nscd/getsrvbypt_r.c
new file mode 100644
index 0000000000..10a3d57ab4
--- /dev/null
+++ b/REORG.TODO/nscd/getsrvbypt_r.c
@@ -0,0 +1,30 @@
+/* Copyright (C) 1996-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published
+   by the Free Software Foundation; version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
+
+#include <netdb.h>
+
+
+#define LOOKUP_TYPE		struct servent
+#define FUNCTION_NAME		getservbyport
+#define DATABASE_NAME		services
+#define ADD_PARAMS		int port, const char *proto
+#define ADD_VARIABLES		port, proto
+
+/* We are nscd, so we don't want to be talking to ourselves.  */
+#undef	USE_NSCD
+
+#include "../nss/getXXbyYY_r.c"
diff --git a/REORG.TODO/nscd/grpcache.c b/REORG.TODO/nscd/grpcache.c
new file mode 100644
index 0000000000..d2ad53509d
--- /dev/null
+++ b/REORG.TODO/nscd/grpcache.c
@@ -0,0 +1,572 @@
+/* Cache handling for group lookup.
+   Copyright (C) 1998-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published
+   by the Free Software Foundation; version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
+
+#include <alloca.h>
+#include <assert.h>
+#include <errno.h>
+#include <error.h>
+#include <grp.h>
+#include <libintl.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <stackinfo.h>
+
+#include "nscd.h"
+#include "dbg_log.h"
+#ifdef HAVE_SENDFILE
+# include <kernel-features.h>
+#endif
+
+/* This is the standard reply in case the service is disabled.  */
+static const gr_response_header disabled =
+{
+  .version = NSCD_VERSION,
+  .found = -1,
+  .gr_name_len = 0,
+  .gr_passwd_len = 0,
+  .gr_gid = -1,
+  .gr_mem_cnt = 0,
+};
+
+/* This is the struct describing how to write this record.  */
+const struct iovec grp_iov_disabled =
+{
+  .iov_base = (void *) &disabled,
+  .iov_len = sizeof (disabled)
+};
+
+
+/* This is the standard reply in case we haven't found the dataset.  */
+static const gr_response_header notfound =
+{
+  .version = NSCD_VERSION,
+  .found = 0,
+  .gr_name_len = 0,
+  .gr_passwd_len = 0,
+  .gr_gid = -1,
+  .gr_mem_cnt = 0,
+};
+
+
+static time_t
+cache_addgr (struct database_dyn *db, int fd, request_header *req,
+	     const void *key, struct group *grp, uid_t owner,
+	     struct hashentry *const he, struct datahead *dh, int errval)
+{
+  bool all_written = true;
+  ssize_t total;
+  time_t t = time (NULL);
+
+  /* We allocate all data in one memory block: the iov vector,
+     the response header and the dataset itself.  */
+  struct dataset
+  {
+    struct datahead head;
+    gr_response_header resp;
+    char strdata[0];
+  } *dataset;
+
+  assert (offsetof (struct dataset, resp) == offsetof (struct datahead, data));
+
+  time_t timeout = MAX_TIMEOUT_VALUE;
+  if (grp == NULL)
+    {
+      if (he != NULL && errval == EAGAIN)
+	{
+	  /* If we have an old record available but cannot find one
+	     now because the service is not available we keep the old
+	     record and make sure it does not get removed.  */
+	  if (reload_count != UINT_MAX)
+	    /* Do not reset the value if we never not reload the record.  */
+	    dh->nreloads = reload_count - 1;
+
+	  /* Reload with the same time-to-live value.  */
+	  timeout = dh->timeout = t + db->postimeout;
+
+	  total = 0;
+	}
+      else
+	{
+	  /* We have no data.  This means we send the standard reply for this
+	     case.  */
+	  total = sizeof (notfound);
+
+	  if (fd != -1
+	      && TEMP_FAILURE_RETRY (send (fd, &notfound, total,
+					   MSG_NOSIGNAL)) != total)
+	    all_written = false;
+
+	  /* If we have a transient error or cannot permanently store
+	     the result, so be it.  */
+	  if (errno == EAGAIN || __builtin_expect (db->negtimeout == 0, 0))
+	    {
+	      /* Mark the old entry as obsolete.  */
+	      if (dh != NULL)
+		dh->usable = false;
+	    }
+	  else if ((dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len, 1)) != NULL)
+	    {
+	      timeout = datahead_init_neg (&dataset->head,
+					   (sizeof (struct dataset)
+					    + req->key_len), total,
+					   db->negtimeout);
+
+	      /* This is the reply.  */
+	      memcpy (&dataset->resp, &notfound, total);
+
+	      /* Copy the key data.  */
+	      memcpy (dataset->strdata, key, req->key_len);
+
+	      /* If necessary, we also propagate the data to disk.  */
+	      if (db->persistent)
+		{
+		  // XXX async OK?
+		  uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
+		  msync ((void *) pval,
+			 ((uintptr_t) dataset & pagesize_m1)
+			 + sizeof (struct dataset) + req->key_len, MS_ASYNC);
+		}
+
+	      (void) cache_add (req->type, &dataset->strdata, req->key_len,
+				&dataset->head, true, db, owner, he == NULL);
+
+	      pthread_rwlock_unlock (&db->lock);
+
+	      /* Mark the old entry as obsolete.  */
+	      if (dh != NULL)
+		dh->usable = false;
+	    }
+	}
+    }
+  else
+    {
+      /* Determine the I/O structure.  */
+      size_t gr_name_len = strlen (grp->gr_name) + 1;
+      size_t gr_passwd_len = strlen (grp->gr_passwd) + 1;
+      size_t gr_mem_cnt = 0;
+      uint32_t *gr_mem_len;
+      size_t gr_mem_len_total = 0;
+      char *gr_name;
+      char *cp;
+      const size_t key_len = strlen (key);
+      const size_t buf_len = 3 * sizeof (grp->gr_gid) + key_len + 1;
+      size_t alloca_used = 0;
+      char *buf = alloca_account (buf_len, alloca_used);
+      ssize_t n;
+      size_t cnt;
+
+      /* We need this to insert the `bygid' entry.  */
+      int key_offset;
+      n = snprintf (buf, buf_len, "%d%c%n%s", grp->gr_gid, '\0',
+		    &key_offset, (char *) key) + 1;
+
+      /* Determine the length of all members.  */
+      while (grp->gr_mem[gr_mem_cnt])
+	++gr_mem_cnt;
+      gr_mem_len = alloca_account (gr_mem_cnt * sizeof (uint32_t), alloca_used);
+      for (gr_mem_cnt = 0; grp->gr_mem[gr_mem_cnt]; ++gr_mem_cnt)
+	{
+	  gr_mem_len[gr_mem_cnt] = strlen (grp->gr_mem[gr_mem_cnt]) + 1;
+	  gr_mem_len_total += gr_mem_len[gr_mem_cnt];
+	}
+
+      total = (offsetof (struct dataset, strdata)
+	       + gr_mem_cnt * sizeof (uint32_t)
+	       + gr_name_len + gr_passwd_len + gr_mem_len_total);
+
+      /* If we refill the cache, first assume the reconrd did not
+	 change.  Allocate memory on the cache since it is likely
+	 discarded anyway.  If it turns out to be necessary to have a
+	 new record we can still allocate real memory.  */
+      bool dataset_temporary = false;
+      bool dataset_malloced = false;
+      dataset = NULL;
+
+      if (he == NULL)
+	{
+	  /* Prevent an INVALIDATE request from pruning the data between
+	     the two calls to cache_add.  */
+	  if (db->propagate)
+	    pthread_mutex_lock (&db->prune_run_lock);
+	  dataset = (struct dataset *) mempool_alloc (db, total + n, 1);
+	}
+
+      if (dataset == NULL)
+	{
+	  if (he == NULL && db->propagate)
+	    pthread_mutex_unlock (&db->prune_run_lock);
+
+	  /* We cannot permanently add the result in the moment.  But
+	     we can provide the result as is.  Store the data in some
+	     temporary memory.  */
+	  if (! __libc_use_alloca (alloca_used + total + n))
+	    {
+	      dataset = malloc (total + n);
+	      /* Perhaps we should log a message that we were unable
+		 to allocate memory for a large request.  */
+	      if (dataset == NULL)
+		goto out;
+	      dataset_malloced = true;
+	    }
+	  else
+	    dataset = alloca_account (total + n, alloca_used);
+
+	  /* We cannot add this record to the permanent database.  */
+	  dataset_temporary = true;
+	}
+
+      timeout = datahead_init_pos (&dataset->head, total + n,
+				   total - offsetof (struct dataset, resp),
+				   he == NULL ? 0 : dh->nreloads + 1,
+				   db->postimeout);
+
+      dataset->resp.version = NSCD_VERSION;
+      dataset->resp.found = 1;
+      dataset->resp.gr_name_len = gr_name_len;
+      dataset->resp.gr_passwd_len = gr_passwd_len;
+      dataset->resp.gr_gid = grp->gr_gid;
+      dataset->resp.gr_mem_cnt = gr_mem_cnt;
+
+      cp = dataset->strdata;
+
+      /* This is the member string length array.  */
+      cp = mempcpy (cp, gr_mem_len, gr_mem_cnt * sizeof (uint32_t));
+      gr_name = cp;
+      cp = mempcpy (cp, grp->gr_name, gr_name_len);
+      cp = mempcpy (cp, grp->gr_passwd, gr_passwd_len);
+
+      for (cnt = 0; cnt < gr_mem_cnt; ++cnt)
+	cp = mempcpy (cp, grp->gr_mem[cnt], gr_mem_len[cnt]);
+
+      /* Finally the stringified GID value.  */
+      memcpy (cp, buf, n);
+      char *key_copy = cp + key_offset;
+      assert (key_copy == (char *) rawmemchr (cp, '\0') + 1);
+
+      assert (cp == dataset->strdata + total - offsetof (struct dataset,
+							 strdata));
+
+      /* Now we can determine whether on refill we have to create a new
+	 record or not.  */
+      if (he != NULL)
+	{
+	  assert (fd == -1);
+
+	  if (total + n == dh->allocsize
+	      && total - offsetof (struct dataset, resp) == dh->recsize
+	      && memcmp (&dataset->resp, dh->data,
+			 dh->allocsize - offsetof (struct dataset, resp)) == 0)
+	    {
+	      /* The data has not changed.  We will just bump the
+		 timeout value.  Note that the new record has been
+		 allocated on the stack and need not be freed.  */
+	      dh->timeout = dataset->head.timeout;
+	      ++dh->nreloads;
+
+	      /* If the new record was allocated via malloc, then we must free
+		 it here.  */
+	      if (dataset_malloced)
+		free (dataset);
+	    }
+	  else
+	    {
+	      /* We have to create a new record.  Just allocate
+		 appropriate memory and copy it.  */
+	      struct dataset *newp
+		= (struct dataset *) mempool_alloc (db, total + n, 1);
+	      if (newp != NULL)
+		{
+		  /* Adjust pointers into the memory block.  */
+		  gr_name = (char *) newp + (gr_name - (char *) dataset);
+		  cp = (char *) newp + (cp - (char *) dataset);
+		  key_copy = (char *) newp + (key_copy - (char *) dataset);
+
+		  dataset = memcpy (newp, dataset, total + n);
+		  dataset_temporary = false;
+		}
+
+	      /* Mark the old record as obsolete.  */
+	      dh->usable = false;
+	    }
+	}
+      else
+	{
+	  /* We write the dataset before inserting it to the database
+	     since while inserting this thread might block and so would
+	     unnecessarily let the receiver wait.  */
+	  assert (fd != -1);
+
+#ifdef HAVE_SENDFILE
+	  if (__builtin_expect (db->mmap_used, 1) && ! dataset_temporary)
+	    {
+	      assert (db->wr_fd != -1);
+	      assert ((char *) &dataset->resp > (char *) db->data);
+	      assert ((char *) dataset - (char *) db->head
+		      + total
+		      <= (sizeof (struct database_pers_head)
+			  + db->head->module * sizeof (ref_t)
+			  + db->head->data_size));
+	      ssize_t written = sendfileall (fd, db->wr_fd,
+					     (char *) &dataset->resp
+					     - (char *) db->head,
+					     dataset->head.recsize);
+	      if (written != dataset->head.recsize)
+		{
+# ifndef __ASSUME_SENDFILE
+		  if (written == -1 && errno == ENOSYS)
+		    goto use_write;
+# endif
+		  all_written = false;
+		}
+	    }
+	  else
+# ifndef __ASSUME_SENDFILE
+	  use_write:
+# endif
+#endif
+	    if (writeall (fd, &dataset->resp, dataset->head.recsize)
+		!= dataset->head.recsize)
+	      all_written = false;
+	}
+
+      /* Add the record to the database.  But only if it has not been
+	 stored on the stack.  */
+      if (! dataset_temporary)
+	{
+	  /* If necessary, we also propagate the data to disk.  */
+	  if (db->persistent)
+	    {
+	      // XXX async OK?
+	      uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
+	      msync ((void *) pval,
+		     ((uintptr_t) dataset & pagesize_m1) + total + n,
+		     MS_ASYNC);
+	    }
+
+	  /* NB: in the following code we always must add the entry
+	     marked with FIRST first.  Otherwise we end up with
+	     dangling "pointers" in case a latter hash entry cannot be
+	     added.  */
+	  bool first = true;
+
+	  /* If the request was by GID, add that entry first.  */
+	  if (req->type == GETGRBYGID)
+	    {
+	      if (cache_add (GETGRBYGID, cp, key_offset, &dataset->head, true,
+			     db, owner, he == NULL) < 0)
+		goto out;
+
+	      first = false;
+	    }
+	  /* If the key is different from the name add a separate entry.  */
+	  else if (strcmp (key_copy, gr_name) != 0)
+	    {
+	      if (cache_add (GETGRBYNAME, key_copy, key_len + 1,
+			     &dataset->head, true, db, owner, he == NULL) < 0)
+		goto out;
+
+	      first = false;
+	    }
+
+	  /* We have to add the value for both, byname and byuid.  */
+	  if ((req->type == GETGRBYNAME || db->propagate)
+	      && __builtin_expect (cache_add (GETGRBYNAME, gr_name,
+					      gr_name_len,
+					      &dataset->head, first, db, owner,
+					      he == NULL)
+				   == 0, 1))
+	    {
+	      if (req->type == GETGRBYNAME && db->propagate)
+		(void) cache_add (GETGRBYGID, cp, key_offset, &dataset->head,
+				  false, db, owner, false);
+	    }
+
+	out:
+	  pthread_rwlock_unlock (&db->lock);
+	  if (he == NULL && db->propagate)
+	    pthread_mutex_unlock (&db->prune_run_lock);
+	}
+    }
+
+  if (__builtin_expect (!all_written, 0) && debug_level > 0)
+    {
+      char buf[256];
+      dbg_log (_("short write in %s: %s"),  __FUNCTION__,
+	       strerror_r (errno, buf, sizeof (buf)));
+    }
+
+  return timeout;
+}
+
+
+union keytype
+{
+  void *v;
+  gid_t g;
+};
+
+
+static int
+lookup (int type, union keytype key, struct group *resultbufp, char *buffer,
+	size_t buflen, struct group **grp)
+{
+  if (type == GETGRBYNAME)
+    return __getgrnam_r (key.v, resultbufp, buffer, buflen, grp);
+  else
+    return __getgrgid_r (key.g, resultbufp, buffer, buflen, grp);
+}
+
+
+static time_t
+addgrbyX (struct database_dyn *db, int fd, request_header *req,
+	  union keytype key, const char *keystr, uid_t uid,
+	  struct hashentry *he, struct datahead *dh)
+{
+  /* Search for the entry matching the key.  Please note that we don't
+     look again in the table whether the dataset is now available.  We
+     simply insert it.  It does not matter if it is in there twice.  The
+     pruning function only will look at the timestamp.  */
+  size_t buflen = 1024;
+  char *buffer = (char *) alloca (buflen);
+  struct group resultbuf;
+  struct group *grp;
+  bool use_malloc = false;
+  int errval = 0;
+
+  if (__glibc_unlikely (debug_level > 0))
+    {
+      if (he == NULL)
+	dbg_log (_("Haven't found \"%s\" in group cache!"), keystr);
+      else
+	dbg_log (_("Reloading \"%s\" in group cache!"), keystr);
+    }
+
+  while (lookup (req->type, key, &resultbuf, buffer, buflen, &grp) != 0
+	 && (errval = errno) == ERANGE)
+    {
+      errno = 0;
+
+      if (__glibc_unlikely (buflen > 32768))
+	{
+	  char *old_buffer = buffer;
+	  buflen *= 2;
+	  buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen);
+	  if (buffer == NULL)
+	    {
+	      /* We ran out of memory.  We cannot do anything but
+		 sending a negative response.  In reality this should
+		 never happen.  */
+	      grp = NULL;
+	      buffer = old_buffer;
+
+	      /* We set the error to indicate this is (possibly) a
+		 temporary error and that it does not mean the entry
+		 is not available at all.  */
+	      errval = EAGAIN;
+	      break;
+	    }
+	  use_malloc = true;
+	}
+      else
+	/* Allocate a new buffer on the stack.  If possible combine it
+	   with the previously allocated buffer.  */
+	buffer = (char *) extend_alloca (buffer, buflen, 2 * buflen);
+    }
+
+  time_t timeout = cache_addgr (db, fd, req, keystr, grp, uid, he, dh, errval);
+
+  if (use_malloc)
+    free (buffer);
+
+  return timeout;
+}
+
+
+void
+addgrbyname (struct database_dyn *db, int fd, request_header *req,
+	     void *key, uid_t uid)
+{
+  union keytype u = { .v = key };
+
+  addgrbyX (db, fd, req, u, key, uid, NULL, NULL);
+}
+
+
+time_t
+readdgrbyname (struct database_dyn *db, struct hashentry *he,
+	       struct datahead *dh)
+{
+  request_header req =
+    {
+      .type = GETGRBYNAME,
+      .key_len = he->len
+    };
+  union keytype u = { .v = db->data + he->key };
+
+  return addgrbyX (db, -1, &req, u, db->data + he->key, he->owner, he, dh);
+}
+
+
+void
+addgrbygid (struct database_dyn *db, int fd, request_header *req,
+	    void *key, uid_t uid)
+{
+  char *ep;
+  gid_t gid = strtoul ((char *) key, &ep, 10);
+
+  if (*(char *) key == '\0' || *ep != '\0')  /* invalid numeric uid */
+    {
+      if (debug_level > 0)
+	dbg_log (_("Invalid numeric gid \"%s\"!"), (char *) key);
+
+      errno = EINVAL;
+      return;
+    }
+
+  union keytype u = { .g = gid };
+
+  addgrbyX (db, fd, req, u, key, uid, NULL, NULL);
+}
+
+
+time_t
+readdgrbygid (struct database_dyn *db, struct hashentry *he,
+	      struct datahead *dh)
+{
+  char *ep;
+  gid_t gid = strtoul (db->data + he->key, &ep, 10);
+
+  /* Since the key has been added before it must be OK.  */
+  assert (*(db->data + he->key) != '\0' && *ep == '\0');
+
+  request_header req =
+    {
+      .type = GETGRBYGID,
+      .key_len = he->len
+    };
+  union keytype u = { .g = gid };
+
+  return addgrbyX (db, -1, &req, u, db->data + he->key, he->owner, he, dh);
+}
diff --git a/REORG.TODO/nscd/hstcache.c b/REORG.TODO/nscd/hstcache.c
new file mode 100644
index 0000000000..9f6ce979ac
--- /dev/null
+++ b/REORG.TODO/nscd/hstcache.c
@@ -0,0 +1,619 @@
+/* Cache handling for host lookup.
+   Copyright (C) 1998-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published
+   by the Free Software Foundation; version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
+
+#include <alloca.h>
+#include <assert.h>
+#include <errno.h>
+#include <error.h>
+#include <libintl.h>
+#include <netdb.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <sys/mman.h>
+#include <stackinfo.h>
+
+#include "nscd.h"
+#include "dbg_log.h"
+#ifdef HAVE_SENDFILE
+# include <kernel-features.h>
+#endif
+
+
+/* This is the standard reply in case the service is disabled.  */
+static const hst_response_header disabled =
+{
+  .version = NSCD_VERSION,
+  .found = -1,
+  .h_name_len = 0,
+  .h_aliases_cnt = 0,
+  .h_addrtype = -1,
+  .h_length = -1,
+  .h_addr_list_cnt = 0,
+  .error = NETDB_INTERNAL
+};
+
+/* This is the struct describing how to write this record.  */
+const struct iovec hst_iov_disabled =
+{
+  .iov_base = (void *) &disabled,
+  .iov_len = sizeof (disabled)
+};
+
+
+/* This is the standard reply in case we haven't found the dataset.  */
+static const hst_response_header notfound =
+{
+  .version = NSCD_VERSION,
+  .found = 0,
+  .h_name_len = 0,
+  .h_aliases_cnt = 0,
+  .h_addrtype = -1,
+  .h_length = -1,
+  .h_addr_list_cnt = 0,
+  .error = HOST_NOT_FOUND
+};
+
+
+/* This is the standard reply in case there are temporary problems.  */
+static const hst_response_header tryagain =
+{
+  .version = NSCD_VERSION,
+  .found = 0,
+  .h_name_len = 0,
+  .h_aliases_cnt = 0,
+  .h_addrtype = -1,
+  .h_length = -1,
+  .h_addr_list_cnt = 0,
+  .error = TRY_AGAIN
+};
+
+
+static time_t
+cache_addhst (struct database_dyn *db, int fd, request_header *req,
+	      const void *key, struct hostent *hst, uid_t owner,
+	      struct hashentry *const he, struct datahead *dh, int errval,
+	      int32_t ttl)
+{
+  bool all_written = true;
+  time_t t = time (NULL);
+
+  /* We allocate all data in one memory block: the iov vector,
+     the response header and the dataset itself.  */
+  struct dataset
+  {
+    struct datahead head;
+    hst_response_header resp;
+    char strdata[0];
+  } *dataset;
+
+  assert (offsetof (struct dataset, resp) == offsetof (struct datahead, data));
+
+  time_t timeout = MAX_TIMEOUT_VALUE;
+  if (hst == NULL)
+    {
+      if (he != NULL && errval == EAGAIN)
+	{
+	  /* If we have an old record available but cannot find one
+	     now because the service is not available we keep the old
+	     record and make sure it does not get removed.  */
+	  if (reload_count != UINT_MAX)
+	    /* Do not reset the value if we never not reload the record.  */
+	    dh->nreloads = reload_count - 1;
+
+	  /* Reload with the same time-to-live value.  */
+	  timeout = dh->timeout = t + dh->ttl;
+	}
+      else
+	{
+	  /* We have no data.  This means we send the standard reply for this
+	     case.  Possibly this is only temporary.  */
+	  ssize_t total = sizeof (notfound);
+	  assert (sizeof (notfound) == sizeof (tryagain));
+
+	  const hst_response_header *resp = (errval == EAGAIN
+					     ? &tryagain : &notfound);
+
+	  if (fd != -1 &&
+	      TEMP_FAILURE_RETRY (send (fd, resp, total,
+					MSG_NOSIGNAL)) != total)
+	    all_written = false;
+
+	  /* If we have a transient error or cannot permanently store
+	     the result, so be it.  */
+	  if (errval == EAGAIN || __builtin_expect (db->negtimeout == 0, 0))
+	    {
+	      /* Mark the old entry as obsolete.  */
+	      if (dh != NULL)
+		dh->usable = false;
+	    }
+	  else if ((dataset = mempool_alloc (db, (sizeof (struct dataset)
+						  + req->key_len), 1)) != NULL)
+	    {
+	      timeout = datahead_init_neg (&dataset->head,
+					   (sizeof (struct dataset)
+					    + req->key_len), total,
+					   (ttl == INT32_MAX
+					    ? db->negtimeout : ttl));
+
+	      /* This is the reply.  */
+	      memcpy (&dataset->resp, resp, total);
+
+	      /* Copy the key data.  */
+	      memcpy (dataset->strdata, key, req->key_len);
+
+	      /* If necessary, we also propagate the data to disk.  */
+	      if (db->persistent)
+		{
+		  // XXX async OK?
+		  uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
+		  msync ((void *) pval,
+			 ((uintptr_t) dataset & pagesize_m1)
+			 + sizeof (struct dataset) + req->key_len, MS_ASYNC);
+		}
+
+	      (void) cache_add (req->type, &dataset->strdata, req->key_len,
+				&dataset->head, true, db, owner, he == NULL);
+
+	      pthread_rwlock_unlock (&db->lock);
+
+	      /* Mark the old entry as obsolete.  */
+	      if (dh != NULL)
+		dh->usable = false;
+	    }
+	}
+    }
+  else
+    {
+      /* Determine the I/O structure.  */
+      size_t h_name_len = strlen (hst->h_name) + 1;
+      size_t h_aliases_cnt;
+      uint32_t *h_aliases_len;
+      size_t h_addr_list_cnt;
+      char *addresses;
+      char *aliases;
+      char *key_copy = NULL;
+      char *cp;
+      size_t cnt;
+      ssize_t total;
+
+      /* Determine the number of aliases.  */
+      h_aliases_cnt = 0;
+      for (cnt = 0; hst->h_aliases[cnt] != NULL; ++cnt)
+	++h_aliases_cnt;
+      /* Determine the length of all aliases.  */
+      h_aliases_len = (uint32_t *) alloca (h_aliases_cnt * sizeof (uint32_t));
+      total = 0;
+      for (cnt = 0; cnt < h_aliases_cnt; ++cnt)
+	{
+	  h_aliases_len[cnt] = strlen (hst->h_aliases[cnt]) + 1;
+	  total += h_aliases_len[cnt];
+	}
+
+      /* Determine the number of addresses.  */
+      h_addr_list_cnt = 0;
+      while (hst->h_addr_list[h_addr_list_cnt] != NULL)
+	++h_addr_list_cnt;
+
+      if (h_addr_list_cnt == 0)
+	/* Invalid entry.  */
+	return MAX_TIMEOUT_VALUE;
+
+      total += (sizeof (struct dataset)
+		+ h_name_len
+		+ h_aliases_cnt * sizeof (uint32_t)
+		+ h_addr_list_cnt * hst->h_length);
+
+      /* If we refill the cache, first assume the reconrd did not
+	 change.  Allocate memory on the cache since it is likely
+	 discarded anyway.  If it turns out to be necessary to have a
+	 new record we can still allocate real memory.  */
+      bool alloca_used = false;
+      dataset = NULL;
+
+      /* If the record contains more than one IP address (used for
+	 load balancing etc) don't cache the entry.  This is something
+	 the current cache handling cannot handle and it is more than
+	 questionable whether it is worthwhile complicating the cache
+	 handling just for handling such a special case. */
+      if (he == NULL && h_addr_list_cnt == 1)
+	dataset = (struct dataset *) mempool_alloc (db, total + req->key_len,
+						    1);
+
+      if (dataset == NULL)
+	{
+	  /* We cannot permanently add the result in the moment.  But
+	     we can provide the result as is.  Store the data in some
+	     temporary memory.  */
+	  dataset = (struct dataset *) alloca (total + req->key_len);
+
+	  /* We cannot add this record to the permanent database.  */
+	  alloca_used = true;
+	}
+
+      timeout = datahead_init_pos (&dataset->head, total + req->key_len,
+				   total - offsetof (struct dataset, resp),
+				   he == NULL ? 0 : dh->nreloads + 1,
+				   ttl == INT32_MAX ? db->postimeout : ttl);
+
+      dataset->resp.version = NSCD_VERSION;
+      dataset->resp.found = 1;
+      dataset->resp.h_name_len = h_name_len;
+      dataset->resp.h_aliases_cnt = h_aliases_cnt;
+      dataset->resp.h_addrtype = hst->h_addrtype;
+      dataset->resp.h_length = hst->h_length;
+      dataset->resp.h_addr_list_cnt = h_addr_list_cnt;
+      dataset->resp.error = NETDB_SUCCESS;
+
+      /* Make sure there is no gap.  */
+      assert ((char *) (&dataset->resp.error + 1) == dataset->strdata);
+
+      cp = dataset->strdata;
+
+      cp = mempcpy (cp, hst->h_name, h_name_len);
+      cp = mempcpy (cp, h_aliases_len, h_aliases_cnt * sizeof (uint32_t));
+
+      /* The normal addresses first.  */
+      addresses = cp;
+      for (cnt = 0; cnt < h_addr_list_cnt; ++cnt)
+	cp = mempcpy (cp, hst->h_addr_list[cnt], hst->h_length);
+
+      /* Then the aliases.  */
+      aliases = cp;
+      for (cnt = 0; cnt < h_aliases_cnt; ++cnt)
+	cp = mempcpy (cp, hst->h_aliases[cnt], h_aliases_len[cnt]);
+
+      assert (cp
+	      == dataset->strdata + total - offsetof (struct dataset,
+						      strdata));
+
+      /* If we are adding a GETHOSTBYNAME{,v6} entry we must be prepared
+	 that the answer we get from the NSS does not contain the key
+	 itself.  This is the case if the resolver is used and the name
+	 is extended by the domainnames from /etc/resolv.conf.  Therefore
+	 we explicitly add the name here.  */
+      key_copy = memcpy (cp, key, req->key_len);
+
+      assert ((char *) &dataset->resp + dataset->head.recsize == cp);
+
+      /* Now we can determine whether on refill we have to create a new
+	 record or not.  */
+      if (he != NULL)
+	{
+	  assert (fd == -1);
+
+	  if (total + req->key_len == dh->allocsize
+	      && total - offsetof (struct dataset, resp) == dh->recsize
+	      && memcmp (&dataset->resp, dh->data,
+			 dh->allocsize - offsetof (struct dataset, resp)) == 0)
+	    {
+	      /* The data has not changed.  We will just bump the
+		 timeout value.  Note that the new record has been
+		 allocated on the stack and need not be freed.  */
+	      assert (h_addr_list_cnt == 1);
+	      dh->ttl = dataset->head.ttl;
+	      dh->timeout = dataset->head.timeout;
+	      ++dh->nreloads;
+	    }
+	  else
+	    {
+	      if (h_addr_list_cnt == 1)
+		{
+		  /* We have to create a new record.  Just allocate
+		     appropriate memory and copy it.  */
+		  struct dataset *newp
+		    = (struct dataset *) mempool_alloc (db,
+							total + req->key_len,
+							1);
+		  if (newp != NULL)
+		    {
+		      /* Adjust pointers into the memory block.  */
+		      addresses = (char *) newp + (addresses
+						   - (char *) dataset);
+		      aliases = (char *) newp + (aliases - (char *) dataset);
+		      assert (key_copy != NULL);
+		      key_copy = (char *) newp + (key_copy - (char *) dataset);
+
+		      dataset = memcpy (newp, dataset, total + req->key_len);
+		      alloca_used = false;
+		    }
+		}
+
+	      /* Mark the old record as obsolete.  */
+	      dh->usable = false;
+	    }
+	}
+      else
+	{
+	  /* We write the dataset before inserting it to the database
+	     since while inserting this thread might block and so would
+	     unnecessarily keep the receiver waiting.  */
+	  assert (fd != -1);
+
+#ifdef HAVE_SENDFILE
+	  if (__builtin_expect (db->mmap_used, 1) && !alloca_used)
+	    {
+	      assert (db->wr_fd != -1);
+	      assert ((char *) &dataset->resp > (char *) db->data);
+	      assert ((char *) dataset - (char *) db->head
+		      + total
+		      <= (sizeof (struct database_pers_head)
+			  + db->head->module * sizeof (ref_t)
+			  + db->head->data_size));
+	      ssize_t written = sendfileall (fd, db->wr_fd,
+					     (char *) &dataset->resp
+					     - (char *) db->head,
+					     dataset->head.recsize);
+	      if (written != dataset->head.recsize)
+		{
+# ifndef __ASSUME_SENDFILE
+		  if (written == -1 && errno == ENOSYS)
+		    goto use_write;
+# endif
+		  all_written = false;
+		}
+	    }
+	  else
+# ifndef __ASSUME_SENDFILE
+	  use_write:
+# endif
+#endif
+	    if (writeall (fd, &dataset->resp, dataset->head.recsize)
+		!= dataset->head.recsize)
+	      all_written = false;
+	}
+
+      /* Add the record to the database.  But only if it has not been
+	 stored on the stack.
+
+	 If the record contains more than one IP address (used for
+	 load balancing etc) don't cache the entry.  This is something
+	 the current cache handling cannot handle and it is more than
+	 questionable whether it is worthwhile complicating the cache
+	 handling just for handling such a special case. */
+      if (! alloca_used)
+	{
+	  /* If necessary, we also propagate the data to disk.  */
+	  if (db->persistent)
+	    {
+	      // XXX async OK?
+	      uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
+	      msync ((void *) pval,
+		     ((uintptr_t) dataset & pagesize_m1)
+		     + total + req->key_len, MS_ASYNC);
+	    }
+
+	  /* NB: the following code is really complicated.  It has
+	     seemlingly duplicated code paths which do the same.  The
+	     problem is that we always must add the hash table entry
+	     with the FIRST flag set first.  Otherwise we get dangling
+	     pointers in case memory allocation fails.  */
+	  assert (hst->h_addr_list[1] == NULL);
+
+	  /* Avoid adding names if more than one address is available.  See
+	     above for more info.  */
+	  assert (req->type == GETHOSTBYNAME
+		  || req->type == GETHOSTBYNAMEv6
+		  || req->type == GETHOSTBYADDR
+		  || req->type == GETHOSTBYADDRv6);
+
+	  (void) cache_add (req->type, key_copy, req->key_len,
+			    &dataset->head, true, db, owner, he == NULL);
+
+	  pthread_rwlock_unlock (&db->lock);
+	}
+    }
+
+  if (__builtin_expect (!all_written, 0) && debug_level > 0)
+    {
+      char buf[256];
+      dbg_log (_("short write in %s: %s"),  __FUNCTION__,
+	       strerror_r (errno, buf, sizeof (buf)));
+    }
+
+  return timeout;
+}
+
+
+static int
+lookup (int type, void *key, struct hostent *resultbufp, char *buffer,
+	size_t buflen, struct hostent **hst, int32_t *ttlp)
+{
+  if (type == GETHOSTBYNAME)
+    return __gethostbyname3_r (key, AF_INET, resultbufp, buffer, buflen, hst,
+			       &h_errno, ttlp, NULL);
+  if (type == GETHOSTBYNAMEv6)
+    return __gethostbyname3_r (key, AF_INET6, resultbufp, buffer, buflen, hst,
+			       &h_errno, ttlp, NULL);
+  if (type == GETHOSTBYADDR)
+    return __gethostbyaddr2_r (key, NS_INADDRSZ, AF_INET, resultbufp, buffer,
+			       buflen, hst, &h_errno, ttlp);
+  return __gethostbyaddr2_r (key, NS_IN6ADDRSZ, AF_INET6, resultbufp, buffer,
+			     buflen, hst, &h_errno, ttlp);
+}
+
+
+static time_t
+addhstbyX (struct database_dyn *db, int fd, request_header *req,
+	   void *key, uid_t uid, struct hashentry *he, struct datahead *dh)
+{
+  /* Search for the entry matching the key.  Please note that we don't
+     look again in the table whether the dataset is now available.  We
+     simply insert it.  It does not matter if it is in there twice.  The
+     pruning function only will look at the timestamp.  */
+  int buflen = 1024;
+  char *buffer = (char *) alloca (buflen);
+  struct hostent resultbuf;
+  struct hostent *hst;
+  bool use_malloc = false;
+  int errval = 0;
+  int32_t ttl = INT32_MAX;
+
+  if (__glibc_unlikely (debug_level > 0))
+    {
+      const char *str;
+      char buf[INET6_ADDRSTRLEN + 1];
+      if (req->type == GETHOSTBYNAME || req->type == GETHOSTBYNAMEv6)
+	str = key;
+      else
+	str = inet_ntop (req->type == GETHOSTBYADDR ? AF_INET : AF_INET6,
+			 key, buf, sizeof (buf));
+
+      if (he == NULL)
+	dbg_log (_("Haven't found \"%s\" in hosts cache!"), (char *) str);
+      else
+	dbg_log (_("Reloading \"%s\" in hosts cache!"), (char *) str);
+    }
+
+  while (lookup (req->type, key, &resultbuf, buffer, buflen, &hst, &ttl) != 0
+	 && h_errno == NETDB_INTERNAL
+	 && (errval = errno) == ERANGE)
+    {
+      errno = 0;
+
+      if (__glibc_unlikely (buflen > 32768))
+	{
+	  char *old_buffer = buffer;
+	  buflen *= 2;
+	  buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen);
+	  if (buffer == NULL)
+	    {
+	      /* We ran out of memory.  We cannot do anything but
+		 sending a negative response.  In reality this should
+		 never happen.  */
+	      hst = NULL;
+	      buffer = old_buffer;
+
+	      /* We set the error to indicate this is (possibly) a
+		 temporary error and that it does not mean the entry
+		 is not available at all.  */
+	      h_errno = TRY_AGAIN;
+	      errval = EAGAIN;
+	      break;
+	    }
+	  use_malloc = true;
+	}
+      else
+	/* Allocate a new buffer on the stack.  If possible combine it
+	   with the previously allocated buffer.  */
+	buffer = (char *) extend_alloca (buffer, buflen, 2 * buflen);
+    }
+
+  time_t timeout = cache_addhst (db, fd, req, key, hst, uid, he, dh,
+				 h_errno == TRY_AGAIN ? errval : 0, ttl);
+
+  if (use_malloc)
+    free (buffer);
+
+  return timeout;
+}
+
+
+void
+addhstbyname (struct database_dyn *db, int fd, request_header *req,
+	      void *key, uid_t uid)
+{
+  addhstbyX (db, fd, req, key, uid, NULL, NULL);
+}
+
+
+time_t
+readdhstbyname (struct database_dyn *db, struct hashentry *he,
+		struct datahead *dh)
+{
+  request_header req =
+    {
+      .type = GETHOSTBYNAME,
+      .key_len = he->len
+    };
+
+  return addhstbyX (db, -1, &req, db->data + he->key, he->owner, he, dh);
+}
+
+
+void
+addhstbyaddr (struct database_dyn *db, int fd, request_header *req,
+	      void *key, uid_t uid)
+{
+  addhstbyX (db, fd, req, key, uid, NULL, NULL);
+}
+
+
+time_t
+readdhstbyaddr (struct database_dyn *db, struct hashentry *he,
+		struct datahead *dh)
+{
+  request_header req =
+    {
+      .type = GETHOSTBYADDR,
+      .key_len = he->len
+    };
+
+  return addhstbyX (db, -1, &req, db->data + he->key, he->owner, he, dh);
+}
+
+
+void
+addhstbynamev6 (struct database_dyn *db, int fd, request_header *req,
+		void *key, uid_t uid)
+{
+  addhstbyX (db, fd, req, key, uid, NULL, NULL);
+}
+
+
+time_t
+readdhstbynamev6 (struct database_dyn *db, struct hashentry *he,
+		  struct datahead *dh)
+{
+  request_header req =
+    {
+      .type = GETHOSTBYNAMEv6,
+      .key_len = he->len
+    };
+
+  return addhstbyX (db, -1, &req, db->data + he->key, he->owner, he, dh);
+}
+
+
+void
+addhstbyaddrv6 (struct database_dyn *db, int fd, request_header *req,
+		void *key, uid_t uid)
+{
+  addhstbyX (db, fd, req, key, uid, NULL, NULL);
+}
+
+
+time_t
+readdhstbyaddrv6 (struct database_dyn *db, struct hashentry *he,
+		  struct datahead *dh)
+{
+  request_header req =
+    {
+      .type = GETHOSTBYADDRv6,
+      .key_len = he->len
+    };
+
+  return addhstbyX (db, -1, &req, db->data + he->key, he->owner, he, dh);
+}
diff --git a/REORG.TODO/nscd/initgrcache.c b/REORG.TODO/nscd/initgrcache.c
new file mode 100644
index 0000000000..4deb483fbb
--- /dev/null
+++ b/REORG.TODO/nscd/initgrcache.c
@@ -0,0 +1,438 @@
+/* Cache handling for host lookup.
+   Copyright (C) 2004-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@redhat.com>, 2004.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published
+   by the Free Software Foundation; version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
+
+#include <assert.h>
+#include <errno.h>
+#include <grp.h>
+#include <libintl.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <scratch_buffer.h>
+
+#include "dbg_log.h"
+#include "nscd.h"
+#ifdef HAVE_SENDFILE
+# include <kernel-features.h>
+#endif
+
+#include "../nss/nsswitch.h"
+
+
+/* Type of the lookup function.  */
+typedef enum nss_status (*initgroups_dyn_function) (const char *, gid_t,
+						    long int *, long int *,
+						    gid_t **, long int, int *);
+
+
+static const initgr_response_header notfound =
+{
+  .version = NSCD_VERSION,
+  .found = 0,
+  .ngrps = 0
+};
+
+
+#include "../grp/compat-initgroups.c"
+
+
+static time_t
+addinitgroupsX (struct database_dyn *db, int fd, request_header *req,
+		void *key, uid_t uid, struct hashentry *const he,
+		struct datahead *dh)
+{
+  /* Search for the entry matching the key.  Please note that we don't
+     look again in the table whether the dataset is now available.  We
+     simply insert it.  It does not matter if it is in there twice.  The
+     pruning function only will look at the timestamp.  */
+
+
+  /* We allocate all data in one memory block: the iov vector,
+     the response header and the dataset itself.  */
+  struct dataset
+  {
+    struct datahead head;
+    initgr_response_header resp;
+    char strdata[0];
+  } *dataset = NULL;
+
+  if (__glibc_unlikely (debug_level > 0))
+    {
+      if (he == NULL)
+	dbg_log (_("Haven't found \"%s\" in group cache!"), (char *) key);
+      else
+	dbg_log (_("Reloading \"%s\" in group cache!"), (char *) key);
+    }
+
+  static service_user *group_database;
+  service_user *nip;
+  int no_more;
+
+  if (group_database == NULL)
+    no_more = __nss_database_lookup ("group", NULL,
+				     "compat [NOTFOUND=return] files",
+				     &group_database);
+  else
+    no_more = 0;
+  nip = group_database;
+
+ /* We always use sysconf even if NGROUPS_MAX is defined.  That way, the
+     limit can be raised in the kernel configuration without having to
+     recompile libc.  */
+  long int limit = __sysconf (_SC_NGROUPS_MAX);
+
+  long int size;
+  if (limit > 0)
+    /* We limit the size of the intially allocated array.  */
+    size = MIN (limit, 64);
+  else
+    /* No fixed limit on groups.  Pick a starting buffer size.  */
+    size = 16;
+
+  long int start = 0;
+  bool all_tryagain = true;
+  bool any_success = false;
+
+  /* This is temporary memory, we need not (and must not) call
+     mempool_alloc.  */
+  // XXX This really should use alloca.  need to change the backends.
+  gid_t *groups = (gid_t *) malloc (size * sizeof (gid_t));
+  if (__glibc_unlikely (groups == NULL))
+    /* No more memory.  */
+    goto out;
+
+  /* Nothing added yet.  */
+  while (! no_more)
+    {
+      long int prev_start = start;
+      enum nss_status status;
+      initgroups_dyn_function fct;
+      fct = __nss_lookup_function (nip, "initgroups_dyn");
+
+      if (fct == NULL)
+	{
+	  status = compat_call (nip, key, -1, &start, &size, &groups,
+				limit, &errno);
+
+	  if (nss_next_action (nip, NSS_STATUS_UNAVAIL) != NSS_ACTION_CONTINUE)
+	    break;
+	}
+      else
+	status = DL_CALL_FCT (fct, (key, -1, &start, &size, &groups,
+				    limit, &errno));
+
+      /* Remove duplicates.  */
+      long int cnt = prev_start;
+      while (cnt < start)
+	{
+	  long int inner;
+	  for (inner = 0; inner < prev_start; ++inner)
+	    if (groups[inner] == groups[cnt])
+	      break;
+
+	  if (inner < prev_start)
+	    groups[cnt] = groups[--start];
+	  else
+	    ++cnt;
+	}
+
+      if (status != NSS_STATUS_TRYAGAIN)
+	all_tryagain = false;
+
+      /* This is really only for debugging.  */
+      if (NSS_STATUS_TRYAGAIN > status || status > NSS_STATUS_RETURN)
+	__libc_fatal ("illegal status in internal_getgrouplist");
+
+      any_success |= status == NSS_STATUS_SUCCESS;
+
+      if (status != NSS_STATUS_SUCCESS
+	  && nss_next_action (nip, status) == NSS_ACTION_RETURN)
+	 break;
+
+      if (nip->next == NULL)
+	no_more = -1;
+      else
+	nip = nip->next;
+    }
+
+  bool all_written;
+  ssize_t total;
+  time_t timeout;
+ out:
+  all_written = true;
+  timeout = MAX_TIMEOUT_VALUE;
+  if (!any_success)
+    {
+      /* Nothing found.  Create a negative result record.  */
+      total = sizeof (notfound);
+
+      if (he != NULL && all_tryagain)
+	{
+	  /* If we have an old record available but cannot find one now
+	     because the service is not available we keep the old record
+	     and make sure it does not get removed.  */
+	  if (reload_count != UINT_MAX && dh->nreloads == reload_count)
+	    /* Do not reset the value if we never not reload the record.  */
+	    dh->nreloads = reload_count - 1;
+
+	  /* Reload with the same time-to-live value.  */
+	  timeout = dh->timeout = time (NULL) + db->postimeout;
+	}
+      else
+	{
+	  /* We have no data.  This means we send the standard reply for this
+	     case.  */
+	  if (fd != -1
+	      && TEMP_FAILURE_RETRY (send (fd, &notfound, total,
+					   MSG_NOSIGNAL)) != total)
+	    all_written = false;
+
+	  /* If we have a transient error or cannot permanently store
+	     the result, so be it.  */
+	  if (all_tryagain || __builtin_expect (db->negtimeout == 0, 0))
+	    {
+	      /* Mark the old entry as obsolete.  */
+	      if (dh != NULL)
+		dh->usable = false;
+	    }
+	  else if ((dataset = mempool_alloc (db, (sizeof (struct dataset)
+						  + req->key_len), 1)) != NULL)
+	    {
+	      timeout = datahead_init_neg (&dataset->head,
+					   (sizeof (struct dataset)
+					    + req->key_len), total,
+					   db->negtimeout);
+
+	      /* This is the reply.  */
+	      memcpy (&dataset->resp, &notfound, total);
+
+	      /* Copy the key data.  */
+	      char *key_copy = memcpy (dataset->strdata, key, req->key_len);
+
+	      /* If necessary, we also propagate the data to disk.  */
+	      if (db->persistent)
+		{
+		  // XXX async OK?
+		  uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
+		  msync ((void *) pval,
+			 ((uintptr_t) dataset & pagesize_m1)
+			 + sizeof (struct dataset) + req->key_len, MS_ASYNC);
+		}
+
+	      (void) cache_add (req->type, key_copy, req->key_len,
+				&dataset->head, true, db, uid, he == NULL);
+
+	      pthread_rwlock_unlock (&db->lock);
+
+	      /* Mark the old entry as obsolete.  */
+	      if (dh != NULL)
+		dh->usable = false;
+	    }
+	}
+    }
+  else
+    {
+
+      total = offsetof (struct dataset, strdata) + start * sizeof (int32_t);
+
+      /* If we refill the cache, first assume the reconrd did not
+	 change.  Allocate memory on the cache since it is likely
+	 discarded anyway.  If it turns out to be necessary to have a
+	 new record we can still allocate real memory.  */
+      bool alloca_used = false;
+      dataset = NULL;
+
+      if (he == NULL)
+	dataset = (struct dataset *) mempool_alloc (db, total + req->key_len,
+						    1);
+
+      if (dataset == NULL)
+	{
+	  /* We cannot permanently add the result in the moment.  But
+	     we can provide the result as is.  Store the data in some
+	     temporary memory.  */
+	  dataset = (struct dataset *) alloca (total + req->key_len);
+
+	  /* We cannot add this record to the permanent database.  */
+	  alloca_used = true;
+	}
+
+      timeout = datahead_init_pos (&dataset->head, total + req->key_len,
+				   total - offsetof (struct dataset, resp),
+				   he == NULL ? 0 : dh->nreloads + 1,
+				   db->postimeout);
+
+      dataset->resp.version = NSCD_VERSION;
+      dataset->resp.found = 1;
+      dataset->resp.ngrps = start;
+
+      char *cp = dataset->strdata;
+
+      /* Copy the GID values.  If the size of the types match this is
+	 very simple.  */
+      if (sizeof (gid_t) == sizeof (int32_t))
+	cp = mempcpy (cp, groups, start * sizeof (gid_t));
+      else
+	{
+	  gid_t *gcp = (gid_t *) cp;
+
+	  for (int i = 0; i < start; ++i)
+	    *gcp++ = groups[i];
+
+	  cp = (char *) gcp;
+	}
+
+      /* Finally the user name.  */
+      memcpy (cp, key, req->key_len);
+
+      assert (cp == dataset->strdata + total - offsetof (struct dataset,
+							 strdata));
+
+      /* Now we can determine whether on refill we have to create a new
+	 record or not.  */
+      if (he != NULL)
+	{
+	  assert (fd == -1);
+
+	  if (total + req->key_len == dh->allocsize
+	      && total - offsetof (struct dataset, resp) == dh->recsize
+	      && memcmp (&dataset->resp, dh->data,
+			 dh->allocsize - offsetof (struct dataset, resp)) == 0)
+	    {
+	      /* The data has not changed.  We will just bump the
+		 timeout value.  Note that the new record has been
+		 allocated on the stack and need not be freed.  */
+	      dh->timeout = dataset->head.timeout;
+	      ++dh->nreloads;
+	    }
+	  else
+	    {
+	      /* We have to create a new record.  Just allocate
+		 appropriate memory and copy it.  */
+	      struct dataset *newp
+		= (struct dataset *) mempool_alloc (db, total + req->key_len,
+						    1);
+	      if (newp != NULL)
+		{
+		  /* Adjust pointer into the memory block.  */
+		  cp = (char *) newp + (cp - (char *) dataset);
+
+		  dataset = memcpy (newp, dataset, total + req->key_len);
+		  alloca_used = false;
+		}
+
+	      /* Mark the old record as obsolete.  */
+	      dh->usable = false;
+	    }
+	}
+      else
+	{
+	  /* We write the dataset before inserting it to the database
+	     since while inserting this thread might block and so would
+	     unnecessarily let the receiver wait.  */
+	  assert (fd != -1);
+
+#ifdef HAVE_SENDFILE
+	  if (__builtin_expect (db->mmap_used, 1) && !alloca_used)
+	    {
+	      assert (db->wr_fd != -1);
+	      assert ((char *) &dataset->resp > (char *) db->data);
+	      assert ((char *) dataset - (char *) db->head
+		      + total
+		      <= (sizeof (struct database_pers_head)
+			  + db->head->module * sizeof (ref_t)
+			  + db->head->data_size));
+	      ssize_t written = sendfileall (fd, db->wr_fd,
+					     (char *) &dataset->resp
+					     - (char *) db->head,
+					     dataset->head.recsize);
+	      if (written != dataset->head.recsize)
+		{
+# ifndef __ASSUME_SENDFILE
+		  if (written == -1 && errno == ENOSYS)
+		    goto use_write;
+# endif
+		  all_written = false;
+		}
+	    }
+	  else
+# ifndef __ASSUME_SENDFILE
+	  use_write:
+# endif
+#endif
+	    if (writeall (fd, &dataset->resp, dataset->head.recsize)
+		!= dataset->head.recsize)
+	      all_written = false;
+	}
+
+
+      /* Add the record to the database.  But only if it has not been
+	 stored on the stack.  */
+      if (! alloca_used)
+	{
+	  /* If necessary, we also propagate the data to disk.  */
+	  if (db->persistent)
+	    {
+	      // XXX async OK?
+	      uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
+	      msync ((void *) pval,
+		     ((uintptr_t) dataset & pagesize_m1) + total +
+		     req->key_len, MS_ASYNC);
+	    }
+
+	  (void) cache_add (INITGROUPS, cp, req->key_len, &dataset->head, true,
+			    db, uid, he == NULL);
+
+	  pthread_rwlock_unlock (&db->lock);
+	}
+    }
+
+  free (groups);
+
+  if (__builtin_expect (!all_written, 0) && debug_level > 0)
+    {
+      char buf[256];
+      dbg_log (_("short write in %s: %s"), __FUNCTION__,
+	       strerror_r (errno, buf, sizeof (buf)));
+    }
+
+  return timeout;
+}
+
+
+void
+addinitgroups (struct database_dyn *db, int fd, request_header *req, void *key,
+	       uid_t uid)
+{
+  addinitgroupsX (db, fd, req, key, uid, NULL, NULL);
+}
+
+
+time_t
+readdinitgroups (struct database_dyn *db, struct hashentry *he,
+		 struct datahead *dh)
+{
+  request_header req =
+    {
+      .type = INITGROUPS,
+      .key_len = he->len
+    };
+
+  return addinitgroupsX (db, -1, &req, db->data + he->key, he->owner, he, dh);
+}
diff --git a/REORG.TODO/nscd/mem.c b/REORG.TODO/nscd/mem.c
new file mode 100644
index 0000000000..092f3ae7c1
--- /dev/null
+++ b/REORG.TODO/nscd/mem.c
@@ -0,0 +1,589 @@
+/* Cache memory handling.
+   Copyright (C) 2004-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@redhat.com>, 2004.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published
+   by the Free Software Foundation; version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
+
+#include <assert.h>
+#include <errno.h>
+#include <error.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <libintl.h>
+#include <limits.h>
+#include <obstack.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+
+#include "dbg_log.h"
+#include "nscd.h"
+
+
+static int
+sort_he (const void *p1, const void *p2)
+{
+  struct hashentry *h1 = *(struct hashentry **) p1;
+  struct hashentry *h2 = *(struct hashentry **) p2;
+
+  if (h1 < h2)
+    return -1;
+  if (h1 > h2)
+    return 1;
+  return 0;
+}
+
+
+static int
+sort_he_data (const void *p1, const void *p2)
+{
+  struct hashentry *h1 = *(struct hashentry **) p1;
+  struct hashentry *h2 = *(struct hashentry **) p2;
+
+  if (h1->packet < h2->packet)
+    return -1;
+  if (h1->packet > h2->packet)
+    return 1;
+  return 0;
+}
+
+
+/* Basic definitions for the bitmap implementation.  Only BITMAP_T
+   needs to be changed to choose a different word size.  */
+#define BITMAP_T uint8_t
+#define BITS (CHAR_BIT * sizeof (BITMAP_T))
+#define ALLBITS ((((BITMAP_T) 1) << BITS) - 1)
+#define HIGHBIT (((BITMAP_T) 1) << (BITS - 1))
+
+
+static void
+markrange (BITMAP_T *mark, ref_t start, size_t len)
+{
+  /* Adjust parameters for block alignment.  */
+  assert ((start & BLOCK_ALIGN_M1) == 0);
+  start /= BLOCK_ALIGN;
+  len = (len + BLOCK_ALIGN_M1) / BLOCK_ALIGN;
+
+  size_t elem = start / BITS;
+
+  if (start % BITS != 0)
+    {
+      if (start % BITS + len <= BITS)
+	{
+	  /* All fits in the partial byte.  */
+	  mark[elem] |= (ALLBITS >> (BITS - len)) << (start % BITS);
+	  return;
+	}
+
+      mark[elem++] |= ALLBITS << (start % BITS);
+      len -= BITS - (start % BITS);
+    }
+
+  while (len >= BITS)
+    {
+      mark[elem++] = ALLBITS;
+      len -= BITS;
+    }
+
+  if (len > 0)
+    mark[elem] |= ALLBITS >> (BITS - len);
+}
+
+
+void
+gc (struct database_dyn *db)
+{
+  /* We need write access.  */
+  pthread_rwlock_wrlock (&db->lock);
+
+  /* And the memory handling lock.  */
+  pthread_mutex_lock (&db->memlock);
+
+  /* We need an array representing the data area.  All memory
+     allocation is BLOCK_ALIGN aligned so this is the level at which
+     we have to look at the memory.  We use a mark and sweep algorithm
+     where the marks are placed in this array.  */
+  assert (db->head->first_free % BLOCK_ALIGN == 0);
+
+  BITMAP_T *mark;
+  bool mark_use_malloc;
+  /* In prune_cache we are also using a dynamically allocated array.
+     If the array in the caller is too large we have malloc'ed it.  */
+  size_t stack_used = sizeof (bool) * db->head->module;
+  if (__glibc_unlikely (stack_used > MAX_STACK_USE))
+    stack_used = 0;
+  size_t nmark = (db->head->first_free / BLOCK_ALIGN + BITS - 1) / BITS;
+  size_t memory_needed = nmark * sizeof (BITMAP_T);
+  if (__glibc_likely (stack_used + memory_needed <= MAX_STACK_USE))
+    {
+      mark = (BITMAP_T *) alloca_account (memory_needed, stack_used);
+      mark_use_malloc = false;
+      memset (mark, '\0', memory_needed);
+    }
+  else
+    {
+      mark = (BITMAP_T *) xcalloc (1, memory_needed);
+      mark_use_malloc = true;
+    }
+
+  /* Create an array which can hold pointer to all the entries in hash
+     entries.  */
+  memory_needed = 2 * db->head->nentries * sizeof (struct hashentry *);
+  struct hashentry **he;
+  struct hashentry **he_data;
+  bool he_use_malloc;
+  if (__glibc_likely (stack_used + memory_needed <= MAX_STACK_USE))
+    {
+      he = alloca_account (memory_needed, stack_used);
+      he_use_malloc = false;
+    }
+  else
+    {
+      he = xmalloc (memory_needed);
+      he_use_malloc = true;
+    }
+  he_data = &he[db->head->nentries];
+
+  size_t cnt = 0;
+  for (size_t idx = 0; idx < db->head->module; ++idx)
+    {
+      ref_t *prevp = &db->head->array[idx];
+      ref_t run = *prevp;
+
+      while (run != ENDREF)
+	{
+	  assert (cnt < db->head->nentries);
+	  he[cnt] = (struct hashentry *) (db->data + run);
+
+	  he[cnt]->prevp = prevp;
+	  prevp = &he[cnt]->next;
+
+	  /* This is the hash entry itself.  */
+	  markrange (mark, run, sizeof (struct hashentry));
+
+	  /* Add the information for the data itself.  We do this
+	     only for the one special entry marked with FIRST.  */
+	  if (he[cnt]->first)
+	    {
+	      struct datahead *dh
+		= (struct datahead *) (db->data + he[cnt]->packet);
+	      markrange (mark, he[cnt]->packet, dh->allocsize);
+	    }
+
+	  run = he[cnt]->next;
+
+	  ++cnt;
+	}
+    }
+  assert (cnt == db->head->nentries);
+
+  /* Sort the entries by the addresses of the referenced data.  All
+     the entries pointing to the same DATAHEAD object will have the
+     same key.  Stability of the sorting is unimportant.  */
+  memcpy (he_data, he, cnt * sizeof (struct hashentry *));
+  qsort (he_data, cnt, sizeof (struct hashentry *), sort_he_data);
+
+  /* Sort the entries by their address.  */
+  qsort (he, cnt, sizeof (struct hashentry *), sort_he);
+
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
+  struct obstack ob;
+  obstack_init (&ob);
+
+  /* Determine the highest used address.  */
+  size_t high = nmark;
+  while (high > 0 && mark[high - 1] == 0)
+    --high;
+
+  /* No memory used.  */
+  if (high == 0)
+    {
+      db->head->first_free = 0;
+      goto out;
+    }
+
+  /* Determine the highest offset.  */
+  BITMAP_T mask = HIGHBIT;
+  ref_t highref = (high * BITS - 1) * BLOCK_ALIGN;
+  while ((mark[high - 1] & mask) == 0)
+    {
+      mask >>= 1;
+      highref -= BLOCK_ALIGN;
+    }
+
+  /* Now we can iterate over the MARK array and find bits which are not
+     set.  These represent memory which can be recovered.  */
+  size_t byte = 0;
+  /* Find the first gap.  */
+  while (byte < high && mark[byte] == ALLBITS)
+    ++byte;
+
+  if (byte == high
+      || (byte == high - 1 && (mark[byte] & ~(mask | (mask - 1))) == 0))
+    /* No gap.  */
+    goto out;
+
+  mask = 1;
+  cnt = 0;
+  while ((mark[byte] & mask) != 0)
+    {
+      ++cnt;
+      mask <<= 1;
+    }
+  ref_t off_free = (byte * BITS + cnt) * BLOCK_ALIGN;
+  assert (off_free <= db->head->first_free);
+
+  struct hashentry **next_hash = he;
+  struct hashentry **next_data = he_data;
+
+  /* Skip over the hash entries in the first block which does not get
+     moved.  */
+  while (next_hash < &he[db->head->nentries]
+	 && *next_hash < (struct hashentry *) (db->data + off_free))
+    ++next_hash;
+
+  while (next_data < &he_data[db->head->nentries]
+	 && (*next_data)->packet < off_free)
+    ++next_data;
+
+
+  /* Now we start modifying the data.  Make sure all readers of the
+     data are aware of this and temporarily don't use the data.  */
+  ++db->head->gc_cycle;
+  assert ((db->head->gc_cycle & 1) == 1);
+
+
+  /* We do not perform the move operations right away since the
+     he_data array is not sorted by the address of the data.  */
+  struct moveinfo
+  {
+    void *from;
+    void *to;
+    size_t size;
+    struct moveinfo *next;
+  } *moves = NULL;
+
+  while (byte < high)
+    {
+      /* Search for the next filled block.  BYTE is the index of the
+	 entry in MARK, MASK is the bit, and CNT is the bit number.
+	 OFF_FILLED is the corresponding offset.  */
+      if ((mark[byte] & ~(mask - 1)) == 0)
+	{
+	  /* No other bit set in the same element of MARK.  Search in the
+	     following memory.  */
+	  do
+	    ++byte;
+	  while (byte < high && mark[byte] == 0);
+
+	  if (byte == high)
+	    /* That was it.  */
+	    break;
+
+	  mask = 1;
+	  cnt = 0;
+	}
+      /* Find the exact bit.  */
+      while ((mark[byte] & mask) == 0)
+	{
+	  ++cnt;
+	  mask <<= 1;
+	}
+
+      ref_t off_alloc = (byte * BITS + cnt) * BLOCK_ALIGN;
+      assert (off_alloc <= db->head->first_free);
+
+      /* Find the end of the used area.  */
+      if ((mark[byte] & ~(mask - 1)) == (BITMAP_T) ~(mask - 1))
+	{
+	  /* All other bits set.  Search the next bytes in MARK.  */
+	  do
+	    ++byte;
+	  while (byte < high && mark[byte] == ALLBITS);
+
+	  mask = 1;
+	  cnt = 0;
+	}
+      if (byte < high)
+	{
+	  /* Find the exact bit.  */
+	  while ((mark[byte] & mask) != 0)
+	    {
+	      ++cnt;
+	      mask <<= 1;
+	    }
+	}
+
+      ref_t off_allocend = (byte * BITS + cnt) * BLOCK_ALIGN;
+      assert (off_allocend <= db->head->first_free);
+      /* Now we know that we can copy the area from OFF_ALLOC to
+	 OFF_ALLOCEND (not included) to the memory starting at
+	 OFF_FREE.  First fix up all the entries for the
+	 displacement.  */
+      ref_t disp = off_alloc - off_free;
+
+      struct moveinfo *new_move;
+      if (__builtin_expect (stack_used + sizeof (*new_move) <= MAX_STACK_USE,
+			    1))
+	new_move = alloca_account (sizeof (*new_move), stack_used);
+      else
+	new_move = obstack_alloc (&ob, sizeof (*new_move));
+      new_move->from = db->data + off_alloc;
+      new_move->to = db->data + off_free;
+      new_move->size = off_allocend - off_alloc;
+      /* Create a circular list to be always able to append at the end.  */
+      if (moves == NULL)
+	moves = new_move->next = new_move;
+      else
+	{
+	  new_move->next = moves->next;
+	  moves = moves->next = new_move;
+	}
+
+      /* The following loop will prepare to move this much data.  */
+      off_free += off_allocend - off_alloc;
+
+      while (off_alloc < off_allocend)
+	{
+	  /* Determine whether the next entry is for a hash entry or
+	     the data.  */
+	  if ((struct hashentry *) (db->data + off_alloc) == *next_hash)
+	    {
+	      /* Just correct the forward reference.  */
+	      *(*next_hash++)->prevp -= disp;
+
+	      off_alloc += ((sizeof (struct hashentry) + BLOCK_ALIGN_M1)
+			    & ~BLOCK_ALIGN_M1);
+	    }
+	  else
+	    {
+	      assert (next_data < &he_data[db->head->nentries]);
+	      assert ((*next_data)->packet == off_alloc);
+
+	      struct datahead *dh = (struct datahead *) (db->data + off_alloc);
+	      do
+		{
+		  assert ((*next_data)->key >= (*next_data)->packet);
+		  assert ((*next_data)->key + (*next_data)->len
+			  <= (*next_data)->packet + dh->allocsize);
+
+		  (*next_data)->packet -= disp;
+		  (*next_data)->key -= disp;
+		  ++next_data;
+		}
+	      while (next_data < &he_data[db->head->nentries]
+		     && (*next_data)->packet == off_alloc);
+
+	      off_alloc += (dh->allocsize + BLOCK_ALIGN_M1) & ~BLOCK_ALIGN_M1;
+	    }
+	}
+      assert (off_alloc == off_allocend);
+
+      assert (off_alloc <= db->head->first_free);
+      if (off_alloc == db->head->first_free)
+	/* We are done, that was the last block.  */
+	break;
+    }
+  assert (next_hash == &he[db->head->nentries]);
+  assert (next_data == &he_data[db->head->nentries]);
+
+  /* Now perform the actual moves.  */
+  if (moves != NULL)
+    {
+      struct moveinfo *runp = moves->next;
+      do
+	{
+	  assert ((char *) runp->to >= db->data);
+	  assert ((char *) runp->to + runp->size
+		  <= db->data  + db->head->first_free);
+	  assert ((char *) runp->from >= db->data);
+	  assert ((char *) runp->from + runp->size
+		  <= db->data  + db->head->first_free);
+
+	  /* The regions may overlap.  */
+	  memmove (runp->to, runp->from, runp->size);
+	  runp = runp->next;
+	}
+      while (runp != moves->next);
+
+      if (__glibc_unlikely (debug_level >= 3))
+	dbg_log (_("freed %zu bytes in %s cache"),
+		 (size_t) (db->head->first_free
+			   - ((char *) moves->to + moves->size - db->data)),
+		 dbnames[db - dbs]);
+
+      /* The byte past the end of the last copied block is the next
+	 available byte.  */
+      db->head->first_free = (char *) moves->to + moves->size - db->data;
+
+      /* Consistency check.  */
+      if (__glibc_unlikely (debug_level >= 3))
+	{
+	  for (size_t idx = 0; idx < db->head->module; ++idx)
+	    {
+	      ref_t run = db->head->array[idx];
+	      size_t cnt = 0;
+
+	      while (run != ENDREF)
+		{
+		  if (run + sizeof (struct hashentry) > db->head->first_free)
+		    {
+		      dbg_log ("entry %zu in hash bucket %zu out of bounds: "
+			       "%" PRIu32 "+%zu > %zu\n",
+			       cnt, idx, run, sizeof (struct hashentry),
+			       (size_t) db->head->first_free);
+		      break;
+		    }
+
+		  struct hashentry *he = (struct hashentry *) (db->data + run);
+
+		  if (he->key + he->len > db->head->first_free)
+		    dbg_log ("key of entry %zu in hash bucket %zu out of "
+			     "bounds: %" PRIu32 "+%zu > %zu\n",
+			     cnt, idx, he->key, (size_t) he->len,
+			     (size_t) db->head->first_free);
+
+		  if (he->packet + sizeof (struct datahead)
+		      > db->head->first_free)
+		    dbg_log ("packet of entry %zu in hash bucket %zu out of "
+			     "bounds: %" PRIu32 "+%zu > %zu\n",
+			     cnt, idx, he->packet, sizeof (struct datahead),
+			     (size_t) db->head->first_free);
+		  else
+		    {
+		      struct datahead *dh = (struct datahead *) (db->data
+								 + he->packet);
+		      if (he->packet + dh->allocsize
+			  > db->head->first_free)
+			dbg_log ("full key of entry %zu in hash bucket %zu "
+				 "out of bounds: %" PRIu32 "+%zu > %zu",
+				 cnt, idx, he->packet, (size_t) dh->allocsize,
+				 (size_t) db->head->first_free);
+		    }
+
+		  run = he->next;
+		  ++cnt;
+		}
+	    }
+	}
+    }
+
+  /* Make sure the data on disk is updated.  */
+  if (db->persistent)
+    msync (db->head, db->data + db->head->first_free - (char *) db->head,
+	   MS_ASYNC);
+
+
+  /* Now we are done modifying the data.  */
+  ++db->head->gc_cycle;
+  assert ((db->head->gc_cycle & 1) == 0);
+
+  /* We are done.  */
+ out:
+  pthread_mutex_unlock (&db->memlock);
+  pthread_rwlock_unlock (&db->lock);
+
+  if (he_use_malloc)
+    free (he);
+  if (mark_use_malloc)
+    free (mark);
+
+  obstack_free (&ob, NULL);
+}
+
+
+void *
+mempool_alloc (struct database_dyn *db, size_t len, int data_alloc)
+{
+  /* Make sure LEN is a multiple of our maximum alignment so we can
+     keep track of used memory is multiples of this alignment value.  */
+  if ((len & BLOCK_ALIGN_M1) != 0)
+    len += BLOCK_ALIGN - (len & BLOCK_ALIGN_M1);
+
+  if (data_alloc)
+    pthread_rwlock_rdlock (&db->lock);
+
+  pthread_mutex_lock (&db->memlock);
+
+  assert ((db->head->first_free & BLOCK_ALIGN_M1) == 0);
+
+  bool tried_resize = false;
+  void *res;
+ retry:
+  res = db->data + db->head->first_free;
+
+  if (__glibc_unlikely (db->head->first_free + len > db->head->data_size))
+    {
+      if (! tried_resize)
+	{
+	  /* Try to resize the database.  Grow size of 1/8th.  */
+	  size_t oldtotal = (sizeof (struct database_pers_head)
+			     + roundup (db->head->module * sizeof (ref_t),
+					ALIGN)
+			     + db->head->data_size);
+	  size_t new_data_size = (db->head->data_size
+				  + MAX (2 * len, db->head->data_size / 8));
+	  size_t newtotal = (sizeof (struct database_pers_head)
+			     + roundup (db->head->module * sizeof (ref_t), ALIGN)
+			     + new_data_size);
+	  if (newtotal > db->max_db_size)
+	    {
+	      new_data_size -= newtotal - db->max_db_size;
+	      newtotal = db->max_db_size;
+	    }
+
+	  if (db->mmap_used && newtotal > oldtotal
+	      /* We only have to adjust the file size.  The new pages
+		 become magically available.  */
+	      && TEMP_FAILURE_RETRY_VAL (posix_fallocate (db->wr_fd, oldtotal,
+							  newtotal
+							  - oldtotal)) == 0)
+	    {
+	      db->head->data_size = new_data_size;
+	      tried_resize = true;
+	      goto retry;
+	    }
+	}
+
+      if (data_alloc)
+	pthread_rwlock_unlock (&db->lock);
+
+      if (! db->last_alloc_failed)
+	{
+	  dbg_log (_("no more memory for database '%s'"), dbnames[db - dbs]);
+
+	  db->last_alloc_failed = true;
+	}
+
+      ++db->head->addfailed;
+
+      /* No luck.  */
+      res = NULL;
+    }
+  else
+    {
+      db->head->first_free += len;
+
+      db->last_alloc_failed = false;
+
+    }
+
+  pthread_mutex_unlock (&db->memlock);
+
+  return res;
+}
diff --git a/REORG.TODO/nscd/netgroupcache.c b/REORG.TODO/nscd/netgroupcache.c
new file mode 100644
index 0000000000..cd0c3ea19b
--- /dev/null
+++ b/REORG.TODO/nscd/netgroupcache.c
@@ -0,0 +1,699 @@
+/* Cache handling for netgroup lookup.
+   Copyright (C) 2011-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@gmail.com>, 2011.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published
+   by the Free Software Foundation; version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
+
+#include <alloca.h>
+#include <assert.h>
+#include <errno.h>
+#include <libintl.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+#include "../inet/netgroup.h"
+#include "nscd.h"
+#include "dbg_log.h"
+
+#include <kernel-features.h>
+
+
+/* This is the standard reply in case the service is disabled.  */
+static const netgroup_response_header disabled =
+{
+  .version = NSCD_VERSION,
+  .found = -1,
+  .nresults = 0,
+  .result_len = 0
+};
+
+/* This is the struct describing how to write this record.  */
+const struct iovec netgroup_iov_disabled =
+{
+  .iov_base = (void *) &disabled,
+  .iov_len = sizeof (disabled)
+};
+
+
+/* This is the standard reply in case we haven't found the dataset.  */
+static const netgroup_response_header notfound =
+{
+  .version = NSCD_VERSION,
+  .found = 0,
+  .nresults = 0,
+  .result_len = 0
+};
+
+
+struct dataset
+{
+  struct datahead head;
+  netgroup_response_header resp;
+  char strdata[0];
+};
+
+/* Sends a notfound message and prepares a notfound dataset to write to the
+   cache.  Returns true if there was enough memory to allocate the dataset and
+   returns the dataset in DATASETP, total bytes to write in TOTALP and the
+   timeout in TIMEOUTP.  KEY_COPY is set to point to the copy of the key in the
+   dataset. */
+static bool
+do_notfound (struct database_dyn *db, int fd, request_header *req,
+	       const char *key, struct dataset **datasetp, ssize_t *totalp,
+	       time_t *timeoutp, char **key_copy)
+{
+  struct dataset *dataset;
+  ssize_t total;
+  time_t timeout;
+  bool cacheable = false;
+
+  total = sizeof (notfound);
+  timeout = time (NULL) + db->negtimeout;
+
+  if (fd != -1)
+    TEMP_FAILURE_RETRY (send (fd, &notfound, total, MSG_NOSIGNAL));
+
+  dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len, 1);
+  /* If we cannot permanently store the result, so be it.  */
+  if (dataset != NULL)
+    {
+      timeout = datahead_init_neg (&dataset->head,
+				   sizeof (struct dataset) + req->key_len,
+				   total, db->negtimeout);
+
+      /* This is the reply.  */
+      memcpy (&dataset->resp, &notfound, total);
+
+      /* Copy the key data.  */
+      memcpy (dataset->strdata, key, req->key_len);
+      *key_copy = dataset->strdata;
+
+      cacheable = true;
+    }
+  *timeoutp = timeout;
+  *totalp = total;
+  *datasetp = dataset;
+  return cacheable;
+}
+
+static time_t
+addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
+		 const char *key, uid_t uid, struct hashentry *he,
+		 struct datahead *dh, struct dataset **resultp)
+{
+  if (__glibc_unlikely (debug_level > 0))
+    {
+      if (he == NULL)
+	dbg_log (_("Haven't found \"%s\" in netgroup cache!"), key);
+      else
+	dbg_log (_("Reloading \"%s\" in netgroup cache!"), key);
+    }
+
+  static service_user *netgroup_database;
+  time_t timeout;
+  struct dataset *dataset;
+  bool cacheable = false;
+  ssize_t total;
+  bool found = false;
+
+  char *key_copy = NULL;
+  struct __netgrent data;
+  size_t buflen = MAX (1024, sizeof (*dataset) + req->key_len);
+  size_t buffilled = sizeof (*dataset);
+  char *buffer = NULL;
+  size_t nentries = 0;
+  size_t group_len = strlen (key) + 1;
+  struct name_list *first_needed
+    = alloca (sizeof (struct name_list) + group_len);
+
+  if (netgroup_database == NULL
+      && __nss_database_lookup ("netgroup", NULL, NULL, &netgroup_database))
+    {
+      /* No such service.  */
+      cacheable = do_notfound (db, fd, req, key, &dataset, &total, &timeout,
+			       &key_copy);
+      goto writeout;
+    }
+
+  memset (&data, '\0', sizeof (data));
+  buffer = xmalloc (buflen);
+  first_needed->next = first_needed;
+  memcpy (first_needed->name, key, group_len);
+  data.needed_groups = first_needed;
+
+  while (data.needed_groups != NULL)
+    {
+      /* Add the next group to the list of those which are known.  */
+      struct name_list *this_group = data.needed_groups->next;
+      if (this_group == data.needed_groups)
+	data.needed_groups = NULL;
+      else
+	data.needed_groups->next = this_group->next;
+      this_group->next = data.known_groups;
+      data.known_groups = this_group;
+
+      union
+      {
+	enum nss_status (*f) (const char *, struct __netgrent *);
+	void *ptr;
+      } setfct;
+
+      service_user *nip = netgroup_database;
+      int no_more = __nss_lookup (&nip, "setnetgrent", NULL, &setfct.ptr);
+      while (!no_more)
+	{
+	  enum nss_status status
+	    = DL_CALL_FCT (*setfct.f, (data.known_groups->name, &data));
+
+	  if (status == NSS_STATUS_SUCCESS)
+	    {
+	      found = true;
+	      union
+	      {
+		enum nss_status (*f) (struct __netgrent *, char *, size_t,
+				      int *);
+		void *ptr;
+	      } getfct;
+	      getfct.ptr = __nss_lookup_function (nip, "getnetgrent_r");
+	      if (getfct.f != NULL)
+		while (1)
+		  {
+		    int e;
+		    status = getfct.f (&data, buffer + buffilled,
+				       buflen - buffilled - req->key_len, &e);
+		    if (status == NSS_STATUS_SUCCESS)
+		      {
+			if (data.type == triple_val)
+			  {
+			    const char *nhost = data.val.triple.host;
+			    const char *nuser = data.val.triple.user;
+			    const char *ndomain = data.val.triple.domain;
+
+			    size_t hostlen = strlen (nhost ?: "") + 1;
+			    size_t userlen = strlen (nuser ?: "") + 1;
+			    size_t domainlen = strlen (ndomain ?: "") + 1;
+
+			    if (nhost == NULL || nuser == NULL || ndomain == NULL
+				|| nhost > nuser || nuser > ndomain)
+			      {
+				const char *last = nhost;
+				if (last == NULL
+				    || (nuser != NULL && nuser > last))
+				  last = nuser;
+				if (last == NULL
+				    || (ndomain != NULL && ndomain > last))
+				  last = ndomain;
+
+				size_t bufused
+				  = (last == NULL
+				     ? buffilled
+				     : last + strlen (last) + 1 - buffer);
+
+				/* We have to make temporary copies.  */
+				size_t needed = hostlen + userlen + domainlen;
+
+				if (buflen - req->key_len - bufused < needed)
+				  {
+				    buflen += MAX (buflen, 2 * needed);
+				    /* Save offset in the old buffer.  We don't
+				       bother with the NULL check here since
+				       we'll do that later anyway.  */
+				    size_t nhostdiff = nhost - buffer;
+				    size_t nuserdiff = nuser - buffer;
+				    size_t ndomaindiff = ndomain - buffer;
+
+				    char *newbuf = xrealloc (buffer, buflen);
+				    /* Fix up the triplet pointers into the new
+				       buffer.  */
+				    nhost = (nhost ? newbuf + nhostdiff
+					     : NULL);
+				    nuser = (nuser ? newbuf + nuserdiff
+					     : NULL);
+				    ndomain = (ndomain ? newbuf + ndomaindiff
+					       : NULL);
+				    buffer = newbuf;
+				  }
+
+				nhost = memcpy (buffer + bufused,
+						nhost ?: "", hostlen);
+				nuser = memcpy ((char *) nhost + hostlen,
+						nuser ?: "", userlen);
+				ndomain = memcpy ((char *) nuser + userlen,
+						  ndomain ?: "", domainlen);
+			      }
+
+			    char *wp = buffer + buffilled;
+			    wp = memmove (wp, nhost ?: "", hostlen);
+			    wp += hostlen;
+			    wp = memmove (wp, nuser ?: "", userlen);
+			    wp += userlen;
+			    wp = memmove (wp, ndomain ?: "", domainlen);
+			    wp += domainlen;
+			    buffilled = wp - buffer;
+			    ++nentries;
+			  }
+			else
+			  {
+			    /* Check that the group has not been
+			       requested before.  */
+			    struct name_list *runp = data.needed_groups;
+			    if (runp != NULL)
+			      while (1)
+				{
+				  if (strcmp (runp->name, data.val.group) == 0)
+				    break;
+
+				  runp = runp->next;
+				  if (runp == data.needed_groups)
+				    {
+				      runp = NULL;
+				      break;
+				    }
+				}
+
+			    if (runp == NULL)
+			      {
+				runp = data.known_groups;
+				while (runp != NULL)
+				  if (strcmp (runp->name, data.val.group) == 0)
+				    break;
+				  else
+				    runp = runp->next;
+				}
+
+			    if (runp == NULL)
+			      {
+				/* A new group is requested.  */
+				size_t namelen = strlen (data.val.group) + 1;
+				struct name_list *newg = alloca (sizeof (*newg)
+								 + namelen);
+				memcpy (newg->name, data.val.group, namelen);
+				if (data.needed_groups == NULL)
+				  data.needed_groups = newg->next = newg;
+				else
+				  {
+				    newg->next = data.needed_groups->next;
+				    data.needed_groups->next = newg;
+				    data.needed_groups = newg;
+				  }
+			      }
+			  }
+		      }
+		    else if (status == NSS_STATUS_TRYAGAIN && e == ERANGE)
+		      {
+			buflen *= 2;
+			buffer = xrealloc (buffer, buflen);
+		      }
+		    else if (status == NSS_STATUS_RETURN
+			     || status == NSS_STATUS_NOTFOUND
+			     || status == NSS_STATUS_UNAVAIL)
+		      /* This was either the last one for this group or the
+			 group was empty or the NSS module had an internal
+			 failure.  Look at next group if available.  */
+		      break;
+		  }
+
+	      enum nss_status (*endfct) (struct __netgrent *);
+	      endfct = __nss_lookup_function (nip, "endnetgrent");
+	      if (endfct != NULL)
+		(void) DL_CALL_FCT (*endfct, (&data));
+
+	      break;
+	    }
+
+	  no_more = __nss_next2 (&nip, "setnetgrent", NULL, &setfct.ptr,
+				 status, 0);
+	}
+    }
+
+  /* No results.  Return a failure and write out a notfound record in the
+     cache.  */
+  if (!found)
+    {
+      cacheable = do_notfound (db, fd, req, key, &dataset, &total, &timeout,
+			       &key_copy);
+      goto writeout;
+    }
+
+  total = buffilled;
+
+  /* Fill in the dataset.  */
+  dataset = (struct dataset *) buffer;
+  timeout = datahead_init_pos (&dataset->head, total + req->key_len,
+			       total - offsetof (struct dataset, resp),
+			       he == NULL ? 0 : dh->nreloads + 1,
+			       db->postimeout);
+
+  dataset->resp.version = NSCD_VERSION;
+  dataset->resp.found = 1;
+  dataset->resp.nresults = nentries;
+  dataset->resp.result_len = buffilled - sizeof (*dataset);
+
+  assert (buflen - buffilled >= req->key_len);
+  key_copy = memcpy (buffer + buffilled, key, req->key_len);
+  buffilled += req->key_len;
+
+  /* Now we can determine whether on refill we have to create a new
+     record or not.  */
+  if (he != NULL)
+    {
+      assert (fd == -1);
+
+      if (dataset->head.allocsize == dh->allocsize
+	  && dataset->head.recsize == dh->recsize
+	  && memcmp (&dataset->resp, dh->data,
+		     dh->allocsize - offsetof (struct dataset, resp)) == 0)
+	{
+	  /* The data has not changed.  We will just bump the timeout
+	     value.  Note that the new record has been allocated on
+	     the stack and need not be freed.  */
+	  dh->timeout = dataset->head.timeout;
+	  dh->ttl = dataset->head.ttl;
+	  ++dh->nreloads;
+	  dataset = (struct dataset *) dh;
+
+	  goto out;
+	}
+    }
+
+  {
+    struct dataset *newp
+      = (struct dataset *) mempool_alloc (db, total + req->key_len, 1);
+    if (__glibc_likely (newp != NULL))
+      {
+	/* Adjust pointer into the memory block.  */
+	key_copy = (char *) newp + (key_copy - buffer);
+
+	dataset = memcpy (newp, dataset, total + req->key_len);
+	cacheable = true;
+
+	if (he != NULL)
+	  /* Mark the old record as obsolete.  */
+	  dh->usable = false;
+      }
+  }
+
+  if (he == NULL && fd != -1)
+    {
+      /* We write the dataset before inserting it to the database
+	 since while inserting this thread might block and so would
+	 unnecessarily let the receiver wait.  */
+    writeout:
+#ifdef HAVE_SENDFILE
+      if (__builtin_expect (db->mmap_used, 1) && cacheable)
+	{
+	  assert (db->wr_fd != -1);
+	  assert ((char *) &dataset->resp > (char *) db->data);
+	  assert ((char *) dataset - (char *) db->head + total
+		  <= (sizeof (struct database_pers_head)
+		      + db->head->module * sizeof (ref_t)
+		      + db->head->data_size));
+# ifndef __ASSUME_SENDFILE
+	  ssize_t written =
+# endif
+	    sendfileall (fd, db->wr_fd, (char *) &dataset->resp
+			 - (char *) db->head, dataset->head.recsize);
+# ifndef __ASSUME_SENDFILE
+	  if (written == -1 && errno == ENOSYS)
+	    goto use_write;
+# endif
+	}
+      else
+#endif
+	{
+#if defined HAVE_SENDFILE && !defined __ASSUME_SENDFILE
+	use_write:
+#endif
+	  writeall (fd, &dataset->resp, dataset->head.recsize);
+	}
+    }
+
+  if (cacheable)
+    {
+      /* If necessary, we also propagate the data to disk.  */
+      if (db->persistent)
+	{
+	  // XXX async OK?
+	  uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
+	  msync ((void *) pval,
+		 ((uintptr_t) dataset & pagesize_m1) + total + req->key_len,
+		 MS_ASYNC);
+	}
+
+      (void) cache_add (req->type, key_copy, req->key_len, &dataset->head,
+			true, db, uid, he == NULL);
+
+      pthread_rwlock_unlock (&db->lock);
+
+      /* Mark the old entry as obsolete.  */
+      if (dh != NULL)
+	dh->usable = false;
+    }
+
+ out:
+  free (buffer);
+
+  *resultp = dataset;
+
+  return timeout;
+}
+
+
+static time_t
+addinnetgrX (struct database_dyn *db, int fd, request_header *req,
+	     char *key, uid_t uid, struct hashentry *he,
+	     struct datahead *dh)
+{
+  const char *group = key;
+  key = (char *) rawmemchr (key, '\0') + 1;
+  size_t group_len = key - group - 1;
+  const char *host = *key++ ? key : NULL;
+  if (host != NULL)
+    key = (char *) rawmemchr (key, '\0') + 1;
+  const char *user = *key++ ? key : NULL;
+  if (user != NULL)
+    key = (char *) rawmemchr (key, '\0') + 1;
+  const char *domain = *key++ ? key : NULL;
+
+  if (__glibc_unlikely (debug_level > 0))
+    {
+      if (he == NULL)
+	dbg_log (_("Haven't found \"%s (%s,%s,%s)\" in netgroup cache!"),
+		 group, host ?: "", user ?: "", domain ?: "");
+      else
+	dbg_log (_("Reloading \"%s (%s,%s,%s)\" in netgroup cache!"),
+		 group, host ?: "", user ?: "", domain ?: "");
+    }
+
+  struct dataset *result = (struct dataset *) cache_search (GETNETGRENT,
+							    group, group_len,
+							    db, uid);
+  time_t timeout;
+  if (result != NULL)
+    timeout = result->head.timeout;
+  else
+    {
+      request_header req_get =
+	{
+	  .type = GETNETGRENT,
+	  .key_len = group_len
+	};
+      timeout = addgetnetgrentX (db, -1, &req_get, group, uid, NULL, NULL,
+				 &result);
+    }
+
+  struct indataset
+  {
+    struct datahead head;
+    innetgroup_response_header resp;
+  } *dataset
+      = (struct indataset *) mempool_alloc (db,
+					    sizeof (*dataset) + req->key_len,
+					    1);
+  struct indataset dataset_mem;
+  bool cacheable = true;
+  if (__glibc_unlikely (dataset == NULL))
+    {
+      cacheable = false;
+      dataset = &dataset_mem;
+    }
+
+  datahead_init_pos (&dataset->head, sizeof (*dataset) + req->key_len,
+		     sizeof (innetgroup_response_header),
+		     he == NULL ? 0 : dh->nreloads + 1, result->head.ttl);
+  /* Set the notfound status and timeout based on the result from
+     getnetgrent.  */
+  dataset->head.notfound = result->head.notfound;
+  dataset->head.timeout = timeout;
+
+  dataset->resp.version = NSCD_VERSION;
+  dataset->resp.found = result->resp.found;
+  /* Until we find a matching entry the result is 0.  */
+  dataset->resp.result = 0;
+
+  char *key_copy = memcpy ((char *) (dataset + 1), group, req->key_len);
+
+  if (dataset->resp.found)
+    {
+      const char *triplets = (const char *) (&result->resp + 1);
+
+      for (nscd_ssize_t i = result->resp.nresults; i > 0; --i)
+	{
+	  bool success = true;
+
+	  /* For the host, user and domain in each triplet, we assume success
+	     if the value is blank because that is how the wildcard entry to
+	     match anything is stored in the netgroup cache.  */
+	  if (host != NULL && *triplets != '\0')
+	    success = strcmp (host, triplets) == 0;
+	  triplets = (const char *) rawmemchr (triplets, '\0') + 1;
+
+	  if (success && user != NULL && *triplets != '\0')
+	    success = strcmp (user, triplets) == 0;
+	  triplets = (const char *) rawmemchr (triplets, '\0') + 1;
+
+	  if (success && (domain == NULL || *triplets == '\0'
+			  || strcmp (domain, triplets) == 0))
+	    {
+	      dataset->resp.result = 1;
+	      break;
+	    }
+	  triplets = (const char *) rawmemchr (triplets, '\0') + 1;
+	}
+    }
+
+  if (he != NULL && dh->data[0].innetgroupdata.result == dataset->resp.result)
+    {
+      /* The data has not changed.  We will just bump the timeout
+	 value.  Note that the new record has been allocated on
+	 the stack and need not be freed.  */
+      dh->timeout = timeout;
+      dh->ttl = dataset->head.ttl;
+      ++dh->nreloads;
+      return timeout;
+    }
+
+  if (he == NULL)
+    {
+      /* We write the dataset before inserting it to the database
+	 since while inserting this thread might block and so would
+	 unnecessarily let the receiver wait.  */
+       assert (fd != -1);
+
+#ifdef HAVE_SENDFILE
+      if (__builtin_expect (db->mmap_used, 1) && cacheable)
+	{
+	  assert (db->wr_fd != -1);
+	  assert ((char *) &dataset->resp > (char *) db->data);
+	  assert ((char *) dataset - (char *) db->head + sizeof (*dataset)
+		  <= (sizeof (struct database_pers_head)
+		      + db->head->module * sizeof (ref_t)
+		      + db->head->data_size));
+# ifndef __ASSUME_SENDFILE
+	  ssize_t written =
+# endif
+	    sendfileall (fd, db->wr_fd,
+			 (char *) &dataset->resp - (char *) db->head,
+			 sizeof (innetgroup_response_header));
+# ifndef __ASSUME_SENDFILE
+	  if (written == -1 && errno == ENOSYS)
+	    goto use_write;
+# endif
+	}
+      else
+#endif
+	{
+#if defined HAVE_SENDFILE && !defined __ASSUME_SENDFILE
+	use_write:
+#endif
+	  writeall (fd, &dataset->resp, sizeof (innetgroup_response_header));
+	}
+    }
+
+  if (cacheable)
+    {
+      /* If necessary, we also propagate the data to disk.  */
+      if (db->persistent)
+	{
+	  // XXX async OK?
+	  uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
+	  msync ((void *) pval,
+		 ((uintptr_t) dataset & pagesize_m1) + sizeof (*dataset)
+		 + req->key_len,
+		 MS_ASYNC);
+	}
+
+      (void) cache_add (req->type, key_copy, req->key_len, &dataset->head,
+			true, db, uid, he == NULL);
+
+      pthread_rwlock_unlock (&db->lock);
+
+      /* Mark the old entry as obsolete.  */
+      if (dh != NULL)
+	dh->usable = false;
+    }
+
+  return timeout;
+}
+
+
+void
+addgetnetgrent (struct database_dyn *db, int fd, request_header *req,
+		void *key, uid_t uid)
+{
+  struct dataset *ignore;
+
+  addgetnetgrentX (db, fd, req, key, uid, NULL, NULL, &ignore);
+}
+
+
+time_t
+readdgetnetgrent (struct database_dyn *db, struct hashentry *he,
+		  struct datahead *dh)
+{
+  request_header req =
+    {
+      .type = GETNETGRENT,
+      .key_len = he->len
+    };
+  struct dataset *ignore;
+
+  return addgetnetgrentX (db, -1, &req, db->data + he->key, he->owner, he, dh,
+			  &ignore);
+}
+
+
+void
+addinnetgr (struct database_dyn *db, int fd, request_header *req,
+	    void *key, uid_t uid)
+{
+  addinnetgrX (db, fd, req, key, uid, NULL, NULL);
+}
+
+
+time_t
+readdinnetgr (struct database_dyn *db, struct hashentry *he,
+	      struct datahead *dh)
+{
+  request_header req =
+    {
+      .type = INNETGR,
+      .key_len = he->len
+    };
+
+  return addinnetgrX (db, -1, &req, db->data + he->key, he->owner, he, dh);
+}
diff --git a/REORG.TODO/nscd/nscd-client.h b/REORG.TODO/nscd/nscd-client.h
new file mode 100644
index 0000000000..96170bff1b
--- /dev/null
+++ b/REORG.TODO/nscd/nscd-client.h
@@ -0,0 +1,452 @@
+/* Copyright (c) 1998-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Thorsten Kukuk <kukuk@suse.de>, 1998.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+/* This file defines everything that client code should need to
+   know to talk to the nscd daemon.  */
+
+#ifndef _NSCD_CLIENT_H
+#define _NSCD_CLIENT_H	1
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include <atomic.h>
+#include <nscd-types.h>
+#include <sys/uio.h>
+
+
+/* Version number of the daemon interface */
+#define NSCD_VERSION 2
+
+/* Path of the file where the PID of the running system is stored.  */
+#define _PATH_NSCDPID	 "/var/run/nscd/nscd.pid"
+
+/* Path for the Unix domain socket.  */
+#define _PATH_NSCDSOCKET "/var/run/nscd/socket"
+
+/* Path for the configuration file.  */
+#define _PATH_NSCDCONF	 "/etc/nscd.conf"
+
+/* Maximum allowed length for the key.  */
+#define MAXKEYLEN 1024
+
+
+/* Available services.  */
+typedef enum
+{
+  GETPWBYNAME,
+  GETPWBYUID,
+  GETGRBYNAME,
+  GETGRBYGID,
+  GETHOSTBYNAME,
+  GETHOSTBYNAMEv6,
+  GETHOSTBYADDR,
+  GETHOSTBYADDRv6,
+  SHUTDOWN,		/* Shut the server down.  */
+  GETSTAT,		/* Get the server statistic.  */
+  INVALIDATE,           /* Invalidate one special cache.  */
+  GETFDPW,
+  GETFDGR,
+  GETFDHST,
+  GETAI,
+  INITGROUPS,
+  GETSERVBYNAME,
+  GETSERVBYPORT,
+  GETFDSERV,
+  GETNETGRENT,
+  INNETGR,
+  GETFDNETGR,
+  LASTREQ
+} request_type;
+
+
+/* Header common to all requests */
+typedef struct
+{
+  int32_t version;	/* Version number of the daemon interface.  */
+  request_type type;	/* Service requested.  */
+  int32_t key_len;	/* Key length.  */
+} request_header;
+
+
+/* Structure sent in reply to password query.  Note that this struct is
+   sent also if the service is disabled or there is no record found.  */
+typedef struct
+{
+  int32_t version;
+  int32_t found;
+  nscd_ssize_t pw_name_len;
+  nscd_ssize_t pw_passwd_len;
+  uid_t pw_uid;
+  gid_t pw_gid;
+  nscd_ssize_t pw_gecos_len;
+  nscd_ssize_t pw_dir_len;
+  nscd_ssize_t pw_shell_len;
+} pw_response_header;
+
+
+/* Structure sent in reply to group query.  Note that this struct is
+   sent also if the service is disabled or there is no record found.  */
+typedef struct
+{
+  int32_t version;
+  int32_t found;
+  nscd_ssize_t gr_name_len;
+  nscd_ssize_t gr_passwd_len;
+  gid_t gr_gid;
+  nscd_ssize_t gr_mem_cnt;
+} gr_response_header;
+
+
+/* Structure sent in reply to host query.  Note that this struct is
+   sent also if the service is disabled or there is no record found.  */
+typedef struct
+{
+  int32_t version;
+  int32_t found;
+  nscd_ssize_t h_name_len;
+  nscd_ssize_t h_aliases_cnt;
+  int32_t h_addrtype;
+  int32_t h_length;
+  nscd_ssize_t h_addr_list_cnt;
+  int32_t error;
+} hst_response_header;
+
+
+/* Structure sent in reply to addrinfo query.  Note that this struct is
+   sent also if the service is disabled or there is no record found.  */
+typedef struct
+{
+  int32_t version;
+  int32_t found;
+  nscd_ssize_t naddrs;
+  nscd_ssize_t addrslen;
+  nscd_ssize_t canonlen;
+  int32_t error;
+} ai_response_header;
+
+/* Structure filled in by __nscd_getai.  */
+struct nscd_ai_result
+{
+  int naddrs;
+  char *canon;
+  uint8_t *family;
+  char *addrs;
+};
+
+/* Structure sent in reply to initgroups query.  Note that this struct is
+   sent also if the service is disabled or there is no record found.  */
+typedef struct
+{
+  int32_t version;
+  int32_t found;
+  nscd_ssize_t ngrps;
+} initgr_response_header;
+
+
+/* Structure sent in reply to services query.  Note that this struct is
+   sent also if the service is disabled or there is no record found.  */
+typedef struct
+{
+  int32_t version;
+  int32_t found;
+  nscd_ssize_t s_name_len;
+  nscd_ssize_t s_proto_len;
+  nscd_ssize_t s_aliases_cnt;
+  int32_t s_port;
+} serv_response_header;
+
+
+/* Structure send in reply to netgroup query.  Note that this struct is
+   sent also if the service is disabled or there is no record found.  */
+typedef struct
+{
+  int32_t version;
+  int32_t found;
+  nscd_ssize_t nresults;
+  nscd_ssize_t result_len;
+} netgroup_response_header;
+
+typedef struct
+{
+  int32_t version;
+  int32_t found;
+  int32_t result;
+} innetgroup_response_header;
+
+
+/* Type for offsets in data part of database.  */
+typedef uint32_t ref_t;
+/* Value for invalid/no reference.  */
+#define ENDREF	UINT32_MAX
+
+/* Timestamp type.  */
+typedef uint64_t nscd_time_t;
+
+/* Maximum timestamp.  */
+#define MAX_TIMEOUT_VALUE \
+  (sizeof (time_t) == sizeof (long int) ? LONG_MAX : INT_MAX)
+
+/* Alignment requirement of the beginning of the data region.  */
+#define ALIGN 16
+
+
+/* Head of record in data part of database.  */
+struct datahead
+{
+  nscd_ssize_t allocsize;	/* Allocated Bytes.  */
+  nscd_ssize_t recsize;		/* Size of the record.  */
+  nscd_time_t timeout;		/* Time when this entry becomes invalid.  */
+  uint8_t notfound;		/* Nonzero if data has not been found.  */
+  uint8_t nreloads;		/* Reloads without use.  */
+  uint8_t usable;		/* False if the entry must be ignored.  */
+  uint8_t unused;		/* Unused.  */
+  uint32_t ttl;			/* TTL value used.  */
+
+  /* We need to have the following element aligned for the response
+     header data types and their use in the 'struct dataset' types
+     defined in the XXXcache.c files.  */
+  union
+  {
+    pw_response_header pwdata;
+    gr_response_header grdata;
+    hst_response_header hstdata;
+    ai_response_header aidata;
+    initgr_response_header initgrdata;
+    serv_response_header servdata;
+    netgroup_response_header netgroupdata;
+    innetgroup_response_header innetgroupdata;
+    nscd_ssize_t align1;
+    nscd_time_t align2;
+  } data[0];
+};
+
+static inline time_t
+datahead_init_common (struct datahead *head, nscd_ssize_t allocsize,
+		      nscd_ssize_t recsize, uint32_t ttl)
+{
+  /* Initialize so that we don't write out junk in uninitialized data to the
+     cache.  */
+  memset (head, 0, sizeof (*head));
+
+  head->allocsize = allocsize;
+  head->recsize = recsize;
+  head->usable = true;
+
+  head->ttl = ttl;
+
+  /* Compute and return the timeout time.  */
+  return head->timeout = time (NULL) + ttl;
+}
+
+static inline time_t
+datahead_init_pos (struct datahead *head, nscd_ssize_t allocsize,
+		   nscd_ssize_t recsize, uint8_t nreloads, uint32_t ttl)
+{
+  time_t ret = datahead_init_common (head, allocsize, recsize, ttl);
+
+  head->notfound = false;
+  head->nreloads = nreloads;
+
+  return ret;
+}
+
+static inline time_t
+datahead_init_neg (struct datahead *head, nscd_ssize_t allocsize,
+		   nscd_ssize_t recsize, uint32_t ttl)
+{
+  time_t ret = datahead_init_common (head, allocsize, recsize, ttl);
+
+  /* We don't need to touch nreloads here since it is set to our desired value
+     (0) when we clear the structure.  */
+  head->notfound = true;
+
+  return ret;
+}
+
+/* Structure for one hash table entry.  */
+struct hashentry
+{
+  request_type type:8;		/* Which type of dataset.  */
+  bool first;			/* True if this was the original key.  */
+  nscd_ssize_t len;		/* Length of key.  */
+  ref_t key;			/* Pointer to key.  */
+  int32_t owner;		/* If secure table, this is the owner.  */
+  ref_t next;			/* Next entry in this hash bucket list.  */
+  ref_t packet;			/* Records for the result.  */
+  union
+  {
+    struct hashentry *dellist;	/* Next record to be deleted.  This can be a
+				   pointer since only nscd uses this field.  */
+    ref_t *prevp;		/* Pointer to field containing forward
+				   reference.  */
+  };
+};
+
+
+/* Current persistent database version.  */
+#define DB_VERSION	2
+
+/* Maximum time allowed between updates of the timestamp.  */
+#define MAPPING_TIMEOUT (5 * 60)
+
+
+/* Used indices for the EXTRA_DATA element of 'database_pers_head'.
+   Each database has its own indices.  */
+#define NSCD_HST_IDX_CONF_TIMESTAMP	0
+
+
+/* Header of persistent database file.  */
+struct database_pers_head
+{
+  int32_t version;
+  int32_t header_size;
+  volatile int32_t gc_cycle;
+  volatile int32_t nscd_certainly_running;
+  volatile nscd_time_t timestamp;
+  /* Room for extensions.  */
+  volatile uint32_t extra_data[4];
+
+  nscd_ssize_t module;
+  nscd_ssize_t data_size;
+
+  nscd_ssize_t first_free;	/* Offset of first free byte in data area.  */
+
+  nscd_ssize_t nentries;
+  nscd_ssize_t maxnentries;
+  nscd_ssize_t maxnsearched;
+
+  uint64_t poshit;
+  uint64_t neghit;
+  uint64_t posmiss;
+  uint64_t negmiss;
+
+  uint64_t rdlockdelayed;
+  uint64_t wrlockdelayed;
+
+  uint64_t addfailed;
+
+  ref_t array[0];
+};
+
+
+/* Mapped database record.  */
+struct mapped_database
+{
+  const struct database_pers_head *head;
+  const char *data;
+  size_t mapsize;
+  int counter;		/* > 0 indicates it is usable.  */
+  size_t datasize;
+};
+#define NO_MAPPING ((struct mapped_database *) -1l)
+
+struct locked_map_ptr
+{
+  int lock;
+  struct mapped_database *mapped;
+};
+#define libc_locked_map_ptr(class, name) class struct locked_map_ptr name
+
+/* Try acquiring lock for mapptr, returns true if it succeeds, false
+   if not.  */
+static inline bool
+__nscd_acquire_maplock (volatile struct locked_map_ptr *mapptr)
+{
+  int cnt = 0;
+  while (__builtin_expect (atomic_compare_and_exchange_val_acq (&mapptr->lock,
+								1, 0) != 0, 0))
+    {
+      // XXX Best number of rounds?
+      if (__glibc_unlikely (++cnt > 5))
+	return false;
+
+      atomic_spin_nop ();
+    }
+
+  return true;
+}
+
+
+/* Open socket connection to nscd server.  */
+extern int __nscd_open_socket (const char *key, size_t keylen,
+			       request_type type, void *response,
+			       size_t responselen) attribute_hidden;
+
+/* Try to get a file descriptor for the shared meory segment
+   containing the database.  */
+extern struct mapped_database *__nscd_get_mapping (request_type type,
+						   const char *key,
+						   struct mapped_database **mappedp) attribute_hidden;
+
+/* Get reference of mapping.  */
+extern struct mapped_database *__nscd_get_map_ref (request_type type,
+						   const char *name,
+						   volatile struct locked_map_ptr *mapptr,
+						   int *gc_cyclep);
+
+/* Unmap database.  */
+extern void __nscd_unmap (struct mapped_database *mapped);
+
+/* Drop reference of mapping.  */
+static int
+__attribute__ ((unused))
+__nscd_drop_map_ref (struct mapped_database *map, int *gc_cycle)
+{
+  if (map != NO_MAPPING)
+    {
+      int now_cycle = map->head->gc_cycle;
+      if (__glibc_unlikely (now_cycle != *gc_cycle))
+	{
+	  /* We might have read inconsistent data.  */
+	  *gc_cycle = now_cycle;
+	  return -1;
+	}
+
+      if (atomic_decrement_val (&map->counter) == 0)
+	__nscd_unmap (map);
+    }
+
+  return 0;
+}
+
+
+/* Search the mapped database.  */
+extern struct datahead *__nscd_cache_search (request_type type,
+					     const char *key,
+					     size_t keylen,
+					     const struct mapped_database *mapped,
+					     size_t datalen);
+
+/* Wrappers around read, readv and write that only read/write less than LEN
+   bytes on error or EOF.  */
+extern ssize_t __readall (int fd, void *buf, size_t len)
+  attribute_hidden;
+extern ssize_t __readvall (int fd, const struct iovec *iov, int iovcnt)
+  attribute_hidden;
+extern ssize_t writeall (int fd, const void *buf, size_t len)
+  attribute_hidden;
+extern ssize_t sendfileall (int tofd, int fromfd, off_t off, size_t len)
+  attribute_hidden;
+
+/* Get netlink timestamp counter from mapped area or zero.  */
+extern uint32_t __nscd_get_nl_timestamp (void);
+
+#endif /* nscd.h */
diff --git a/REORG.TODO/nscd/nscd.c b/REORG.TODO/nscd/nscd.c
new file mode 100644
index 0000000000..69ef41366c
--- /dev/null
+++ b/REORG.TODO/nscd/nscd.c
@@ -0,0 +1,700 @@
+/* Copyright (c) 1998-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Thorsten Kukuk <kukuk@suse.de>, 1998.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published
+   by the Free Software Foundation; version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
+
+/* nscd - Name Service Cache Daemon. Caches passwd, group, and hosts.  */
+
+#include <argp.h>
+#include <assert.h>
+#include <dirent.h>
+#include <errno.h>
+#include <error.h>
+#include <fcntl.h>
+#include <libintl.h>
+#include <locale.h>
+#include <paths.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+#include <stdarg.h>
+
+#include "dbg_log.h"
+#include "nscd.h"
+#include "selinux.h"
+#include "../nss/nsswitch.h"
+#include <device-nrs.h>
+#ifdef HAVE_INOTIFY
+# include <sys/inotify.h>
+#endif
+#include <kernel-features.h>
+
+/* Get libc version number.  */
+#include <version.h>
+
+#define PACKAGE _libc_intl_domainname
+
+int do_shutdown;
+int disabled_passwd;
+int disabled_group;
+
+typedef enum
+{
+  /* Running in background as daemon.  */
+  RUN_DAEMONIZE,
+  /* Running in foreground but otherwise behave like a daemon,
+     i.e., detach from terminal and use syslog.  This allows
+     better integration with services like systemd.  */
+  RUN_FOREGROUND,
+  /* Run in foreground in debug mode.  */
+  RUN_DEBUG
+} run_modes;
+
+static run_modes run_mode = RUN_DAEMONIZE;
+
+static const char *conffile = _PATH_NSCDCONF;
+
+time_t start_time;
+
+uintptr_t pagesize_m1;
+
+int paranoia;
+time_t restart_time;
+time_t restart_interval = RESTART_INTERVAL;
+const char *oldcwd;
+uid_t old_uid;
+gid_t old_gid;
+
+static int check_pid (const char *file);
+static int write_pid (const char *file);
+static int monitor_child (int fd);
+
+/* Name and version of program.  */
+static void print_version (FILE *stream, struct argp_state *state);
+void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
+
+/* Function to print some extra text in the help message.  */
+static char *more_help (int key, const char *text, void *input);
+
+/* Definitions of arguments for argp functions.  */
+static const struct argp_option options[] =
+{
+  { "config-file", 'f', N_("NAME"), 0,
+    N_("Read configuration data from NAME") },
+  { "debug", 'd', NULL, 0,
+    N_("Do not fork and display messages on the current tty") },
+  { "foreground", 'F', NULL, 0,
+    N_("Do not fork, but otherwise behave like a daemon") },
+  { "nthreads", 't', N_("NUMBER"), 0, N_("Start NUMBER threads") },
+  { "shutdown", 'K', NULL, 0, N_("Shut the server down") },
+  { "statistics", 'g', NULL, 0, N_("Print current configuration statistics") },
+  { "invalidate", 'i', N_("TABLE"), 0,
+    N_("Invalidate the specified cache") },
+  { "secure", 'S', N_("TABLE,yes"), OPTION_HIDDEN,
+    N_("Use separate cache for each user")},
+  { NULL, 0, NULL, 0, NULL }
+};
+
+/* Short description of program.  */
+static const char doc[] = N_("Name Service Cache Daemon.");
+
+/* Prototype for option handler.  */
+static error_t parse_opt (int key, char *arg, struct argp_state *state);
+
+/* Data structure to communicate with argp functions.  */
+static struct argp argp =
+{
+  options, parse_opt, NULL, doc, NULL, more_help
+};
+
+/* True if only statistics are requested.  */
+static bool get_stats;
+static int parent_fd = -1;
+
+int
+main (int argc, char **argv)
+{
+  int remaining;
+
+  /* Set locale via LC_ALL.  */
+  setlocale (LC_ALL, "");
+  /* Set the text message domain.  */
+  textdomain (PACKAGE);
+
+  /* Determine if the kernel has SELinux support.  */
+  nscd_selinux_enabled (&selinux_enabled);
+
+  /* Parse and process arguments.  */
+  argp_parse (&argp, argc, argv, 0, &remaining, NULL);
+
+  if (remaining != argc)
+    {
+      error (0, 0, gettext ("wrong number of arguments"));
+      argp_help (&argp, stdout, ARGP_HELP_SEE, program_invocation_short_name);
+      exit (1);
+    }
+
+  /* Read the configuration file.  */
+  if (nscd_parse_file (conffile, dbs) != 0)
+    /* We couldn't read the configuration file.  We don't start the
+       server.  */
+    error (EXIT_FAILURE, 0,
+	   _("failure while reading configuration file; this is fatal"));
+
+  /* Do we only get statistics?  */
+  if (get_stats)
+    /* Does not return.  */
+    receive_print_stats ();
+
+  /* Check if we are already running. */
+  if (check_pid (_PATH_NSCDPID))
+    error (EXIT_FAILURE, 0, _("already running"));
+
+  /* Remember when we started.  */
+  start_time = time (NULL);
+
+  /* Determine page size.  */
+  pagesize_m1 = getpagesize () - 1;
+
+  if (run_mode == RUN_DAEMONIZE || run_mode == RUN_FOREGROUND)
+    {
+      int i;
+      pid_t pid;
+
+      /* Behave like a daemon.  */
+      if (run_mode == RUN_DAEMONIZE)
+	{
+	  int fd[2];
+
+	  if (pipe (fd) != 0)
+	    error (EXIT_FAILURE, errno,
+		   _("cannot create a pipe to talk to the child"));
+
+	  pid = fork ();
+	  if (pid == -1)
+	    error (EXIT_FAILURE, errno, _("cannot fork"));
+	  if (pid != 0)
+	    {
+	      /* The parent only reads from the child.  */
+	      close (fd[1]);
+	      exit (monitor_child (fd[0]));
+	    }
+	  else
+	    {
+	      /* The child only writes to the parent.  */
+	      close (fd[0]);
+	      parent_fd = fd[1];
+	    }
+	}
+
+      int nullfd = open (_PATH_DEVNULL, O_RDWR);
+      if (nullfd != -1)
+	{
+	  struct stat64 st;
+
+	  if (fstat64 (nullfd, &st) == 0 && S_ISCHR (st.st_mode) != 0
+#if defined DEV_NULL_MAJOR && defined DEV_NULL_MINOR
+	      && st.st_rdev == makedev (DEV_NULL_MAJOR, DEV_NULL_MINOR)
+#endif
+	      )
+	    {
+	      /* It is the /dev/null special device alright.  */
+	      (void) dup2 (nullfd, STDIN_FILENO);
+	      (void) dup2 (nullfd, STDOUT_FILENO);
+	      (void) dup2 (nullfd, STDERR_FILENO);
+
+	      if (nullfd > 2)
+		close (nullfd);
+	    }
+	  else
+	    {
+	      /* Ugh, somebody is trying to play a trick on us.  */
+	      close (nullfd);
+	      nullfd = -1;
+	    }
+	}
+      int min_close_fd = nullfd == -1 ? 0 : STDERR_FILENO + 1;
+
+      DIR *d = opendir ("/proc/self/fd");
+      if (d != NULL)
+	{
+	  struct dirent64 *dirent;
+	  int dfdn = dirfd (d);
+
+	  while ((dirent = readdir64 (d)) != NULL)
+	    {
+	      char *endp;
+	      long int fdn = strtol (dirent->d_name, &endp, 10);
+
+	      if (*endp == '\0' && fdn != dfdn && fdn >= min_close_fd
+		  && fdn != parent_fd)
+		close ((int) fdn);
+	    }
+
+	  closedir (d);
+	}
+      else
+	for (i = min_close_fd; i < getdtablesize (); i++)
+	  if (i != parent_fd)
+	    close (i);
+
+      setsid ();
+
+      if (chdir ("/") != 0)
+	do_exit (EXIT_FAILURE, errno,
+		 _("cannot change current working directory to \"/\""));
+
+      openlog ("nscd", LOG_CONS | LOG_ODELAY, LOG_DAEMON);
+
+      if (write_pid (_PATH_NSCDPID) < 0)
+	dbg_log ("%s: %s", _PATH_NSCDPID, strerror (errno));
+
+      if (!init_logfile ())
+	dbg_log (_("Could not create log file"));
+
+      /* Ignore job control signals.  */
+      signal (SIGTTOU, SIG_IGN);
+      signal (SIGTTIN, SIG_IGN);
+      signal (SIGTSTP, SIG_IGN);
+    }
+  else
+    /* In debug mode we are not paranoid.  */
+    paranoia = 0;
+
+  signal (SIGINT, termination_handler);
+  signal (SIGQUIT, termination_handler);
+  signal (SIGTERM, termination_handler);
+  signal (SIGPIPE, SIG_IGN);
+
+  /* Cleanup files created by a previous 'bind'.  */
+  unlink (_PATH_NSCDSOCKET);
+
+#ifdef HAVE_INOTIFY
+  /* Use inotify to recognize changed files.  */
+  inotify_fd = inotify_init1 (IN_NONBLOCK);
+# ifndef __ASSUME_IN_NONBLOCK
+  if (inotify_fd == -1 && errno == ENOSYS)
+    {
+      inotify_fd = inotify_init ();
+      if (inotify_fd != -1)
+	fcntl (inotify_fd, F_SETFL, O_RDONLY | O_NONBLOCK);
+    }
+# endif
+#endif
+
+#ifdef USE_NSCD
+  /* Make sure we do not get recursive calls.  */
+  __nss_disable_nscd (register_traced_file);
+#endif
+
+  /* Init databases.  */
+  nscd_init ();
+
+  /* Start the SELinux AVC.  */
+  if (selinux_enabled)
+    nscd_avc_init ();
+
+  /* Handle incoming requests */
+  start_threads ();
+
+  return 0;
+}
+
+
+static void __attribute__ ((noreturn))
+invalidate_db (const char *dbname)
+{
+  int sock = nscd_open_socket ();
+
+  if (sock == -1)
+    exit (EXIT_FAILURE);
+
+  size_t dbname_len = strlen (dbname) + 1;
+  size_t reqlen = sizeof (request_header) + dbname_len;
+  struct
+  {
+    request_header req;
+    char dbname[];
+  } *reqdata = alloca (reqlen);
+
+  reqdata->req.key_len = dbname_len;
+  reqdata->req.version = NSCD_VERSION;
+  reqdata->req.type = INVALIDATE;
+  memcpy (reqdata->dbname, dbname, dbname_len);
+
+  ssize_t nbytes = TEMP_FAILURE_RETRY (send (sock, reqdata, reqlen,
+					     MSG_NOSIGNAL));
+
+  if (nbytes != reqlen)
+    {
+      int err = errno;
+      close (sock);
+      error (EXIT_FAILURE, err, _("write incomplete"));
+    }
+
+  /* Wait for ack.  Older nscd just closed the socket when
+     prune_cache finished, silently ignore that.  */
+  int32_t resp = 0;
+  nbytes = TEMP_FAILURE_RETRY (read (sock, &resp, sizeof (resp)));
+  if (nbytes != 0 && nbytes != sizeof (resp))
+    {
+      int err = errno;
+      close (sock);
+      error (EXIT_FAILURE, err, _("cannot read invalidate ACK"));
+    }
+
+  close (sock);
+
+  if (resp != 0)
+    error (EXIT_FAILURE, resp, _("invalidation failed"));
+
+  exit (0);
+}
+
+static void __attribute__ ((noreturn))
+send_shutdown (void)
+{
+  int sock = nscd_open_socket ();
+
+  if (sock == -1)
+    exit (EXIT_FAILURE);
+
+  request_header req;
+  req.version = NSCD_VERSION;
+  req.type = SHUTDOWN;
+  req.key_len = 0;
+
+  ssize_t nbytes = TEMP_FAILURE_RETRY (send (sock, &req, sizeof req,
+                                             MSG_NOSIGNAL));
+  close (sock);
+  exit (nbytes != sizeof (request_header) ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+/* Handle program arguments.  */
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+  switch (key)
+    {
+    case 'd':
+      ++debug_level;
+      run_mode = RUN_DEBUG;
+      break;
+
+    case 'F':
+      run_mode = RUN_FOREGROUND;
+      break;
+
+    case 'f':
+      conffile = arg;
+      break;
+
+    case 'K':
+      if (getuid () != 0)
+	error (4, 0, _("Only root is allowed to use this option!"));
+      else
+        send_shutdown ();
+      break;
+
+    case 'g':
+      get_stats = true;
+      break;
+
+    case 'i':
+      {
+        /* Validate the database name.  */
+
+        dbtype cnt;
+        for (cnt = pwddb; cnt < lastdb; ++cnt)
+          if (strcmp (arg, dbnames[cnt]) == 0)
+            break;
+
+        if (cnt == lastdb)
+          {
+            argp_error (state, _("'%s' is not a known database"), arg);
+            return EINVAL;
+          }
+      }
+      if (getuid () != 0)
+	error (4, 0, _("Only root is allowed to use this option!"));
+      else
+        invalidate_db (arg);
+      break;
+
+    case 't':
+      nthreads = atol (arg);
+      break;
+
+    case 'S':
+      error (0, 0, _("secure services not implemented anymore"));
+      break;
+
+    default:
+      return ARGP_ERR_UNKNOWN;
+    }
+
+  return 0;
+}
+
+/* Print bug-reporting information in the help message.  */
+static char *
+more_help (int key, const char *text, void *input)
+{
+  switch (key)
+    {
+    case ARGP_KEY_HELP_EXTRA:
+      {
+	/* We print some extra information.  */
+
+	char *tables = xstrdup (dbnames[0]);
+	for (dbtype i = 1; i < lastdb; ++i)
+	  {
+	    char *more_tables;
+	    if (asprintf (&more_tables, "%s %s", tables, dbnames[i]) < 0)
+	      more_tables = NULL;
+	    free (tables);
+	    if (more_tables == NULL)
+	      return NULL;
+	    tables = more_tables;
+	  }
+
+	char *tp;
+	if (asprintf (&tp, gettext ("\
+Supported tables:\n\
+%s\n\
+\n\
+For bug reporting instructions, please see:\n\
+%s.\n\
+"), tables, REPORT_BUGS_TO) < 0)
+	  tp = NULL;
+	free (tables);
+	return tp;
+      }
+
+    default:
+      break;
+    }
+
+  return (char *) text;
+}
+
+/* Print the version information.  */
+static void
+print_version (FILE *stream, struct argp_state *state)
+{
+  fprintf (stream, "nscd %s%s\n", PKGVERSION, VERSION);
+  fprintf (stream, gettext ("\
+Copyright (C) %s Free Software Foundation, Inc.\n\
+This is free software; see the source for copying conditions.  There is NO\n\
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
+"), "2017");
+  fprintf (stream, gettext ("Written by %s.\n"),
+	   "Thorsten Kukuk and Ulrich Drepper");
+}
+
+
+/* Create a socket connected to a name.  */
+int
+nscd_open_socket (void)
+{
+  struct sockaddr_un addr;
+  int sock;
+
+  sock = socket (PF_UNIX, SOCK_STREAM, 0);
+  if (sock < 0)
+    return -1;
+
+  addr.sun_family = AF_UNIX;
+  assert (sizeof (addr.sun_path) >= sizeof (_PATH_NSCDSOCKET));
+  strcpy (addr.sun_path, _PATH_NSCDSOCKET);
+  if (connect (sock, (struct sockaddr *) &addr, sizeof (addr)) < 0)
+    {
+      close (sock);
+      return -1;
+    }
+
+  return sock;
+}
+
+
+/* Cleanup.  */
+void
+termination_handler (int signum)
+{
+  close_sockets ();
+
+  /* Clean up the file created by 'bind'.  */
+  unlink (_PATH_NSCDSOCKET);
+
+  /* Clean up pid file.  */
+  unlink (_PATH_NSCDPID);
+
+  // XXX Terminate threads.
+
+  /* Synchronize memory.  */
+  for (int cnt = 0; cnt < lastdb; ++cnt)
+    {
+      if (!dbs[cnt].enabled || dbs[cnt].head == NULL)
+	continue;
+
+      /* Make sure nobody keeps using the database.  */
+      dbs[cnt].head->timestamp = 0;
+
+      if (dbs[cnt].persistent)
+	// XXX async OK?
+	msync (dbs[cnt].head, dbs[cnt].memsize, MS_ASYNC);
+    }
+
+  _exit (EXIT_SUCCESS);
+}
+
+/* Returns 1 if the process in pid file FILE is running, 0 if not.  */
+static int
+check_pid (const char *file)
+{
+  FILE *fp;
+
+  fp = fopen (file, "r");
+  if (fp)
+    {
+      pid_t pid;
+      int n;
+
+      n = fscanf (fp, "%d", &pid);
+      fclose (fp);
+
+      /* If we cannot parse the file default to assuming nscd runs.
+	 If the PID is alive, assume it is running.  That all unless
+	 the PID is the same as the current process' since tha latter
+	 can mean we re-exec.  */
+      if ((n != 1 || kill (pid, 0) == 0) && pid != getpid ())
+	return 1;
+    }
+
+  return 0;
+}
+
+/* Write the current process id to the file FILE.
+   Returns 0 if successful, -1 if not.  */
+static int
+write_pid (const char *file)
+{
+  FILE *fp;
+
+  fp = fopen (file, "w");
+  if (fp == NULL)
+    return -1;
+
+  fprintf (fp, "%d\n", getpid ());
+
+  int result = fflush (fp) || ferror (fp) ? -1 : 0;
+
+  fclose (fp);
+
+  return result;
+}
+
+static int
+monitor_child (int fd)
+{
+  int child_ret = 0;
+  int ret = read (fd, &child_ret, sizeof (child_ret));
+
+  /* The child terminated with an error, either via exit or some other abnormal
+     method, like a segfault.  */
+  if (ret <= 0 || child_ret != 0)
+    {
+      int status;
+      int err = wait (&status);
+
+      if (err < 0)
+	{
+	  fprintf (stderr, _("'wait' failed\n"));
+	  return 1;
+	}
+
+      if (WIFEXITED (status))
+	{
+	  child_ret = WEXITSTATUS (status);
+	  fprintf (stderr, _("child exited with status %d\n"), child_ret);
+	}
+      if (WIFSIGNALED (status))
+	{
+	  child_ret = WTERMSIG (status);
+	  fprintf (stderr, _("child terminated by signal %d\n"), child_ret);
+	}
+    }
+
+  /* We have the child status, so exit with that code.  */
+  close (fd);
+
+  return child_ret;
+}
+
+void
+do_exit (int child_ret, int errnum, const char *format, ...)
+{
+  if (parent_fd != -1)
+    {
+      int ret __attribute__ ((unused));
+      ret = write (parent_fd, &child_ret, sizeof (child_ret));
+      assert (ret == sizeof (child_ret));
+      close (parent_fd);
+    }
+
+  if (format != NULL)
+    {
+      /* Emulate error() since we don't have a va_list variant for it.  */
+      va_list argp;
+
+      fflush (stdout);
+
+      fprintf (stderr, "%s: ", program_invocation_name);
+
+      va_start (argp, format);
+      vfprintf (stderr, format, argp);
+      va_end (argp);
+
+      fprintf (stderr, ": %s\n", strerror (errnum));
+      fflush (stderr);
+    }
+
+  /* Finally, exit.  */
+  exit (child_ret);
+}
+
+void
+notify_parent (int child_ret)
+{
+  if (parent_fd == -1)
+    return;
+
+  int ret __attribute__ ((unused));
+  ret = write (parent_fd, &child_ret, sizeof (child_ret));
+  assert (ret == sizeof (child_ret));
+  close (parent_fd);
+  parent_fd = -1;
+}
diff --git a/REORG.TODO/nscd/nscd.conf b/REORG.TODO/nscd/nscd.conf
new file mode 100644
index 0000000000..39b875912d
--- /dev/null
+++ b/REORG.TODO/nscd/nscd.conf
@@ -0,0 +1,88 @@
+#
+# /etc/nscd.conf
+#
+# An example Name Service Cache config file.  This file is needed by nscd.
+#
+# Legal entries are:
+#
+#	logfile			<file>
+#	debug-level		<level>
+#	threads			<initial #threads to use>
+#	max-threads		<maximum #threads to use>
+#	server-user             <user to run server as instead of root>
+#		server-user is ignored if nscd is started with -S parameters
+#       stat-user               <user who is allowed to request statistics>
+#	reload-count		unlimited|<number>
+#	paranoia		<yes|no>
+#	restart-interval	<time in seconds>
+#
+#       enable-cache		<service> <yes|no>
+#	positive-time-to-live	<service> <time in seconds>
+#	negative-time-to-live   <service> <time in seconds>
+#       suggested-size		<service> <prime number>
+#	check-files		<service> <yes|no>
+#	persistent		<service> <yes|no>
+#	shared			<service> <yes|no>
+#	max-db-size		<service> <number bytes>
+#	auto-propagate		<service> <yes|no>
+#
+# Currently supported cache names (services): passwd, group, hosts, services
+#
+
+
+#	logfile			/var/log/nscd.log
+#	threads			4
+#	max-threads		32
+#	server-user		nobody
+#	stat-user		somebody
+	debug-level		0
+#	reload-count		5
+	paranoia		no
+#	restart-interval	3600
+
+	enable-cache		passwd		yes
+	positive-time-to-live	passwd		600
+	negative-time-to-live	passwd		20
+	suggested-size		passwd		211
+	check-files		passwd		yes
+	persistent		passwd		yes
+	shared			passwd		yes
+	max-db-size		passwd		33554432
+	auto-propagate		passwd		yes
+
+	enable-cache		group		yes
+	positive-time-to-live	group		3600
+	negative-time-to-live	group		60
+	suggested-size		group		211
+	check-files		group		yes
+	persistent		group		yes
+	shared			group		yes
+	max-db-size		group		33554432
+	auto-propagate		group		yes
+
+	enable-cache		hosts		yes
+	positive-time-to-live	hosts		3600
+	negative-time-to-live	hosts		20
+	suggested-size		hosts		211
+	check-files		hosts		yes
+	persistent		hosts		yes
+	shared			hosts		yes
+	max-db-size		hosts		33554432
+
+	enable-cache		services	yes
+	positive-time-to-live	services	28800
+	negative-time-to-live	services	20
+	suggested-size		services	211
+	check-files		services	yes
+	persistent		services	yes
+	shared			services	yes
+	max-db-size		services	33554432
+
+	enable-cache		netgroup	yes
+	positive-time-to-live	netgroup	28800
+	negative-time-to-live	netgroup	20
+	suggested-size		netgroup	211
+	check-files		netgroup	yes
+	persistent		netgroup	yes
+	shared			netgroup	yes
+	max-db-size		netgroup	33554432
diff --git a/REORG.TODO/nscd/nscd.h b/REORG.TODO/nscd/nscd.h
new file mode 100644
index 0000000000..c6b0a3c836
--- /dev/null
+++ b/REORG.TODO/nscd/nscd.h
@@ -0,0 +1,377 @@
+/* Copyright (c) 1998-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Thorsten Kukuk <kukuk@suse.de>, 1998.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef _NSCD_H
+#define _NSCD_H	1
+
+#include <pthread.h>
+#include <stdbool.h>
+#include <time.h>
+#include <sys/uio.h>
+
+/* The declarations for the request and response types are in the file
+   "nscd-client.h", which should contain everything needed by client
+   functions.  */
+#include "nscd-client.h"
+
+
+/* Handle databases.  */
+typedef enum
+{
+  pwddb,
+  grpdb,
+  hstdb,
+  servdb,
+  netgrdb,
+  lastdb
+} dbtype;
+
+
+/* Default limit on the number of times a value gets reloaded without
+   being used in the meantime.  NSCD does not throw a value out as
+   soon as it times out.  It tries to reload the value from the
+   server.  Only if the value has not been used for so many rounds it
+   is removed.  */
+#define DEFAULT_RELOAD_LIMIT 5
+
+
+/* Time before restarting the process in paranoia mode.  */
+#define RESTART_INTERVAL (60 * 60)
+
+
+/* Stack size for worker threads.  */
+#define NSCD_THREAD_STACKSIZE 1024 * 1024 * (sizeof (void *) / 4)
+
+/* Maximum size of stack frames we allow the thread to use.  We use
+   80% of the thread stack size.  */
+#define MAX_STACK_USE ((8 * NSCD_THREAD_STACKSIZE) / 10)
+
+/* 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;
+  /* 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
+{
+  pthread_rwlock_t lock;
+  pthread_cond_t prune_cond;
+  pthread_mutex_t prune_lock;
+  pthread_mutex_t prune_run_lock;
+  time_t wakeup_time;
+
+  int enabled;
+  int check_file;
+  int clear_cache;
+  int persistent;
+  int shared;
+  int propagate;
+  struct traced_file *traced_files;
+  const char *db_filename;
+  size_t suggested_module;
+  size_t max_db_size;
+
+  unsigned long int postimeout;	/* In seconds.  */
+  unsigned long int negtimeout;	/* In seconds.  */
+
+  int wr_fd;			/* Writable file descriptor.  */
+  int ro_fd;			/* Unwritable file descriptor.  */
+
+  const struct iovec *disabled_iov;
+
+  struct database_pers_head *head;
+  char *data;
+  size_t memsize;
+  pthread_mutex_t memlock;
+  bool mmap_used;
+  bool last_alloc_failed;
+};
+
+
+/* Paths of the file for the persistent storage.  */
+#define _PATH_NSCD_PASSWD_DB	"/var/db/nscd/passwd"
+#define _PATH_NSCD_GROUP_DB	"/var/db/nscd/group"
+#define _PATH_NSCD_HOSTS_DB	"/var/db/nscd/hosts"
+#define _PATH_NSCD_SERVICES_DB	"/var/db/nscd/services"
+#define _PATH_NSCD_NETGROUP_DB	"/var/db/nscd/netgroup"
+
+/* Path used when not using persistent storage.  */
+#define _PATH_NSCD_XYZ_DB_TMP	"/var/run/nscd/dbXXXXXX"
+
+/* Maximum alignment requirement we will encounter.  */
+#define BLOCK_ALIGN_LOG 3
+#define BLOCK_ALIGN (1 << BLOCK_ALIGN_LOG)
+#define BLOCK_ALIGN_M1 (BLOCK_ALIGN - 1)
+
+/* Default value for the maximum size of the database files.  */
+#define DEFAULT_MAX_DB_SIZE	(32 * 1024 * 1024)
+
+/* Number of bytes of data we initially reserve for each hash table bucket.  */
+#define DEFAULT_DATASIZE_PER_BUCKET 1024
+
+/* Default module of hash table.  */
+#define DEFAULT_SUGGESTED_MODULE 211
+
+
+/* Number of seconds between two cache pruning runs if we do not have
+   better information when it is really needed.  */
+#define CACHE_PRUNE_INTERVAL	15
+
+
+/* Global variables.  */
+extern struct database_dyn dbs[lastdb] attribute_hidden;
+extern const char *const dbnames[lastdb];
+extern const char *const serv2str[LASTREQ];
+
+extern const struct iovec pwd_iov_disabled;
+extern const struct iovec grp_iov_disabled;
+extern const struct iovec hst_iov_disabled;
+extern const struct iovec serv_iov_disabled;
+extern const struct iovec netgroup_iov_disabled;
+
+
+/* Initial number of threads to run.  */
+extern int nthreads;
+/* Maximum number of threads to use.  */
+extern int max_nthreads;
+
+/* Inotify descriptor.  */
+extern int inotify_fd;
+
+/* User name to run server processes as.  */
+extern const char *server_user;
+
+/* Name and UID of user who is allowed to request statistics.  */
+extern const char *stat_user;
+extern uid_t stat_uid;
+
+/* Time the server was started.  */
+extern time_t start_time;
+
+/* Number of times clients had to wait.  */
+extern unsigned long int client_queued;
+
+/* Maximum needed alignment.  */
+extern const size_t block_align;
+
+/* Number of times a value is reloaded without being used.  UINT_MAX
+   means unlimited.  */
+extern unsigned int reload_count;
+
+/* Pagesize minus one.  */
+extern uintptr_t pagesize_m1;
+
+/* Nonzero if paranoia mode is enabled.  */
+extern int paranoia;
+/* Time after which the process restarts.  */
+extern time_t restart_time;
+/* How much time between restarts.  */
+extern time_t restart_interval;
+/* Old current working directory.  */
+extern const char *oldcwd;
+/* Old user and group ID.  */
+extern uid_t old_uid;
+extern gid_t old_gid;
+
+
+/* Prototypes for global functions.  */
+
+/* Wrapper functions with error checking for standard functions.  */
+#include <programs/xmalloc.h>
+
+/* nscd.c */
+extern void termination_handler (int signum) __attribute__ ((__noreturn__));
+extern int nscd_open_socket (void);
+void notify_parent (int child_ret);
+void do_exit (int child_ret, int errnum, const char *format, ...);
+
+/* 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__));
+
+/* nscd_conf.c */
+extern int nscd_parse_file (const char *fname,
+			    struct database_dyn dbs[lastdb]);
+
+/* nscd_stat.c */
+extern void send_stats (int fd, struct database_dyn dbs[lastdb]);
+extern int receive_print_stats (void) __attribute__ ((__noreturn__));
+
+/* cache.c */
+extern struct datahead *cache_search (request_type, const void *key,
+				      size_t len, struct database_dyn *table,
+				      uid_t owner);
+extern int cache_add (int type, const void *key, size_t len,
+		      struct datahead *packet, bool first,
+		      struct database_dyn *table, uid_t owner,
+		      bool prune_wakeup);
+extern time_t prune_cache (struct database_dyn *table, time_t now, int fd);
+
+/* pwdcache.c */
+extern void addpwbyname (struct database_dyn *db, int fd, request_header *req,
+			 void *key, uid_t uid);
+extern void addpwbyuid (struct database_dyn *db, int fd, request_header *req,
+			void *key, uid_t uid);
+extern time_t readdpwbyname (struct database_dyn *db, struct hashentry *he,
+			     struct datahead *dh);
+extern time_t readdpwbyuid (struct database_dyn *db, struct hashentry *he,
+			    struct datahead *dh);
+
+/* grpcache.c */
+extern void addgrbyname (struct database_dyn *db, int fd, request_header *req,
+			 void *key, uid_t uid);
+extern void addgrbygid (struct database_dyn *db, int fd, request_header *req,
+			void *key, uid_t uid);
+extern time_t readdgrbyname (struct database_dyn *db, struct hashentry *he,
+			     struct datahead *dh);
+extern time_t readdgrbygid (struct database_dyn *db, struct hashentry *he,
+			    struct datahead *dh);
+
+/* hstcache.c */
+extern void addhstbyname (struct database_dyn *db, int fd, request_header *req,
+			  void *key, uid_t uid);
+extern void addhstbyaddr (struct database_dyn *db, int fd, request_header *req,
+			  void *key, uid_t uid);
+extern void addhstbynamev6 (struct database_dyn *db, int fd,
+			    request_header *req, void *key, uid_t uid);
+extern void addhstbyaddrv6 (struct database_dyn *db, int fd,
+			    request_header *req, void *key, uid_t uid);
+extern time_t readdhstbyname (struct database_dyn *db, struct hashentry *he,
+			      struct datahead *dh);
+extern time_t readdhstbyaddr (struct database_dyn *db, struct hashentry *he,
+			      struct datahead *dh);
+extern time_t readdhstbynamev6 (struct database_dyn *db, struct hashentry *he,
+				struct datahead *dh);
+extern time_t readdhstbyaddrv6 (struct database_dyn *db, struct hashentry *he,
+				struct datahead *dh);
+
+/* aicache.c */
+extern void addhstai (struct database_dyn *db, int fd, request_header *req,
+		      void *key, uid_t uid);
+extern time_t readdhstai (struct database_dyn *db, struct hashentry *he,
+			  struct datahead *dh);
+
+
+/* initgrcache.c */
+extern void addinitgroups (struct database_dyn *db, int fd,
+			   request_header *req, void *key, uid_t uid);
+extern time_t readdinitgroups (struct database_dyn *db, struct hashentry *he,
+			       struct datahead *dh);
+
+/* servicecache.c */
+extern void addservbyname (struct database_dyn *db, int fd,
+			   request_header *req, void *key, uid_t uid);
+extern time_t readdservbyname (struct database_dyn *db, struct hashentry *he,
+			       struct datahead *dh);
+extern void addservbyport (struct database_dyn *db, int fd,
+			   request_header *req, void *key, uid_t uid);
+extern time_t readdservbyport (struct database_dyn *db, struct hashentry *he,
+			       struct datahead *dh);
+
+/* netgroupcache.c */
+extern void addinnetgr (struct database_dyn *db, int fd, request_header *req,
+			void *key, uid_t uid);
+extern time_t readdinnetgr (struct database_dyn *db, struct hashentry *he,
+			    struct datahead *dh);
+extern void addgetnetgrent (struct database_dyn *db, int fd,
+			    request_header *req, void *key, uid_t uid);
+extern time_t readdgetnetgrent (struct database_dyn *db, struct hashentry *he,
+				struct datahead *dh);
+
+/* mem.c */
+extern void *mempool_alloc (struct database_dyn *db, size_t len,
+			    int data_alloc);
+extern void gc (struct database_dyn *db);
+
+
+/* nscd_setup_thread.c */
+extern int setup_thread (struct database_dyn *db);
+
+
+/* Special version of TEMP_FAILURE_RETRY for functions returning error
+   values.  */
+#define TEMP_FAILURE_RETRY_VAL(expression) \
+  (__extension__							      \
+    ({ long int __result;						      \
+       do __result = (long int) (expression);				      \
+       while (__result == EINTR);					      \
+       __result; }))
+
+#endif /* nscd.h */
diff --git a/REORG.TODO/nscd/nscd.init b/REORG.TODO/nscd/nscd.init
new file mode 100644
index 0000000000..a882da7d8b
--- /dev/null
+++ b/REORG.TODO/nscd/nscd.init
@@ -0,0 +1,116 @@
+#!/bin/bash
+#
+# nscd:		Starts the Name Switch Cache Daemon
+#
+# chkconfig: - 30 74
+# description:  This is a daemon which handles passwd and group lookups \
+#		for running programs and cache the results for the next \
+#		query.  You should start this daemon if you use \
+#		slow naming services like NIS, NIS+, LDAP, or hesiod.
+# processname: /usr/sbin/nscd
+# config: /etc/nscd.conf
+#
+### BEGIN INIT INFO
+# Provides: nscd
+# Required-Start: $syslog
+# Default-Stop: 0 1 6
+# Short-Description: Starts the Name Switch Cache Daemon
+# Description:  This is a daemon which handles passwd and group lookups \
+#		for running programs and cache the results for the next \
+#		query.  You should start this daemon if you use \
+#		slow naming services like NIS, NIS+, LDAP, or hesiod.
+### END INIT INFO
+
+# Sanity checks.
+[ -f /etc/nscd.conf ] || exit 0
+[ -x /usr/sbin/nscd ] || exit 0
+
+# Source function library.
+. /etc/init.d/functions
+
+# nscd does not run on any kernel lower than 2.2.0 because of threading
+# problems, so we require that in first place.
+case $(uname -r) in
+    2.[2-9].*)
+	# this is okay
+	;;
+    [3-9]*)
+	# these are of course also okay
+	;;
+    *)
+	#this is not
+	exit 1
+	;;
+esac
+
+RETVAL=0
+prog=nscd
+
+start () {
+    [ -d /var/run/nscd ] || mkdir /var/run/nscd
+    [ -d /var/db/nscd ] || mkdir /var/db/nscd
+    echo -n $"Starting $prog: "
+    daemon /usr/sbin/nscd
+    RETVAL=$?
+    echo
+    [ $RETVAL -eq 0 ] && touch /var/lock/subsys/nscd
+    return $RETVAL
+}
+
+stop () {
+    echo -n $"Stopping $prog: "
+    /usr/sbin/nscd -K
+    RETVAL=$?
+    if [ $RETVAL -eq 0 ]; then
+       	rm -f /var/lock/subsys/nscd
+	# nscd won't be able to remove these if it is running as
+	# a non-privileged user
+	rm -f /var/run/nscd/nscd.pid
+	rm -f /var/run/nscd/socket
+       	success $"$prog shutdown"
+    else
+       	failure $"$prog shutdown"
+    fi
+    echo
+    return $RETVAL
+}
+
+restart() {
+    stop
+    start
+}
+
+# See how we were called.
+case "$1" in
+    start)
+	start
+	RETVAL=$?
+	;;
+    stop)
+	stop
+	RETVAL=$?
+	;;
+    status)
+	status nscd
+	RETVAL=$?
+	;;
+    restart)
+	restart
+	RETVAL=$?
+	;;
+    try-restart | condrestart)
+	[ -e /var/lock/subsys/nscd ] && restart
+	RETVAL=$?
+	;;
+    force-reload | reload)
+    	echo -n $"Reloading $prog: "
+	killproc /usr/sbin/nscd -HUP
+	RETVAL=$?
+	echo
+	;;
+    *)
+	echo $"Usage: $0 {start|stop|status|restart|reload|condrestart}"
+	RETVAL=1
+	;;
+esac
+exit $RETVAL
diff --git a/REORG.TODO/nscd/nscd.service b/REORG.TODO/nscd/nscd.service
new file mode 100644
index 0000000000..ab38e8f982
--- /dev/null
+++ b/REORG.TODO/nscd/nscd.service
@@ -0,0 +1,19 @@
+# systemd service file for nscd
+
+[Unit]
+Description=Name Service Cache Daemon
+
+[Service]
+Type=forking
+ExecStart=/usr/sbin/nscd
+ExecStop=/usr/sbin/nscd --shutdown
+ExecReload=/usr/sbin/nscd -i passwd
+ExecReload=/usr/sbin/nscd -i group
+ExecReload=/usr/sbin/nscd -i hosts
+ExecReload=/usr/sbin/nscd -i services
+ExecReload=/usr/sbin/nscd -i netgroup
+Restart=always
+PIDFile=/run/nscd/nscd.pid
+
+[Install]
+WantedBy=multi-user.target
diff --git a/REORG.TODO/nscd/nscd.tmpfiles b/REORG.TODO/nscd/nscd.tmpfiles
new file mode 100644
index 0000000000..52edbba673
--- /dev/null
+++ b/REORG.TODO/nscd/nscd.tmpfiles
@@ -0,0 +1,4 @@
+# Configuration to create /run/nscd directory
+# Used as part of systemd's tmpfiles
+
+d /run/nscd 0755 root root
diff --git a/REORG.TODO/nscd/nscd_conf.c b/REORG.TODO/nscd/nscd_conf.c
new file mode 100644
index 0000000000..9c301ad73d
--- /dev/null
+++ b/REORG.TODO/nscd/nscd_conf.c
@@ -0,0 +1,315 @@
+/* Copyright (c) 1998-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Thorsten Kukuk <kukuk@suse.de>, 1998.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published
+   by the Free Software Foundation; version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
+
+#include <ctype.h>
+#include <errno.h>
+#include <error.h>
+#include <libintl.h>
+#include <malloc.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdio_ext.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/types.h>
+
+#include "dbg_log.h"
+#include "nscd.h"
+
+
+/* Names of the databases.  */
+const char *const dbnames[lastdb] =
+{
+  [pwddb] = "passwd",
+  [grpdb] = "group",
+  [hstdb] = "hosts",
+  [servdb] = "services",
+  [netgrdb] = "netgroup"
+};
+
+
+static int
+find_db (const char *name)
+{
+  for (int cnt = 0; cnt < lastdb; ++cnt)
+    if (strcmp (name, dbnames[cnt]) == 0)
+      return cnt;
+
+  error (0, 0, _("database %s is not supported"), name);
+  return -1;
+}
+
+int
+nscd_parse_file (const char *fname, struct database_dyn dbs[lastdb])
+{
+  FILE *fp;
+  char *line, *cp, *entry, *arg1, *arg2;
+  size_t len;
+  int cnt;
+  const unsigned int initial_error_message_count = error_message_count;
+
+  /* Open the configuration file.  */
+  fp = fopen (fname, "r");
+  if (fp == NULL)
+    return -1;
+
+  /* The stream is not used by more than one thread.  */
+  (void) __fsetlocking (fp, FSETLOCKING_BYCALLER);
+
+  line = NULL;
+  len = 0;
+
+  do
+    {
+      ssize_t n = getline (&line, &len, fp);
+      if (n < 0)
+	break;
+      if (line[n - 1] == '\n')
+	line[n - 1] = '\0';
+
+      /* Because the file format does not know any form of quoting we
+	 can search forward for the next '#' character and if found
+	 make it terminating the line.  */
+      *strchrnul (line, '#') = '\0';
+
+      /* If the line is blank it is ignored.  */
+      if (line[0] == '\0')
+	continue;
+
+      entry = line;
+      while (isspace (*entry) && *entry != '\0')
+	++entry;
+      cp = entry;
+      while (!isspace (*cp) && *cp != '\0')
+	++cp;
+      arg1 = cp;
+      ++arg1;
+      *cp = '\0';
+      if (strlen (entry) == 0)
+	error (0, 0, _("Parse error: %s"), line);
+      while (isspace (*arg1) && *arg1 != '\0')
+	++arg1;
+      cp = arg1;
+      while (!isspace (*cp) && *cp != '\0')
+	++cp;
+      arg2 = cp;
+      ++arg2;
+      *cp = '\0';
+      if (strlen (arg2) > 0)
+	{
+	  while (isspace (*arg2) && *arg2 != '\0')
+	    ++arg2;
+	  cp = arg2;
+	  while (!isspace (*cp) && *cp != '\0')
+	    ++cp;
+	  *cp = '\0';
+	}
+
+      if (strcmp (entry, "positive-time-to-live") == 0)
+	{
+	  int idx = find_db (arg1);
+	  if (idx >= 0)
+	    dbs[idx].postimeout = atol (arg2);
+	}
+      else if (strcmp (entry, "negative-time-to-live") == 0)
+	{
+	  int idx = find_db (arg1);
+	  if (idx >= 0)
+	    dbs[idx].negtimeout = atol (arg2);
+	}
+      else if (strcmp (entry, "suggested-size") == 0)
+	{
+	  int idx = find_db (arg1);
+	  if (idx >= 0)
+	    dbs[idx].suggested_module
+	      = atol (arg2) ?: DEFAULT_SUGGESTED_MODULE;
+	}
+      else if (strcmp (entry, "enable-cache") == 0)
+	{
+	  int idx = find_db (arg1);
+	  if (idx >= 0)
+	    {
+	      if (strcmp (arg2, "no") == 0)
+		dbs[idx].enabled = 0;
+	      else if (strcmp (arg2, "yes") == 0)
+		dbs[idx].enabled = 1;
+	    }
+	}
+      else if (strcmp (entry, "check-files") == 0)
+	{
+	  int idx = find_db (arg1);
+	  if (idx >= 0)
+	    {
+	      if (strcmp (arg2, "no") == 0)
+		dbs[idx].check_file = 0;
+	      else if (strcmp (arg2, "yes") == 0)
+		dbs[idx].check_file = 1;
+	    }
+	}
+      else if (strcmp (entry, "max-db-size") == 0)
+	{
+	  int idx = find_db (arg1);
+	  if (idx >= 0)
+	    dbs[idx].max_db_size = atol (arg2) ?: DEFAULT_MAX_DB_SIZE;
+	}
+      else if (strcmp (entry, "logfile") == 0)
+	set_logfile (arg1);
+      else if (strcmp (entry, "debug-level") == 0)
+	{
+	  int level = atoi (arg1);
+	  if (level > 0)
+	    debug_level = level;
+	}
+      else if (strcmp (entry, "threads") == 0)
+	{
+	  if (nthreads == -1)
+	    nthreads = MAX (atol (arg1), lastdb);
+	}
+      else if (strcmp (entry, "max-threads") == 0)
+	{
+	  max_nthreads = MAX (atol (arg1), lastdb);
+	}
+      else if (strcmp (entry, "server-user") == 0)
+	{
+	  if (!arg1)
+	    error (0, 0, _("Must specify user name for server-user option"));
+	  else
+	    server_user = xstrdup (arg1);
+	}
+      else if (strcmp (entry, "stat-user") == 0)
+	{
+	  if (arg1 == NULL)
+	    error (0, 0, _("Must specify user name for stat-user option"));
+	  else
+	    {
+	      stat_user = xstrdup (arg1);
+
+	      struct passwd *pw = getpwnam (stat_user);
+	      if (pw != NULL)
+		stat_uid = pw->pw_uid;
+	    }
+	}
+      else if (strcmp (entry, "persistent") == 0)
+	{
+	  int idx = find_db (arg1);
+	  if (idx >= 0)
+	    {
+	      if (strcmp (arg2, "no") == 0)
+		dbs[idx].persistent = 0;
+	      else if (strcmp (arg2, "yes") == 0)
+		dbs[idx].persistent = 1;
+	    }
+	}
+      else if (strcmp (entry, "shared") == 0)
+	{
+	  int idx = find_db (arg1);
+	  if (idx >= 0)
+	    {
+	      if (strcmp (arg2, "no") == 0)
+		dbs[idx].shared = 0;
+	      else if (strcmp (arg2, "yes") == 0)
+		dbs[idx].shared = 1;
+	    }
+	}
+      else if (strcmp (entry, "reload-count") == 0)
+	{
+	  if (strcasecmp (arg1, "unlimited") == 0)
+	    reload_count = UINT_MAX;
+	  else
+	    {
+	      unsigned long int count = strtoul (arg1, NULL, 0);
+	      if (count > UINT8_MAX - 1)
+		reload_count = UINT_MAX;
+	      else
+		reload_count = count;
+	    }
+	}
+      else if (strcmp (entry, "paranoia") == 0)
+	{
+	  if (strcmp (arg1, "no") == 0)
+	    paranoia = 0;
+	  else if (strcmp (arg1, "yes") == 0)
+	    paranoia = 1;
+	}
+      else if (strcmp (entry, "restart-interval") == 0)
+	{
+	  if (arg1 != NULL)
+	    restart_interval = atol (arg1);
+	  else
+	    error (0, 0, _("Must specify value for restart-interval option"));
+	}
+      else if (strcmp (entry, "auto-propagate") == 0)
+	{
+	  int idx = find_db (arg1);
+	  if (idx >= 0)
+	    {
+	      if (strcmp (arg2, "no") == 0)
+		dbs[idx].propagate = 0;
+	      else if (strcmp (arg2, "yes") == 0)
+		dbs[idx].propagate = 1;
+	    }
+	}
+      else
+	error (0, 0, _("Unknown option: %s %s %s"), entry, arg1, arg2);
+    }
+  while (!feof_unlocked (fp));
+
+  if (paranoia)
+    {
+      restart_time = time (NULL) + restart_interval;
+
+      /* Save the old current workding directory if we are in paranoia
+	 mode.  We have to change back to it.  */
+      oldcwd = get_current_dir_name ();
+      if (oldcwd == NULL)
+	{
+	  error (0, 0, _("\
+cannot get current working directory: %s; disabling paranoia mode"),
+		   strerror (errno));
+	  paranoia = 0;
+	}
+    }
+
+  /* Enforce sanity.  */
+  if (max_nthreads < nthreads)
+    max_nthreads = nthreads;
+
+  for (cnt = 0; cnt < lastdb; ++cnt)
+    {
+      size_t datasize = (sizeof (struct database_pers_head)
+			 + roundup (dbs[cnt].suggested_module
+				    * sizeof (ref_t), ALIGN)
+			 + (dbs[cnt].suggested_module
+			    * DEFAULT_DATASIZE_PER_BUCKET));
+      if (datasize > dbs[cnt].max_db_size)
+	{
+	  error (0, 0, _("maximum file size for %s database too small"),
+		   dbnames[cnt]);
+	  dbs[cnt].max_db_size = datasize;
+	}
+
+    }
+
+  /* Free the buffer.  */
+  free (line);
+  /* Close configuration file.  */
+  fclose (fp);
+
+  return error_message_count != initial_error_message_count;
+}
diff --git a/REORG.TODO/nscd/nscd_getai.c b/REORG.TODO/nscd/nscd_getai.c
new file mode 100644
index 0000000000..daaf6d68b2
--- /dev/null
+++ b/REORG.TODO/nscd/nscd_getai.c
@@ -0,0 +1,216 @@
+/* Copyright (C) 2004-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@redhat.com>, 2004.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <assert.h>
+#include <errno.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <not-cancel.h>
+
+#include "nscd-client.h"
+#include "nscd_proto.h"
+
+
+/* Define in nscd_gethst_r.c.  */
+extern int __nss_not_use_nscd_hosts;
+
+
+/* We use the mapping from nscd_gethst.  */
+libc_locked_map_ptr (extern, __hst_map_handle) attribute_hidden;
+
+/* Defined in nscd_gethst_r.c.  */
+extern int __nss_have_localdomain attribute_hidden;
+
+
+int
+__nscd_getai (const char *key, struct nscd_ai_result **result, int *h_errnop)
+{
+  if (__glibc_unlikely (__nss_have_localdomain >= 0))
+    {
+      if (__nss_have_localdomain == 0)
+	__nss_have_localdomain = getenv ("LOCALDOMAIN") != NULL ? 1 : -1;
+      if (__nss_have_localdomain > 0)
+	{
+	  __nss_not_use_nscd_hosts = 1;
+	  return -1;
+	}
+    }
+
+  size_t keylen = strlen (key) + 1;
+  int gc_cycle;
+  int nretries = 0;
+
+  /* If the mapping is available, try to search there instead of
+     communicating with the nscd.  */
+  struct mapped_database *mapped;
+  mapped = __nscd_get_map_ref (GETFDHST, "hosts", &__hst_map_handle,
+			       &gc_cycle);
+
+ retry:;
+  struct nscd_ai_result *resultbuf = NULL;
+  const char *recend = (const char *) ~UINTMAX_C (0);
+  char *respdata = NULL;
+  int retval = -1;
+  int sock = -1;
+  ai_response_header ai_resp;
+
+  if (mapped != NO_MAPPING)
+    {
+      struct datahead *found = __nscd_cache_search (GETAI, key, keylen,
+						    mapped, sizeof ai_resp);
+      if (found != NULL)
+	{
+	  respdata = (char *) (&found->data[0].aidata + 1);
+	  ai_resp = found->data[0].aidata;
+	  recend = (const char *) found->data + found->recsize;
+	  /* Now check if we can trust ai_resp fields.  If GC is
+	     in progress, it can contain anything.  */
+	  if (mapped->head->gc_cycle != gc_cycle)
+	    {
+	      retval = -2;
+	      goto out;
+	    }
+	}
+    }
+
+  /* If we do not have the cache mapped, try to get the data over the
+     socket.  */
+  if (respdata == NULL)
+    {
+      sock = __nscd_open_socket (key, keylen, GETAI, &ai_resp,
+				 sizeof (ai_resp));
+      if (sock == -1)
+	{
+	  /* nscd not running or wrong version.  */
+	  __nss_not_use_nscd_hosts = 1;
+	  goto out;
+	}
+    }
+
+  if (ai_resp.found == 1)
+    {
+      size_t datalen = ai_resp.naddrs + ai_resp.addrslen + ai_resp.canonlen;
+
+      /* This check really only affects the case where the data
+	 comes from the mapped cache.  */
+      if (respdata + datalen > recend)
+	{
+	  assert (sock == -1);
+	  goto out;
+	}
+
+      /* Create result.  */
+      resultbuf = (struct nscd_ai_result *) malloc (sizeof (*resultbuf)
+						    + datalen);
+      if (resultbuf == NULL)
+	{
+	  *h_errnop = NETDB_INTERNAL;
+	  goto out_close;
+	}
+
+      /* Set up the data structure, including pointers.  */
+      resultbuf->naddrs = ai_resp.naddrs;
+      resultbuf->addrs = (char *) (resultbuf + 1);
+      resultbuf->family = (uint8_t *) (resultbuf->addrs + ai_resp.addrslen);
+      if (ai_resp.canonlen != 0)
+	resultbuf->canon = (char *) (resultbuf->family + resultbuf->naddrs);
+      else
+	resultbuf->canon = NULL;
+
+      if (respdata == NULL)
+	{
+	  /* Read the data from the socket.  */
+	  if ((size_t) __readall (sock, resultbuf + 1, datalen) == datalen)
+	    {
+	      retval = 0;
+	      *result = resultbuf;
+	    }
+	  else
+	    {
+	      free (resultbuf);
+	      *h_errnop = NETDB_INTERNAL;
+	    }
+	}
+      else
+	{
+	  /* Copy the data in the block.  */
+	  memcpy (resultbuf + 1, respdata, datalen);
+
+	  /* Try to detect corrupt databases.  */
+	  if (resultbuf->canon != NULL
+	      && resultbuf->canon[ai_resp.canonlen - 1] != '\0')
+	    /* We cannot use the database.  */
+	    {
+	      if (mapped->head->gc_cycle != gc_cycle)
+		retval = -2;
+	      else
+		free (resultbuf);
+	      goto out_close;
+	    }
+
+	  retval = 0;
+	  *result = resultbuf;
+	}
+    }
+  else
+    {
+      if (__glibc_unlikely (ai_resp.found == -1))
+	{
+	  /* The daemon does not cache this database.  */
+	  __nss_not_use_nscd_hosts = 1;
+	  goto out_close;
+	}
+
+      /* Store the error number.  */
+      *h_errnop = ai_resp.error;
+
+      /* Set errno to 0 to indicate no error, just no found record.  */
+      __set_errno (0);
+      /* Even though we have not found anything, the result is zero.  */
+      retval = 0;
+    }
+
+ out_close:
+  if (sock != -1)
+    close_not_cancel_no_status (sock);
+ out:
+  if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0)
+    {
+      /* When we come here this means there has been a GC cycle while we
+	 were looking for the data.  This means the data might have been
+	 inconsistent.  Retry if possible.  */
+      if ((gc_cycle & 1) != 0 || ++nretries == 5 || retval == -1)
+	{
+	  /* nscd is just running gc now.  Disable using the mapping.  */
+	  if (atomic_decrement_val (&mapped->counter) == 0)
+	    __nscd_unmap (mapped);
+	  mapped = NO_MAPPING;
+	}
+
+      if (retval != -1)
+	{
+	  *result = NULL;
+	  free (resultbuf);
+	  goto retry;
+	}
+    }
+
+  return retval;
+}
diff --git a/REORG.TODO/nscd/nscd_getgr_r.c b/REORG.TODO/nscd/nscd_getgr_r.c
new file mode 100644
index 0000000000..87b4552197
--- /dev/null
+++ b/REORG.TODO/nscd/nscd_getgr_r.c
@@ -0,0 +1,330 @@
+/* Copyright (C) 1998-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Thorsten Kukuk <kukuk@uni-paderborn.de>, 1998.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <alloca.h>
+#include <assert.h>
+#include <errno.h>
+#include <grp.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+#include <not-cancel.h>
+#include <_itoa.h>
+#include <scratch_buffer.h>
+
+#include "nscd-client.h"
+#include "nscd_proto.h"
+
+int __nss_not_use_nscd_group;
+
+static int nscd_getgr_r (const char *key, size_t keylen, request_type type,
+			 struct group *resultbuf, char *buffer,
+			 size_t buflen, struct group **result)
+     internal_function;
+
+
+int
+__nscd_getgrnam_r (const char *name, struct group *resultbuf, char *buffer,
+		   size_t buflen, struct group **result)
+{
+  return nscd_getgr_r (name, strlen (name) + 1, GETGRBYNAME, resultbuf,
+		       buffer, buflen, result);
+}
+
+
+int
+__nscd_getgrgid_r (gid_t gid, struct group *resultbuf, char *buffer,
+		   size_t buflen, struct group **result)
+{
+  char buf[3 * sizeof (gid_t)];
+  buf[sizeof (buf) - 1] = '\0';
+  char *cp = _itoa_word (gid, buf + sizeof (buf) - 1, 10, 0);
+
+  return nscd_getgr_r (cp, buf + sizeof (buf) - cp, GETGRBYGID, resultbuf,
+		       buffer, buflen, result);
+}
+
+
+libc_locked_map_ptr (,__gr_map_handle) attribute_hidden;
+/* Note that we only free the structure if necessary.  The memory
+   mapping is not removed since it is not visible to the malloc
+   handling.  */
+libc_freeres_fn (gr_map_free)
+{
+  if (__gr_map_handle.mapped != NO_MAPPING)
+    {
+      void *p = __gr_map_handle.mapped;
+      __gr_map_handle.mapped = NO_MAPPING;
+      free (p);
+    }
+}
+
+
+static int
+internal_function
+nscd_getgr_r (const char *key, size_t keylen, request_type type,
+	      struct group *resultbuf, char *buffer, size_t buflen,
+	      struct group **result)
+{
+  int gc_cycle;
+  int nretries = 0;
+  const uint32_t *len = NULL;
+  struct scratch_buffer lenbuf;
+  scratch_buffer_init (&lenbuf);
+
+  /* If the mapping is available, try to search there instead of
+     communicating with the nscd.  */
+  struct mapped_database *mapped = __nscd_get_map_ref (GETFDGR, "group",
+						       &__gr_map_handle,
+						       &gc_cycle);
+ retry:;
+  const char *gr_name = NULL;
+  size_t gr_name_len = 0;
+  int retval = -1;
+  const char *recend = (const char *) ~UINTMAX_C (0);
+  gr_response_header gr_resp;
+
+  if (mapped != NO_MAPPING)
+    {
+      struct datahead *found = __nscd_cache_search (type, key, keylen, mapped,
+						    sizeof gr_resp);
+      if (found != NULL)
+	{
+	  len = (const uint32_t *) (&found->data[0].grdata + 1);
+	  gr_resp = found->data[0].grdata;
+	  gr_name = ((const char *) len
+		     + gr_resp.gr_mem_cnt * sizeof (uint32_t));
+	  gr_name_len = gr_resp.gr_name_len + gr_resp.gr_passwd_len;
+	  recend = (const char *) found->data + found->recsize;
+	  /* Now check if we can trust gr_resp fields.  If GC is
+	     in progress, it can contain anything.  */
+	  if (mapped->head->gc_cycle != gc_cycle)
+	    {
+	      retval = -2;
+	      goto out;
+	    }
+
+	  /* The alignment is always sufficient, unless GC is in progress.  */
+	  assert (((uintptr_t) len & (__alignof__ (*len) - 1)) == 0);
+	}
+    }
+
+  int sock = -1;
+  if (gr_name == NULL)
+    {
+      sock = __nscd_open_socket (key, keylen, type, &gr_resp,
+				 sizeof (gr_resp));
+      if (sock == -1)
+	{
+	  __nss_not_use_nscd_group = 1;
+	  goto out;
+	}
+    }
+
+  /* No value found so far.  */
+  *result = NULL;
+
+  if (__glibc_unlikely (gr_resp.found == -1))
+    {
+      /* The daemon does not cache this database.  */
+      __nss_not_use_nscd_group = 1;
+      goto out_close;
+    }
+
+  if (gr_resp.found == 1)
+    {
+      struct iovec vec[2];
+      char *p = buffer;
+      size_t total_len;
+      uintptr_t align;
+      nscd_ssize_t cnt;
+
+      /* Now allocate the buffer the array for the group members.  We must
+	 align the pointer.  */
+      align = ((__alignof__ (char *) - (p - ((char *) 0)))
+	       & (__alignof__ (char *) - 1));
+      total_len = (align + (1 + gr_resp.gr_mem_cnt) * sizeof (char *)
+		   + gr_resp.gr_name_len + gr_resp.gr_passwd_len);
+      if (__glibc_unlikely (buflen < total_len))
+	{
+	no_room:
+	  __set_errno (ERANGE);
+	  retval = ERANGE;
+	  goto out_close;
+	}
+      buflen -= total_len;
+
+      p += align;
+      resultbuf->gr_mem = (char **) p;
+      p += (1 + gr_resp.gr_mem_cnt) * sizeof (char *);
+
+      /* Set pointers for strings.  */
+      resultbuf->gr_name = p;
+      p += gr_resp.gr_name_len;
+      resultbuf->gr_passwd = p;
+      p += gr_resp.gr_passwd_len;
+
+      /* Fill in what we know now.  */
+      resultbuf->gr_gid = gr_resp.gr_gid;
+
+      /* Read the length information, group name, and password.  */
+      if (gr_name == NULL)
+	{
+	  /* Handle a simple, usual case: no group members.  */
+	  if (__glibc_likely (gr_resp.gr_mem_cnt == 0))
+	    {
+	      size_t n = gr_resp.gr_name_len + gr_resp.gr_passwd_len;
+	      if (__builtin_expect (__readall (sock, resultbuf->gr_name, n)
+				    != (ssize_t) n, 0))
+		goto out_close;
+	    }
+	  else
+	    {
+	      /* Allocate array to store lengths.  */
+	      if (!scratch_buffer_set_array_size
+		  (&lenbuf, gr_resp.gr_mem_cnt, sizeof (uint32_t)))
+		goto out_close;
+	      len = lenbuf.data;
+
+	      vec[0].iov_base = (void *) len;
+	      vec[0].iov_len = gr_resp.gr_mem_cnt * sizeof (uint32_t);
+	      vec[1].iov_base = resultbuf->gr_name;
+	      vec[1].iov_len = gr_resp.gr_name_len + gr_resp.gr_passwd_len;
+	      total_len = vec[0].iov_len + vec[1].iov_len;
+
+	      /* Get this data.  */
+	      size_t n = __readvall (sock, vec, 2);
+	      if (__glibc_unlikely (n != total_len))
+		goto out_close;
+	    }
+	}
+      else
+	/* We already have the data.  Just copy the group name and
+	   password.  */
+	memcpy (resultbuf->gr_name, gr_name,
+		gr_resp.gr_name_len + gr_resp.gr_passwd_len);
+
+      /* Clear the terminating entry.  */
+      resultbuf->gr_mem[gr_resp.gr_mem_cnt] = NULL;
+
+      /* Prepare reading the group members.  */
+      total_len = 0;
+      for (cnt = 0; cnt < gr_resp.gr_mem_cnt; ++cnt)
+	{
+	  resultbuf->gr_mem[cnt] = p;
+	  total_len += len[cnt];
+	  p += len[cnt];
+	}
+
+      if (__glibc_unlikely (gr_name + gr_name_len + total_len > recend))
+	{
+	  /* len array might contain garbage during nscd GC cycle,
+	     retry rather than fail in that case.  */
+	  if (gr_name != NULL && mapped->head->gc_cycle != gc_cycle)
+	    retval = -2;
+	  goto out_close;
+	}
+      if (__glibc_unlikely (total_len > buflen))
+	{
+	  /* len array might contain garbage during nscd GC cycle,
+	     retry rather than fail in that case.  */
+	  if (gr_name != NULL && mapped->head->gc_cycle != gc_cycle)
+	    {
+	      retval = -2;
+	      goto out_close;
+	    }
+	  else
+	    goto no_room;
+	}
+
+      retval = 0;
+
+      /* If there are no group members TOTAL_LEN is zero.  */
+      if (gr_name == NULL)
+	{
+	  if (total_len > 0
+	      && __builtin_expect (__readall (sock, resultbuf->gr_mem[0],
+					      total_len) != total_len, 0))
+	    {
+	      /* The `errno' to some value != ERANGE.  */
+	      __set_errno (ENOENT);
+	      retval = ENOENT;
+	    }
+	  else
+	    *result = resultbuf;
+	}
+      else
+	{
+	  /* Copy the group member names.  */
+	  memcpy (resultbuf->gr_mem[0], gr_name + gr_name_len, total_len);
+
+	  /* Try to detect corrupt databases.  */
+	  if (resultbuf->gr_name[gr_name_len - 1] != '\0'
+	      || resultbuf->gr_passwd[gr_resp.gr_passwd_len - 1] != '\0'
+	      || ({for (cnt = 0; cnt < gr_resp.gr_mem_cnt; ++cnt)
+		    if (resultbuf->gr_mem[cnt][len[cnt] - 1] != '\0')
+		      break;
+		  cnt < gr_resp.gr_mem_cnt; }))
+	    {
+	      /* We cannot use the database.  */
+	      retval = mapped->head->gc_cycle != gc_cycle ? -2 : -1;
+	      goto out_close;
+	    }
+
+	  *result = resultbuf;
+	}
+    }
+  else
+    {
+      /* Set errno to 0 to indicate no error, just no found record.  */
+      __set_errno (0);
+      /* Even though we have not found anything, the result is zero.  */
+      retval = 0;
+    }
+
+ out_close:
+  if (sock != -1)
+    close_not_cancel_no_status (sock);
+ out:
+  if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0)
+    {
+      /* When we come here this means there has been a GC cycle while we
+	 were looking for the data.  This means the data might have been
+	 inconsistent.  Retry if possible.  */
+      if ((gc_cycle & 1) != 0 || ++nretries == 5 || retval == -1)
+	{
+	  /* nscd is just running gc now.  Disable using the mapping.  */
+	  if (atomic_decrement_val (&mapped->counter) == 0)
+	    __nscd_unmap (mapped);
+	  mapped = NO_MAPPING;
+	}
+
+      if (retval != -1)
+	goto retry;
+    }
+
+  scratch_buffer_free (&lenbuf);
+
+  return retval;
+}
diff --git a/REORG.TODO/nscd/nscd_gethst_r.c b/REORG.TODO/nscd/nscd_gethst_r.c
new file mode 100644
index 0000000000..daa708b3d3
--- /dev/null
+++ b/REORG.TODO/nscd/nscd_gethst_r.c
@@ -0,0 +1,459 @@
+/* Copyright (C) 1998-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <errno.h>
+#include <resolv/resolv-internal.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <arpa/nameser.h>
+#include <not-cancel.h>
+
+#include "nscd-client.h"
+#include "nscd_proto.h"
+
+int __nss_not_use_nscd_hosts;
+
+static int nscd_gethst_r (const char *key, size_t keylen, request_type type,
+			  struct hostent *resultbuf, char *buffer,
+			  size_t buflen, struct hostent **result,
+			  int *h_errnop) internal_function;
+
+
+int
+__nscd_gethostbyname_r (const char *name, struct hostent *resultbuf,
+			char *buffer, size_t buflen, struct hostent **result,
+			int *h_errnop)
+{
+  request_type reqtype;
+
+  reqtype = res_use_inet6 () ? GETHOSTBYNAMEv6 : GETHOSTBYNAME;
+
+  return nscd_gethst_r (name, strlen (name) + 1, reqtype, resultbuf,
+			buffer, buflen, result, h_errnop);
+}
+
+
+int
+__nscd_gethostbyname2_r (const char *name, int af, struct hostent *resultbuf,
+			 char *buffer, size_t buflen, struct hostent **result,
+			 int *h_errnop)
+{
+  request_type reqtype;
+
+  reqtype = af == AF_INET6 ? GETHOSTBYNAMEv6 : GETHOSTBYNAME;
+
+  return nscd_gethst_r (name, strlen (name) + 1, reqtype, resultbuf,
+			buffer, buflen, result, h_errnop);
+}
+
+
+int
+__nscd_gethostbyaddr_r (const void *addr, socklen_t len, int type,
+			struct hostent *resultbuf, char *buffer, size_t buflen,
+			struct hostent **result, int *h_errnop)
+{
+  request_type reqtype;
+
+  if (!((len == INADDRSZ && type == AF_INET)
+	|| (len == IN6ADDRSZ && type == AF_INET6)))
+    /* LEN and TYPE do not match.  */
+    return -1;
+
+  reqtype = type == AF_INET6 ? GETHOSTBYADDRv6 : GETHOSTBYADDR;
+
+  return nscd_gethst_r (addr, len, reqtype, resultbuf, buffer, buflen, result,
+			h_errnop);
+}
+
+
+libc_locked_map_ptr (, __hst_map_handle) attribute_hidden;
+/* Note that we only free the structure if necessary.  The memory
+   mapping is not removed since it is not visible to the malloc
+   handling.  */
+libc_freeres_fn (hst_map_free)
+{
+  if (__hst_map_handle.mapped != NO_MAPPING)
+    {
+      void *p = __hst_map_handle.mapped;
+      __hst_map_handle.mapped = NO_MAPPING;
+      free (p);
+    }
+}
+
+
+uint32_t
+__nscd_get_nl_timestamp (void)
+{
+  uint32_t retval;
+  if (__nss_not_use_nscd_hosts != 0)
+    return 0;
+
+  /* __nscd_get_mapping can change hst_map_handle.mapped to NO_MAPPING.
+   However, __nscd_get_mapping assumes the prior value was not NO_MAPPING.
+   Thus we have to acquire the lock to prevent this thread from changing
+   hst_map_handle.mapped to NO_MAPPING while another thread is inside
+    __nscd_get_mapping.  */
+  if (!__nscd_acquire_maplock (&__hst_map_handle))
+    return 0;
+
+  struct mapped_database *map = __hst_map_handle.mapped;
+
+  if (map == NULL
+      || (map != NO_MAPPING
+	  && map->head->nscd_certainly_running == 0
+	  && map->head->timestamp + MAPPING_TIMEOUT < time (NULL)))
+    map = __nscd_get_mapping (GETFDHST, "hosts", &__hst_map_handle.mapped);
+
+  if (map == NO_MAPPING)
+    retval = 0;
+  else
+    retval = map->head->extra_data[NSCD_HST_IDX_CONF_TIMESTAMP];
+
+  /* Release the lock.  */
+  __hst_map_handle.lock = 0;
+
+  return retval;
+}
+
+
+int __nss_have_localdomain attribute_hidden;
+
+static int
+internal_function
+nscd_gethst_r (const char *key, size_t keylen, request_type type,
+	       struct hostent *resultbuf, char *buffer, size_t buflen,
+	       struct hostent **result, int *h_errnop)
+{
+  if (__glibc_unlikely (__nss_have_localdomain >= 0))
+    {
+      if (__nss_have_localdomain == 0)
+	__nss_have_localdomain = getenv ("LOCALDOMAIN") != NULL ? 1 : -1;
+      if (__nss_have_localdomain > 0)
+	{
+	  __nss_not_use_nscd_hosts = 1;
+	  return -1;
+	}
+    }
+
+  int gc_cycle;
+  int nretries = 0;
+
+  /* If the mapping is available, try to search there instead of
+     communicating with the nscd.  */
+  struct mapped_database *mapped;
+  mapped = __nscd_get_map_ref (GETFDHST, "hosts", &__hst_map_handle,
+			       &gc_cycle);
+
+ retry:;
+  const char *h_name = NULL;
+  const uint32_t *aliases_len = NULL;
+  const char *addr_list = NULL;
+  size_t addr_list_len = 0;
+  int retval = -1;
+  const char *recend = (const char *) ~UINTMAX_C (0);
+  int sock = -1;
+  hst_response_header hst_resp;
+  if (mapped != NO_MAPPING)
+    {
+      /* No const qualifier, as it can change during garbage collection.  */
+      struct datahead *found = __nscd_cache_search (type, key, keylen, mapped,
+						    sizeof hst_resp);
+      if (found != NULL)
+	{
+	  h_name = (char *) (&found->data[0].hstdata + 1);
+	  hst_resp = found->data[0].hstdata;
+	  aliases_len = (uint32_t *) (h_name + hst_resp.h_name_len);
+	  addr_list = ((char *) aliases_len
+		       + hst_resp.h_aliases_cnt * sizeof (uint32_t));
+	  addr_list_len = hst_resp.h_addr_list_cnt * INADDRSZ;
+	  recend = (const char *) found->data + found->recsize;
+	  /* Now check if we can trust hst_resp fields.  If GC is
+	     in progress, it can contain anything.  */
+	  if (mapped->head->gc_cycle != gc_cycle)
+	    {
+	      retval = -2;
+	      goto out;
+	    }
+
+#if !_STRING_ARCH_unaligned
+	  /* The aliases_len array in the mapped database might very
+	     well be unaligned.  We will access it word-wise so on
+	     platforms which do not tolerate unaligned accesses we
+	     need to make an aligned copy.  */
+	  if (((uintptr_t) aliases_len & (__alignof__ (*aliases_len) - 1))
+	      != 0)
+	    {
+	      uint32_t *tmp = alloca (hst_resp.h_aliases_cnt
+				      * sizeof (uint32_t));
+	      aliases_len = memcpy (tmp, aliases_len,
+				    hst_resp.h_aliases_cnt
+				    * sizeof (uint32_t));
+	    }
+#endif
+	  if (type != GETHOSTBYADDR && type != GETHOSTBYNAME)
+	    {
+	      if (hst_resp.h_length == INADDRSZ)
+		addr_list += addr_list_len;
+	      addr_list_len = hst_resp.h_addr_list_cnt * IN6ADDRSZ;
+	    }
+	  if (__builtin_expect ((const char *) addr_list + addr_list_len
+				> recend, 0))
+	    goto out;
+	}
+    }
+
+  if (h_name == NULL)
+    {
+      sock = __nscd_open_socket (key, keylen, type, &hst_resp,
+				 sizeof (hst_resp));
+      if (sock == -1)
+	{
+	  __nss_not_use_nscd_hosts = 1;
+	  goto out;
+	}
+    }
+
+  /* No value found so far.  */
+  *result = NULL;
+
+  if (__glibc_unlikely (hst_resp.found == -1))
+    {
+      /* The daemon does not cache this database.  */
+      __nss_not_use_nscd_hosts = 1;
+      goto out_close;
+    }
+
+  if (hst_resp.found == 1)
+    {
+      char *cp = buffer;
+      uintptr_t align1;
+      uintptr_t align2;
+      size_t total_len;
+      ssize_t cnt;
+      char *ignore;
+      int n;
+
+      /* A first check whether the buffer is sufficiently large is possible.  */
+      /* Now allocate the buffer the array for the group members.  We must
+	 align the pointer and the base of the h_addr_list pointers.  */
+      align1 = ((__alignof__ (char *) - (cp - ((char *) 0)))
+		& (__alignof__ (char *) - 1));
+      align2 = ((__alignof__ (char *) - ((cp + align1 + hst_resp.h_name_len)
+					 - ((char *) 0)))
+		& (__alignof__ (char *) - 1));
+      if (buflen < (align1 + hst_resp.h_name_len + align2
+		    + ((hst_resp.h_aliases_cnt + hst_resp.h_addr_list_cnt
+			+ 2)
+		       * sizeof (char *))
+		    + hst_resp.h_addr_list_cnt * (type == AF_INET
+						  ? INADDRSZ : IN6ADDRSZ)))
+	{
+	no_room:
+	  *h_errnop = NETDB_INTERNAL;
+	  __set_errno (ERANGE);
+	  retval = ERANGE;
+	  goto out_close;
+	}
+      cp += align1;
+
+      /* Prepare the result as far as we can.  */
+      resultbuf->h_aliases = (char **) cp;
+      cp += (hst_resp.h_aliases_cnt + 1) * sizeof (char *);
+      resultbuf->h_addr_list = (char **) cp;
+      cp += (hst_resp.h_addr_list_cnt + 1) * sizeof (char *);
+
+      resultbuf->h_name = cp;
+      cp += hst_resp.h_name_len + align2;
+
+      if (type == GETHOSTBYADDR || type == GETHOSTBYNAME)
+	{
+	  resultbuf->h_addrtype = AF_INET;
+	  resultbuf->h_length = INADDRSZ;
+	}
+      else
+	{
+	  resultbuf->h_addrtype = AF_INET6;
+	  resultbuf->h_length = IN6ADDRSZ;
+	}
+      for (cnt = 0; cnt < hst_resp.h_addr_list_cnt; ++cnt)
+	{
+	  resultbuf->h_addr_list[cnt] = cp;
+	  cp += resultbuf->h_length;
+	}
+      resultbuf->h_addr_list[cnt] = NULL;
+
+      if (h_name == NULL)
+	{
+	  struct iovec vec[4];
+
+	  vec[0].iov_base = resultbuf->h_name;
+	  vec[0].iov_len = hst_resp.h_name_len;
+	  total_len = hst_resp.h_name_len;
+	  n = 1;
+
+	  if (hst_resp.h_aliases_cnt > 0)
+	    {
+	      aliases_len = alloca (hst_resp.h_aliases_cnt
+				    * sizeof (uint32_t));
+	      vec[n].iov_base = (void *) aliases_len;
+	      vec[n].iov_len = hst_resp.h_aliases_cnt * sizeof (uint32_t);
+
+	      total_len += hst_resp.h_aliases_cnt * sizeof (uint32_t);
+	      ++n;
+	    }
+
+	  if (type == GETHOSTBYADDR || type == GETHOSTBYNAME)
+	    {
+	      vec[n].iov_base = resultbuf->h_addr_list[0];
+	      vec[n].iov_len = hst_resp.h_addr_list_cnt * INADDRSZ;
+
+	      total_len += hst_resp.h_addr_list_cnt * INADDRSZ;
+
+	      ++n;
+	    }
+	  else
+	    {
+	      if (hst_resp.h_length == INADDRSZ)
+		{
+		  ignore = alloca (hst_resp.h_addr_list_cnt * INADDRSZ);
+		  vec[n].iov_base = ignore;
+		  vec[n].iov_len = hst_resp.h_addr_list_cnt * INADDRSZ;
+
+		  total_len += hst_resp.h_addr_list_cnt * INADDRSZ;
+
+		  ++n;
+		}
+
+	      vec[n].iov_base = resultbuf->h_addr_list[0];
+	      vec[n].iov_len = hst_resp.h_addr_list_cnt * IN6ADDRSZ;
+
+	      total_len += hst_resp.h_addr_list_cnt * IN6ADDRSZ;
+
+	      ++n;
+	    }
+
+	  if ((size_t) __readvall (sock, vec, n) != total_len)
+	    goto out_close;
+	}
+      else
+	{
+	  memcpy (resultbuf->h_name, h_name, hst_resp.h_name_len);
+	  memcpy (resultbuf->h_addr_list[0], addr_list, addr_list_len);
+	}
+
+      /*  Now we also can read the aliases.  */
+      total_len = 0;
+      for (cnt = 0; cnt < hst_resp.h_aliases_cnt; ++cnt)
+	{
+	  resultbuf->h_aliases[cnt] = cp;
+	  cp += aliases_len[cnt];
+	  total_len += aliases_len[cnt];
+	}
+      resultbuf->h_aliases[cnt] = NULL;
+
+      if (__builtin_expect ((const char *) addr_list + addr_list_len
+			    + total_len > recend, 0))
+	{
+	  /* aliases_len array might contain garbage during nscd GC cycle,
+	     retry rather than fail in that case.  */
+	  if (addr_list != NULL && mapped->head->gc_cycle != gc_cycle)
+	    retval = -2;
+	  goto out_close;
+	}
+      /* See whether this would exceed the buffer capacity.  */
+      if (__glibc_unlikely (cp > buffer + buflen))
+	{
+	  /* aliases_len array might contain garbage during nscd GC cycle,
+	     retry rather than fail in that case.  */
+	  if (addr_list != NULL && mapped->head->gc_cycle != gc_cycle)
+	    {
+	      retval = -2;
+	      goto out_close;
+	    }
+	  goto no_room;
+	}
+
+      /* And finally read the aliases.  */
+      if (addr_list == NULL)
+	{
+	  if (total_len == 0
+	      || ((size_t) __readall (sock, resultbuf->h_aliases[0], total_len)
+		  == total_len))
+	    {
+	      retval = 0;
+	      *result = resultbuf;
+	    }
+	}
+      else
+	{
+	  memcpy (resultbuf->h_aliases[0],
+		  (const char *) addr_list + addr_list_len, total_len);
+
+	  /* Try to detect corrupt databases.  */
+	  if (resultbuf->h_name[hst_resp.h_name_len - 1] != '\0'
+	      || ({for (cnt = 0; cnt < hst_resp.h_aliases_cnt; ++cnt)
+		     if (resultbuf->h_aliases[cnt][aliases_len[cnt] - 1]
+			 != '\0')
+		       break;
+		   cnt < hst_resp.h_aliases_cnt; }))
+	    {
+	      /* We cannot use the database.  */
+	      if (mapped->head->gc_cycle != gc_cycle)
+		retval = -2;
+	      goto out_close;
+	    }
+
+	  retval = 0;
+	  *result = resultbuf;
+	}
+    }
+  else
+    {
+      /* Store the error number.  */
+      *h_errnop = hst_resp.error;
+
+      /* Set errno to 0 to indicate no error, just no found record.  */
+      __set_errno (0);
+      /* Even though we have not found anything, the result is zero.  */
+      retval = 0;
+    }
+
+ out_close:
+  if (sock != -1)
+    close_not_cancel_no_status (sock);
+ out:
+  if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0)
+    {
+      /* When we come here this means there has been a GC cycle while we
+	 were looking for the data.  This means the data might have been
+	 inconsistent.  Retry if possible.  */
+      if ((gc_cycle & 1) != 0 || ++nretries == 5 || retval == -1)
+	{
+	  /* nscd is just running gc now.  Disable using the mapping.  */
+	  if (atomic_decrement_val (&mapped->counter) == 0)
+	    __nscd_unmap (mapped);
+	  mapped = NO_MAPPING;
+	}
+
+      if (retval != -1)
+	goto retry;
+    }
+
+  return retval;
+}
diff --git a/REORG.TODO/nscd/nscd_getpw_r.c b/REORG.TODO/nscd/nscd_getpw_r.c
new file mode 100644
index 0000000000..b291d2fa44
--- /dev/null
+++ b/REORG.TODO/nscd/nscd_getpw_r.c
@@ -0,0 +1,241 @@
+/* Copyright (C) 1998-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Thorsten Kukuk <kukuk@uni-paderborn.de>, 1998.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <assert.h>
+#include <errno.h>
+#include <pwd.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+#include <not-cancel.h>
+#include <_itoa.h>
+
+#include "nscd-client.h"
+#include "nscd_proto.h"
+
+int __nss_not_use_nscd_passwd;
+
+static int nscd_getpw_r (const char *key, size_t keylen, request_type type,
+			 struct passwd *resultbuf, char *buffer,
+			 size_t buflen, struct passwd **result)
+     internal_function;
+
+int
+__nscd_getpwnam_r (const char *name, struct passwd *resultbuf, char *buffer,
+		   size_t buflen, struct passwd **result)
+{
+  if (name == NULL)
+    return -1;
+
+  return nscd_getpw_r (name, strlen (name) + 1, GETPWBYNAME, resultbuf,
+		       buffer, buflen, result);
+}
+
+int
+__nscd_getpwuid_r (uid_t uid, struct passwd *resultbuf, char *buffer,
+		   size_t buflen, struct passwd **result)
+{
+  char buf[3 * sizeof (uid_t)];
+  buf[sizeof (buf) - 1] = '\0';
+  char *cp = _itoa_word (uid, buf + sizeof (buf) - 1, 10, 0);
+
+  return nscd_getpw_r (cp, buf + sizeof (buf) - cp, GETPWBYUID, resultbuf,
+		       buffer, buflen, result);
+}
+
+
+libc_locked_map_ptr (static, map_handle);
+/* Note that we only free the structure if necessary.  The memory
+   mapping is not removed since it is not visible to the malloc
+   handling.  */
+libc_freeres_fn (pw_map_free)
+{
+  if (map_handle.mapped != NO_MAPPING)
+    {
+      void *p = map_handle.mapped;
+      map_handle.mapped = NO_MAPPING;
+      free (p);
+    }
+}
+
+
+static int
+internal_function
+nscd_getpw_r (const char *key, size_t keylen, request_type type,
+	      struct passwd *resultbuf, char *buffer, size_t buflen,
+	      struct passwd **result)
+{
+  int gc_cycle;
+  int nretries = 0;
+
+  /* If the mapping is available, try to search there instead of
+     communicating with the nscd.  */
+  struct mapped_database *mapped;
+  mapped = __nscd_get_map_ref (GETFDPW, "passwd", &map_handle, &gc_cycle);
+
+ retry:;
+  const char *pw_name = NULL;
+  int retval = -1;
+  const char *recend = (const char *) ~UINTMAX_C (0);
+  pw_response_header pw_resp;
+
+  if (mapped != NO_MAPPING)
+    {
+      struct datahead *found = __nscd_cache_search (type, key, keylen, mapped,
+						    sizeof pw_resp);
+      if (found != NULL)
+	{
+	  pw_name = (const char *) (&found->data[0].pwdata + 1);
+	  pw_resp = found->data[0].pwdata;
+	  recend = (const char *) found->data + found->recsize;
+	  /* Now check if we can trust pw_resp fields.  If GC is
+	     in progress, it can contain anything.  */
+	  if (mapped->head->gc_cycle != gc_cycle)
+	    {
+	      retval = -2;
+	      goto out;
+	    }
+	}
+    }
+
+  int sock = -1;
+  if (pw_name == NULL)
+    {
+      sock = __nscd_open_socket (key, keylen, type, &pw_resp,
+				 sizeof (pw_resp));
+      if (sock == -1)
+	{
+	  __nss_not_use_nscd_passwd = 1;
+	  goto out;
+	}
+    }
+
+  /* No value found so far.  */
+  *result = NULL;
+
+  if (__glibc_unlikely (pw_resp.found == -1))
+    {
+      /* The daemon does not cache this database.  */
+      __nss_not_use_nscd_passwd = 1;
+      goto out_close;
+    }
+
+  if (pw_resp.found == 1)
+    {
+      /* Set the information we already have.  */
+      resultbuf->pw_uid = pw_resp.pw_uid;
+      resultbuf->pw_gid = pw_resp.pw_gid;
+
+      char *p = buffer;
+      /* get pw_name */
+      resultbuf->pw_name = p;
+      p += pw_resp.pw_name_len;
+      /* get pw_passwd */
+      resultbuf->pw_passwd = p;
+      p += pw_resp.pw_passwd_len;
+      /* get pw_gecos */
+      resultbuf->pw_gecos = p;
+      p += pw_resp.pw_gecos_len;
+      /* get pw_dir */
+      resultbuf->pw_dir = p;
+      p += pw_resp.pw_dir_len;
+      /* get pw_pshell */
+      resultbuf->pw_shell = p;
+      p += pw_resp.pw_shell_len;
+
+      ssize_t total = p - buffer;
+      if (__glibc_unlikely (pw_name + total > recend))
+	goto out_close;
+      if (__glibc_unlikely (buflen < total))
+	{
+	  __set_errno (ERANGE);
+	  retval = ERANGE;
+	  goto out_close;
+	}
+
+      retval = 0;
+      if (pw_name == NULL)
+	{
+	  ssize_t nbytes = __readall (sock, buffer, total);
+
+	  if (__glibc_unlikely (nbytes != total))
+	    {
+	      /* The `errno' to some value != ERANGE.  */
+	      __set_errno (ENOENT);
+	      retval = ENOENT;
+	    }
+	  else
+	    *result = resultbuf;
+	}
+      else
+	{
+	  /* Copy the various strings.  */
+	  memcpy (resultbuf->pw_name, pw_name, total);
+
+	  /* Try to detect corrupt databases.  */
+	  if (resultbuf->pw_name[pw_resp.pw_name_len - 1] != '\0'
+	      || resultbuf->pw_passwd[pw_resp.pw_passwd_len - 1] != '\0'
+	      || resultbuf->pw_gecos[pw_resp.pw_gecos_len - 1] != '\0'
+	      || resultbuf->pw_dir[pw_resp.pw_dir_len - 1] != '\0'
+	      || resultbuf->pw_shell[pw_resp.pw_shell_len - 1] != '\0')
+	    {
+	      /* We cannot use the database.  */
+	      retval = mapped->head->gc_cycle != gc_cycle ? -2 : -1;
+	      goto out_close;
+	    }
+
+	  *result = resultbuf;
+	}
+    }
+  else
+    {
+      /* Set errno to 0 to indicate no error, just no found record.  */
+      __set_errno (0);
+      /* Even though we have not found anything, the result is zero.  */
+      retval = 0;
+    }
+
+ out_close:
+  if (sock != -1)
+    close_not_cancel_no_status (sock);
+ out:
+  if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0)
+    {
+      /* When we come here this means there has been a GC cycle while we
+	 were looking for the data.  This means the data might have been
+	 inconsistent.  Retry if possible.  */
+      if ((gc_cycle & 1) != 0 || ++nretries == 5 || retval == -1)
+	{
+	  /* nscd is just running gc now.  Disable using the mapping.  */
+	  if (atomic_decrement_val (&mapped->counter) == 0)
+	    __nscd_unmap (mapped);
+	  mapped = NO_MAPPING;
+	}
+
+      if (retval != -1)
+	goto retry;
+    }
+
+  return retval;
+}
diff --git a/REORG.TODO/nscd/nscd_getserv_r.c b/REORG.TODO/nscd/nscd_getserv_r.c
new file mode 100644
index 0000000000..7dfb1c828d
--- /dev/null
+++ b/REORG.TODO/nscd/nscd_getserv_r.c
@@ -0,0 +1,388 @@
+/* Copyright (C) 2007-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@redhat.com>, 2007.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <not-cancel.h>
+#include <_itoa.h>
+#include <stdint.h>
+
+#include "nscd-client.h"
+#include "nscd_proto.h"
+
+
+int __nss_not_use_nscd_services;
+
+
+static int nscd_getserv_r (const char *crit, size_t critlen, const char *proto,
+			   request_type type, struct servent *resultbuf,
+			   char *buf, size_t buflen, struct servent **result);
+
+
+int
+__nscd_getservbyname_r (const char *name, const char *proto,
+			struct servent *result_buf, char *buf, size_t buflen,
+			struct servent **result)
+{
+  return nscd_getserv_r (name, strlen (name), proto, GETSERVBYNAME, result_buf,
+			 buf, buflen, result);
+}
+
+
+int
+__nscd_getservbyport_r (int port, const char *proto,
+			struct servent *result_buf, char *buf, size_t buflen,
+			struct servent **result)
+{
+  char portstr[3 * sizeof (int) + 2];
+  portstr[sizeof (portstr) - 1] = '\0';
+  char *cp = _itoa_word (port, portstr + sizeof (portstr) - 1, 10, 0);
+
+  return nscd_getserv_r (cp, portstr + sizeof (portstr) - 1 - cp, proto,
+			 GETSERVBYPORT, result_buf, buf, buflen, result);
+}
+
+
+libc_locked_map_ptr (, __serv_map_handle) attribute_hidden;
+/* Note that we only free the structure if necessary.  The memory
+   mapping is not removed since it is not visible to the malloc
+   handling.  */
+libc_freeres_fn (serv_map_free)
+{
+  if (__serv_map_handle.mapped != NO_MAPPING)
+    {
+      void *p = __serv_map_handle.mapped;
+      __serv_map_handle.mapped = NO_MAPPING;
+      free (p);
+    }
+}
+
+
+static int
+nscd_getserv_r (const char *crit, size_t critlen, const char *proto,
+		request_type type, struct servent *resultbuf,
+		char *buf, size_t buflen, struct servent **result)
+{
+  int gc_cycle;
+  int nretries = 0;
+  size_t alloca_used = 0;
+
+  /* If the mapping is available, try to search there instead of
+     communicating with the nscd.  */
+  struct mapped_database *mapped;
+  mapped = __nscd_get_map_ref (GETFDSERV, "services", &__serv_map_handle,
+			       &gc_cycle);
+  size_t protolen = proto == NULL ? 0 : strlen (proto);
+  size_t keylen = critlen + 1 + protolen + 1;
+  int alloca_key = __libc_use_alloca (keylen);
+  char *key;
+  if (alloca_key)
+    key = alloca_account (keylen, alloca_used);
+  else
+    {
+      key = malloc (keylen);
+      if (key == NULL)
+	return -1;
+    }
+  memcpy (__mempcpy (__mempcpy (key, crit, critlen),
+		     "/", 1), proto ?: "", protolen + 1);
+
+ retry:;
+  const char *s_name = NULL;
+  const char *s_proto = NULL;
+  int alloca_aliases_len = 0;
+  const uint32_t *aliases_len = NULL;
+  const char *aliases_list = NULL;
+  int retval = -1;
+  const char *recend = (const char *) ~UINTMAX_C (0);
+  int sock = -1;
+  serv_response_header serv_resp;
+
+  if (mapped != NO_MAPPING)
+    {
+      struct datahead *found = __nscd_cache_search (type, key, keylen, mapped,
+						    sizeof serv_resp);
+
+      if (found != NULL)
+	{
+	  s_name = (char *) (&found->data[0].servdata + 1);
+	  serv_resp = found->data[0].servdata;
+	  s_proto = s_name + serv_resp.s_name_len;
+	  alloca_aliases_len = 1;
+	  aliases_len = (uint32_t *) (s_proto + serv_resp.s_proto_len);
+	  aliases_list = ((char *) aliases_len
+			  + serv_resp.s_aliases_cnt * sizeof (uint32_t));
+	  recend = (const char *) found->data + found->recsize;
+	  /* Now check if we can trust serv_resp fields.  If GC is
+	     in progress, it can contain anything.  */
+	  if (mapped->head->gc_cycle != gc_cycle)
+	    {
+	      retval = -2;
+	      goto out;
+	    }
+	  if (__builtin_expect ((const char *) aliases_len
+				+ serv_resp.s_aliases_cnt * sizeof (uint32_t)
+				> recend, 0))
+	    goto out;
+
+#if !_STRING_ARCH_unaligned
+	  /* The aliases_len array in the mapped database might very
+	     well be unaligned.  We will access it word-wise so on
+	     platforms which do not tolerate unaligned accesses we
+	     need to make an aligned copy.  */
+	  if (((uintptr_t) aliases_len & (__alignof__ (*aliases_len) - 1))
+	      != 0)
+	    {
+	      uint32_t *tmp;
+	      alloca_aliases_len
+		= __libc_use_alloca (alloca_used
+				     + (serv_resp.s_aliases_cnt
+					* sizeof (uint32_t)));
+	      if (alloca_aliases_len)
+		tmp = alloca_account (serv_resp.s_aliases_cnt
+				      * sizeof (uint32_t),
+				      alloca_used);
+	      else
+		{
+		  tmp = malloc (serv_resp.s_aliases_cnt * sizeof (uint32_t));
+		  if (tmp == NULL)
+		    {
+		      retval = ENOMEM;
+		      goto out;
+		    }
+		}
+	      aliases_len = memcpy (tmp, aliases_len,
+				    serv_resp.s_aliases_cnt
+				    * sizeof (uint32_t));
+	    }
+#endif
+	}
+    }
+
+  if (s_name == NULL)
+    {
+      sock = __nscd_open_socket (key, keylen, type, &serv_resp,
+				 sizeof (serv_resp));
+      if (sock == -1)
+	{
+	  __nss_not_use_nscd_services = 1;
+	  goto out;
+	}
+    }
+
+  /* No value found so far.  */
+  *result = NULL;
+
+  if (__glibc_unlikely (serv_resp.found == -1))
+    {
+      /* The daemon does not cache this database.  */
+      __nss_not_use_nscd_services = 1;
+      goto out_close;
+    }
+
+  if (serv_resp.found == 1)
+    {
+      char *cp = buf;
+      uintptr_t align1;
+      uintptr_t align2;
+      size_t total_len;
+      ssize_t cnt;
+      int n;
+
+      /* A first check whether the buffer is sufficiently large is possible.  */
+      /* Now allocate the buffer the array for the group members.  We must
+	 align the pointer and the base of the h_addr_list pointers.  */
+      align1 = ((__alignof__ (char *) - (cp - ((char *) 0)))
+		& (__alignof__ (char *) - 1));
+      align2 = ((__alignof__ (char *) - ((cp + align1 + serv_resp.s_name_len
+					  + serv_resp.s_proto_len)
+					 - ((char *) 0)))
+		& (__alignof__ (char *) - 1));
+      if (buflen < (align1 + serv_resp.s_name_len + serv_resp.s_proto_len
+		    + align2
+		    + (serv_resp.s_aliases_cnt + 1) * sizeof (char *)))
+	{
+	no_room:
+	  __set_errno (ERANGE);
+	  retval = ERANGE;
+	  goto out_close;
+	}
+      cp += align1;
+
+      /* Prepare the result as far as we can.  */
+      resultbuf->s_aliases = (char **) cp;
+      cp += (serv_resp.s_aliases_cnt + 1) * sizeof (char *);
+
+      resultbuf->s_name = cp;
+      cp += serv_resp.s_name_len;
+      resultbuf->s_proto = cp;
+      cp += serv_resp.s_proto_len + align2;
+      resultbuf->s_port = serv_resp.s_port;
+
+      if (s_name == NULL)
+	{
+	  struct iovec vec[2];
+
+	  vec[0].iov_base = resultbuf->s_name;
+	  vec[0].iov_len = serv_resp.s_name_len + serv_resp.s_proto_len;
+	  total_len = vec[0].iov_len;
+	  n = 1;
+
+	  if (serv_resp.s_aliases_cnt > 0)
+	    {
+	      assert (alloca_aliases_len == 0);
+	      alloca_aliases_len
+		= __libc_use_alloca (alloca_used
+				     + (serv_resp.s_aliases_cnt
+					* sizeof (uint32_t)));
+	      if (alloca_aliases_len)
+		aliases_len = alloca_account (serv_resp.s_aliases_cnt
+					      * sizeof (uint32_t),
+					      alloca_used);
+	      else
+		{
+		  aliases_len = malloc (serv_resp.s_aliases_cnt
+					* sizeof (uint32_t));
+		  if (aliases_len == NULL)
+		    {
+		      retval = ENOMEM;
+		      goto out_close;
+		    }
+		}
+	      vec[n].iov_base = (void *) aliases_len;
+	      vec[n].iov_len = serv_resp.s_aliases_cnt * sizeof (uint32_t);
+
+	      total_len += serv_resp.s_aliases_cnt * sizeof (uint32_t);
+	      ++n;
+	    }
+
+	  if ((size_t) __readvall (sock, vec, n) != total_len)
+	    goto out_close;
+	}
+      else
+	memcpy (resultbuf->s_name, s_name,
+		serv_resp.s_name_len + serv_resp.s_proto_len);
+
+      /*  Now we also can read the aliases.  */
+      total_len = 0;
+      for (cnt = 0; cnt < serv_resp.s_aliases_cnt; ++cnt)
+	{
+	  resultbuf->s_aliases[cnt] = cp;
+	  cp += aliases_len[cnt];
+	  total_len += aliases_len[cnt];
+	}
+      resultbuf->s_aliases[cnt] = NULL;
+
+      if (__builtin_expect ((const char *) aliases_list + total_len > recend,
+			    0))
+	{
+	  /* aliases_len array might contain garbage during nscd GC cycle,
+	     retry rather than fail in that case.  */
+	  if (aliases_list != NULL && mapped->head->gc_cycle != gc_cycle)
+	    retval = -2;
+	  goto out_close;
+	}
+
+      /* See whether this would exceed the buffer capacity.  */
+      if (__glibc_unlikely (cp > buf + buflen))
+	{
+	  /* aliases_len array might contain garbage during nscd GC cycle,
+	     retry rather than fail in that case.  */
+	  if (aliases_list != NULL && mapped->head->gc_cycle != gc_cycle)
+	    {
+	      retval = -2;
+	      goto out_close;
+	    }
+	  goto no_room;
+	}
+
+      /* And finally read the aliases.  */
+      if (aliases_list == NULL)
+	{
+	  if (total_len == 0
+	      || ((size_t) __readall (sock, resultbuf->s_aliases[0], total_len)
+		  == total_len))
+	    {
+	      retval = 0;
+	      *result = resultbuf;
+	    }
+	}
+      else
+	{
+	  memcpy (resultbuf->s_aliases[0], aliases_list, total_len);
+
+	  /* Try to detect corrupt databases.  */
+	  if (resultbuf->s_name[serv_resp.s_name_len - 1] != '\0'
+	      || resultbuf->s_proto[serv_resp.s_proto_len - 1] != '\0'
+	      || ({for (cnt = 0; cnt < serv_resp.s_aliases_cnt; ++cnt)
+		     if (resultbuf->s_aliases[cnt][aliases_len[cnt] - 1]
+			 != '\0')
+		       break;
+		   cnt < serv_resp.s_aliases_cnt; }))
+	    {
+	      /* We cannot use the database.  */
+	      if (mapped->head->gc_cycle != gc_cycle)
+		retval = -2;
+	      goto out_close;
+	    }
+
+	  retval = 0;
+	  *result = resultbuf;
+	}
+    }
+  else
+    {
+      /* Set errno to 0 to indicate no error, just no found record.  */
+      __set_errno (0);
+      /* Even though we have not found anything, the result is zero.  */
+      retval = 0;
+    }
+
+ out_close:
+  if (sock != -1)
+    close_not_cancel_no_status (sock);
+ out:
+  if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0)
+    {
+      /* When we come here this means there has been a GC cycle while we
+	 were looking for the data.  This means the data might have been
+	 inconsistent.  Retry if possible.  */
+      if ((gc_cycle & 1) != 0 || ++nretries == 5 || retval == -1)
+	{
+	  /* nscd is just running gc now.  Disable using the mapping.  */
+	  if (atomic_decrement_val (&mapped->counter) == 0)
+	    __nscd_unmap (mapped);
+	  mapped = NO_MAPPING;
+	}
+
+      if (retval != -1)
+	{
+	  if (!alloca_aliases_len)
+	    free ((void *) aliases_len);
+	  goto retry;
+	}
+    }
+
+  if (!alloca_aliases_len)
+    free ((void *) aliases_len);
+  if (!alloca_key)
+    free (key);
+
+  return retval;
+}
diff --git a/REORG.TODO/nscd/nscd_helper.c b/REORG.TODO/nscd/nscd_helper.c
new file mode 100644
index 0000000000..22905d0b83
--- /dev/null
+++ b/REORG.TODO/nscd/nscd_helper.c
@@ -0,0 +1,564 @@
+/* Copyright (C) 1998-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <sys/mman.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+#include <not-cancel.h>
+#include <nis/rpcsvc/nis.h>
+#include <kernel-features.h>
+
+#include "nscd-client.h"
+
+
+/* Extra time we wait if the socket is still receiving data.  This
+   value is in milliseconds.  Note that the other side is nscd on the
+   local machine and it is already transmitting data.  So the wait
+   time need not be long.  */
+#define EXTRA_RECEIVE_TIME 200
+
+
+static int
+wait_on_socket (int sock, long int usectmo)
+{
+  struct pollfd fds[1];
+  fds[0].fd = sock;
+  fds[0].events = POLLIN | POLLERR | POLLHUP;
+  int n = __poll (fds, 1, usectmo);
+  if (n == -1 && __builtin_expect (errno == EINTR, 0))
+    {
+      /* Handle the case where the poll() call is interrupted by a
+	 signal.  We cannot just use TEMP_FAILURE_RETRY since it might
+	 lead to infinite loops.  */
+      struct timeval now;
+      (void) __gettimeofday (&now, NULL);
+      long int end = now.tv_sec * 1000 + usectmo + (now.tv_usec + 500) / 1000;
+      long int timeout = usectmo;
+      while (1)
+	{
+	  n = __poll (fds, 1, timeout);
+	  if (n != -1 || errno != EINTR)
+	    break;
+
+	  /* Recompute the timeout time.  */
+	  (void) __gettimeofday (&now, NULL);
+	  timeout = end - (now.tv_sec * 1000 + (now.tv_usec + 500) / 1000);
+	}
+    }
+
+  return n;
+}
+
+
+ssize_t
+__readall (int fd, void *buf, size_t len)
+{
+  size_t n = len;
+  ssize_t ret;
+  do
+    {
+    again:
+      ret = TEMP_FAILURE_RETRY (__read (fd, buf, n));
+      if (ret <= 0)
+	{
+	  if (__builtin_expect (ret < 0 && errno == EAGAIN, 0)
+	      /* The socket is still receiving data.  Wait a bit more.  */
+	      && wait_on_socket (fd, EXTRA_RECEIVE_TIME) > 0)
+	    goto again;
+
+	  break;
+	}
+      buf = (char *) buf + ret;
+      n -= ret;
+    }
+  while (n > 0);
+  return ret < 0 ? ret : len - n;
+}
+
+
+ssize_t
+__readvall (int fd, const struct iovec *iov, int iovcnt)
+{
+  ssize_t ret = TEMP_FAILURE_RETRY (__readv (fd, iov, iovcnt));
+  if (ret <= 0)
+    {
+      if (__glibc_likely (ret == 0 || errno != EAGAIN))
+	/* A genuine error or no data to read.  */
+	return ret;
+
+      /* The data has not all yet been received.  Do as if we have not
+	 read anything yet.  */
+      ret = 0;
+    }
+
+  size_t total = 0;
+  for (int i = 0; i < iovcnt; ++i)
+    total += iov[i].iov_len;
+
+  if (ret < total)
+    {
+      struct iovec iov_buf[iovcnt];
+      ssize_t r = ret;
+
+      struct iovec *iovp = memcpy (iov_buf, iov, iovcnt * sizeof (*iov));
+      do
+	{
+	  while (iovp->iov_len <= r)
+	    {
+	      r -= iovp->iov_len;
+	      --iovcnt;
+	      ++iovp;
+	    }
+	  iovp->iov_base = (char *) iovp->iov_base + r;
+	  iovp->iov_len -= r;
+	again:
+	  r = TEMP_FAILURE_RETRY (__readv (fd, iovp, iovcnt));
+	  if (r <= 0)
+	    {
+	      if (__builtin_expect (r < 0 && errno == EAGAIN, 0)
+		  /* The socket is still receiving data.  Wait a bit more.  */
+		  && wait_on_socket (fd, EXTRA_RECEIVE_TIME) > 0)
+		goto again;
+
+	      break;
+	    }
+	  ret += r;
+	}
+      while (ret < total);
+      if (r < 0)
+	ret = r;
+    }
+  return ret;
+}
+
+
+static int
+open_socket (request_type type, const char *key, size_t keylen)
+{
+  int sock;
+
+  sock = __socket (PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
+  if (sock < 0)
+    return -1;
+
+  size_t real_sizeof_reqdata = sizeof (request_header) + keylen;
+  struct
+  {
+    request_header req;
+    char key[];
+  } *reqdata = alloca (real_sizeof_reqdata);
+
+  struct sockaddr_un sun;
+  sun.sun_family = AF_UNIX;
+  strcpy (sun.sun_path, _PATH_NSCDSOCKET);
+  if (__connect (sock, (struct sockaddr *) &sun, sizeof (sun)) < 0
+      && errno != EINPROGRESS)
+    goto out;
+
+  reqdata->req.version = NSCD_VERSION;
+  reqdata->req.type = type;
+  reqdata->req.key_len = keylen;
+
+  memcpy (reqdata->key, key, keylen);
+
+  bool first_try = true;
+  struct timeval tvend;
+  /* Fake initializing tvend.  */
+  asm ("" : "=m" (tvend));
+  while (1)
+    {
+#ifndef MSG_NOSIGNAL
+# define MSG_NOSIGNAL 0
+#endif
+      ssize_t wres = TEMP_FAILURE_RETRY (__send (sock, reqdata,
+						 real_sizeof_reqdata,
+						 MSG_NOSIGNAL));
+      if (__glibc_likely (wres == (ssize_t) real_sizeof_reqdata))
+	/* We managed to send the request.  */
+	return sock;
+
+      if (wres != -1 || errno != EAGAIN)
+	/* Something is really wrong, no chance to continue.  */
+	break;
+
+      /* The daemon is busy wait for it.  */
+      int to;
+      struct timeval now;
+      (void) __gettimeofday (&now, NULL);
+      if (first_try)
+	{
+	  tvend.tv_usec = now.tv_usec;
+	  tvend.tv_sec = now.tv_sec + 5;
+	  to = 5 * 1000;
+	  first_try = false;
+	}
+      else
+	to = ((tvend.tv_sec - now.tv_sec) * 1000
+	      + (tvend.tv_usec - now.tv_usec) / 1000);
+
+      struct pollfd fds[1];
+      fds[0].fd = sock;
+      fds[0].events = POLLOUT | POLLERR | POLLHUP;
+      if (__poll (fds, 1, to) <= 0)
+	/* The connection timed out or broke down.  */
+	break;
+
+      /* We try to write again.  */
+    }
+
+ out:
+  close_not_cancel_no_status (sock);
+
+  return -1;
+}
+
+
+void
+__nscd_unmap (struct mapped_database *mapped)
+{
+  assert (mapped->counter == 0);
+  __munmap ((void *) mapped->head, mapped->mapsize);
+  free (mapped);
+}
+
+
+/* Try to get a file descriptor for the shared meory segment
+   containing the database.  */
+struct mapped_database *
+__nscd_get_mapping (request_type type, const char *key,
+		    struct mapped_database **mappedp)
+{
+  struct mapped_database *result = NO_MAPPING;
+#ifdef SCM_RIGHTS
+  const size_t keylen = strlen (key) + 1;
+  int saved_errno = errno;
+
+  int mapfd = -1;
+  char resdata[keylen];
+
+  /* Open a socket and send the request.  */
+  int sock = open_socket (type, key, keylen);
+  if (sock < 0)
+    goto out;
+
+  /* Room for the data sent along with the file descriptor.  We expect
+     the key name back.  */
+  uint64_t mapsize;
+  struct iovec iov[2];
+  iov[0].iov_base = resdata;
+  iov[0].iov_len = keylen;
+  iov[1].iov_base = &mapsize;
+  iov[1].iov_len = sizeof (mapsize);
+
+  union
+  {
+    struct cmsghdr hdr;
+    char bytes[CMSG_SPACE (sizeof (int))];
+  } buf;
+  struct msghdr msg = { .msg_iov = iov, .msg_iovlen = 2,
+			.msg_control = buf.bytes,
+			.msg_controllen = sizeof (buf) };
+  struct cmsghdr *cmsg = CMSG_FIRSTHDR (&msg);
+
+  cmsg->cmsg_level = SOL_SOCKET;
+  cmsg->cmsg_type = SCM_RIGHTS;
+  cmsg->cmsg_len = CMSG_LEN (sizeof (int));
+
+  /* This access is well-aligned since BUF is correctly aligned for an
+     int and CMSG_DATA preserves this alignment.  */
+  memset (CMSG_DATA (cmsg), '\xff', sizeof (int));
+
+  msg.msg_controllen = cmsg->cmsg_len;
+
+  if (wait_on_socket (sock, 5 * 1000) <= 0)
+    goto out_close2;
+
+# ifndef MSG_CMSG_CLOEXEC
+#  define MSG_CMSG_CLOEXEC 0
+# endif
+  ssize_t n = TEMP_FAILURE_RETRY (__recvmsg (sock, &msg, MSG_CMSG_CLOEXEC));
+
+  if (__builtin_expect (CMSG_FIRSTHDR (&msg) == NULL
+			|| (CMSG_FIRSTHDR (&msg)->cmsg_len
+			    != CMSG_LEN (sizeof (int))), 0))
+    goto out_close2;
+
+  int *ip = (void *) CMSG_DATA (cmsg);
+  mapfd = *ip;
+
+  if (__glibc_unlikely (n != keylen && n != keylen + sizeof (mapsize)))
+    goto out_close;
+
+  if (__glibc_unlikely (strcmp (resdata, key) != 0))
+    goto out_close;
+
+  if (__glibc_unlikely (n == keylen))
+    {
+      struct stat64 st;
+      if (__builtin_expect (fstat64 (mapfd, &st) != 0, 0)
+	  || __builtin_expect (st.st_size < sizeof (struct database_pers_head),
+			       0))
+	goto out_close;
+
+      mapsize = st.st_size;
+    }
+
+  /* The file is large enough, map it now.  */
+  void *mapping = __mmap (NULL, mapsize, PROT_READ, MAP_SHARED, mapfd, 0);
+  if (__glibc_likely (mapping != MAP_FAILED))
+    {
+      /* Check whether the database is correct and up-to-date.  */
+      struct database_pers_head *head = mapping;
+
+      if (__builtin_expect (head->version != DB_VERSION, 0)
+	  || __builtin_expect (head->header_size != sizeof (*head), 0)
+	  /* Catch some misconfiguration.  The server should catch
+	     them now but some older versions did not.  */
+	  || __builtin_expect (head->module == 0, 0)
+	  /* This really should not happen but who knows, maybe the update
+	     thread got stuck.  */
+	  || __builtin_expect (! head->nscd_certainly_running
+			       && (head->timestamp + MAPPING_TIMEOUT
+				   < time (NULL)), 0))
+	{
+	out_unmap:
+	  __munmap (mapping, mapsize);
+	  goto out_close;
+	}
+
+      size_t size = (sizeof (*head) + roundup (head->module * sizeof (ref_t),
+					       ALIGN)
+		     + head->data_size);
+
+      if (__glibc_unlikely (mapsize < size))
+	goto out_unmap;
+
+      /* Allocate a record for the mapping.  */
+      struct mapped_database *newp = malloc (sizeof (*newp));
+      if (newp == NULL)
+	/* Ugh, after all we went through the memory allocation failed.  */
+	goto out_unmap;
+
+      newp->head = mapping;
+      newp->data = ((char *) mapping + head->header_size
+		    + roundup (head->module * sizeof (ref_t), ALIGN));
+      newp->mapsize = size;
+      newp->datasize = head->data_size;
+      /* Set counter to 1 to show it is usable.  */
+      newp->counter = 1;
+
+      result = newp;
+    }
+
+ out_close:
+  __close (mapfd);
+ out_close2:
+  __close (sock);
+ out:
+  __set_errno (saved_errno);
+#endif	/* SCM_RIGHTS */
+
+  struct mapped_database *oldval = *mappedp;
+  *mappedp = result;
+
+  if (oldval != NULL && atomic_decrement_val (&oldval->counter) == 0)
+    __nscd_unmap (oldval);
+
+  return result;
+}
+
+struct mapped_database *
+__nscd_get_map_ref (request_type type, const char *name,
+		    volatile struct locked_map_ptr *mapptr, int *gc_cyclep)
+{
+  struct mapped_database *cur = mapptr->mapped;
+  if (cur == NO_MAPPING)
+    return cur;
+
+  if (!__nscd_acquire_maplock (mapptr))
+    return NO_MAPPING;
+
+  cur = mapptr->mapped;
+
+  if (__glibc_likely (cur != NO_MAPPING))
+    {
+      /* If not mapped or timestamp not updated, request new map.  */
+      if (cur == NULL
+	  || (cur->head->nscd_certainly_running == 0
+	      && cur->head->timestamp + MAPPING_TIMEOUT < time (NULL))
+	  || cur->head->data_size > cur->datasize)
+	cur = __nscd_get_mapping (type, name,
+				  (struct mapped_database **) &mapptr->mapped);
+
+      if (__glibc_likely (cur != NO_MAPPING))
+	{
+	  if (__builtin_expect (((*gc_cyclep = cur->head->gc_cycle) & 1) != 0,
+				0))
+	    cur = NO_MAPPING;
+	  else
+	    atomic_increment (&cur->counter);
+	}
+    }
+
+  mapptr->lock = 0;
+
+  return cur;
+}
+
+
+/* Using sizeof (hashentry) is not always correct to determine the size of
+   the data structure as found in the nscd cache.  The program could be
+   a 64-bit process and nscd could be a 32-bit process.  In this case
+   sizeof (hashentry) would overestimate the size.  The following is
+   the minimum size of such an entry, good enough for our tests here.  */
+#define MINIMUM_HASHENTRY_SIZE \
+  (offsetof (struct hashentry, dellist) + sizeof (int32_t))
+
+
+/* Don't return const struct datahead *, as eventhough the record
+   is normally constant, it can change arbitrarily during nscd
+   garbage collection.  */
+struct datahead *
+__nscd_cache_search (request_type type, const char *key, size_t keylen,
+		     const struct mapped_database *mapped, size_t datalen)
+{
+  unsigned long int hash = __nis_hash (key, keylen) % mapped->head->module;
+  size_t datasize = mapped->datasize;
+
+  ref_t trail = mapped->head->array[hash];
+  trail = atomic_forced_read (trail);
+  ref_t work = trail;
+  size_t loop_cnt = datasize / (MINIMUM_HASHENTRY_SIZE
+				+ offsetof (struct datahead, data) / 2);
+  int tick = 0;
+
+  while (work != ENDREF && work + MINIMUM_HASHENTRY_SIZE <= datasize)
+    {
+      struct hashentry *here = (struct hashentry *) (mapped->data + work);
+      ref_t here_key, here_packet;
+
+#if !_STRING_ARCH_unaligned
+      /* Although during garbage collection when moving struct hashentry
+	 records around we first copy from old to new location and then
+	 adjust pointer from previous hashentry to it, there is no barrier
+	 between those memory writes.  It is very unlikely to hit it,
+	 so check alignment only if a misaligned load can crash the
+	 application.  */
+      if ((uintptr_t) here & (__alignof__ (*here) - 1))
+	return NULL;
+#endif
+
+      if (type == here->type
+	  && keylen == here->len
+	  && (here_key = atomic_forced_read (here->key)) + keylen <= datasize
+	  && memcmp (key, mapped->data + here_key, keylen) == 0
+	  && ((here_packet = atomic_forced_read (here->packet))
+	      + sizeof (struct datahead) <= datasize))
+	{
+	  /* We found the entry.  Increment the appropriate counter.  */
+	  struct datahead *dh
+	    = (struct datahead *) (mapped->data + here_packet);
+
+#if !_STRING_ARCH_unaligned
+	  if ((uintptr_t) dh & (__alignof__ (*dh) - 1))
+	    return NULL;
+#endif
+
+	  /* See whether we must ignore the entry or whether something
+	     is wrong because garbage collection is in progress.  */
+	  if (dh->usable
+	      && here_packet + dh->allocsize <= datasize
+	      && (here_packet + offsetof (struct datahead, data) + datalen
+		  <= datasize))
+	    return dh;
+	}
+
+      work = atomic_forced_read (here->next);
+      /* Prevent endless loops.  This should never happen but perhaps
+	 the database got corrupted, accidentally or deliberately.  */
+      if (work == trail || loop_cnt-- == 0)
+	break;
+      if (tick)
+	{
+	  struct hashentry *trailelem;
+	  trailelem = (struct hashentry *) (mapped->data + trail);
+
+#if !_STRING_ARCH_unaligned
+	  /* We have to redo the checks.  Maybe the data changed.  */
+	  if ((uintptr_t) trailelem & (__alignof__ (*trailelem) - 1))
+	    return NULL;
+#endif
+
+	  if (trail + MINIMUM_HASHENTRY_SIZE > datasize)
+	    return NULL;
+
+	  trail = atomic_forced_read (trailelem->next);
+	}
+      tick = 1 - tick;
+    }
+
+  return NULL;
+}
+
+
+/* Create a socket connected to a name. */
+int
+__nscd_open_socket (const char *key, size_t keylen, request_type type,
+		    void *response, size_t responselen)
+{
+  /* This should never happen and it is something the nscd daemon
+     enforces, too.  He it helps to limit the amount of stack
+     used.  */
+  if (keylen > MAXKEYLEN)
+    return -1;
+
+  int saved_errno = errno;
+
+  int sock = open_socket (type, key, keylen);
+  if (sock >= 0)
+    {
+      /* Wait for data.  */
+      if (wait_on_socket (sock, 5 * 1000) > 0)
+	{
+	  ssize_t nbytes = TEMP_FAILURE_RETRY (__read (sock, response,
+						       responselen));
+	  if (nbytes == (ssize_t) responselen)
+	    return sock;
+	}
+
+      close_not_cancel_no_status (sock);
+    }
+
+  __set_errno (saved_errno);
+
+  return -1;
+}
diff --git a/REORG.TODO/nscd/nscd_initgroups.c b/REORG.TODO/nscd/nscd_initgroups.c
new file mode 100644
index 0000000000..00c650896a
--- /dev/null
+++ b/REORG.TODO/nscd/nscd_initgroups.c
@@ -0,0 +1,180 @@
+/* Copyright (C) 2004-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@redhat.com>, 2004.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <assert.h>
+#include <errno.h>
+#include <grp.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <not-cancel.h>
+
+#include "nscd-client.h"
+#include "nscd_proto.h"
+
+
+/* We use the same mapping as in nscd_getgr.   */
+libc_locked_map_ptr (extern, __gr_map_handle) attribute_hidden;
+
+
+int
+__nscd_getgrouplist (const char *user, gid_t group, long int *size,
+		     gid_t **groupsp, long int limit)
+{
+  size_t userlen = strlen (user) + 1;
+  int gc_cycle;
+  int nretries = 0;
+
+  /* If the mapping is available, try to search there instead of
+     communicating with the nscd.  */
+  struct mapped_database *mapped;
+  mapped = __nscd_get_map_ref (GETFDGR, "group", &__gr_map_handle, &gc_cycle);
+
+ retry:;
+  char *respdata = NULL;
+  int retval = -1;
+  int sock = -1;
+  initgr_response_header initgr_resp;
+
+  if (mapped != NO_MAPPING)
+    {
+      struct datahead *found = __nscd_cache_search (INITGROUPS, user,
+						    userlen, mapped,
+						    sizeof initgr_resp);
+      if (found != NULL)
+	{
+	  respdata = (char *) (&found->data[0].initgrdata + 1);
+	  initgr_resp = found->data[0].initgrdata;
+	  char *recend = (char *) found->data + found->recsize;
+
+	  /* Now check if we can trust initgr_resp fields.  If GC is
+	     in progress, it can contain anything.  */
+	  if (mapped->head->gc_cycle != gc_cycle)
+	    {
+	      retval = -2;
+	      goto out;
+	    }
+
+	  if (respdata + initgr_resp.ngrps * sizeof (int32_t) > recend)
+	    goto out;
+	}
+    }
+
+  /* If we do not have the cache mapped, try to get the data over the
+     socket.  */
+  if (respdata == NULL)
+    {
+      sock = __nscd_open_socket (user, userlen, INITGROUPS, &initgr_resp,
+				 sizeof (initgr_resp));
+      if (sock == -1)
+	{
+	  /* nscd not running or wrong version.  */
+	  __nss_not_use_nscd_group = 1;
+	  goto out;
+	}
+    }
+
+  if (initgr_resp.found == 1)
+    {
+      /* The following code assumes that gid_t and int32_t are the
+	 same size.  This is the case for al existing implementation.
+	 If this should change some code needs to be added which
+	 doesn't use memcpy but instead copies each array element one
+	 by one.  */
+      assert (sizeof (int32_t) == sizeof (gid_t));
+      assert (initgr_resp.ngrps >= 0);
+
+      /* Make sure we have enough room.  We always count GROUP in even
+	 though we might not end up adding it.  */
+      if (*size < initgr_resp.ngrps + 1)
+	{
+	  gid_t *newp = realloc (*groupsp,
+				 (initgr_resp.ngrps + 1) * sizeof (gid_t));
+	  if (newp == NULL)
+	    /* We cannot increase the buffer size.  */
+	    goto out_close;
+
+	  *groupsp = newp;
+	  *size = initgr_resp.ngrps + 1;
+	}
+
+      if (respdata == NULL)
+	{
+	  /* Read the data from the socket.  */
+	  if ((size_t) __readall (sock, *groupsp, initgr_resp.ngrps
+						  * sizeof (gid_t))
+	      == initgr_resp.ngrps * sizeof (gid_t))
+	    retval = initgr_resp.ngrps;
+	}
+      else
+	{
+	  /* Just copy the data.  */
+	  retval = initgr_resp.ngrps;
+	  memcpy (*groupsp, respdata, retval * sizeof (gid_t));
+	}
+    }
+  else
+    {
+      if (__glibc_unlikely (initgr_resp.found == -1))
+	{
+	  /* The daemon does not cache this database.  */
+	  __nss_not_use_nscd_group = 1;
+	  goto out_close;
+	}
+
+      /* No group found yet.   */
+      retval = 0;
+
+      assert (*size >= 1);
+    }
+
+  /* Check whether GROUP is part of the mix.  If not, add it.  */
+  if (retval >= 0)
+    {
+      int cnt;
+      for (cnt = 0; cnt < retval; ++cnt)
+	if ((*groupsp)[cnt] == group)
+	  break;
+
+      if (cnt == retval)
+	(*groupsp)[retval++] = group;
+    }
+
+ out_close:
+  if (sock != -1)
+    close_not_cancel_no_status (sock);
+ out:
+  if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0)
+    {
+      /* When we come here this means there has been a GC cycle while we
+	 were looking for the data.  This means the data might have been
+	 inconsistent.  Retry if possible.  */
+      if ((gc_cycle & 1) != 0 || ++nretries == 5 || retval == -1)
+	{
+	  /* nscd is just running gc now.  Disable using the mapping.  */
+	  if (atomic_decrement_val (&mapped->counter) == 0)
+	    __nscd_unmap (mapped);
+	  mapped = NO_MAPPING;
+	}
+
+      if (retval != -1)
+	goto retry;
+    }
+
+  return retval;
+}
diff --git a/REORG.TODO/nscd/nscd_netgroup.c b/REORG.TODO/nscd/nscd_netgroup.c
new file mode 100644
index 0000000000..44f37ef957
--- /dev/null
+++ b/REORG.TODO/nscd/nscd_netgroup.c
@@ -0,0 +1,289 @@
+/* Copyright (C) 2011-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@gmail.com>, 2011.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <alloca.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <not-cancel.h>
+
+#include "nscd-client.h"
+#include "nscd_proto.h"
+
+int __nss_not_use_nscd_netgroup;
+
+
+libc_locked_map_ptr (static, map_handle);
+/* Note that we only free the structure if necessary.  The memory
+   mapping is not removed since it is not visible to the malloc
+   handling.  */
+libc_freeres_fn (pw_map_free)
+{
+  if (map_handle.mapped != NO_MAPPING)
+    {
+      void *p = map_handle.mapped;
+      map_handle.mapped = NO_MAPPING;
+      free (p);
+    }
+}
+
+
+int
+__nscd_setnetgrent (const char *group, struct __netgrent *datap)
+{
+  int gc_cycle;
+  int nretries = 0;
+  size_t group_len = strlen (group) + 1;
+
+  /* If the mapping is available, try to search there instead of
+     communicating with the nscd.  */
+  struct mapped_database *mapped;
+  mapped = __nscd_get_map_ref (GETFDNETGR, "netgroup", &map_handle, &gc_cycle);
+
+ retry:;
+  char *respdata = NULL;
+  int retval = -1;
+  netgroup_response_header netgroup_resp;
+
+  if (mapped != NO_MAPPING)
+    {
+      struct datahead *found = __nscd_cache_search (GETNETGRENT, group,
+						    group_len, mapped,
+						    sizeof netgroup_resp);
+      if (found != NULL)
+	{
+	  respdata = (char *) (&found->data[0].netgroupdata + 1);
+	  netgroup_resp = found->data[0].netgroupdata;
+	  /* Now check if we can trust pw_resp fields.  If GC is
+	     in progress, it can contain anything.  */
+	  if (mapped->head->gc_cycle != gc_cycle)
+	    {
+	      retval = -2;
+	      goto out;
+	    }
+	}
+    }
+
+  int sock = -1;
+  if (respdata == NULL)
+    {
+      sock = __nscd_open_socket (group, group_len, GETNETGRENT,
+				 &netgroup_resp, sizeof (netgroup_resp));
+      if (sock == -1)
+	{
+	  /* nscd not running or wrong version.  */
+	  __nss_not_use_nscd_netgroup = 1;
+	  goto out;
+	}
+    }
+
+  if (netgroup_resp.found == 1)
+    {
+      size_t datalen = netgroup_resp.result_len;
+
+      /* If we do not have to read the data here it comes from the
+	 mapped data and does not have to be freed.  */
+      if (respdata == NULL)
+	{
+	  /* The data will come via the socket.  */
+	  respdata = malloc (datalen);
+	  if (respdata == NULL)
+	    goto out_close;
+
+	  if ((size_t) __readall (sock, respdata, datalen) != datalen)
+	    {
+	      free (respdata);
+	      goto out_close;
+	    }
+	}
+
+      datap->data = respdata;
+      datap->data_size = datalen;
+      datap->cursor = respdata;
+      datap->first = 1;
+      datap->nip = (service_user *) -1l;
+      datap->known_groups = NULL;
+      datap->needed_groups = NULL;
+
+      retval = 1;
+    }
+  else
+    {
+      if (__glibc_unlikely (netgroup_resp.found == -1))
+	{
+	  /* The daemon does not cache this database.  */
+	  __nss_not_use_nscd_netgroup = 1;
+	  goto out_close;
+	}
+
+      /* Set errno to 0 to indicate no error, just no found record.  */
+      __set_errno (0);
+      /* Even though we have not found anything, the result is zero.  */
+      retval = 0;
+    }
+
+ out_close:
+  if (sock != -1)
+    close_not_cancel_no_status (sock);
+ out:
+  if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0)
+    {
+      /* When we come here this means there has been a GC cycle while we
+	 were looking for the data.  This means the data might have been
+	 inconsistent.  Retry if possible.  */
+      if ((gc_cycle & 1) != 0 || ++nretries == 5 || retval == -1)
+	{
+	  /* nscd is just running gc now.  Disable using the mapping.  */
+	  if (atomic_decrement_val (&mapped->counter) == 0)
+	    __nscd_unmap (mapped);
+	  mapped = NO_MAPPING;
+	}
+
+      if (retval != -1)
+	goto retry;
+    }
+
+  return retval;
+}
+
+
+int
+__nscd_innetgr (const char *netgroup, const char *host, const char *user,
+		const char *domain)
+{
+  size_t key_len = (strlen (netgroup) + strlen (host ?: "")
+		    + strlen (user ?: "") + strlen (domain ?: "") + 7);
+  char *key;
+  bool use_alloca = __libc_use_alloca (key_len);
+  if (use_alloca)
+    key = alloca (key_len);
+  else
+    {
+      key = malloc (key_len);
+      if (key == NULL)
+	return -1;
+    }
+  char *wp = stpcpy (key, netgroup) + 1;
+  if (host != NULL)
+    {
+      *wp++ = '\1';
+      wp = stpcpy (wp, host) + 1;
+    }
+  else
+    *wp++ = '\0';
+  if (user != NULL)
+    {
+      *wp++ = '\1';
+      wp = stpcpy (wp, user) + 1;
+    }
+  else
+    *wp++ = '\0';
+  if (domain != NULL)
+    {
+      *wp++ = '\1';
+      wp = stpcpy (wp, domain) + 1;
+    }
+  else
+    *wp++ = '\0';
+  key_len = wp - key;
+
+  /* If the mapping is available, try to search there instead of
+     communicating with the nscd.  */
+  int gc_cycle;
+  int nretries = 0;
+  struct mapped_database *mapped;
+  mapped = __nscd_get_map_ref (GETFDNETGR, "netgroup", &map_handle, &gc_cycle);
+
+ retry:;
+  int retval = -1;
+  innetgroup_response_header innetgroup_resp;
+  int sock = -1;
+
+  if (mapped != NO_MAPPING)
+    {
+      struct datahead *found = __nscd_cache_search (INNETGR, key,
+						    key_len, mapped,
+						    sizeof innetgroup_resp);
+      if (found != NULL)
+	{
+	  innetgroup_resp = found->data[0].innetgroupdata;
+	  /* Now check if we can trust pw_resp fields.  If GC is
+	     in progress, it can contain anything.  */
+	  if (mapped->head->gc_cycle != gc_cycle)
+	    {
+	      retval = -2;
+	      goto out;
+	    }
+
+	  goto found_entry;
+	}
+    }
+
+  sock = __nscd_open_socket (key, key_len, INNETGR,
+			     &innetgroup_resp, sizeof (innetgroup_resp));
+  if (sock == -1)
+    {
+      /* nscd not running or wrong version.  */
+      __nss_not_use_nscd_netgroup = 1;
+      goto out;
+    }
+
+ found_entry:
+  if (innetgroup_resp.found == 1)
+    retval = innetgroup_resp.result;
+  else
+    {
+      if (__glibc_unlikely (innetgroup_resp.found == -1))
+	{
+	  /* The daemon does not cache this database.  */
+	  __nss_not_use_nscd_netgroup = 1;
+	  goto out_close;
+	}
+
+      /* Set errno to 0 to indicate no error, just no found record.  */
+      __set_errno (0);
+      /* Even though we have not found anything, the result is zero.  */
+      retval = 0;
+    }
+
+ out_close:
+  if (sock != -1)
+    close_not_cancel_no_status (sock);
+ out:
+  if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0)
+    {
+      /* When we come here this means there has been a GC cycle while we
+	 were looking for the data.  This means the data might have been
+	 inconsistent.  Retry if possible.  */
+      if ((gc_cycle & 1) != 0 || ++nretries == 5 || retval == -1)
+	{
+	  /* nscd is just running gc now.  Disable using the mapping.  */
+	  if (atomic_decrement_val (&mapped->counter) == 0)
+	    __nscd_unmap (mapped);
+	  mapped = NO_MAPPING;
+	}
+
+      if (retval != -1)
+	goto retry;
+    }
+
+  if (! use_alloca)
+    free (key);
+
+  return retval;
+}
diff --git a/REORG.TODO/nscd/nscd_proto.h b/REORG.TODO/nscd/nscd_proto.h
new file mode 100644
index 0000000000..7c61821e74
--- /dev/null
+++ b/REORG.TODO/nscd/nscd_proto.h
@@ -0,0 +1,79 @@
+/* Copyright (C) 1998-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Thorsten Kukuk <kukuk@suse.de>, 1998.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef _NSCD_PROTO_H
+#define _NSCD_PROTO_H 1
+
+#include <grp.h>
+#include <netdb.h>
+#include <pwd.h>
+
+/* Interval in which we transfer retry to contact the NSCD.  */
+#define NSS_NSCD_RETRY	100
+
+/* Type needed in the interfaces.  */
+struct nscd_ai_result;
+
+
+/* Variables for communication between NSCD handler functions and NSS.  */
+extern int __nss_not_use_nscd_passwd attribute_hidden;
+extern int __nss_not_use_nscd_group attribute_hidden;
+extern int __nss_not_use_nscd_hosts attribute_hidden;
+extern int __nss_not_use_nscd_services attribute_hidden;
+extern int __nss_not_use_nscd_netgroup attribute_hidden;
+
+extern int __nscd_getpwnam_r (const char *name, struct passwd *resultbuf,
+			      char *buffer, size_t buflen,
+			      struct passwd **result);
+extern int __nscd_getpwuid_r (uid_t uid, struct passwd *resultbuf,
+			      char *buffer,  size_t buflen,
+			      struct passwd **result);
+extern int __nscd_getgrnam_r (const char *name, struct group *resultbuf,
+			      char *buffer, size_t buflen,
+			      struct group **result);
+extern int __nscd_getgrgid_r (gid_t gid, struct group *resultbuf,
+			      char *buffer,  size_t buflen,
+			      struct group **result);
+extern int __nscd_gethostbyname_r (const char *name,
+				   struct hostent *resultbuf,
+				   char *buffer, size_t buflen,
+				   struct hostent **result, int *h_errnop);
+extern int __nscd_gethostbyname2_r (const char *name, int af,
+				    struct hostent *resultbuf,
+				    char *buffer, size_t buflen,
+				    struct hostent **result, int *h_errnop);
+extern int __nscd_gethostbyaddr_r (const void *addr, socklen_t len, int type,
+				   struct hostent *resultbuf,
+				   char *buffer, size_t buflen,
+				   struct hostent **result, int *h_errnop);
+extern int __nscd_getai (const char *key, struct nscd_ai_result **result,
+			 int *h_errnop);
+extern int __nscd_getgrouplist (const char *user, gid_t group, long int *size,
+				gid_t **groupsp, long int limit);
+extern int __nscd_getservbyname_r (const char *name, const char *proto,
+				   struct servent *result_buf, char *buf,
+				   size_t buflen, struct servent **result);
+extern int __nscd_getservbyport_r (int port, const char *proto,
+				   struct servent *result_buf, char *buf,
+				   size_t buflen, struct servent **result);
+extern int __nscd_innetgr (const char *netgroup, const char *host,
+			   const char *user, const char *domain);
+extern int __nscd_setnetgrent (const char *group, struct __netgrent *datap);
+
+
+#endif /* _NSCD_PROTO_H */
diff --git a/REORG.TODO/nscd/nscd_setup_thread.c b/REORG.TODO/nscd/nscd_setup_thread.c
new file mode 100644
index 0000000000..c3670ad943
--- /dev/null
+++ b/REORG.TODO/nscd/nscd_setup_thread.c
@@ -0,0 +1,27 @@
+/* Setup of nscd worker threads.  Stub verison.
+   Copyright (C) 2004-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@redhat.com>, 2004.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published
+   by the Free Software Foundation; version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
+
+#include <nscd.h>
+
+
+int
+setup_thread (struct database_dyn *db)
+{
+  /* Nothing.  */
+  return 0;
+}
diff --git a/REORG.TODO/nscd/nscd_stat.c b/REORG.TODO/nscd/nscd_stat.c
new file mode 100644
index 0000000000..feb1c98ac3
--- /dev/null
+++ b/REORG.TODO/nscd/nscd_stat.c
@@ -0,0 +1,318 @@
+/* Copyright (c) 1998-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1998.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <errno.h>
+#include <error.h>
+#include <inttypes.h>
+#include <langinfo.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <libintl.h>
+
+#include "nscd.h"
+#include "dbg_log.h"
+#include "selinux.h"
+#ifdef HAVE_SELINUX
+# include <selinux/selinux.h>
+# include <selinux/avc.h>
+#endif /* HAVE_SELINUX */
+
+
+/* We use this to make sure the receiver is the same.  */
+static const char compilation[21] = __DATE__ " " __TIME__;
+
+/* Statistic data for one database.  */
+struct dbstat
+{
+  int enabled;
+  int check_file;
+  int shared;
+  int persistent;
+  size_t module;
+
+  unsigned long int postimeout;
+  unsigned long int negtimeout;
+
+  size_t nentries;
+  size_t maxnentries;
+  size_t maxnsearched;
+  size_t datasize;
+  size_t dataused;
+
+  uintmax_t poshit;
+  uintmax_t neghit;
+  uintmax_t posmiss;
+  uintmax_t negmiss;
+
+  uintmax_t rdlockdelayed;
+  uintmax_t wrlockdelayed;
+
+  uintmax_t addfailed;
+};
+
+/* Record for transmitting statistics.  */
+struct statdata
+{
+  char version[sizeof (compilation)];
+  int debug_level;
+  time_t runtime;
+  unsigned long int client_queued;
+  int nthreads;
+  int max_nthreads;
+  int paranoia;
+  time_t restart_interval;
+  unsigned int reload_count;
+  int ndbs;
+  struct dbstat dbs[lastdb];
+#ifdef HAVE_SELINUX
+  struct avc_cache_stats cstats;
+#endif /* HAVE_SELINUX */
+};
+
+
+void
+send_stats (int fd, struct database_dyn dbs[lastdb])
+{
+  struct statdata data;
+  int cnt;
+
+  memset (&data, 0, sizeof (data));
+
+  memcpy (data.version, compilation, sizeof (compilation));
+  data.debug_level = debug_level;
+  data.runtime = time (NULL) - start_time;
+  data.client_queued = client_queued;
+  data.nthreads = nthreads;
+  data.max_nthreads = max_nthreads;
+  data.paranoia = paranoia;
+  data.restart_interval = restart_interval;
+  data.reload_count = reload_count;
+  data.ndbs = lastdb;
+
+  for (cnt = 0; cnt < lastdb; ++cnt)
+    {
+      memset (&data.dbs[cnt], 0, sizeof (data.dbs[cnt]));
+      data.dbs[cnt].enabled = dbs[cnt].enabled;
+      data.dbs[cnt].check_file = dbs[cnt].check_file;
+      data.dbs[cnt].shared = dbs[cnt].shared;
+      data.dbs[cnt].persistent = dbs[cnt].persistent;
+      data.dbs[cnt].postimeout = dbs[cnt].postimeout;
+      data.dbs[cnt].negtimeout = dbs[cnt].negtimeout;
+      if (dbs[cnt].head != NULL)
+	{
+	  data.dbs[cnt].module = dbs[cnt].head->module;
+	  data.dbs[cnt].poshit = dbs[cnt].head->poshit;
+	  data.dbs[cnt].neghit = dbs[cnt].head->neghit;
+	  data.dbs[cnt].posmiss = dbs[cnt].head->posmiss;
+	  data.dbs[cnt].negmiss = dbs[cnt].head->negmiss;
+	  data.dbs[cnt].nentries = dbs[cnt].head->nentries;
+	  data.dbs[cnt].maxnentries = dbs[cnt].head->maxnentries;
+	  data.dbs[cnt].datasize = dbs[cnt].head->data_size;
+	  data.dbs[cnt].dataused = dbs[cnt].head->first_free;
+	  data.dbs[cnt].maxnsearched = dbs[cnt].head->maxnsearched;
+	  data.dbs[cnt].rdlockdelayed = dbs[cnt].head->rdlockdelayed;
+	  data.dbs[cnt].wrlockdelayed = dbs[cnt].head->wrlockdelayed;
+	  data.dbs[cnt].addfailed = dbs[cnt].head->addfailed;
+	}
+    }
+
+  if (selinux_enabled)
+    nscd_avc_cache_stats (&data.cstats);
+
+  if (TEMP_FAILURE_RETRY (send (fd, &data, sizeof (data), MSG_NOSIGNAL))
+      != sizeof (data))
+    {
+      char buf[256];
+      dbg_log (_("cannot write statistics: %s"),
+	       strerror_r (errno, buf, sizeof (buf)));
+    }
+}
+
+
+int
+receive_print_stats (void)
+{
+  struct statdata data;
+  request_header req;
+  ssize_t nbytes;
+  int fd;
+  int i;
+  uid_t uid = getuid ();
+  const char *yesstr = _("yes");
+  const char *nostr = _("no");
+
+  /* Find out whether there is another user but root allowed to
+     request statistics.  */
+  if (uid != 0)
+    {
+      /* User specified?  */
+      if(stat_user == NULL || stat_uid != uid)
+	{
+	  if (stat_user != NULL)
+	    error (EXIT_FAILURE, 0,
+		   _("Only root or %s is allowed to use this option!"),
+		   stat_user);
+	  else
+	    error (EXIT_FAILURE, 0,
+		   _("Only root is allowed to use this option!"));
+	}
+    }
+
+  /* Open a socket to the running nscd.  */
+  fd = nscd_open_socket ();
+  if (fd == -1)
+    error (EXIT_FAILURE, 0, _("nscd not running!\n"));
+
+  /* Send the request.  */
+  req.version = NSCD_VERSION;
+  req.type = GETSTAT;
+  req.key_len = 0;
+  nbytes = TEMP_FAILURE_RETRY (send (fd, &req, sizeof (request_header),
+				     MSG_NOSIGNAL));
+  if (nbytes != sizeof (request_header))
+    {
+      int err = errno;
+      close (fd);
+      error (EXIT_FAILURE, err, _("write incomplete"));
+    }
+
+  /* Read as much data as we expect.  */
+  if (TEMP_FAILURE_RETRY (read (fd, &data, sizeof (data))) != sizeof (data)
+      || (memcmp (data.version, compilation, sizeof (compilation)) != 0
+	  /* Yes, this is an assignment!  */
+	  && (errno = EINVAL)))
+    {
+      /* Not the right version.  */
+      int err = errno;
+      close (fd);
+      error (EXIT_FAILURE, err, _("cannot read statistics data"));
+    }
+
+  printf (_("nscd configuration:\n\n%15d  server debug level\n"),
+	  data.debug_level);
+
+  /* We know that we can simply subtract time_t values.  */
+  unsigned long int diff = data.runtime;
+  unsigned int ndays = 0;
+  unsigned int nhours = 0;
+  unsigned int nmins = 0;
+  if (diff > 24 * 60 * 60)
+    {
+      ndays = diff / (24 * 60 * 60);
+      diff %= 24 * 60 * 60;
+    }
+  if (diff > 60 * 60)
+    {
+      nhours = diff / (60 * 60);
+      diff %= 60 * 60;
+    }
+  if (diff > 60)
+    {
+      nmins = diff / 60;
+      diff %= 60;
+    }
+  if (ndays != 0)
+    printf (_("%3ud %2uh %2um %2lus  server runtime\n"),
+	    ndays, nhours, nmins, diff);
+  else if (nhours != 0)
+    printf (_("    %2uh %2um %2lus  server runtime\n"), nhours, nmins, diff);
+  else if (nmins != 0)
+    printf (_("        %2um %2lus  server runtime\n"), nmins, diff);
+  else
+    printf (_("            %2lus  server runtime\n"), diff);
+
+  printf (_("%15d  current number of threads\n"
+	    "%15d  maximum number of threads\n"
+	    "%15lu  number of times clients had to wait\n"
+	    "%15s  paranoia mode enabled\n"
+	    "%15lu  restart internal\n"
+	    "%15u  reload count\n"),
+	  data.nthreads, data.max_nthreads, data.client_queued,
+	  data.paranoia ? yesstr : nostr,
+	  (unsigned long int) data.restart_interval, data.reload_count);
+
+  for (i = 0; i < lastdb; ++i)
+    {
+      unsigned long int hit = data.dbs[i].poshit + data.dbs[i].neghit;
+      unsigned long int all = hit + data.dbs[i].posmiss + data.dbs[i].negmiss;
+      const char *enabled = data.dbs[i].enabled ? yesstr : nostr;
+      const char *check_file = data.dbs[i].check_file ? yesstr : nostr;
+      const char *shared = data.dbs[i].shared ? yesstr : nostr;
+      const char *persistent = data.dbs[i].persistent ? yesstr : nostr;
+
+      if (enabled[0] == '\0')
+	/* The locale does not provide this information so we have to
+	   translate it ourself.  Since we should avoid short translation
+	   terms we artifically increase the length.  */
+	enabled = data.dbs[i].enabled ? yesstr : nostr;
+      if (check_file[0] == '\0')
+	check_file = data.dbs[i].check_file ? yesstr : nostr;
+      if (shared[0] == '\0')
+	shared = data.dbs[i].shared ? yesstr : nostr;
+      if (persistent[0] == '\0')
+	persistent = data.dbs[i].persistent ? yesstr : nostr;
+
+      if (all == 0)
+	/* If nothing happened so far report a 0% hit rate.  */
+	all = 1;
+
+      printf (_("\n%s cache:\n\n"
+		"%15s  cache is enabled\n"
+		"%15s  cache is persistent\n"
+		"%15s  cache is shared\n"
+		"%15zu  suggested size\n"
+		"%15zu  total data pool size\n"
+		"%15zu  used data pool size\n"
+		"%15lu  seconds time to live for positive entries\n"
+		"%15lu  seconds time to live for negative entries\n"
+		"%15" PRIuMAX "  cache hits on positive entries\n"
+		"%15" PRIuMAX "  cache hits on negative entries\n"
+		"%15" PRIuMAX "  cache misses on positive entries\n"
+		"%15" PRIuMAX "  cache misses on negative entries\n"
+		"%15lu%% cache hit rate\n"
+		"%15zu  current number of cached values\n"
+		"%15zu  maximum number of cached values\n"
+		"%15zu  maximum chain length searched\n"
+		"%15" PRIuMAX "  number of delays on rdlock\n"
+		"%15" PRIuMAX "  number of delays on wrlock\n"
+		"%15" PRIuMAX "  memory allocations failed\n"
+		"%15s  check /etc/%s for changes\n"),
+	      dbnames[i], enabled, persistent, shared,
+	      data.dbs[i].module,
+	      data.dbs[i].datasize, data.dbs[i].dataused,
+	      data.dbs[i].postimeout, data.dbs[i].negtimeout,
+	      data.dbs[i].poshit, data.dbs[i].neghit,
+	      data.dbs[i].posmiss, data.dbs[i].negmiss,
+	      (100 * hit) / all,
+	      data.dbs[i].nentries, data.dbs[i].maxnentries,
+	      data.dbs[i].maxnsearched,
+	      data.dbs[i].rdlockdelayed,
+	      data.dbs[i].wrlockdelayed,
+	      data.dbs[i].addfailed, check_file, dbnames[i]);
+    }
+
+  if (selinux_enabled)
+    nscd_avc_print_stats (&data.cstats);
+
+  close (fd);
+
+  exit (0);
+}
diff --git a/REORG.TODO/nscd/pwdcache.c b/REORG.TODO/nscd/pwdcache.c
new file mode 100644
index 0000000000..721f4c617b
--- /dev/null
+++ b/REORG.TODO/nscd/pwdcache.c
@@ -0,0 +1,552 @@
+/* Cache handling for passwd lookup.
+   Copyright (C) 1998-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published
+   by the Free Software Foundation; version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
+
+#include <alloca.h>
+#include <assert.h>
+#include <errno.h>
+#include <error.h>
+#include <libintl.h>
+#include <pwd.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <stackinfo.h>
+
+#include "nscd.h"
+#include "dbg_log.h"
+#ifdef HAVE_SENDFILE
+# include <kernel-features.h>
+#endif
+
+/* This is the standard reply in case the service is disabled.  */
+static const pw_response_header disabled =
+{
+  .version = NSCD_VERSION,
+  .found = -1,
+  .pw_name_len = 0,
+  .pw_passwd_len = 0,
+  .pw_uid = -1,
+  .pw_gid = -1,
+  .pw_gecos_len = 0,
+  .pw_dir_len = 0,
+  .pw_shell_len = 0
+};
+
+/* This is the struct describing how to write this record.  */
+const struct iovec pwd_iov_disabled =
+{
+  .iov_base = (void *) &disabled,
+  .iov_len = sizeof (disabled)
+};
+
+
+/* This is the standard reply in case we haven't found the dataset.  */
+static const pw_response_header notfound =
+{
+  .version = NSCD_VERSION,
+  .found = 0,
+  .pw_name_len = 0,
+  .pw_passwd_len = 0,
+  .pw_uid = -1,
+  .pw_gid = -1,
+  .pw_gecos_len = 0,
+  .pw_dir_len = 0,
+  .pw_shell_len = 0
+};
+
+
+static time_t
+cache_addpw (struct database_dyn *db, int fd, request_header *req,
+	     const void *key, struct passwd *pwd, uid_t owner,
+	     struct hashentry *const he, struct datahead *dh, int errval)
+{
+  bool all_written = true;
+  ssize_t total;
+  time_t t = time (NULL);
+
+  /* We allocate all data in one memory block: the iov vector,
+     the response header and the dataset itself.  */
+  struct dataset
+  {
+    struct datahead head;
+    pw_response_header resp;
+    char strdata[0];
+  } *dataset;
+
+  assert (offsetof (struct dataset, resp) == offsetof (struct datahead, data));
+
+  time_t timeout = MAX_TIMEOUT_VALUE;
+  if (pwd == NULL)
+    {
+      if (he != NULL && errval == EAGAIN)
+	{
+	  /* If we have an old record available but cannot find one
+	     now because the service is not available we keep the old
+	     record and make sure it does not get removed.  */
+	  if (reload_count != UINT_MAX && dh->nreloads == reload_count)
+	    /* Do not reset the value if we never not reload the record.  */
+	    dh->nreloads = reload_count - 1;
+
+	  /* Reload with the same time-to-live value.  */
+	  timeout = dh->timeout = t + db->postimeout;
+
+	  total = 0;
+	}
+      else
+	{
+	  /* We have no data.  This means we send the standard reply for this
+	     case.  */
+	  total = sizeof (notfound);
+
+	  if (fd != -1
+	      && TEMP_FAILURE_RETRY (send (fd, &notfound, total,
+					   MSG_NOSIGNAL)) != total)
+	    all_written = false;
+
+	  /* If we have a transient error or cannot permanently store
+	     the result, so be it.  */
+	  if (errno == EAGAIN || __builtin_expect (db->negtimeout == 0, 0))
+	    {
+	      /* Mark the old entry as obsolete.  */
+	      if (dh != NULL)
+		dh->usable = false;
+	    }
+	  else if ((dataset = mempool_alloc (db, (sizeof (struct dataset)
+						  + req->key_len), 1)) != NULL)
+	    {
+	      timeout = datahead_init_neg (&dataset->head,
+					   (sizeof (struct dataset)
+					    + req->key_len), total,
+					   db->negtimeout);
+
+	      /* This is the reply.  */
+	      memcpy (&dataset->resp, &notfound, total);
+
+	      /* Copy the key data.  */
+	      char *key_copy = memcpy (dataset->strdata, key, req->key_len);
+
+	      /* If necessary, we also propagate the data to disk.  */
+	      if (db->persistent)
+		{
+		  // XXX async OK?
+		  uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
+		  msync ((void *) pval,
+			 ((uintptr_t) dataset & pagesize_m1)
+			 + sizeof (struct dataset) + req->key_len, MS_ASYNC);
+		}
+
+	      (void) cache_add (req->type, key_copy, req->key_len,
+				&dataset->head, true, db, owner, he == NULL);
+
+	      pthread_rwlock_unlock (&db->lock);
+
+	      /* Mark the old entry as obsolete.  */
+	      if (dh != NULL)
+		dh->usable = false;
+	    }
+	}
+    }
+  else
+    {
+      /* Determine the I/O structure.  */
+      size_t pw_name_len = strlen (pwd->pw_name) + 1;
+      size_t pw_passwd_len = strlen (pwd->pw_passwd) + 1;
+      size_t pw_gecos_len = strlen (pwd->pw_gecos) + 1;
+      size_t pw_dir_len = strlen (pwd->pw_dir) + 1;
+      size_t pw_shell_len = strlen (pwd->pw_shell) + 1;
+      char *cp;
+      const size_t key_len = strlen (key);
+      const size_t buf_len = 3 * sizeof (pwd->pw_uid) + key_len + 1;
+      char *buf = alloca (buf_len);
+      ssize_t n;
+
+      /* We need this to insert the `byuid' entry.  */
+      int key_offset;
+      n = snprintf (buf, buf_len, "%d%c%n%s", pwd->pw_uid, '\0',
+		    &key_offset, (char *) key) + 1;
+
+      total = (offsetof (struct dataset, strdata)
+	       + pw_name_len + pw_passwd_len
+	       + pw_gecos_len + pw_dir_len + pw_shell_len);
+
+      /* If we refill the cache, first assume the reconrd did not
+	 change.  Allocate memory on the cache since it is likely
+	 discarded anyway.  If it turns out to be necessary to have a
+	 new record we can still allocate real memory.  */
+      bool alloca_used = false;
+      dataset = NULL;
+
+      if (he == NULL)
+	{
+	  /* Prevent an INVALIDATE request from pruning the data between
+	     the two calls to cache_add.  */
+	  if (db->propagate)
+	    pthread_mutex_lock (&db->prune_run_lock);
+	  dataset = (struct dataset *) mempool_alloc (db, total + n, 1);
+	}
+
+      if (dataset == NULL)
+	{
+	  if (he == NULL && db->propagate)
+	    pthread_mutex_unlock (&db->prune_run_lock);
+
+	  /* We cannot permanently add the result in the moment.  But
+	     we can provide the result as is.  Store the data in some
+	     temporary memory.  */
+	  dataset = (struct dataset *) alloca (total + n);
+
+	  /* We cannot add this record to the permanent database.  */
+	  alloca_used = true;
+	}
+
+      timeout = datahead_init_pos (&dataset->head, total + n,
+				   total - offsetof (struct dataset, resp),
+				   he == NULL ? 0 : dh->nreloads + 1,
+				   db->postimeout);
+
+      dataset->resp.version = NSCD_VERSION;
+      dataset->resp.found = 1;
+      dataset->resp.pw_name_len = pw_name_len;
+      dataset->resp.pw_passwd_len = pw_passwd_len;
+      dataset->resp.pw_uid = pwd->pw_uid;
+      dataset->resp.pw_gid = pwd->pw_gid;
+      dataset->resp.pw_gecos_len = pw_gecos_len;
+      dataset->resp.pw_dir_len = pw_dir_len;
+      dataset->resp.pw_shell_len = pw_shell_len;
+
+      cp = dataset->strdata;
+
+      /* Copy the strings over into the buffer.  */
+      cp = mempcpy (cp, pwd->pw_name, pw_name_len);
+      cp = mempcpy (cp, pwd->pw_passwd, pw_passwd_len);
+      cp = mempcpy (cp, pwd->pw_gecos, pw_gecos_len);
+      cp = mempcpy (cp, pwd->pw_dir, pw_dir_len);
+      cp = mempcpy (cp, pwd->pw_shell, pw_shell_len);
+
+      /* Finally the stringified UID value.  */
+      memcpy (cp, buf, n);
+      char *key_copy = cp + key_offset;
+      assert (key_copy == (char *) rawmemchr (cp, '\0') + 1);
+
+      assert (cp == dataset->strdata + total - offsetof (struct dataset,
+							 strdata));
+
+      /* Now we can determine whether on refill we have to create a new
+	 record or not.  */
+      if (he != NULL)
+	{
+	  assert (fd == -1);
+
+	  if (dataset->head.allocsize == dh->allocsize
+	      && dataset->head.recsize == dh->recsize
+	      && memcmp (&dataset->resp, dh->data,
+			 dh->allocsize - offsetof (struct dataset, resp)) == 0)
+	    {
+	      /* The data has not changed.  We will just bump the
+		 timeout value.  Note that the new record has been
+		 allocated on the stack and need not be freed.  */
+	      dh->timeout = dataset->head.timeout;
+	      ++dh->nreloads;
+	    }
+	  else
+	    {
+	      /* We have to create a new record.  Just allocate
+		 appropriate memory and copy it.  */
+	      struct dataset *newp
+		= (struct dataset *) mempool_alloc (db, total + n, 1);
+	      if (newp != NULL)
+		{
+		  /* Adjust pointer into the memory block.  */
+		  cp = (char *) newp + (cp - (char *) dataset);
+		  key_copy = (char *) newp + (key_copy - (char *) dataset);
+
+		  dataset = memcpy (newp, dataset, total + n);
+		  alloca_used = false;
+		}
+
+	      /* Mark the old record as obsolete.  */
+	      dh->usable = false;
+	    }
+	}
+      else
+	{
+	  /* We write the dataset before inserting it to the database
+	     since while inserting this thread might block and so would
+	     unnecessarily let the receiver wait.  */
+	  assert (fd != -1);
+
+#ifdef HAVE_SENDFILE
+	  if (__builtin_expect (db->mmap_used, 1) && !alloca_used)
+	    {
+	      assert (db->wr_fd != -1);
+	      assert ((char *) &dataset->resp > (char *) db->data);
+	      assert ((char *) dataset - (char *) db->head
+		      + total
+		      <= (sizeof (struct database_pers_head)
+			  + db->head->module * sizeof (ref_t)
+			  + db->head->data_size));
+	      ssize_t written = sendfileall (fd, db->wr_fd,
+					     (char *) &dataset->resp
+					     - (char *) db->head,
+					     dataset->head.recsize);
+	      if (written != dataset->head.recsize)
+		{
+# ifndef __ASSUME_SENDFILE
+		  if (written == -1 && errno == ENOSYS)
+		    goto use_write;
+# endif
+		  all_written = false;
+		}
+	    }
+	  else
+# ifndef __ASSUME_SENDFILE
+	  use_write:
+# endif
+#endif
+	    if (writeall (fd, &dataset->resp, dataset->head.recsize)
+		!= dataset->head.recsize)
+	      all_written = false;
+	}
+
+
+      /* Add the record to the database.  But only if it has not been
+	 stored on the stack.  */
+      if (! alloca_used)
+	{
+	  /* If necessary, we also propagate the data to disk.  */
+	  if (db->persistent)
+	    {
+	      // XXX async OK?
+	      uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
+	      msync ((void *) pval,
+		     ((uintptr_t) dataset & pagesize_m1) + total + n,
+		     MS_ASYNC);
+	    }
+
+	  /* NB: in the following code we always must add the entry
+	     marked with FIRST first.  Otherwise we end up with
+	     dangling "pointers" in case a latter hash entry cannot be
+	     added.  */
+	  bool first = true;
+
+	  /* If the request was by UID, add that entry first.  */
+	  if (req->type == GETPWBYUID)
+	    {
+	      if (cache_add (GETPWBYUID, cp, key_offset, &dataset->head, true,
+			     db, owner, he == NULL) < 0)
+		goto out;
+
+	      first = false;
+	    }
+	  /* If the key is different from the name add a separate entry.  */
+	  else if (strcmp (key_copy, dataset->strdata) != 0)
+	    {
+	      if (cache_add (GETPWBYNAME, key_copy, key_len + 1,
+			     &dataset->head, true, db, owner, he == NULL) < 0)
+		goto out;
+
+	      first = false;
+	    }
+
+	  /* We have to add the value for both, byname and byuid.  */
+	  if ((req->type == GETPWBYNAME || db->propagate)
+	      && __builtin_expect (cache_add (GETPWBYNAME, dataset->strdata,
+					      pw_name_len, &dataset->head,
+					      first, db, owner, he == NULL)
+				   == 0, 1))
+	    {
+	      if (req->type == GETPWBYNAME && db->propagate)
+		(void) cache_add (GETPWBYUID, cp, key_offset, &dataset->head,
+				  false, db, owner, false);
+	    }
+
+	out:
+	  pthread_rwlock_unlock (&db->lock);
+	  if (he == NULL && db->propagate)
+	    pthread_mutex_unlock (&db->prune_run_lock);
+	}
+    }
+
+  if (__builtin_expect (!all_written, 0) && debug_level > 0)
+    {
+      char buf[256];
+      dbg_log (_("short write in %s: %s"),  __FUNCTION__,
+	       strerror_r (errno, buf, sizeof (buf)));
+    }
+
+  return timeout;
+}
+
+
+union keytype
+{
+  void *v;
+  uid_t u;
+};
+
+
+static int
+lookup (int type, union keytype key, struct passwd *resultbufp, char *buffer,
+	size_t buflen, struct passwd **pwd)
+{
+  if (type == GETPWBYNAME)
+    return __getpwnam_r (key.v, resultbufp, buffer, buflen, pwd);
+  else
+    return __getpwuid_r (key.u, resultbufp, buffer, buflen, pwd);
+}
+
+
+static time_t
+addpwbyX (struct database_dyn *db, int fd, request_header *req,
+	  union keytype key, const char *keystr, uid_t c_uid,
+	  struct hashentry *he, struct datahead *dh)
+{
+  /* Search for the entry matching the key.  Please note that we don't
+     look again in the table whether the dataset is now available.  We
+     simply insert it.  It does not matter if it is in there twice.  The
+     pruning function only will look at the timestamp.  */
+  size_t buflen = 1024;
+  char *buffer = (char *) alloca (buflen);
+  struct passwd resultbuf;
+  struct passwd *pwd;
+  bool use_malloc = false;
+  int errval = 0;
+
+  if (__glibc_unlikely (debug_level > 0))
+    {
+      if (he == NULL)
+	dbg_log (_("Haven't found \"%s\" in password cache!"), keystr);
+      else
+	dbg_log (_("Reloading \"%s\" in password cache!"), keystr);
+    }
+
+  while (lookup (req->type, key, &resultbuf, buffer, buflen, &pwd) != 0
+	 && (errval = errno) == ERANGE)
+    {
+      errno = 0;
+
+      if (__glibc_unlikely (buflen > 32768))
+	{
+	  char *old_buffer = buffer;
+	  buflen *= 2;
+	  buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen);
+	  if (buffer == NULL)
+	    {
+	      /* We ran out of memory.  We cannot do anything but
+		 sending a negative response.  In reality this should
+		 never happen.  */
+	      pwd = NULL;
+	      buffer = old_buffer;
+
+	      /* We set the error to indicate this is (possibly) a
+		 temporary error and that it does not mean the entry
+		 is not available at all.  */
+	      errval = EAGAIN;
+	      break;
+	    }
+	  use_malloc = true;
+	}
+      else
+	/* Allocate a new buffer on the stack.  If possible combine it
+	   with the previously allocated buffer.  */
+	buffer = (char *) extend_alloca (buffer, buflen, 2 * buflen);
+    }
+
+  /* Add the entry to the cache.  */
+  time_t timeout = cache_addpw (db, fd, req, keystr, pwd, c_uid, he, dh,
+				errval);
+
+  if (use_malloc)
+    free (buffer);
+
+  return timeout;
+}
+
+
+void
+addpwbyname (struct database_dyn *db, int fd, request_header *req,
+	     void *key, uid_t c_uid)
+{
+  union keytype u = { .v = key };
+
+  addpwbyX (db, fd, req, u, key, c_uid, NULL, NULL);
+}
+
+
+time_t
+readdpwbyname (struct database_dyn *db, struct hashentry *he,
+	       struct datahead *dh)
+{
+  request_header req =
+    {
+      .type = GETPWBYNAME,
+      .key_len = he->len
+    };
+  union keytype u = { .v = db->data + he->key };
+
+  return addpwbyX (db, -1, &req, u, db->data + he->key, he->owner, he, dh);
+}
+
+
+void
+addpwbyuid (struct database_dyn *db, int fd, request_header *req,
+	    void *key, uid_t c_uid)
+{
+  char *ep;
+  uid_t uid = strtoul ((char *) key, &ep, 10);
+
+  if (*(char *) key == '\0' || *ep != '\0')  /* invalid numeric uid */
+    {
+      if (debug_level > 0)
+	dbg_log (_("Invalid numeric uid \"%s\"!"), (char *) key);
+
+      errno = EINVAL;
+      return;
+    }
+
+  union keytype u = { .u = uid };
+
+  addpwbyX (db, fd, req, u, key, c_uid, NULL, NULL);
+}
+
+
+time_t
+readdpwbyuid (struct database_dyn *db, struct hashentry *he,
+	      struct datahead *dh)
+{
+  char *ep;
+  uid_t uid = strtoul (db->data + he->key, &ep, 10);
+
+  /* Since the key has been added before it must be OK.  */
+  assert (*(db->data + he->key) != '\0' && *ep == '\0');
+
+  request_header req =
+    {
+      .type = GETPWBYUID,
+      .key_len = he->len
+    };
+  union keytype u = { .u = uid };
+
+  return addpwbyX (db, -1, &req, u, db->data + he->key, he->owner, he, dh);
+}
diff --git a/REORG.TODO/nscd/res_hconf.c b/REORG.TODO/nscd/res_hconf.c
new file mode 100644
index 0000000000..14b0e300bc
--- /dev/null
+++ b/REORG.TODO/nscd/res_hconf.c
@@ -0,0 +1,13 @@
+/* Add the include here so that we can redefine __fxprintf.  */
+#include <stdio.h>
+
+/* Rename symbols for protected names used in libc itself.  */
+#define __ioctl ioctl
+#define __socket socket
+#define __strchrnul strchrnul
+#define __strncasecmp strncasecmp
+
+#define __fxprintf(args...) /* ignore */
+
+
+#include "../resolv/res_hconf.c"
diff --git a/REORG.TODO/nscd/selinux.c b/REORG.TODO/nscd/selinux.c
new file mode 100644
index 0000000000..f7bcd8e4c8
--- /dev/null
+++ b/REORG.TODO/nscd/selinux.c
@@ -0,0 +1,453 @@
+/* SELinux access controls for nscd.
+   Copyright (C) 2004-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Matthew Rickard <mjricka@epoch.ncsc.mil>, 2004.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include <error.h>
+#include <errno.h>
+#include <libintl.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <sys/prctl.h>
+#include <selinux/avc.h>
+#include <selinux/selinux.h>
+#ifdef HAVE_LIBAUDIT
+# include <libaudit.h>
+#endif
+
+#include "dbg_log.h"
+#include "selinux.h"
+
+
+#ifdef HAVE_SELINUX
+/* Global variable to tell if the kernel has SELinux support.  */
+int selinux_enabled;
+
+/* Define mappings of request type to AVC permission name.  */
+static const char *perms[LASTREQ] =
+{
+  [GETPWBYNAME] = "getpwd",
+  [GETPWBYUID] = "getpwd",
+  [GETGRBYNAME] = "getgrp",
+  [GETGRBYGID] = "getgrp",
+  [GETHOSTBYNAME] = "gethost",
+  [GETHOSTBYNAMEv6] = "gethost",
+  [GETHOSTBYADDR] = "gethost",
+  [GETHOSTBYADDRv6] = "gethost",
+  [SHUTDOWN] = "admin",
+  [GETSTAT] = "getstat",
+  [INVALIDATE] = "admin",
+  [GETFDPW] = "shmempwd",
+  [GETFDGR] = "shmemgrp",
+  [GETFDHST] = "shmemhost",
+  [GETAI] = "gethost",
+  [INITGROUPS] = "getgrp",
+  [GETSERVBYNAME] = "getserv",
+  [GETSERVBYPORT] = "getserv",
+  [GETFDSERV] = "shmemserv",
+  [GETNETGRENT] = "getnetgrp",
+  [INNETGR] = "getnetgrp",
+  [GETFDNETGR] = "shmemnetgrp",
+};
+
+/* Store an entry ref to speed AVC decisions.  */
+static struct avc_entry_ref aeref;
+
+/* Thread to listen for SELinux status changes via netlink.  */
+static pthread_t avc_notify_thread;
+
+#ifdef HAVE_LIBAUDIT
+/* Prototype for supporting the audit daemon */
+static void log_callback (const char *fmt, ...);
+#endif
+
+/* Prototypes for AVC callback functions.  */
+static void *avc_create_thread (void (*run) (void));
+static void avc_stop_thread (void *thread);
+static void *avc_alloc_lock (void);
+static void avc_get_lock (void *lock);
+static void avc_release_lock (void *lock);
+static void avc_free_lock (void *lock);
+
+/* AVC callback structures for use in avc_init.  */
+static const struct avc_log_callback log_cb =
+{
+#ifdef HAVE_LIBAUDIT
+  .func_log = log_callback,
+#else
+  .func_log = dbg_log,
+#endif
+  .func_audit = NULL
+};
+static const struct avc_thread_callback thread_cb =
+{
+  .func_create_thread = avc_create_thread,
+  .func_stop_thread = avc_stop_thread
+};
+static const struct avc_lock_callback lock_cb =
+{
+  .func_alloc_lock = avc_alloc_lock,
+  .func_get_lock = avc_get_lock,
+  .func_release_lock = avc_release_lock,
+  .func_free_lock = avc_free_lock
+};
+
+#ifdef HAVE_LIBAUDIT
+/* The audit system's netlink socket descriptor */
+static int audit_fd = -1;
+
+/* When an avc denial occurs, log it to audit system */
+static void
+log_callback (const char *fmt, ...)
+{
+  if (audit_fd >= 0)
+    {
+      va_list ap;
+      va_start (ap, fmt);
+
+      char *buf;
+      int e = vasprintf (&buf, fmt, ap);
+      if (e < 0)
+	{
+	  buf = alloca (BUFSIZ);
+	  vsnprintf (buf, BUFSIZ, fmt, ap);
+	}
+
+      /* FIXME: need to attribute this to real user, using getuid for now */
+      audit_log_user_avc_message (audit_fd, AUDIT_USER_AVC, buf, NULL, NULL,
+				  NULL, getuid ());
+
+      if (e >= 0)
+	free (buf);
+
+      va_end (ap);
+    }
+}
+
+/* Initialize the connection to the audit system */
+static void
+audit_init (void)
+{
+  audit_fd = audit_open ();
+  if (audit_fd < 0
+      /* If kernel doesn't support audit, bail out */
+      && errno != EINVAL && errno != EPROTONOSUPPORT && errno != EAFNOSUPPORT)
+    dbg_log (_("Failed opening connection to the audit subsystem: %m"));
+}
+
+
+# ifdef HAVE_LIBCAP
+static const cap_value_t new_cap_list[] =
+  { CAP_AUDIT_WRITE };
+#  define nnew_cap_list (sizeof (new_cap_list) / sizeof (new_cap_list[0]))
+static const cap_value_t tmp_cap_list[] =
+  { CAP_AUDIT_WRITE, CAP_SETUID, CAP_SETGID };
+#  define ntmp_cap_list (sizeof (tmp_cap_list) / sizeof (tmp_cap_list[0]))
+
+cap_t
+preserve_capabilities (void)
+{
+  if (getuid () != 0)
+    /* Not root, then we cannot preserve anything.  */
+    return NULL;
+
+  if (prctl (PR_SET_KEEPCAPS, 1) == -1)
+    {
+      dbg_log (_("Failed to set keep-capabilities"));
+      do_exit (EXIT_FAILURE, errno, _("prctl(KEEPCAPS) failed"));
+      /* NOTREACHED */
+    }
+
+  cap_t tmp_caps = cap_init ();
+  cap_t new_caps = NULL;
+  if (tmp_caps != NULL)
+    new_caps = cap_init ();
+
+  if (tmp_caps == NULL || new_caps == NULL)
+    {
+      if (tmp_caps != NULL)
+	cap_free (tmp_caps);
+
+      dbg_log (_("Failed to initialize drop of capabilities"));
+      do_exit (EXIT_FAILURE, 0, _("cap_init failed"));
+    }
+
+  /* There is no reason why these should not work.  */
+  cap_set_flag (new_caps, CAP_PERMITTED, nnew_cap_list,
+		(cap_value_t *) new_cap_list, CAP_SET);
+  cap_set_flag (new_caps, CAP_EFFECTIVE, nnew_cap_list,
+		(cap_value_t *) new_cap_list, CAP_SET);
+
+  cap_set_flag (tmp_caps, CAP_PERMITTED, ntmp_cap_list,
+		(cap_value_t *) tmp_cap_list, CAP_SET);
+  cap_set_flag (tmp_caps, CAP_EFFECTIVE, ntmp_cap_list,
+		(cap_value_t *) tmp_cap_list, CAP_SET);
+
+  int res = cap_set_proc (tmp_caps);
+
+  cap_free (tmp_caps);
+
+  if (__glibc_unlikely (res != 0))
+    {
+      cap_free (new_caps);
+      dbg_log (_("Failed to drop capabilities"));
+      do_exit (EXIT_FAILURE, 0, _("cap_set_proc failed"));
+    }
+
+  return new_caps;
+}
+
+void
+install_real_capabilities (cap_t new_caps)
+{
+  /* If we have no capabilities there is nothing to do here.  */
+  if (new_caps == NULL)
+    return;
+
+  if (cap_set_proc (new_caps))
+    {
+      cap_free (new_caps);
+      dbg_log (_("Failed to drop capabilities"));
+      do_exit (EXIT_FAILURE, 0, _("cap_set_proc failed"));
+      /* NOTREACHED */
+    }
+
+  cap_free (new_caps);
+
+  if (prctl (PR_SET_KEEPCAPS, 0) == -1)
+    {
+      dbg_log (_("Failed to unset keep-capabilities"));
+      do_exit (EXIT_FAILURE, errno, _("prctl(KEEPCAPS) failed"));
+      /* NOTREACHED */
+    }
+}
+# endif /* HAVE_LIBCAP */
+#endif /* HAVE_LIBAUDIT */
+
+/* Determine if we are running on an SELinux kernel. Set selinux_enabled
+   to the result.  */
+void
+nscd_selinux_enabled (int *selinux_enabled)
+{
+  *selinux_enabled = is_selinux_enabled ();
+  if (*selinux_enabled < 0)
+    {
+      dbg_log (_("Failed to determine if kernel supports SELinux"));
+      do_exit (EXIT_FAILURE, 0, NULL);
+    }
+}
+
+
+/* Create thread for AVC netlink notification.  */
+static void *
+avc_create_thread (void (*run) (void))
+{
+  int rc;
+
+  rc =
+    pthread_create (&avc_notify_thread, NULL, (void *(*) (void *)) run, NULL);
+  if (rc != 0)
+    do_exit (EXIT_FAILURE, rc, _("Failed to start AVC thread"));
+
+  return &avc_notify_thread;
+}
+
+
+/* Stop AVC netlink thread.  */
+static void
+avc_stop_thread (void *thread)
+{
+  pthread_cancel (*(pthread_t *) thread);
+}
+
+
+/* Allocate a new AVC lock.  */
+static void *
+avc_alloc_lock (void)
+{
+  pthread_mutex_t *avc_mutex;
+
+  avc_mutex = malloc (sizeof (pthread_mutex_t));
+  if (avc_mutex == NULL)
+    do_exit (EXIT_FAILURE, errno, _("Failed to create AVC lock"));
+  pthread_mutex_init (avc_mutex, NULL);
+
+  return avc_mutex;
+}
+
+
+/* Acquire an AVC lock.  */
+static void
+avc_get_lock (void *lock)
+{
+  pthread_mutex_lock (lock);
+}
+
+
+/* Release an AVC lock.  */
+static void
+avc_release_lock (void *lock)
+{
+  pthread_mutex_unlock (lock);
+}
+
+
+/* Free an AVC lock.  */
+static void
+avc_free_lock (void *lock)
+{
+  pthread_mutex_destroy (lock);
+  free (lock);
+}
+
+
+/* Initialize the user space access vector cache (AVC) for NSCD along with
+   log/thread/lock callbacks.  */
+void
+nscd_avc_init (void)
+{
+  avc_entry_ref_init (&aeref);
+
+  if (avc_init ("avc", NULL, &log_cb, &thread_cb, &lock_cb) < 0)
+    do_exit (EXIT_FAILURE, errno, _("Failed to start AVC"));
+  else
+    dbg_log (_("Access Vector Cache (AVC) started"));
+#ifdef HAVE_LIBAUDIT
+  audit_init ();
+#endif
+}
+
+
+/* Check the permission from the caller (via getpeercon) to nscd.
+   Returns 0 if access is allowed, 1 if denied, and -1 on error.
+
+   The SELinux policy, enablement, and permission bits are all dynamic and the
+   caching done by glibc is not entirely correct.  This nscd support should be
+   rewritten to use selinux_check_permission.  A rewrite is risky though and
+   requires some refactoring.  Currently we use symbolic mappings instead of
+   compile time constants (which SELinux upstream says are going away), and we
+   use security_deny_unknown to determine what to do if selinux-policy* doesn't
+   have a definition for the the permission or object class we are looking
+   up.  */
+int
+nscd_request_avc_has_perm (int fd, request_type req)
+{
+  /* Initialize to NULL so we know what to free in case of failure.  */
+  security_context_t scon = NULL;
+  security_context_t tcon = NULL;
+  security_id_t ssid = NULL;
+  security_id_t tsid = NULL;
+  int rc = -1;
+  security_class_t sc_nscd;
+  access_vector_t perm;
+  int avc_deny_unknown;
+
+  /* Check if SELinux denys or allows unknown object classes
+     and permissions.  It is 0 if they are allowed, 1 if they
+     are not allowed and -1 on error.  */
+  if ((avc_deny_unknown = security_deny_unknown ()) == -1)
+    dbg_log (_("Error querying policy for undefined object classes "
+	       "or permissions."));
+
+  /* Get the security class for nscd.  If this fails we will likely be
+     unable to do anything unless avc_deny_unknown is 0.  */
+  sc_nscd = string_to_security_class ("nscd");
+  if (sc_nscd == 0 && avc_deny_unknown == 1)
+    dbg_log (_("Error getting security class for nscd."));
+
+  /* Convert permission to AVC bits.  */
+  perm = string_to_av_perm (sc_nscd, perms[req]);
+  if (perm == 0 && avc_deny_unknown == 1)
+      dbg_log (_("Error translating permission name "
+		 "\"%s\" to access vector bit."), perms[req]);
+
+  /* If the nscd security class was not found or perms were not
+     found and AVC does not deny unknown values then allow it.  */
+  if ((sc_nscd == 0 || perm == 0) && avc_deny_unknown == 0)
+    return 0;
+
+  if (getpeercon (fd, &scon) < 0)
+    {
+      dbg_log (_("Error getting context of socket peer"));
+      goto out;
+    }
+  if (getcon (&tcon) < 0)
+    {
+      dbg_log (_("Error getting context of nscd"));
+      goto out;
+    }
+  if (avc_context_to_sid (scon, &ssid) < 0
+      || avc_context_to_sid (tcon, &tsid) < 0)
+    {
+      dbg_log (_("Error getting sid from context"));
+      goto out;
+    }
+
+  /* The SELinux API for avc_has_perm conflates access denied and error into
+     the return code -1, while nscd_request_avs_has_perm has distinct error
+     (-1) and denied (1) return codes. We map the avc_has_perm access denied or
+     error into an access denied at the nscd interface level (we do accurately
+     report error for the getpeercon, getcon, and avc_context_to_sid interfaces
+     used above).  */
+  rc = avc_has_perm (ssid, tsid, sc_nscd, perm, &aeref, NULL) < 0;
+
+out:
+  if (scon)
+    freecon (scon);
+  if (tcon)
+    freecon (tcon);
+  if (ssid)
+    sidput (ssid);
+  if (tsid)
+    sidput (tsid);
+
+  return rc;
+}
+
+
+/* Wrapper to get AVC statistics.  */
+void
+nscd_avc_cache_stats (struct avc_cache_stats *cstats)
+{
+  avc_cache_stats (cstats);
+}
+
+
+/* Print the AVC statistics to stdout.  */
+void
+nscd_avc_print_stats (struct avc_cache_stats *cstats)
+{
+  printf (_("\nSELinux AVC Statistics:\n\n"
+	    "%15u  entry lookups\n"
+	    "%15u  entry hits\n"
+	    "%15u  entry misses\n"
+	    "%15u  entry discards\n"
+	    "%15u  CAV lookups\n"
+	    "%15u  CAV hits\n"
+	    "%15u  CAV probes\n"
+	    "%15u  CAV misses\n"),
+	  cstats->entry_lookups, cstats->entry_hits, cstats->entry_misses,
+	  cstats->entry_discards, cstats->cav_lookups, cstats->cav_hits,
+	  cstats->cav_probes, cstats->cav_misses);
+}
+
+#endif /* HAVE_SELINUX */
diff --git a/REORG.TODO/nscd/selinux.h b/REORG.TODO/nscd/selinux.h
new file mode 100644
index 0000000000..052d62a6fb
--- /dev/null
+++ b/REORG.TODO/nscd/selinux.h
@@ -0,0 +1,61 @@
+/* Header for nscd SELinux access controls.
+   Copyright (C) 2004-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Matthew Rickard <mjricka@epoch.ncsc.mil>, 2004.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef _SELINUX_H
+#define _SELINUX_H 1
+
+#include "nscd.h"
+#ifdef HAVE_LIBCAP
+# include <sys/capability.h>
+#endif
+
+#ifdef HAVE_SELINUX
+/* Global variable to tell if the kernel has SELinux support.  */
+extern int selinux_enabled;
+
+/* Define this for AVC stat usage.  */
+struct avc_cache_stats;
+
+/* Initialize the userspace AVC.  */
+extern void nscd_avc_init (void);
+/* Determine if we are running on an SELinux kernel.  */
+extern void nscd_selinux_enabled (int *selinux_enabled);
+/* Check if the client has permission for the request type.  */
+extern int nscd_request_avc_has_perm (int fd, request_type req);
+/* Initialize AVC statistic information.  */
+extern void nscd_avc_cache_stats (struct avc_cache_stats *cstats);
+/* Display statistics on AVC usage.  */
+extern void nscd_avc_print_stats (struct avc_cache_stats *cstats);
+
+# ifdef HAVE_LIBCAP
+/* Preserve capabilities to connect to the audit daemon. */
+extern cap_t preserve_capabilities (void);
+/* Install final capabilities.  */
+extern void install_real_capabilities (cap_t new_caps);
+# endif
+#else
+# define selinux_enabled 0
+# define nscd_avc_init() (void) 0
+# define nscd_selinux_enabled(selinux_enabled) (void) 0
+# define nscd_request_avc_has_perm(fd, req) 0
+# define nscd_avc_cache_stats(cstats) (void) 0
+# define nscd_avc_print_stats(cstats) (void) 0
+#endif /* HAVE_SELINUX */
+
+#endif /* _SELINUX_H */
diff --git a/REORG.TODO/nscd/servicescache.c b/REORG.TODO/nscd/servicescache.c
new file mode 100644
index 0000000000..131ba6ddcc
--- /dev/null
+++ b/REORG.TODO/nscd/servicescache.c
@@ -0,0 +1,474 @@
+/* Cache handling for services lookup.
+   Copyright (C) 2007-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@drepper.com>, 2007.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published
+   by the Free Software Foundation; version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
+
+#include <alloca.h>
+#include <assert.h>
+#include <errno.h>
+#include <libintl.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <sys/mman.h>
+#include <kernel-features.h>
+
+#include "nscd.h"
+#include "dbg_log.h"
+
+
+/* This is the standard reply in case the service is disabled.  */
+static const serv_response_header disabled =
+{
+  .version = NSCD_VERSION,
+  .found = -1,
+  .s_name_len = 0,
+  .s_proto_len = 0,
+  .s_aliases_cnt = 0,
+  .s_port = -1
+};
+
+/* This is the struct describing how to write this record.  */
+const struct iovec serv_iov_disabled =
+{
+  .iov_base = (void *) &disabled,
+  .iov_len = sizeof (disabled)
+};
+
+
+/* This is the standard reply in case we haven't found the dataset.  */
+static const serv_response_header notfound =
+{
+  .version = NSCD_VERSION,
+  .found = 0,
+  .s_name_len = 0,
+  .s_proto_len = 0,
+  .s_aliases_cnt = 0,
+  .s_port = -1
+};
+
+
+static time_t
+cache_addserv (struct database_dyn *db, int fd, request_header *req,
+	       const void *key, struct servent *serv, uid_t owner,
+	       struct hashentry *const he, struct datahead *dh, int errval)
+{
+  bool all_written = true;
+  ssize_t total;
+  time_t t = time (NULL);
+
+  /* We allocate all data in one memory block: the iov vector,
+     the response header and the dataset itself.  */
+  struct dataset
+  {
+    struct datahead head;
+    serv_response_header resp;
+    char strdata[0];
+  } *dataset;
+
+  assert (offsetof (struct dataset, resp) == offsetof (struct datahead, data));
+
+  time_t timeout = MAX_TIMEOUT_VALUE;
+  if (serv == NULL)
+    {
+      if (he != NULL && errval == EAGAIN)
+	{
+	  /* If we have an old record available but cannot find one
+	     now because the service is not available we keep the old
+	     record and make sure it does not get removed.  */
+	  if (reload_count != UINT_MAX)
+	    /* Do not reset the value if we never not reload the record.  */
+	    dh->nreloads = reload_count - 1;
+
+	  /* Reload with the same time-to-live value.  */
+	  timeout = dh->timeout = t + db->postimeout;
+
+	  total = 0;
+	}
+      else
+	{
+	  /* We have no data.  This means we send the standard reply for this
+	     case.  */
+	  total = sizeof (notfound);
+
+	  if (fd != -1
+	      && TEMP_FAILURE_RETRY (send (fd, &notfound, total,
+					   MSG_NOSIGNAL)) != total)
+	    all_written = false;
+
+	  /* If we have a transient error or cannot permanently store
+	     the result, so be it.  */
+	  if (errval == EAGAIN || __builtin_expect (db->negtimeout == 0, 0))
+	    {
+	      /* Mark the old entry as obsolete.  */
+	      if (dh != NULL)
+		dh->usable = false;
+	    }
+	  else if ((dataset = mempool_alloc (db, (sizeof (struct dataset)
+						  + req->key_len), 1)) != NULL)
+	    {
+	      timeout = datahead_init_neg (&dataset->head,
+					   (sizeof (struct dataset)
+					    + req->key_len), total,
+					   db->negtimeout);
+
+	      /* This is the reply.  */
+	      memcpy (&dataset->resp, &notfound, total);
+
+	      /* Copy the key data.  */
+	      memcpy (dataset->strdata, key, req->key_len);
+
+	      /* If necessary, we also propagate the data to disk.  */
+	      if (db->persistent)
+		{
+		  // XXX async OK?
+		  uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
+		  msync ((void *) pval,
+			 ((uintptr_t) dataset & pagesize_m1)
+			 + sizeof (struct dataset) + req->key_len, MS_ASYNC);
+		}
+
+	      (void) cache_add (req->type, &dataset->strdata, req->key_len,
+				&dataset->head, true, db, owner, he == NULL);
+
+	      pthread_rwlock_unlock (&db->lock);
+
+	      /* Mark the old entry as obsolete.  */
+	      if (dh != NULL)
+		dh->usable = false;
+	    }
+	}
+    }
+  else
+    {
+      /* Determine the I/O structure.  */
+      size_t s_name_len = strlen (serv->s_name) + 1;
+      size_t s_proto_len = strlen (serv->s_proto) + 1;
+      uint32_t *s_aliases_len;
+      size_t s_aliases_cnt;
+      char *aliases;
+      char *cp;
+      size_t cnt;
+
+      /* Determine the number of aliases.  */
+      s_aliases_cnt = 0;
+      for (cnt = 0; serv->s_aliases[cnt] != NULL; ++cnt)
+	++s_aliases_cnt;
+      /* Determine the length of all aliases.  */
+      s_aliases_len = (uint32_t *) alloca (s_aliases_cnt * sizeof (uint32_t));
+      total = 0;
+      for (cnt = 0; cnt < s_aliases_cnt; ++cnt)
+	{
+	  s_aliases_len[cnt] = strlen (serv->s_aliases[cnt]) + 1;
+	  total += s_aliases_len[cnt];
+	}
+
+      total += (offsetof (struct dataset, strdata)
+		+ s_name_len
+		+ s_proto_len
+		+ s_aliases_cnt * sizeof (uint32_t));
+
+      /* If we refill the cache, first assume the reconrd did not
+	 change.  Allocate memory on the cache since it is likely
+	 discarded anyway.  If it turns out to be necessary to have a
+	 new record we can still allocate real memory.  */
+      bool alloca_used = false;
+      dataset = NULL;
+
+      if (he == NULL)
+	dataset = (struct dataset *) mempool_alloc (db, total + req->key_len,
+						    1);
+
+      if (dataset == NULL)
+	{
+	  /* We cannot permanently add the result in the moment.  But
+	     we can provide the result as is.  Store the data in some
+	     temporary memory.  */
+	  dataset = (struct dataset *) alloca (total + req->key_len);
+
+	  /* We cannot add this record to the permanent database.  */
+	  alloca_used = true;
+	}
+
+      timeout = datahead_init_pos (&dataset->head, total + req->key_len,
+				   total - offsetof (struct dataset, resp),
+				   he == NULL ? 0 : dh->nreloads + 1,
+				   db->postimeout);
+
+      dataset->resp.version = NSCD_VERSION;
+      dataset->resp.found = 1;
+      dataset->resp.s_name_len = s_name_len;
+      dataset->resp.s_proto_len = s_proto_len;
+      dataset->resp.s_port = serv->s_port;
+      dataset->resp.s_aliases_cnt = s_aliases_cnt;
+
+      cp = dataset->strdata;
+
+      cp = mempcpy (cp, serv->s_name, s_name_len);
+      cp = mempcpy (cp, serv->s_proto, s_proto_len);
+      cp = mempcpy (cp, s_aliases_len, s_aliases_cnt * sizeof (uint32_t));
+
+      /* Then the aliases.  */
+      aliases = cp;
+      for (cnt = 0; cnt < s_aliases_cnt; ++cnt)
+	cp = mempcpy (cp, serv->s_aliases[cnt], s_aliases_len[cnt]);
+
+      assert (cp
+	      == dataset->strdata + total - offsetof (struct dataset,
+						      strdata));
+
+      char *key_copy = memcpy (cp, key, req->key_len);
+
+      /* Now we can determine whether on refill we have to create a new
+	 record or not.  */
+      if (he != NULL)
+	{
+	  assert (fd == -1);
+
+	  if (total + req->key_len == dh->allocsize
+	      && total - offsetof (struct dataset, resp) == dh->recsize
+	      && memcmp (&dataset->resp, dh->data,
+			 dh->allocsize - offsetof (struct dataset, resp)) == 0)
+	    {
+	      /* The data has not changed.  We will just bump the
+		 timeout value.  Note that the new record has been
+		 allocated on the stack and need not be freed.  */
+	      dh->timeout = dataset->head.timeout;
+	      ++dh->nreloads;
+	    }
+	  else
+	    {
+	      /* We have to create a new record.  Just allocate
+		 appropriate memory and copy it.  */
+	      struct dataset *newp
+		= (struct dataset *) mempool_alloc (db, total + req->key_len,
+						    1);
+	      if (newp != NULL)
+		{
+		  /* Adjust pointers into the memory block.  */
+		  aliases = (char *) newp + (aliases - (char *) dataset);
+		  assert (key_copy != NULL);
+		  key_copy = (char *) newp + (key_copy - (char *) dataset);
+
+		  dataset = memcpy (newp, dataset, total + req->key_len);
+		  alloca_used = false;
+		}
+
+	      /* Mark the old record as obsolete.  */
+	      dh->usable = false;
+	    }
+	}
+      else
+	{
+	  /* We write the dataset before inserting it to the database
+	     since while inserting this thread might block and so would
+	     unnecessarily keep the receiver waiting.  */
+	  assert (fd != -1);
+
+#ifdef HAVE_SENDFILE
+	  if (__builtin_expect (db->mmap_used, 1) && !alloca_used)
+	    {
+	      assert (db->wr_fd != -1);
+	      assert ((char *) &dataset->resp > (char *) db->data);
+	      assert ((char *) dataset - (char *) db->head
+		      + total
+		      <= (sizeof (struct database_pers_head)
+			  + db->head->module * sizeof (ref_t)
+			  + db->head->data_size));
+	      ssize_t written = sendfileall (fd, db->wr_fd,
+					     (char *) &dataset->resp
+					     - (char *) db->head,
+					     dataset->head.recsize);
+	      if (written != dataset->head.recsize)
+		{
+# ifndef __ASSUME_SENDFILE
+		  if (written == -1 && errno == ENOSYS)
+		    goto use_write;
+# endif
+		  all_written = false;
+		}
+	    }
+	  else
+# ifndef __ASSUME_SENDFILE
+	  use_write:
+# endif
+#endif
+	    if (writeall (fd, &dataset->resp, dataset->head.recsize)
+		!= dataset->head.recsize)
+	      all_written = false;
+	}
+
+      /* Add the record to the database.  But only if it has not been
+	 stored on the stack.  */
+      if (! alloca_used)
+	{
+	  /* If necessary, we also propagate the data to disk.  */
+	  if (db->persistent)
+	    {
+	      // XXX async OK?
+	      uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
+	      msync ((void *) pval,
+		     ((uintptr_t) dataset & pagesize_m1)
+		     + total + req->key_len, MS_ASYNC);
+	    }
+
+	  (void) cache_add (req->type, key_copy, req->key_len,
+			    &dataset->head, true, db, owner, he == NULL);
+
+	  pthread_rwlock_unlock (&db->lock);
+	}
+    }
+
+  if (__builtin_expect (!all_written, 0) && debug_level > 0)
+    {
+      char buf[256];
+      dbg_log (_("short write in %s: %s"),  __FUNCTION__,
+	       strerror_r (errno, buf, sizeof (buf)));
+    }
+
+  return timeout;
+}
+
+
+static int
+lookup (int type, char *key, struct servent *resultbufp, char *buffer,
+	size_t buflen, struct servent **serv)
+{
+  char *proto = strrchr (key, '/');
+  if (proto != NULL && proto != key)
+    {
+      key = strndupa (key, proto - key);
+      if (proto[1] == '\0')
+	proto = NULL;
+      else
+	++proto;
+    }
+
+  if (type == GETSERVBYNAME)
+    return __getservbyname_r (key, proto, resultbufp, buffer, buflen, serv);
+
+  assert (type == GETSERVBYPORT);
+  return __getservbyport_r (atol (key), proto, resultbufp, buffer, buflen,
+			    serv);
+}
+
+
+static time_t
+addservbyX (struct database_dyn *db, int fd, request_header *req,
+	    char *key, uid_t uid, struct hashentry *he, struct datahead *dh)
+{
+  /* Search for the entry matching the key.  Please note that we don't
+     look again in the table whether the dataset is now available.  We
+     simply insert it.  It does not matter if it is in there twice.  The
+     pruning function only will look at the timestamp.  */
+  size_t buflen = 1024;
+  char *buffer = (char *) alloca (buflen);
+  struct servent resultbuf;
+  struct servent *serv;
+  bool use_malloc = false;
+  int errval = 0;
+
+  if (__glibc_unlikely (debug_level > 0))
+    {
+      if (he == NULL)
+	dbg_log (_("Haven't found \"%s\" in services cache!"), key);
+      else
+	dbg_log (_("Reloading \"%s\" in services cache!"), key);
+    }
+
+  while (lookup (req->type, key, &resultbuf, buffer, buflen, &serv) != 0
+	 && (errval = errno) == ERANGE)
+    {
+      errno = 0;
+
+      if (__glibc_unlikely (buflen > 32768))
+	{
+	  char *old_buffer = buffer;
+	  buflen *= 2;
+	  buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen);
+	  if (buffer == NULL)
+	    {
+	      /* We ran out of memory.  We cannot do anything but
+		 sending a negative response.  In reality this should
+		 never happen.  */
+	      serv = NULL;
+	      buffer = old_buffer;
+
+	      /* We set the error to indicate this is (possibly) a
+		 temporary error and that it does not mean the entry
+		 is not available at all.  */
+	      errval = EAGAIN;
+	      break;
+	    }
+	  use_malloc = true;
+	}
+      else
+	/* Allocate a new buffer on the stack.  If possible combine it
+	   with the previously allocated buffer.  */
+	buffer = (char *) extend_alloca (buffer, buflen, 2 * buflen);
+    }
+
+  time_t timeout = cache_addserv (db, fd, req, key, serv, uid, he, dh, errval);
+
+  if (use_malloc)
+    free (buffer);
+
+  return timeout;
+}
+
+
+void
+addservbyname (struct database_dyn *db, int fd, request_header *req,
+	       void *key, uid_t uid)
+{
+  addservbyX (db, fd, req, key, uid, NULL, NULL);
+}
+
+
+time_t
+readdservbyname (struct database_dyn *db, struct hashentry *he,
+		 struct datahead *dh)
+{
+  request_header req =
+    {
+      .type = GETSERVBYNAME,
+      .key_len = he->len
+    };
+
+  return addservbyX (db, -1, &req, db->data + he->key, he->owner, he, dh);
+}
+
+
+void
+addservbyport (struct database_dyn *db, int fd, request_header *req,
+	       void *key, uid_t uid)
+{
+  addservbyX (db, fd, req, key, uid, NULL, NULL);
+}
+
+
+time_t
+readdservbyport (struct database_dyn *db, struct hashentry *he,
+		 struct datahead *dh)
+{
+  request_header req =
+    {
+      .type = GETSERVBYPORT,
+      .key_len = he->len
+    };
+
+  return addservbyX (db, -1, &req, db->data + he->key, he->owner, he, dh);
+}