Update.
authorUlrich Drepper <drepper@redhat.com>
Wed, 8 Sep 2004 15:46:42 +0000 (15:46 +0000)
committerUlrich Drepper <drepper@redhat.com>
Wed, 8 Sep 2004 15:46:42 +0000 (15:46 +0000)
* nscd/connections.c: Implement r/o sharing of nscd's cache with client
processes via shared memory.
* nscd/nscd-client.h: Likewise.
* nscd/nscd.h: Likewise.
* nscd/nscd_conf.c: Likewise.
* nscd/nscd_getgr_r.c: Likewise.
* nscd/nscd_getpw_r.c: Likewise.
* nscd/nscd_gethst_r.c: Likewise.
* nscd/nscd.conf: Add new config parameters.
* nscd/Makefile (aux): Add nscd_helper.
* nscd/nscd_helper.c: New file.
* nscd/mem.c (gc): Indicate beginning and end of the gc cycle.

* nscd/hstcache.c: Simplify a lot.  We cache only the request itself,
no derived information.
* connections.c (nscd_init): Fix bug in testing size of the persistent.

* nis/Makefile (aux): Add nis_hash.
* nis/nis_hash.c: New file.  Split out from nis_util.c.
* nis/nis_util.c: Move __nis_hash code in separate file.

* csu/tst-atomic.c: Improve atomic_increment_val test which would
not have found a ppc bug.

17 files changed:
ChangeLog
csu/tst-atomic.c
nis/Makefile
nis/nis_hash.c [new file with mode: 0644]
nis/nis_util.c
nscd/Makefile
nscd/connections.c
nscd/hstcache.c
nscd/mem.c
nscd/nscd-client.h
nscd/nscd.conf
nscd/nscd.h
nscd/nscd_conf.c
nscd/nscd_getgr_r.c
nscd/nscd_gethst_r.c
nscd/nscd_getpw_r.c
nscd/nscd_helper.c [new file with mode: 0644]

index 2a4f1c4..4338896 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,29 @@
 2004-09-08  Ulrich Drepper  <drepper@redhat.com>
 
+       * nscd/connections.c: Implement r/o sharing of nscd's cache with client
+       processes via shared memory.
+       * nscd/nscd-client.h: Likewise.
+       * nscd/nscd.h: Likewise.
+       * nscd/nscd_conf.c: Likewise.
+       * nscd/nscd_getgr_r.c: Likewise.
+       * nscd/nscd_getpw_r.c: Likewise.
+       * nscd/nscd_gethst_r.c: Likewise.
+       * nscd/nscd.conf: Add new config parameters.
+       * nscd/Makefile (aux): Add nscd_helper.
+       * nscd/nscd_helper.c: New file.
+       * nscd/mem.c (gc): Indicate beginning and end of the gc cycle.
+
+       * nscd/hstcache.c: Simplify a lot.  We cache only the request itself,
+       no derived information.
+       * connections.c (nscd_init): Fix bug in testing size of the persistent.
+
+       * nis/Makefile (aux): Add nis_hash.
+       * nis/nis_hash.c: New file.  Split out from nis_util.c.
+       * nis/nis_util.c: Move __nis_hash code in separate file.
+
+       * csu/tst-atomic.c: Improve atomic_increment_val test which would
+       not have found a ppc bug.
+
        * sysdeps/s390/fpu/bits/mathinline.h: Remove unnecessary includes.
 
        * malloc/arena.c: Remove __MALLOC_P uses.
index cb6b6ba..7a2e3d0 100644 (file)
@@ -130,7 +130,8 @@ do_test (void)
       ret = 1;
     }
 
-  if (atomic_increment_val (&mem) != 1)
+  mem = 2;
+  if (atomic_increment_val (&mem) != 3)
     {
       puts ("atomic_increment_val test failed");
       ret = 1;
index 9f429ff..fa5c4df 100644 (file)
@@ -21,6 +21,8 @@
 #
 subdir := nis
 
+aux                    := nis_hash
+
 headers                        := $(wildcard rpcsvc/*.[hx])
 distribute             := nss-nis.h nss-nisplus.h nis_intern.h Banner \
                           nisplus-parser.h nis_xdr.h nss
diff --git a/nis/nis_hash.c b/nis/nis_hash.c
new file mode 100644 (file)
index 0000000..ddd9151
--- /dev/null
@@ -0,0 +1,76 @@
+/* Copyright (c) 1997, 1998, 1999, 2004 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Thorsten Kukuk <kukuk@suse.de>, 1997.
+
+   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, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <rpcsvc/nis.h>
+
+/* This is from libc/db/hash/hash_func.c, hash3 is static there */
+/*
+ * This is INCREDIBLY ugly, but fast.  We break the string up into 8 byte
+ * units.  On the first time through the loop we get the "leftover bytes"
+ * (strlen % 8).  On every other iteration, we perform 8 HASHC's so we handle
+ * all 8 bytes.  Essentially, this saves us 7 cmp & branch instructions.  If
+ * this routine is heavily used enough, it's worth the ugly coding.
+ *
+ * OZ's original sdbm hash
+ */
+uint32_t
+__nis_hash (const void *keyarg, register size_t len)
+{
+  register const u_char *key;
+  register size_t loop;
+  register uint32_t h;
+
+#define HASHC   h = *key++ + 65599 * h
+
+  h = 0;
+  key = keyarg;
+  if (len > 0)
+    {
+      loop = (len + 8 - 1) >> 3;
+      switch (len & (8 - 1))
+       {
+       case 0:
+         do {
+           HASHC;
+           /* FALLTHROUGH */
+         case 7:
+           HASHC;
+           /* FALLTHROUGH */
+         case 6:
+           HASHC;
+           /* FALLTHROUGH */
+         case 5:
+           HASHC;
+           /* FALLTHROUGH */
+         case 4:
+           HASHC;
+           /* FALLTHROUGH */
+         case 3:
+           HASHC;
+           /* FALLTHROUGH */
+         case 2:
+           HASHC;
+           /* FALLTHROUGH */
+         case 1:
+           HASHC;
+         } while (--loop);
+       }
+    }
+  return h;
+}
index 99313be..1220987 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 1997, 1998, 1999 Free Software Foundation, Inc.
+/* Copyright (c) 1997, 1998, 1999, 2004 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Thorsten Kukuk <kukuk@suse.de>, 1997.
 
@@ -47,58 +47,6 @@ __nis_finddirectory (directory_obj *dir, const_nis_name name)
   return fd_res;
 }
 
-/* This is from libc/db/hash/hash_func.c, hash3 is static there */
-/*
- * This is INCREDIBLY ugly, but fast.  We break the string up into 8 byte
- * units.  On the first time through the loop we get the "leftover bytes"
- * (strlen % 8).  On every other iteration, we perform 8 HASHC's so we handle
- * all 8 bytes.  Essentially, this saves us 7 cmp & branch instructions.  If
- * this routine is heavily used enough, it's worth the ugly coding.
- *
- * OZ's original sdbm hash
- */
-uint32_t
-__nis_hash (const void *keyarg, register size_t len)
-{
-  register const u_char *key;
-  register size_t loop;
-  register uint32_t h;
-
-#define HASHC   h = *key++ + 65599 * h
 
-  h = 0;
-  key = keyarg;
-  if (len > 0)
-    {
-      loop = (len + 8 - 1) >> 3;
-      switch (len & (8 - 1))
-       {
-       case 0:
-         do {
-           HASHC;
-           /* FALLTHROUGH */
-         case 7:
-           HASHC;
-           /* FALLTHROUGH */
-         case 6:
-           HASHC;
-           /* FALLTHROUGH */
-         case 5:
-           HASHC;
-           /* FALLTHROUGH */
-         case 4:
-           HASHC;
-           /* FALLTHROUGH */
-         case 3:
-           HASHC;
-           /* FALLTHROUGH */
-         case 2:
-           HASHC;
-           /* FALLTHROUGH */
-         case 1:
-           HASHC;
-         } while (--loop);
-       }
-    }
-  return (h);
-}
+/* The hash implementation is in a separate file.  */
+#include "nis_hash.c"
index cb82a2e..5b35292 100644 (file)
@@ -22,6 +22,7 @@
 subdir := nscd
 
 routines := nscd_getpw_r nscd_getgr_r nscd_gethst_r
+aux    := nscd_helper
 
 include ../Makeconfig
 
index 53e258b..552d554 100644 (file)
@@ -80,7 +80,10 @@ const char *serv2str[LASTREQ] =
   [GETHOSTBYADDRv6] = "GETHOSTBYADDRv6",
   [SHUTDOWN] = "SHUTDOWN",
   [GETSTAT] = "GETSTAT",
-  [INVALIDATE] = "INVALIDATE"
+  [INVALIDATE] = "INVALIDATE",
+  [GETFDPW] = "GETFDPW",
+  [GETFDGR] = "GETFDGR",
+  [GETFDHST] = "GETFDHST"
 };
 
 /* The control data structures for the services.  */
@@ -91,6 +94,7 @@ struct database_dyn dbs[lastdb] =
     .enabled = 0,
     .check_file = 1,
     .persistent = 0,
+    .shared = 0,
     .filename = "/etc/passwd",
     .db_filename = _PATH_NSCD_PASSWD_DB,
     .disabled_iov = &pwd_iov_disabled,
@@ -105,6 +109,7 @@ struct database_dyn dbs[lastdb] =
     .enabled = 0,
     .check_file = 1,
     .persistent = 0,
+    .shared = 0,
     .filename = "/etc/group",
     .db_filename = _PATH_NSCD_GROUP_DB,
     .disabled_iov = &grp_iov_disabled,
@@ -119,6 +124,7 @@ struct database_dyn dbs[lastdb] =
     .enabled = 0,
     .check_file = 1,
     .persistent = 0,
+    .shared = 0,
     .filename = "/etc/hosts",
     .db_filename = _PATH_NSCD_HOSTS_DB,
     .disabled_iov = &hst_iov_disabled,
@@ -132,7 +138,7 @@ struct database_dyn dbs[lastdb] =
 
 
 /* Mapping of request type to database.  */
-static struct database_dyn *const serv2db[LASTDBREQ + 1] =
+static struct database_dyn *const serv2db[LASTREQ] =
 {
   [GETPWBYNAME] = &dbs[pwddb],
   [GETPWBYUID] = &dbs[pwddb],
@@ -141,7 +147,10 @@ static struct database_dyn *const serv2db[LASTDBREQ + 1] =
   [GETHOSTBYNAME] = &dbs[hstdb],
   [GETHOSTBYNAMEv6] = &dbs[hstdb],
   [GETHOSTBYADDR] = &dbs[hstdb],
-  [GETHOSTBYADDRv6] = &dbs[hstdb]
+  [GETHOSTBYADDRv6] = &dbs[hstdb],
+  [GETFDPW] = &dbs[pwddb],
+  [GETFDGR] = &dbs[grpdb],
+  [GETFDHST] = &dbs[hstdb],
 };
 
 
@@ -158,9 +167,6 @@ static int sock;
 /* Number of times clients had to wait.  */
 unsigned long int client_queued;
 
-/* Alignment requirement of the beginning of the data region.  */
-#define ALIGN 16
-
 
 /* Initialize database information structures.  */
 void
@@ -224,11 +230,10 @@ nscd_init (void)
                    dbs[cnt].persistent = 0;
                  }
                else if ((total = (sizeof (head)
-                                  + roundup (head.module
-                                             * sizeof (struct hashentry),
+                                  + roundup (head.module * sizeof (ref_t),
                                              ALIGN)
                                   + head.data_size))
-                        < st.st_size)
+                        > st.st_size)
                  {
                    dbg_log (_("invalid persistent database file \"%s\": %s"),
                             dbs[cnt].db_filename,
@@ -253,6 +258,7 @@ nscd_init (void)
                               dbnames[cnt]);
 
                    dbs[cnt].wr_fd = fd;
+                   dbs[cnt].ro_fd = open (dbs[cnt].db_filename, O_RDONLY);
                    fd = -1;
                    /* We also need a read-only descriptor.  */
                    dbs[cnt].ro_fd = open (dbs[cnt].db_filename, O_RDONLY);
@@ -439,6 +445,9 @@ cannot create read-only descriptor for \"%s\"; no mmap"),
                                        * 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);
          }
 
        if (dbs[cnt].check_file)
@@ -529,6 +538,43 @@ invalidate_cache (char *key)
 }
 
 
+#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.  */
+  struct iovec iov[1];
+  iov[0].iov_base = key;
+  iov[0].iov_len = strlen (key) + 1;
+
+  /* Prepare the control message to transfer the descriptor.  */
+  char buf[CMSG_SPACE (sizeof (int))];
+  struct msghdr msg = { .msg_iov = iov, .msg_iovlen = 1,
+                       .msg_control = buf, .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 *) CMSG_DATA (cmsg) = db->ro_fd;
+
+  msg.msg_controllen = cmsg->cmsg_len;
+
+  /* Send the control message.  We repeat when we are interrupted but
+     everything else is ignored.  */
+  (void) TEMP_FAILURE_RETRY (sendmsg (fd, &msg, 0));
+
+  if (__builtin_expect (debug_level > 0, 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)
@@ -614,7 +660,7 @@ cannot handle old request version %d; current version is %d"),
   else if (__builtin_expect (debug_level, 0) > 0)
     {
       if (req->type == INVALIDATE)
-       dbg_log ("\t%s (%s)", serv2str[req->type], (char *)key);
+       dbg_log ("\t%s (%s)", serv2str[req->type], (char *) key);
       else
        dbg_log ("\t%s", serv2str[req->type]);
     }
@@ -697,6 +743,14 @@ cannot handle old request version %d; current version is %d"),
        }
       break;
 
+    case GETFDPW:
+    case GETFDGR:
+    case GETFDHST:
+#ifdef SCM_RIGHTS
+      send_ro_fd (serv2db[req->type], key, fd);
+#endif
+      break;
+
     default:
       /* Ignore the command, it's nothing we know.  */
       break;
@@ -733,7 +787,9 @@ nscd_run (void *p)
          int timeout = -1;
          if (run_prune)
            {
-             now = time (NULL);
+             /* NB: we do not flush the timestamp update using msync since
+                this value doesnot matter on disk.  */
+             dbs[my_number].head->timestamp = now = time (NULL);
              timeout = now < next_prune ? 1000 * (next_prune - now) : 0;
            }
 
index 5a536b3..94568d0 100644 (file)
@@ -77,7 +77,7 @@ static const hst_response_header notfound =
 
 static void
 cache_addhst (struct database_dyn *db, int fd, request_header *req,
-             const void *key, struct hostent *hst, uid_t owner, int add_addr,
+             const void *key, struct hostent *hst, uid_t owner,
              struct hashentry *he, struct datahead *dh, int errval)
 {
   ssize_t total;
@@ -208,7 +208,7 @@ cache_addhst (struct database_dyn *db, int fd, request_header *req,
         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 && (add_addr || hst->h_addr_list[1] == NULL))
+      if (he == NULL && hst->h_addr_list[1] == NULL)
        {
          dataset = (struct dataset *) mempool_alloc (db,
                                                      total + req->key_len);
@@ -269,10 +269,7 @@ cache_addhst (struct database_dyn *db, int fd, request_header *req,
         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.  */
-      if (req->type == GETHOSTBYNAME || req->type == GETHOSTBYNAMEv6)
-       key_copy = memcpy (cp, key, req->key_len);
-      else
-       memset (cp, '\0', req->key_len);
+      key_copy = memcpy (cp, key, req->key_len);
 
       /* Now we can determine whether on refill we have to create a new
         record or not.  */
@@ -349,141 +346,21 @@ cache_addhst (struct database_dyn *db, int fd, request_header *req,
             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 (add_addr || hst->h_addr_list[1] == NULL);
-
-         /* Add the normal addresses.  */
-         if (add_addr)
-           {
-             for (cnt = 0; cnt < h_addr_list_cnt; ++cnt)
-               {
-                 if (cache_add (addr_list_type, addresses, hst->h_length,
-                                &dataset->head, cnt == 0, db, owner) < 0)
-                   {
-                     /* Ensure the data can be recovered.  */
-                     if (cnt == 0)
-                       dataset->head.usable = false;
-                     goto out;
-                   }
-                 addresses += hst->h_length;
-               }
-
-             /* If necessary the IPv6 addresses.  */
-             if (addr_list_type == GETHOSTBYADDR)
-               for (cnt = 0; cnt < h_addr_list_cnt; ++cnt)
-                 {
-                   if (cache_add (GETHOSTBYADDRv6, addresses, IN6ADDRSZ,
-                                  &dataset->head, false, db, owner) < 0)
-                     goto out;
+         assert (hst->h_addr_list[1] == NULL);
 
-                   addresses += IN6ADDRSZ;
-                 }
-           }
          /* Avoid adding names if more than one address is available.  See
             above for more info.  */
-         else
-           {
-             assert (req->type == GETHOSTBYNAME
-                     || req->type == GETHOSTBYNAMEv6
-                     || req->type == GETHOSTBYADDR
-                     || req->type == GETHOSTBYADDRv6);
-
-             /* If necessary add the key for this request.  */
-             if (req->type == GETHOSTBYNAME)
-               {
-                 bool first = true;
-                 if (addr_list_type == GETHOSTBYADDR)
-                   {
-                     if (cache_add (GETHOSTBYNAME, key_copy, req->key_len,
-                                    &dataset->head, true, db, owner) < 0)
-                       {
-                         /* Could not allocate memory.  Make sure the
-                            data gets discarded.  */
-                         dataset->head.usable = false;
-                         goto out;
-                       }
-
-                     first = false;
-                   }
-                 if (cache_add (GETHOSTBYNAMEv6, key_copy, req->key_len,
-                                &dataset->head, first, db, owner) < 0)
-                   {
-                     /* Could not allocate memory.  Make sure the
-                        data gets discarded.  */
-                     if (first)
-                       dataset->head.usable = false;
-                     goto out;
-                   }
-               }
-             else if (req->type == GETHOSTBYNAMEv6)
-               {
-                 if (cache_add (GETHOSTBYNAMEv6, key_copy, req->key_len,
-                                &dataset->head, true, db, owner) < 0)
-                   {
-                     /* Could not allocate memory.  Make sure the
-                        data gets discarded.  */
-                     dataset->head.usable = false;
-                     goto out;
-                   }
-
-                 if (addr_list_type == GETHOSTBYADDR
-                     && cache_add (GETHOSTBYNAME, key_copy, req->key_len,
-                                   &dataset->head, false, db, owner) < 0)
-                   goto out;
-               }
-
-             /* And finally the name.  We mark this as the last entry.  */
-             if (addr_list_type == GETHOSTBYADDR
-                 && req->type == GETHOSTBYADDR
-                 && cache_add (GETHOSTBYNAME, dataset->strdata, h_name_len,
-                               &dataset->head, true, db, owner) < 0)
-               {
-                 /* Could not allocate memory.  Make sure the
-                    data gets discarded.  */
-                 dataset->head.usable = false;
-                 goto out;
-               }
-
-             if (cache_add (GETHOSTBYNAMEv6, dataset->strdata,
-                            h_name_len, &dataset->head,
-                            ((req->type == GETHOSTBYADDR
-                              && addr_list_type != GETHOSTBYADDR)
-                             || req->type == GETHOSTBYADDRv6), db,
-                            owner) < 0)
-               {
-                 /* Could not allocate memory.  Make sure the
-                    data gets discarded.  */
-                 if ((req->type == GETHOSTBYADDR
-                      && addr_list_type != GETHOSTBYADDR)
-                     || req->type == GETHOSTBYADDRv6)
-                   dataset->head.usable = false;
-                 goto out;
-               }
+         assert (req->type == GETHOSTBYNAME
+                 || req->type == GETHOSTBYNAMEv6
+                 || req->type == GETHOSTBYADDR
+                 || req->type == GETHOSTBYADDRv6);
 
-             if (addr_list_type == GETHOSTBYADDR
-                 && req->type != GETHOSTBYADDR
-                 && cache_add (GETHOSTBYNAME, dataset->strdata, h_name_len,
-                               &dataset->head, false, db, owner) < 0)
-               goto out;
+         if (cache_add (req->type, key_copy, req->key_len,
+                        &dataset->head, true, db, owner) < 0)
+           /* Could not allocate memory.  Make sure the
+              data gets discarded.  */
+           dataset->head.usable = false;
 
-             /* First add all the aliases.  */
-             for (cnt = 0; cnt < h_aliases_cnt; ++cnt)
-               {
-                 if (addr_list_type == GETHOSTBYADDR)
-                   if (cache_add (GETHOSTBYNAME, aliases,
-                                  h_aliases_len[cnt], &dataset->head,
-                                  false, db, owner) < 0)
-                     break;
-
-                 if (cache_add (GETHOSTBYNAMEv6, aliases,
-                                h_aliases_len[cnt], &dataset->head,
-                                false, db, owner) < 0)
-                   break;
-
-                 aliases += h_aliases_len[cnt];
-               }
-           }
-
-       out:
          pthread_rwlock_unlock (&db->lock);
        }
     }
@@ -534,10 +411,18 @@ addhstbyX (struct database_dyn *db, int fd, request_header *req,
 
   if (__builtin_expect (debug_level > 0, 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 *) key);
+       dbg_log (_("Haven't found \"%s\" in hosts cache!"), (char *) str);
       else
-       dbg_log (_("Reloading \"%s\" in hosts cache!"), (char *) key);
+       dbg_log (_("Reloading \"%s\" in hosts cache!"), (char *) str);
     }
 
   if (db->secure)
@@ -583,7 +468,7 @@ addhstbyX (struct database_dyn *db, int fd, request_header *req,
   if (db->secure)
     seteuid (oldeuid);
 
-  cache_addhst (db, fd, req, key, hst, uid, 0, he, dh,
+  cache_addhst (db, fd, req, key, hst, uid, he, dh,
                h_errno == TRY_AGAIN ? errval : 0);
 
   if (use_malloc)
index a4e3053..5207efc 100644 (file)
@@ -230,6 +230,12 @@ gc (struct database_dyn *db)
     ++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
@@ -445,6 +451,11 @@ gc (struct database_dyn *db)
     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);
index 470bc69..708f62d 100644 (file)
@@ -23,6 +23,7 @@
 #ifndef _NSCD_CLIENT_H
 #define _NSCD_CLIENT_H 1
 
+#include <atomic.h>
 #include <nscd-types.h>
 
 /* Version number of the daemon interface */
@@ -53,6 +54,9 @@ typedef enum
   SHUTDOWN,            /* Shut the server down.  */
   GETSTAT,             /* Get the server statistic.  */
   INVALIDATE,           /* Invalidate one special cache.  */
+  GETFDPW,
+  GETFDGR,
+  GETFDHST,
   LASTREQ
 } request_type;
 
@@ -110,9 +114,151 @@ typedef struct
 } hst_response_header;
 
 
+/* Type for offsets in data part of database.  */
+typedef uint32_t ref_t;
+/* Value for invalid/no reference.  */
+#define ENDREF UINT32_MAX
+
+/* Alignment requirement of the beginning of the data region.  */
+#define ALIGN 16
+
+
+/* Head of record in data part of database.  */
+struct datahead
+{
+  size_t allocsize;    /* Allocated Bytes.  */
+  size_t recsize;      /* Size of the record.  */
+  time_t timeout;      /* Time when this entry becomes invalid.  */
+  bool notfound;       /* Nonzero if data for key has not been found.  */
+  uint8_t nreloads;    /* Reloads without use.  */
+  bool usable;         /* False if the entry must be ignored.  */
+
+  /* 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;
+    ssize_t align1;
+    time_t align2;
+  } data[0];
+};
+
+
+/* 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.  */
+  size_t len;                  /* Length of key.  */
+  ref_t key;                   /* Pointer to key.  */
+  uid_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     1
+
+/* Maximum time allowed between updates of the timestamp.  */
+#define MAPPING_TIMEOUT (5 * 60)
+
+
+/* Header of persistent database file.  */
+struct database_pers_head
+{
+  int version;
+  int header_size;
+  int gc_cycle;
+  volatile time_t timestamp;
+
+  size_t module;
+  size_t data_size;
+
+  size_t first_free;           /* Offset of first free byte in data area.  */
+
+  size_t nentries;
+  size_t maxnentries;
+  size_t maxnsearched;
+
+  uintmax_t poshit;
+  uintmax_t neghit;
+  uintmax_t posmiss;
+  uintmax_t negmiss;
+
+  uintmax_t rdlockdelayed;
+  uintmax_t wrlockdelayed;
+
+  uintmax_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 isusable.  */
+};
+#define NO_MAPPING ((struct mapped_database *) -1l)
+
+struct locked_map_ptr
+{
+  int lock;
+  struct mapped_database *mapped;
+};
+#define libc_locked_map_ptr(name) static struct locked_map_ptr name
+
+
 /* 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;
 
+/* Get reference of mapping.  */
+extern struct mapped_database *__nscd_get_map_ref (request_type type,
+                                                  const char *name,
+                                                  struct locked_map_ptr *mapptr,
+                                                  int *gc_cyclep);
+
+/* Unmap database.  */
+extern void __nscd_unmap (struct mapped_database *mapped);
+
+/* Drop reference of mapping.  */
+static inline int __nscd_drop_map_ref (struct mapped_database *map,
+                                      int gc_cycle)
+{
+  if (map != NO_MAPPING)
+    {
+      if (__builtin_expect (map->head->gc_cycle != gc_cycle, 0))
+       /* We might have read inconsistent data.  */
+       return -1;
+
+      if (atomic_decrement_val (&map->counter) == 0)
+       __nscd_unmap (map);
+    }
+
+  return 0;
+}
+
+
+/* Search the mapped database.  */
+extern const struct datahead * __nscd_cache_search (request_type type,
+                                                   const char *key,
+                                                   size_t keylen,
+                                                   const struct mapped_database *mapped);
+
 #endif /* nscd.h */
index 11d26b9..f972851 100644 (file)
@@ -19,6 +19,7 @@
 #       suggested-size         <service> <prime number>
 #      check-files             <service> <yes|no>
 #      persistent              <service> <yes|no>
+#      shared                  <service> <yes|no>
 #
 # Currently supported cache names (services): passwd, group, hosts
 #
@@ -37,6 +38,7 @@
        suggested-size          passwd          211
        check-files             passwd          yes
        persistent              passwd          yes
+       shared                  passwd          yes
 
        enable-cache            group           yes
        positive-time-to-live   group           3600
@@ -44,6 +46,7 @@
        suggested-size          group           211
        check-files             group           yes
        persistent              group           yes
+       shared                  group           yes
 
        enable-cache            hosts           yes
        positive-time-to-live   hosts           3600
@@ -51,3 +54,4 @@
        suggested-size          hosts           211
        check-files             hosts           yes
        persistent              hosts           yes
+       shared                  hosts           yes
index 45a93fd..889588c 100644 (file)
@@ -42,30 +42,6 @@ typedef enum
 } dbtype;
 
 
-/* Head of record in data part of database.  */
-struct datahead
-{
-  size_t allocsize;    /* Allocated Bytes.  */
-  size_t recsize;      /* Size of the record.  */
-  time_t timeout;      /* Time when this entry becomes invalid.  */
-  bool notfound;       /* Nonzero if data for key has not been found.  */
-  uint8_t nreloads;    /* Reloads without use.  */
-  bool usable;         /* False if the entry must be ignored.  */
-
-  /* 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;
-    ssize_t align1;
-    time_t align2;
-  } data[0];
-};
-
-
 /* 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
@@ -74,63 +50,6 @@ struct datahead
 #define DEFAULT_RELOAD_LIMIT 5
 
 
-/* Type for offsets in data part of database.  */
-typedef uint32_t ref_t;
-/* Value for invalid/no reference.  */
-#define ENDREF UINT32_MAX
-
-
-/* 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.  */
-  size_t len;                  /* Length of key.  */
-  ref_t key;                   /* Pointer to key.  */
-  uid_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     1
-
-/* Header of persistent database file.  */
-struct database_pers_head
-{
-  int version;
-  int header_size;
-
-  size_t module;
-  size_t data_size;
-
-  size_t first_free;           /* Offset of first free byte in data area.  */
-
-  size_t nentries;
-  size_t maxnentries;
-  size_t maxnsearched;
-
-  uintmax_t poshit;
-  uintmax_t neghit;
-  uintmax_t posmiss;
-  uintmax_t negmiss;
-
-  uintmax_t rdlockdelayed;
-  uintmax_t wrlockdelayed;
-
-  uintmax_t addfailed;
-
-  ref_t array[0];
-};
-
 /* Structure describing dynamic part of one database.  */
 struct database_dyn
 {
@@ -139,6 +58,7 @@ struct database_dyn
   int enabled;
   int check_file;
   int persistent;
+  int shared;
   const char *filename;
   const char *db_filename;
   time_t file_mtime;
index be7527e..2e6f812 100644 (file)
@@ -216,6 +216,20 @@ nscd_parse_file (const char *fname, struct database_dyn dbs[lastdb])
          if (cnt == lastdb)
            dbg_log ("database %s is not supported\n", arg1);
        }
+      else if (strcmp (entry, "shared") == 0)
+       {
+         for (cnt = 0; cnt < lastdb; ++cnt)
+           if (strcmp (arg1, dbnames[cnt]) == 0)
+             {
+               if (strcmp (arg2, "no") == 0)
+                 dbs[cnt].shared = 0;
+               else if (strcmp (arg2, "yes") == 0)
+                 dbs[cnt].shared = 1;
+               break;
+             }
+         if (cnt == lastdb)
+           dbg_log ("database %s is not supported\n", arg1);
+       }
       else if (strcmp (entry, "reload-count") == 0)
        {
          if (strcasecmp (arg1, "unlimited") == 0)
index b9cde73..3a66bb8 100644 (file)
@@ -18,6 +18,7 @@
    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
    02111-1307 USA.  */
 
+#include <assert.h>
 #include <errno.h>
 #include <grp.h>
 #include <stdint.h>
@@ -25,6 +26,7 @@
 #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>
@@ -64,36 +66,82 @@ __nscd_getgrgid_r (gid_t gid, struct group *resultbuf, char *buffer,
 }
 
 
+libc_locked_map_ptr (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 (gr_map_free)
+{
+
+  if (map_handle.mapped != NO_MAPPING)
+    free (map_handle.mapped);
+}
+
+
 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)
 {
-  gr_response_header gr_resp;
-  int sock = __nscd_open_socket (key, keylen, type, &gr_resp,
-                                sizeof (gr_resp));
-  if (sock == -1)
+  const gr_response_header *gr_resp = NULL;
+  const uint32_t *len = NULL;
+  const char *gr_name = NULL;
+  size_t gr_name_len = 0;
+  int retval = -1;
+  int gc_cycle;
+  const char *recend = (const char *) ~UINTMAX_C (0);
+
+  /* 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",
+                                                      &map_handle, &gc_cycle);
+ retry:
+  if (mapped != NO_MAPPING)
     {
-      __nss_not_use_nscd_group = 1;
-      return -1;
+      const struct datahead *found = __nscd_cache_search (type, key, keylen,
+                                                         mapped);
+      if (found != NULL)
+       {
+         gr_resp = &found->data[0].grdata;
+         len = (const uint32_t *) (gr_resp + 1);
+         /* The alignment is always sufficient.  */
+         assert (((uintptr_t) len & (__alignof__ (*len) - 1)) == 0);
+         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;
+       }
+    }
+
+  gr_response_header gr_resp_mem;
+  int sock = -1;
+  if (gr_resp == NULL)
+    {
+      sock = __nscd_open_socket (key, keylen, type, &gr_resp_mem,
+                                sizeof (gr_resp_mem));
+      if (sock == -1)
+       {
+         __nss_not_use_nscd_group = 1;
+         goto out;
+       }
+
+      gr_resp = &gr_resp_mem;
     }
 
   /* No value found so far.  */
-  int retval = -1;
   *result = NULL;
 
-  if (gr_resp.found == -1)
+  if (__builtin_expect (gr_resp->found == -1, 0))
     {
       /* The daemon does not cache this database.  */
       __nss_not_use_nscd_group = 1;
-      goto out;
+      goto out_close;
     }
 
-  if (gr_resp.found == 1)
+  if (gr_resp->found == 1)
     {
       struct iovec vec[2];
-      uint32_t *len;
       char *p = buffer;
       size_t total_len;
       uintptr_t align;
@@ -103,71 +151,90 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type,
         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;
+      total_len = (align + (1 + gr_resp->gr_mem_cnt) * sizeof (char *)
+                  + gr_resp->gr_name_len + gr_resp->gr_passwd_len);
       if (__builtin_expect (buflen < total_len, 0))
        {
        no_room:
          __set_errno (ERANGE);
          retval = ERANGE;
-         goto out;
+         goto out_close;
        }
       buflen -= total_len;
 
       p += align;
       resultbuf->gr_mem = (char **) p;
-      p += (1 + gr_resp.gr_mem_cnt) * sizeof (char *);
+      p += (1 + gr_resp->gr_mem_cnt) * sizeof (char *);
 
       /* Set pointers for strings.  */
       resultbuf->gr_name = p;
-      p += gr_resp.gr_name_len;
+      p += gr_resp->gr_name_len;
       resultbuf->gr_passwd = p;
-      p += gr_resp.gr_passwd_len;
+      p += gr_resp->gr_passwd_len;
 
       /* Fill in what we know now.  */
-      resultbuf->gr_gid = gr_resp.gr_gid;
+      resultbuf->gr_gid = gr_resp->gr_gid;
 
-      /* Allocate array to store lengths.  */
-      len = (uint32_t *) alloca (gr_resp.gr_mem_cnt * sizeof (uint32_t));
+      /* Read the length information, group name, and password.  */
+      if (len == NULL)
+       {
+         /* Allocate array to store lengths.  */
+         len = (uint32_t *) alloca (gr_resp->gr_mem_cnt * sizeof (uint32_t));
 
-      total_len = gr_resp.gr_mem_cnt * sizeof (uint32_t);
-      vec[0].iov_base = len;
-      vec[0].iov_len = total_len;
-      vec[1].iov_base = resultbuf->gr_name;
-      vec[1].iov_len = gr_resp.gr_name_len + gr_resp.gr_passwd_len;
-      total_len += gr_resp.gr_name_len + gr_resp.gr_passwd_len;
+         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 = TEMP_FAILURE_RETRY (__readv (sock, vec, 2));
-      if (__builtin_expect (n != total_len, 0))
-       goto out;
+         /* Get this data.  */
+         size_t n = TEMP_FAILURE_RETRY (__readv (sock, vec, 2));
+         if (__builtin_expect (n != total_len, 0))
+           goto out_close;
+       }
+      else
+       /* We already have the data.  Just copy the group name and
+          password.  */
+       memcpy (resultbuf->gr_name, gr_name, gr_name_len);
 
       /* Clear the terminating entry.  */
-      resultbuf->gr_mem[gr_resp.gr_mem_cnt] = NULL;
+      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)
+      for (cnt = 0; cnt < gr_resp->gr_mem_cnt; ++cnt)
        {
          resultbuf->gr_mem[cnt] = p;
          total_len += len[cnt];
          p += len[cnt];
        }
 
+      if (__builtin_expect (gr_name + gr_name_len + total_len > recend, 0))
+       goto out_close;
       if (__builtin_expect (total_len > buflen, 0))
        goto no_room;
 
       retval = 0;
-      n = TEMP_FAILURE_RETRY (__read (sock, resultbuf->gr_mem[0],
-                                            total_len));
-      if (__builtin_expect (n != total_len, 0))
+      if (gr_name == NULL)
        {
-         /* The `errno' to some value != ERANGE.  */
-         __set_errno (ENOENT);
-         retval = ENOENT;
+         size_t n = TEMP_FAILURE_RETRY (__read (sock, resultbuf->gr_mem[0],
+                                                total_len));
+         if (__builtin_expect (n != total_len, 0))
+           {
+             /* The `errno' to some value != ERANGE.  */
+             __set_errno (ENOENT);
+             retval = ENOENT;
+           }
+         else
+           *result = resultbuf;
        }
       else
-       *result = resultbuf;
+       {
+         /* Copy the group member names.  */
+         memcpy (resultbuf->gr_mem[0], gr_name + gr_name_len, total_len);
+
+         *result = resultbuf;
+       }
     }
   else
     {
@@ -177,8 +244,15 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type,
       retval = 0;
     }
 
+ out_close:
+  if (sock != -1)
+    close_not_cancel_no_status (sock);
  out:
-  close_not_cancel_no_status (sock);
+  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.  */
+    goto retry;
 
   return retval;
 }
index 7449aad..42327dd 100644 (file)
@@ -17,6 +17,7 @@
    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
    02111-1307 USA.  */
 
+#include <assert.h>
 #include <errno.h>
 #include <netdb.h>
 #include <resolv.h>
@@ -26,9 +27,7 @@
 #include <string.h>
 #include <unistd.h>
 #include <arpa/nameser.h>
-#include <sys/socket.h>
-#include <sys/uio.h>
-#include <sys/un.h>
+#include <sys/mman.h>
 #include <not-cancel.h>
 
 #include "nscd-client.h"
@@ -89,56 +88,15 @@ __nscd_gethostbyaddr_r (const void *addr, socklen_t len, int type,
 }
 
 
-/* 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)
+libc_locked_map_ptr (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 (gr_map_free)
 {
-  struct sockaddr_un addr;
-  int sock;
-  int saved_errno = errno;
-
-  sock = __socket (PF_UNIX, SOCK_STREAM, 0);
-  if (sock < 0)
-    {
-      __set_errno (saved_errno);
-      return -1;
-    }
-
-  addr.sun_family = AF_UNIX;
-  strcpy (addr.sun_path, _PATH_NSCDSOCKET);
-  if (__connect (sock, (struct sockaddr *) &addr, sizeof (addr)) < 0)
-    {
-      __set_errno (saved_errno);
-      goto out;
-    }
-
-  request_header req;
-  req.version = NSCD_VERSION;
-  req.type = type;
-  req.key_len = keylen;
-
-  struct iovec vec[2];
-  vec[0].iov_base = &req;
-  vec[0].iov_len = sizeof (request_header);
-  vec[1].iov_base = (void *) key;
-  vec[1].iov_len = keylen;
 
-  ssize_t nbytes = TEMP_FAILURE_RETRY (__writev (sock, vec, 2));
-  if (nbytes != (ssize_t) (sizeof (request_header) + keylen))
-    {
-    out:
-      close_not_cancel_no_status (sock);
-      sock = -1;
-    }
-  else
-    {
-      nbytes = TEMP_FAILURE_RETRY (__read (sock, response, responselen));
-      if (nbytes != (ssize_t) responselen)
-       goto out;
-    }
-
-  return sock;
+  if (map_handle.mapped != NO_MAPPING)
+    free (map_handle.mapped);
 }
 
 
@@ -148,30 +106,89 @@ 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)
 {
-  hst_response_header hst_resp;
-  int sock = __nscd_open_socket (key, keylen, type, &hst_resp,
-                                sizeof (hst_resp));
-  if (sock == -1)
+  const hst_response_header *hst_resp = NULL;
+  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;
+  int gc_cycle;
+  const char *recend = (const char *) ~UINTMAX_C (0);
+  int sock = -1;
+
+  /* If the mapping is available, try to search there instead of
+     communicating with the nscd.  */
+  struct mapped_database *mapped = __nscd_get_map_ref (GETFDHST, "hosts",
+                                                      &map_handle, &gc_cycle);
+ retry:
+  if (mapped != MAP_FAILED)
     {
-      __nss_not_use_nscd_hosts = 1;
-      return -1;
+      const struct datahead *found = __nscd_cache_search (type, key, keylen,
+                                                         mapped);
+      if (found != NULL)
+       {
+         hst_resp = &found->data[0].hstdata;
+         h_name = (char *) (hst_resp + 1);
+         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;
+
+#ifndef _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;
+           }
+         recend = (const char *) found->data + found->recsize;
+         if (__builtin_expect ((const char *) addr_list + addr_list_len
+                               > recend, 0))
+           goto out_close;
+       }
+    }
+
+  hst_response_header hst_resp_mem;
+  if (hst_resp == NULL)
+    {
+      sock = __nscd_open_socket (key, keylen, type, &hst_resp_mem,
+                                sizeof (hst_resp_mem));
+      if (sock == -1)
+       {
+         __nss_not_use_nscd_hosts = 1;
+         goto out;;
+       }
+
+      hst_resp = &hst_resp_mem;
     }
 
   /* No value found so far.  */
-  int retval = -1;
   *result = NULL;
 
-  if (hst_resp.found == -1)
+  if (__builtin_expect (hst_resp->found == -1, 0))
     {
       /* The daemon does not cache this database.  */
       __nss_not_use_nscd_hosts = 1;
-      goto out;
+      goto out_close;
     }
 
-  if (hst_resp.found == 1)
+  if (hst_resp->found == 1)
     {
       struct iovec vec[4];
-      uint32_t *aliases_len;
       char *cp = buffer;
       uintptr_t align1;
       uintptr_t align2;
@@ -185,96 +202,110 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
         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)
+      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)
+      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)))
+                   + hst_resp->h_addr_list_cnt * (type == AF_INET
+                                                  ? INADDRSZ : IN6ADDRSZ)))
        {
        no_room:
          __set_errno (ERANGE);
          retval = ERANGE;
-         goto out;
+         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 *);
+      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 *);
+      cp += (hst_resp->h_addr_list_cnt + 1) * sizeof (char *);
 
       resultbuf->h_name = cp;
-      cp += hst_resp.h_name_len + align2;
-      vec[0].iov_base = resultbuf->h_name;
-      vec[0].iov_len = hst_resp.h_name_len;
-
-      aliases_len = alloca (hst_resp.h_aliases_cnt * sizeof (uint32_t));
-      vec[1].iov_base = aliases_len;
-      vec[1].iov_len = hst_resp.h_aliases_cnt * sizeof (uint32_t);
+      cp += hst_resp->h_name_len + align2;
 
-      total_len = (hst_resp.h_name_len
-                  + hst_resp.h_aliases_cnt * sizeof (uint32_t));
-
-      n = 2;
       if (type == GETHOSTBYADDR || type == GETHOSTBYNAME)
        {
-         vec[2].iov_base = cp;
-         vec[2].iov_len = hst_resp.h_addr_list_cnt * INADDRSZ;
-
-         for (cnt = 0; cnt < hst_resp.h_addr_list_cnt; ++cnt)
-           {
-             resultbuf->h_addr_list[cnt] = cp;
-             cp += INADDRSZ;
-           }
-
          resultbuf->h_addrtype = AF_INET;
          resultbuf->h_length = INADDRSZ;
-
-         total_len += hst_resp.h_addr_list_cnt * INADDRSZ;
-
-         n = 3;
        }
       else
        {
-         if (hst_resp.h_length == INADDRSZ)
-           {
-             ignore = alloca (hst_resp.h_addr_list_cnt * INADDRSZ);
-             vec[2].iov_base = ignore;
-             vec[2].iov_len = hst_resp.h_addr_list_cnt * INADDRSZ;
+         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;
 
-             total_len += hst_resp.h_addr_list_cnt * INADDRSZ;
+      if (h_name == NULL)
+       {
+         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;
 
-             n = 3;
-           }
+         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);
 
-         vec[n].iov_base = cp;
-         vec[n].iov_len = hst_resp.h_addr_list_cnt * IN6ADDRSZ;
+             total_len += hst_resp->h_aliases_cnt * sizeof (uint32_t);
+             ++n;
+           }
 
-         for (cnt = 0; cnt < hst_resp.h_addr_list_cnt; ++cnt)
+         if (type == GETHOSTBYADDR || type == GETHOSTBYNAME)
            {
-             resultbuf->h_addr_list[cnt] = cp;
-             cp += IN6ADDRSZ;
+             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;
 
-         resultbuf->h_addrtype = AF_INET6;
-         resultbuf->h_length = IN6ADDRSZ;
+                 total_len += hst_resp->h_addr_list_cnt * INADDRSZ;
 
-         total_len += hst_resp.h_addr_list_cnt * IN6ADDRSZ;
+                 ++n;
+               }
 
-         ++n;
-       }
-      resultbuf->h_addr_list[cnt] = NULL;
+             vec[n].iov_base = resultbuf->h_addr_list[0];
+             vec[n].iov_len = hst_resp->h_addr_list_cnt * IN6ADDRSZ;
 
-      if ((size_t) TEMP_FAILURE_RETRY (__readv (sock, vec, n)) != total_len)
-       goto out;
+             total_len += hst_resp->h_addr_list_cnt * IN6ADDRSZ;
+
+             ++n;
+           }
+
+         if ((size_t) TEMP_FAILURE_RETRY (__readv (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)
+      for (cnt = 0; cnt < hst_resp->h_aliases_cnt; ++cnt)
        {
          resultbuf->h_aliases[cnt] = cp;
          cp += aliases_len[cnt];
@@ -282,14 +313,29 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
        }
       resultbuf->h_aliases[cnt] = NULL;
 
+      if (__builtin_expect ((const char *) addr_list + addr_list_len
+                           + total_len > recend, 0))
+       goto out_close;
       /* See whether this would exceed the buffer capacity.  */
-      if (cp > buffer + buflen)
+      if (__builtin_expect (cp > buffer + buflen, 0))
        goto no_room;
 
       /* And finally read the aliases.  */
-      if ((size_t) TEMP_FAILURE_RETRY (__read (sock, resultbuf->h_aliases[0],
-                                              total_len)) == total_len)
+      if (addr_list == NULL)
+       {
+         if ((size_t) TEMP_FAILURE_RETRY (__read (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);
+
          retval = 0;
          *result = resultbuf;
        }
@@ -297,7 +343,7 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
   else
     {
       /* Store the error number.  */
-      *h_errnop = hst_resp.error;
+      *h_errnop = hst_resp->error;
 
       /* The `errno' to some value != ERANGE.  */
       __set_errno (ENOENT);
@@ -305,8 +351,15 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
       retval = 0;
     }
 
+ out_close:
+  if (sock != -1)
+    close_not_cancel_no_status (sock);
  out:
-  __close (sock);
+  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.  */
+    goto retry;
 
   return retval;
 }
index 21f214d..0d15a0c 100644 (file)
@@ -17,6 +17,7 @@
    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
    02111-1307 USA.  */
 
+#include <assert.h>
 #include <errno.h>
 #include <pwd.h>
 #include <stdint.h>
@@ -24,6 +25,7 @@
 #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>
@@ -64,70 +66,124 @@ __nscd_getpwuid_r (uid_t uid, struct passwd *resultbuf, char *buffer,
 }
 
 
+libc_locked_map_ptr (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 (gr_map_free)
+{
+
+  if (map_handle.mapped != NO_MAPPING)
+    free (map_handle.mapped);
+}
+
+
 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)
 {
-  pw_response_header pw_resp;
-  int sock = __nscd_open_socket (key, keylen, type, &pw_resp,
-                                sizeof (pw_resp));
-  if (sock == -1)
+  const pw_response_header *pw_resp = NULL;
+  const char *pw_name = NULL;
+  int retval = -1;
+  int gc_cycle;
+  const char *recend = (const char *) ~UINTMAX_C (0);
+
+  /* If the mapping is available, try to search there instead of
+     communicating with the nscd.  */
+  struct mapped_database *mapped = __nscd_get_map_ref (GETFDPW, "passwd",
+                                                      &map_handle, &gc_cycle);
+ retry:
+  if (mapped != NO_MAPPING)
     {
-      __nss_not_use_nscd_passwd = 1;
-      return -1;
+      const struct datahead *found = __nscd_cache_search (type, key, keylen,
+                                                         mapped);
+      if (found != NULL)
+       {
+         pw_resp = &found->data[0].pwdata;
+         pw_name = (const char *) (pw_resp + 1);
+         recend = (const char *) found->data + found->recsize;
+       }
+    }
+
+  pw_response_header pw_resp_mem;
+  int sock = -1;
+  if (pw_resp == NULL)
+    {
+      sock = __nscd_open_socket (key, keylen, type, &pw_resp_mem,
+                                sizeof (pw_resp_mem));
+      if (sock == -1)
+       {
+         __nss_not_use_nscd_passwd = 1;
+         goto out;
+       }
+
+      pw_resp = &pw_resp_mem;
     }
 
   /* No value found so far.  */
-  int retval = -1;
   *result = NULL;
 
-  if (__builtin_expect (pw_resp.found == -1, 0))
+  if (__builtin_expect (pw_resp->found == -1, 0))
     {
       /* The daemon does not cache this database.  */
       __nss_not_use_nscd_passwd = 1;
-      goto out;
+      goto out_close;
     }
 
-  if (pw_resp.found == 1)
+  if (pw_resp->found == 1)
     {
-      char *p = buffer;
-      size_t total = (pw_resp.pw_name_len + pw_resp.pw_passwd_len
-                     + pw_resp.pw_gecos_len + pw_resp.pw_dir_len
-                     + pw_resp.pw_shell_len);
-
-      if (__builtin_expect (buflen < total, 0))
-       {
-         __set_errno (ERANGE);
-         retval = ERANGE;
-         goto out;
-       }
-
       /* Set the information we already have.  */
-      resultbuf->pw_uid = pw_resp.pw_uid;
-      resultbuf->pw_gid = pw_resp.pw_gid;
+      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;
+      p += pw_resp->pw_name_len;
       /* get pw_passwd */
       resultbuf->pw_passwd = p;
-      p += pw_resp.pw_passwd_len;
+      p += pw_resp->pw_passwd_len;
       /* get pw_gecos */
       resultbuf->pw_gecos = p;
-      p += pw_resp.pw_gecos_len;
+      p += pw_resp->pw_gecos_len;
       /* get pw_dir */
       resultbuf->pw_dir = p;
-      p += pw_resp.pw_dir_len;
+      p += pw_resp->pw_dir_len;
       /* get pw_pshell */
       resultbuf->pw_shell = p;
+      p += pw_resp->pw_shell_len;
 
-      ssize_t nbytes = TEMP_FAILURE_RETRY (__read (sock, buffer, total));
+      ssize_t total = p - buffer;
+      if (__builtin_expect (pw_name + total > recend, 0))
+       goto out_close;
+      if (__builtin_expect (buflen < total, 0))
+       {
+         __set_errno (ERANGE);
+         retval = ERANGE;
+         goto out_close;
+       }
 
-      if (nbytes == (ssize_t) total)
+      retval = 0;
+      if (pw_name == NULL)
+       {
+         ssize_t nbytes = TEMP_FAILURE_RETRY (__read (sock, buffer, total));
+
+         if (__builtin_expect (nbytes != total, 0))
+           {
+             /* The `errno' to some value != ERANGE.  */
+             __set_errno (ENOENT);
+             retval = ENOENT;
+           }
+         else
+           *result = resultbuf;
+       }
+      else
        {
-         retval = 0;
+         /* Copy the various strings.  */
+         memcpy (resultbuf->pw_name, pw_name, total);
+
          *result = resultbuf;
        }
     }
@@ -139,8 +195,15 @@ nscd_getpw_r (const char *key, size_t keylen, request_type type,
       retval = 0;
     }
 
+ out_close:
+  if (sock != -1)
+    close_not_cancel_no_status (sock);
  out:
-  close_not_cancel_no_status (sock);
+  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.  */
+    goto retry;
 
   return retval;
 }
diff --git a/nscd/nscd_helper.c b/nscd/nscd_helper.c
new file mode 100644 (file)
index 0000000..95661b6
--- /dev/null
@@ -0,0 +1,335 @@
+/* Copyright (C) 1998-2002, 2003, 2004 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, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+#include <not-cancel.h>
+#include <nis/rpcsvc/nis.h>
+
+#include "nscd-client.h"
+
+
+static int
+open_socket (void)
+{
+  int sock = __socket (PF_UNIX, SOCK_STREAM, 0);
+  if (sock < 0)
+    return -1;
+
+  /* Make socket non-blocking.  */
+  int fl = __fcntl (sock, F_GETFL);
+  if (fl != -1)
+    __fcntl (sock, F_SETFL, fl | O_NONBLOCK);
+
+  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;
+
+  struct pollfd fds[1];
+  fds[0].fd = sock;
+  fds[0].events = POLLOUT | POLLERR | POLLHUP;
+  if (__poll (fds, 1, 5 * 1000) > 0)
+    /* Success.  We do not check for success of the connect call here.
+       If it failed, the following operations will fail.  */
+    return sock;
+
+ 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.  */
+static struct mapped_database *
+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;
+  char resdata[keylen];
+  int saved_errno = errno;
+
+  int mapfd = -1;
+
+  /* Send the request.  */
+  struct iovec iov[2];
+  request_header req;
+
+  int sock = open_socket ();
+  if (sock < 0)
+    goto out;
+
+  req.version = NSCD_VERSION;
+  req.type = type;
+  req.key_len = keylen;
+
+  iov[0].iov_base = &req;
+  iov[0].iov_len = sizeof (req);
+  iov[1].iov_base = (void *) key;
+  iov[1].iov_len = keylen;
+
+  if (TEMP_FAILURE_RETRY (__writev (sock, iov, 2))
+      != iov[0].iov_len + iov[1].iov_len)
+    /* We cannot even write the request.  */
+    goto out_close2;
+
+  /* Room for the data sent along with the file descriptor.  We expect
+     the key name back.  */
+  iov[0].iov_base = resdata;
+  iov[0].iov_len = keylen;
+
+  char buf[CMSG_SPACE (sizeof (int))];
+  struct msghdr msg = { .msg_iov = iov, .msg_iovlen = 1,
+                       .msg_control = buf, .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 *) CMSG_DATA (cmsg) = -1;
+
+  msg.msg_controllen = cmsg->cmsg_len;
+
+  struct pollfd fds[1];
+  fds[0].fd = sock;
+  fds[0].events = POLLIN | POLLERR | POLLHUP;
+  if (__poll (fds, 1, 5 * 1000) <= 0)
+    /* Failure or timeout.  */
+    goto out_close2;
+
+  if (TEMP_FAILURE_RETRY (__recvmsg (sock, &msg, 0)) != keylen
+      || msg.msg_controllen != CMSG_LEN (sizeof (int)))
+    goto out_close2;
+
+  mapfd = *(int *) CMSG_DATA (cmsg);
+
+  struct stat64 st;
+  if (strcmp (resdata, key) != 0
+      || fstat64 (mapfd, &st) != 0
+      || st.st_size < sizeof (struct database_pers_head))
+    goto out_close;
+
+  struct database_pers_head head;
+  if (TEMP_FAILURE_RETRY (__pread (mapfd, &head, sizeof (head), 0))
+      != sizeof (head))
+    goto out_close;
+
+  if (head.version != DB_VERSION || head.header_size != sizeof (head)
+      /* This really should not happen but who knows, maybe the update
+        thread got stuck.  */
+      || head.timestamp + MAPPING_TIMEOUT < time (NULL))
+    goto out_close;
+
+  size_t size = (sizeof (head) + roundup (head.module * sizeof (ref_t), ALIGN)
+                + head.data_size);
+
+  if (st.st_size < size)
+    goto out_close;
+
+  /* The file is large enough, map it now.  */
+  void *mapping = __mmap (NULL, size, PROT_READ, MAP_SHARED, mapfd, 0);
+  if (mapping != MAP_FAILED)
+    {
+      /* Allocate a record for the mapping.  */
+      struct mapped_database *newp;
+
+      newp = malloc (sizeof (*newp));
+      if (newp == NULL)
+       {
+         /* Ugh, after all we went through the memory allocation failed.  */
+         munmap (result, size);
+         goto out_close;
+       }
+
+      newp->head = mapping;
+      newp->data = ((char *) mapping + head.header_size
+                   + roundup (head.module * sizeof (ref_t), ALIGN));
+      newp->mapsize = 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,
+                   struct locked_map_ptr *mapptr, int *gc_cyclep)
+{
+  struct mapped_database *cur = mapptr->mapped;
+  if (cur == NO_MAPPING)
+    return cur;
+
+  int cnt = 0;
+  while (atomic_compare_and_exchange_val_acq (&mapptr->lock, 1, 0) != 0)
+    {
+      // XXX Best number of rounds?
+      if (++cnt > 5)
+       return NO_MAPPING;
+
+      atomic_delay ();
+    }
+
+  cur = mapptr->mapped;
+
+  if (__builtin_expect (cur != NO_MAPPING, 1))
+    {
+      /* If not mapped or timestamp not updated, request new map.  */
+      if (cur == NULL
+         // XXX The following syscalls increases the cost of the entire
+         // XXX lookup by a factor of 5 but unfortunately there is not
+         // XXX much we can do except hoping we get a userlevel
+         // XXX implementation soon.
+         || cur->head->timestamp + MAPPING_TIMEOUT < time (NULL))
+       cur = get_mapping (type, name, &mapptr->mapped);
+
+      if (__builtin_expect (cur != NO_MAPPING, 1))
+       {
+         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;
+}
+
+
+const struct datahead *
+__nscd_cache_search (request_type type, const char *key, size_t keylen,
+                    const struct mapped_database *mapped)
+{
+  unsigned long int hash = __nis_hash (key, keylen) % mapped->head->module;
+
+  ref_t work = mapped->head->array[hash];
+  while (work != ENDREF)
+    {
+      struct hashentry *here = (struct hashentry *) (mapped->data + work);
+
+      if (type == here->type && keylen == here->len
+         && memcmp (key, mapped->data + here->key, keylen) == 0)
+       {
+         /* We found the entry.  Increment the appropriate counter.  */
+         const struct datahead *dh
+           = (struct datahead *) (mapped->data + here->packet);
+
+         /* See whether we must ignore the entry or whether something
+            is wrong because garbage collection is in progress.  */
+         if (dh->usable && ((char *) dh + dh->allocsize
+                            <= (char *) mapped->head + mapped->mapsize))
+           return dh;
+       }
+
+      work = here->next;
+    }
+
+  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)
+{
+  int saved_errno = errno;
+
+  int sock = open_socket ();
+  if (sock >= 0)
+    {
+      request_header req;
+      req.version = NSCD_VERSION;
+      req.type = type;
+      req.key_len = keylen;
+
+      struct iovec vec[2];
+      vec[0].iov_base = &req;
+      vec[0].iov_len = sizeof (request_header);
+      vec[1].iov_base = (void *) key;
+      vec[1].iov_len = keylen;
+
+      ssize_t nbytes = TEMP_FAILURE_RETRY (__writev (sock, vec, 2));
+      if (nbytes == (ssize_t) (sizeof (request_header) + keylen))
+       {
+         /* Wait for data.  */
+         struct pollfd fds[1];
+         fds[0].fd = sock;
+         fds[0].events = POLLIN | POLLERR | POLLHUP;
+         if (__poll (fds, 1, 5 * 1000) > 0)
+           {
+             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;
+}