Update.
authorUlrich Drepper <drepper@redhat.com>
Sun, 18 Oct 1998 15:16:22 +0000 (15:16 +0000)
committerUlrich Drepper <drepper@redhat.com>
Sun, 18 Oct 1998 15:16:22 +0000 (15:16 +0000)
1998-10-18  Ulrich Drepper  <drepper@cygnus.com>

* resolv/nss_dns/dns-host.c: Add missing errnop parameter to the
NSS functions.
* resolv/nss_dns/dns-network.c: Likewise.

* grp/Makefile: Don't search for linuxhtreads in add-ons, use
have-thread-library to determine whether threads are available.
* pwd/Makefile: Remove wrong comment.

* inet/Makefile: Define CFLAGS-gethstbyad_r.c, CFLAGS-gethstbynm_r.c,
and CFLAGS-gethstbynm2_r.c to -DUSE_NSCD=1.

* locale/C-messages.c: Define default strings for YESTR and NOSTR.

* nss/Versions: Add __nss_hosts_lookup.

* nss/getXXbyYY.c: Remove unneeded assignment.

* nss/getXXbyYY_r.c: Include nscd/nscd_proto.h only if needed.

Almost complete rewrite of the NSCD to make it smaller, faster,
add more functionnality and make it easier to extend.
* nscd/Makfile (routines): Add nscd_gethst_r.
(nscd-modules): Add hstcache, gethstbyad_r, gethstbynm2_r, and cache.
* nscd/cache.c: New file.
* nscd/gethstbyad_r.c: New file.
* nscd/gethstbynm2_r.c: New file.
* nscd/hstcache.c: New file.
* nscd/nscd_gethst_r.c: New file.
* nscd/connections.c: Rewritten.  Don't start new thread for every
new connection.  Use a fixed set of threads which handle all
connections and also the cache cleanup.
* nscd/grpcache.c: Rewritten to use generic cache handling functions
in cache.c.
* nscd/nscd.c: Recognize new parameter nthreads.  Adjust initialization
for rewrite.  Remove handle_requests function.
* nscd/nscd.h (NSCD_VERSION): Bump to 2.
Define new data structure for the new unified cache and the host
database entries.
* nscd/nscd_conf.c: Rewrite parsing partly to allow adding of more
databases easily.  Recognize check-files and threads definitions.
* nscd/nscd.conf: Add definition of enable-cache and check-files to
passwd and group definitions.  Add new set of definitions for hosts.
* nscd/nscd_getgr_r.c: Rewrite for new protocol.
* nscd/nscd_getpw_r.c: Likewise.
* nscd/nscd_proto.h: Add prototype for host database functions.
* nscd/nscd_stat.c: Rewrite to simplify printing of information
for many databases.
* nscd/dbg_log.c: Remove unnecessary variable initializations.
Global variable debug_flag is renamed to dbg_level.
* nscd/dbg_log.h: Declare set_logfile.

38 files changed:
ChangeLog
PROJECTS
grp/Makefile
inet/Makefile
locale/C-messages.c
nscd/Makefile
nscd/cache.c [new file with mode: 0644]
nscd/connections.c
nscd/dbg_log.c
nscd/dbg_log.h
nscd/gethstbyad_r.c [new file with mode: 0644]
nscd/gethstbynm2_r.c [new file with mode: 0644]
nscd/grpcache.c
nscd/hstcache.c [new file with mode: 0644]
nscd/nscd.c
nscd/nscd.conf
nscd/nscd.h
nscd/nscd_conf.c
nscd/nscd_getgr_r.c
nscd/nscd_gethst_r.c [new file with mode: 0644]
nscd/nscd_getpw_r.c
nscd/nscd_proto.h
nscd/nscd_stat.c
nscd/pwdcache.c
nss/Versions
nss/getXXbyYY.c
nss/getXXbyYY_r.c
pwd/Makefile
resolv/nss_dns/dns-host.c
resolv/nss_dns/dns-network.c
sysdeps/unix/sysv/linux/m68k/setegid.c [new file with mode: 0644]
sysdeps/unix/sysv/linux/m68k/seteuid.c [new file with mode: 0644]
sysdeps/unix/sysv/linux/m68k/setfsgid.c [new file with mode: 0644]
sysdeps/unix/sysv/linux/m68k/setfsuid.c [new file with mode: 0644]
sysdeps/unix/sysv/linux/m68k/setgid.c [new file with mode: 0644]
sysdeps/unix/sysv/linux/m68k/setresgid.c [new file with mode: 0644]
sysdeps/unix/sysv/linux/m68k/setresuid.c [new file with mode: 0644]
sysdeps/unix/sysv/linux/m68k/setuid.c [new file with mode: 0644]

index a3ee00862cfdedab6d6a08caa55520f061577e85..eda87f4f808df2ea8b30fb2b103489ce60b1295e 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,56 @@
+1998-10-18  Ulrich Drepper  <drepper@cygnus.com>
+
+       * resolv/nss_dns/dns-host.c: Add missing errnop parameter to the
+       NSS functions.
+       * resolv/nss_dns/dns-network.c: Likewise.
+
+       * grp/Makefile: Don't search for linuxhtreads in add-ons, use
+       have-thread-library to determine whether threads are available.
+       * pwd/Makefile: Remove wrong comment.
+
+       * inet/Makefile: Define CFLAGS-gethstbyad_r.c, CFLAGS-gethstbynm_r.c,
+       and CFLAGS-gethstbynm2_r.c to -DUSE_NSCD=1.
+
+       * locale/C-messages.c: Define default strings for YESTR and NOSTR.
+
+       * nss/Versions: Add __nss_hosts_lookup.
+
+       * nss/getXXbyYY.c: Remove unneeded assignment.
+
+       * nss/getXXbyYY_r.c: Include nscd/nscd_proto.h only if needed.
+
+       Almost complete rewrite of the NSCD to make it smaller, faster,
+       add more functionnality and make it easier to extend.
+       * nscd/Makfile (routines): Add nscd_gethst_r.
+       (nscd-modules): Add hstcache, gethstbyad_r, gethstbynm2_r, and cache.
+       * nscd/cache.c: New file.
+       * nscd/gethstbyad_r.c: New file.
+       * nscd/gethstbynm2_r.c: New file.
+       * nscd/hstcache.c: New file.
+       * nscd/nscd_gethst_r.c: New file.
+       * nscd/connections.c: Rewritten.  Don't start new thread for every
+       new connection.  Use a fixed set of threads which handle all
+       connections and also the cache cleanup.
+       * nscd/grpcache.c: Rewritten to use generic cache handling functions
+       in cache.c.
+       * nscd/nscd.c: Recognize new parameter nthreads.  Adjust initialization
+       for rewrite.  Remove handle_requests function.
+       * nscd/nscd.h (NSCD_VERSION): Bump to 2.
+       Define new data structure for the new unified cache and the host
+       database entries.
+       * nscd/nscd_conf.c: Rewrite parsing partly to allow adding of more
+       databases easily.  Recognize check-files and threads definitions.
+       * nscd/nscd.conf: Add definition of enable-cache and check-files to
+       passwd and group definitions.  Add new set of definitions for hosts.
+       * nscd/nscd_getgr_r.c: Rewrite for new protocol.
+       * nscd/nscd_getpw_r.c: Likewise.
+       * nscd/nscd_proto.h: Add prototype for host database functions.
+       * nscd/nscd_stat.c: Rewrite to simplify printing of information
+       for many databases.
+       * nscd/dbg_log.c: Remove unnecessary variable initializations.
+       Global variable debug_flag is renamed to dbg_level.
+       * nscd/dbg_log.h: Declare set_logfile.
+
 1998-10-16  Ulrich Drepper  <drepper@cygnus.com>
 
        * sysdeps/unix/sysv/linux/bits/fcntl.h: Add dummy definition of
index 3537b4422774b2af8b2105e93bd27b56e313daba..d5be6313eb87435df7eca1145213f808fcfa9a99 100644 (file)
--- a/PROJECTS
+++ b/PROJECTS
@@ -149,3 +149,13 @@ contact <bug-glibc@gnu.org>.
          {
            ... use the plain file if it exists, otherwise the db ...
          }
+
+[23] The `strptime' function needs to be completed.  This includes among
+     other things that it must get teached about timezones.  The solution
+     envisioned is to extract the timezones from the ADO timezone
+     specifications.  Special care must be given names which are used
+     multiple times.  Here the precedence should (probably) be according
+     to the geograhical distance.  E.g., the timezone EST should be
+     treated as the `Eastern Australia Time' instead of the US `Eastern
+     Standard Time' if the current TZ variable is set to, say,
+     Australia/Canberra or if the current locale is en_AU.
index 0d13495bd8a730e37a8a6371e799ecbc6687e20d..282aeb706a81c5f4d9bfabb6c0627b32972360f1 100644 (file)
@@ -30,8 +30,7 @@ tests := testgrp
 
 include ../Rules
 
-# We can later add the names of other thread packages here.
-ifneq (,$(findstring linuxthreads,$(add-ons)))
+ifeq ($(have-thread-library),yes)
 
 CFLAGS-getgrgid_r.c = -DUSE_NSCD=1
 CFLAGS-getgrnam_r.c = -DUSE_NSCD=1
index 4aa76bd2d3939134ffe732168040b4aba4874156..d1b984a4f5a2afc54880a0e693af6e1e680cf513 100644 (file)
@@ -54,3 +54,11 @@ CFLAGS-rexec.c = -w
 CFLAGS-ruserpass.c = -w
 
 include ../Rules
+
+ifeq ($(have-thread-library),yes)
+
+CFLAGS-gethstbyad_r.c = -DUSE_NSCD=1
+CFLAGS-gethstbynm_r.c = -DUSE_NSCD=1
+CFLAGS-gethstbynm2_r.c = -DUSE_NSCD=1
+
+endif
index 0363020476ca34bbde54882a288360d15e155f5d..87e40e508f2bfd2c67a9542962da29e03d13ff7b 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc.
+/* Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
 
@@ -33,7 +33,7 @@ const struct locale_data _nl_C_LC_MESSAGES =
   {
     { string: "^[yY]" },
     { string: "^[nN]" },
-    { string: "" },
-    { string: "" }
+    { string: "yes" },
+    { string: "no" }
   }
 };
index f00b2f0b3ef69c89771388983c80b0f314d8cff9..ed5c0ba9c5a382d2a72e094e5f662e737ae017a2 100644 (file)
 #
 subdir := nscd
 
-routines := nscd_getpw_r nscd_getgr_r
+routines := nscd_getpw_r nscd_getgr_r nscd_gethst_r
 
 include ../Makeconfig
 
 nscd-modules := nscd connections pwdcache getpwnam_r getpwuid_r grpcache \
-               getgrnam_r getgrgid_r dbg_log nscd_conf nscd_stat
+               getgrnam_r getgrgid_r hstcache gethstbyad_r gethstbynm2_r \
+               dbg_log nscd_conf nscd_stat cache
 
 ifeq ($(have-thread-library),yes)
 
diff --git a/nscd/cache.c b/nscd/cache.c
new file mode 100644 (file)
index 0000000..e957a57
--- /dev/null
@@ -0,0 +1,247 @@
+/* Copyright (c) 1998 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 Library General Public License as
+   published by the Free Software Foundation; either version 2 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA. */
+
+#include <atomicity.h>
+#include <errno.h>
+#include <error.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <rpcsvc/nis.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+
+#include "nscd.h"
+#include "dbg_log.h"
+
+/* 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 hashentry *
+cache_search (int type, void *key, size_t len, struct database *table)
+{
+  unsigned long int hash = __nis_hash (key, len) % table->module;
+  struct hashentry *work;
+
+  work = table->array[hash];
+
+  while (work != NULL)
+    {
+      if (type == work->type
+         && len == work->len && memcmp (key, work->key, len) == 0)
+       {
+         /* We found the entry.  Increment the appropriate counter.  */
+         if (work->data == (void *) -1)
+           ++table->neghit;
+         else
+           ++table->poshit;
+
+         return work;
+       }
+
+      work = work->next;
+    }
+
+  return NULL;
+}
+
+/* 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.  */
+void
+cache_add (int type, void *key, size_t len, const void *packet, size_t total,
+          void *data, int last, time_t t, struct database *table)
+{
+  unsigned long int hash = __nis_hash (key, len) % table->module;
+  struct hashentry *newp;
+
+  newp = malloc (sizeof (struct hashentry));
+  if (newp == NULL)
+    error (EXIT_FAILURE, errno, _("while allocating hash table entry"));
+
+  newp->type = type;
+  newp->len = len;
+  newp->key = key;
+  newp->data = data;
+  newp->timeout = t;
+  newp->packet = packet;
+  newp->total = total;
+
+  newp->last = last;
+
+  /* Put the new entry in the first position.  */
+  do
+    newp->next = table->array[hash];
+  while (! compare_and_swap ((volatile long int *) &table->array[hash],
+                            (long int) newp->next, (long int) newp));
+
+  /* Update the statistics.  */
+  if (data == (void *) -1)
+    ++table->negmiss;
+  else if (last)
+    ++table->posmiss;
+}
+
+/* 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.
+
+   This function must be called with the write-lock held.  */
+void
+prune_cache (struct database *table, time_t now)
+{
+  size_t cnt = table->module;
+  int mark[cnt];
+  int anything = 0;
+  size_t first = cnt + 1;
+  size_t last = 0;
+
+  /* If we check for the modification of the underlying file we invalidate
+     the entries also in this case.  */
+  if (table->check_file)
+    {
+      struct stat st;
+
+      if (stat (table->filename, &st) < 0)
+       {
+         char buf[128];
+         /* We cannot stat() the file, disable file checking.  */
+         dbg_log (_("cannot stat() file `%s': %s"),
+                  table->filename, strerror_r (errno, buf, sizeof (buf)));
+         table->check_file = 0;
+       }
+      else
+       {
+         if (st.st_mtime != table->file_mtime)
+           /* The file changed.  Invalidate all entries.  */
+           now = LONG_MAX;
+       }
+    }
+
+  /* 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.  */
+  do
+    {
+      struct hashentry *runp = table->array[--cnt];
+
+      mark[cnt] = 0;
+
+      while (runp != NULL)
+       {
+         if (runp->timeout < now)
+           {
+             ++mark[cnt];
+             anything = 1;
+             first = MIN (first, cnt);
+             last = MAX (last, cnt);
+           }
+         runp = runp->next;
+       }
+    }
+  while (cnt > 0);
+
+  if (anything)
+    {
+      struct hashentry *head = NULL;
+
+      /* Now we have to get the write lock since we are about to modify
+        the table.  */
+      pthread_rwlock_wrlock (&table->lock);
+
+      while (first <= last)
+       {
+         if (mark[first] > 0)
+           {
+             struct hashentry *runp;
+
+             while (table->array[first]->timeout < now)
+               {
+                 table->array[first]->dellist = head;
+                 head = table->array[first];
+                 table->array[first] = head->next;
+                 if (--mark[first] == 0)
+                   break;
+               }
+
+             runp = table->array[first];
+             while (mark[first] > 0)
+               {
+                 if (runp->next->timeout < now)
+                   {
+                     runp->next->dellist = head;
+                     head = runp->next;
+                     runp->next = head->next;
+                     --mark[first];
+                   }
+                 else
+                   runp = runp->next;
+               }
+           }
+         ++first;
+       }
+
+      /* It's all done.  */
+      pthread_rwlock_unlock (&table->lock);
+
+      /* And another run to free the data.  */
+      do
+       {
+         struct hashentry *old = head;
+
+         if (debug_level > 0)
+           dbg_log ("remove %s entry \"%s\"",
+                    serv2str[old->type],
+                    old->last
+                    ? old->key : old->data == (void *) -1 ? old->key : "???");
+
+         /* Free the data structures.  */
+         if (old->data == (void *) -1)
+           free (old->key);
+         else if (old->last)
+           free (old->data);
+
+         head = head->dellist;
+
+         free (old);
+       }
+      while (head != NULL);
+    }
+}
index 4cf397d2011af17c87a65f41ebfa3e055ca7a2d9..8e6839a1576eabc256299f461bb31b88c8eb9005 100644 (file)
@@ -1,6 +1,7 @@
-/* Copyright (c) 1998 Free Software Foundation, Inc.
+/* Inner loops of cache daemon.
+   Copyright (C) 1998 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
-   Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1998.
+   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 Library General Public License as
    You should have received a copy of the GNU Library General Public
    License along with the GNU C Library; see the file COPYING.LIB.  If not,
    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-   Boston, MA 02111-1307, USA. */
+   Boston, MA 02111-1307, USA.  */
 
-#include <errno.h>
+#include <assert.h>
 #include <error.h>
-#include <fcntl.h>
-#include <libintl.h>
-#include <locale.h>
+#include <errno.h>
 #include <pthread.h>
-#include <pwd.h>
-#include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <sys/param.h>
 #include <sys/poll.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/uio.h>
 #include <sys/un.h>
 
 #include "nscd.h"
 #include "dbg_log.h"
 
-/* Socket 0 in the array is named and exported into the file namespace
-   as a connection point for clients.  There's a one to one
-   correspondence between sock[i] and read_polls[i].  */
-static int sock[MAX_NUM_CONNECTIONS];
-static int socks_active;
-static struct pollfd read_polls[MAX_NUM_CONNECTIONS];
-static pthread_mutex_t sock_lock = PTHREAD_MUTEX_INITIALIZER;
-
 
-/* Cleanup.  */
-void
-close_sockets (void)
+/* Mapping of request type to database.  */
+static const dbtype serv2db[LASTDBREQ + 1] =
 {
-  int i;
-
-  if (debug_flag)
-    dbg_log (_("close_sockets called"));
-
-  pthread_mutex_lock (&sock_lock);
-
-  /* Close sockets.  */
-  for (i = 0; i < MAX_NUM_CONNECTIONS; ++i)
-    if (sock[i] != 0)
-      {
-       if (close (sock[i]))
-         dbg_log (_("socket [%d|%d] close: %s"), i, sock[i], strerror (errno));
-
-       sock[i] = 0;
-       read_polls[i].fd = -1;
-       --socks_active;
-      }
-
-  pthread_mutex_unlock (&sock_lock);
-}
-
-void
-close_socket (int conn)
+  [GETPWBYNAME] = pwddb,
+  [GETPWBYUID] = pwddb,
+  [GETGRBYNAME] = grpdb,
+  [GETGRBYGID] = grpdb,
+  [GETHOSTBYNAME] = hstdb,
+  [GETHOSTBYNAMEv6] = hstdb,
+  [GETHOSTBYADDR] = hstdb,
+  [GETHOSTBYADDRv6] = hstdb,
+};
+
+/* Map request type to a string.  */
+const char *serv2str[LASTREQ] =
 {
-  if (debug_flag > 2)
-    dbg_log (_("close socket (%d|%d)"), conn, sock[conn]);
+  [GETPWBYNAME] = "GETPWBYNAME",
+  [GETPWBYUID] = "GETPWBYUID",
+  [GETGRBYNAME] = "GETGRBYNAME",
+  [GETGRBYGID] = "GETGRBYGID",
+  [GETHOSTBYNAME] = "GETHOSTBYNAME",
+  [GETHOSTBYNAMEv6] = "GETHOSTBYNAMEv6",
+  [GETHOSTBYADDR] = "GETHOSTBYADDR",
+  [GETHOSTBYADDRv6] = "GETHOSTBYADDRv6",
+  [SHUTDOWN] = "SHUTDOWN",
+  [GETSTAT] = "GETSTAT"
+};
+
+/* The control data structures for the services.  */
+static struct database dbs[lastdb] =
+{
+  [pwddb] = {
+    lock: PTHREAD_RWLOCK_INITIALIZER,
+    enabled: 1,
+    check_file: 1,
+    filename: "/etc/passwd",
+    module: 211,
+    disabled_iov: &pwd_iov_disabled
+  },
+  [grpdb] = {
+    lock: PTHREAD_RWLOCK_INITIALIZER,
+    enabled: 1,
+    check_file: 1,
+    filename: "/etc/group",
+    module: 211,
+    disabled_iov: &grp_iov_disabled
+  },
+  [hstdb] = {
+    lock: PTHREAD_RWLOCK_INITIALIZER,
+    enabled: 1,
+    check_file: 1,
+    filename: "/etc/hosts",
+    module: 211,
+    disabled_iov: &hst_iov_disabled
+  }
+};
 
-  pthread_mutex_lock (&sock_lock);
+/* Number of threads to use.  */
+int nthreads = -1;
 
-  close (sock[conn]);
-  sock[conn] = 0;
-  read_polls[conn].fd = -1;
-  --socks_active;
+/* Socket for incoming connections.  */
+static int sock;
 
-  pthread_mutex_unlock (&sock_lock);
-}
 
-/* Local routine, assigns a socket to a new connection request.  */
-static void
-handle_new_connection (void)
+/* Initialize database information structures.  */
+void
+nscd_init (const char *conffile)
 {
-  int i;
-
-  if (debug_flag > 2)
-    dbg_log (_("handle_new_connection"));
-
-  pthread_mutex_lock (&sock_lock);
+  struct sockaddr_un sock_addr;
+  size_t cnt;
 
-  if (socks_active < MAX_NUM_CONNECTIONS)
-    /* Find a free socket entry to use.  */
-    for (i = 1; i < MAX_NUM_CONNECTIONS; ++i)
-      {
-       if (sock[i] == 0)
-         {
-           if ((sock[i] = accept (sock[0], NULL, NULL)) < 0)
-             {
-               dbg_log (_("socket accept: %s"), strerror (errno));
-               return;
-             }
-           ++socks_active;
-           read_polls[i].fd = sock[i];
-           read_polls[i].events = POLLRDNORM;
-           if (debug_flag > 2)
-             dbg_log (_("handle_new_connection used socket %d|%d"), i,
-                      sock[i]);
-           break;
-         }
-      }
-  else
+  /* Read the configuration file.  */
+  if (nscd_parse_file (conffile, dbs) != 0)
     {
-      int black_widow_sock;
-      dbg_log (_("Supported number of simultaneous connections exceeded"));
-      dbg_log (_("Ignoring client connect request"));
-      /* There has to be a better way to ignore a connection request,..
-        when I get my hands on a sockets wiz I'll modify this.  */
-      black_widow_sock  = accept (sock[0], NULL, NULL);
-      close (black_widow_sock);
+      /* We couldn't read the configuration file.  Disable all services
+        by shutting down the srever.  */
+      dbg_log (_("cannot read configuration file; this is fatal"));
+      exit (1);
     }
-  pthread_mutex_unlock (&sock_lock);
-}
-
-/* Local routine, reads a request off a socket indicated by read_polls.  */
-static int
-handle_new_request (int **connp, request_header **reqp, char **key)
-{
-  ssize_t nbytes;
-  int i, found = 0;
+  if (nthreads == -1)
+    /* No configuration for this value, assume a default.  */
+    nthreads = 2 * lastdb;
 
-  if (debug_flag)
-    dbg_log ("handle_new_request");
-
-  /* Find the descriptor.  */
-  for (i = 1; i < MAX_NUM_CONNECTIONS; ++i) {
-    if (read_polls[i].fd >= 0
-       && read_polls[i].revents & (POLLRDNORM|POLLERR|POLLNVAL))
-      {
-       found = i;
-       break;
-      }
-    if (read_polls[i].fd >= 0 && (read_polls[i].revents & POLLHUP))
+  for (cnt = 0; cnt < lastdb; ++cnt)
+    if (dbs[cnt].enabled)
       {
-       /* Don't close the socket, we still need to send data.  Just
-          stop polling for more data now.  */
-       read_polls[i].fd = -1;
-      }
-  }
+       pthread_rwlock_init (&dbs[cnt].lock, NULL);
 
-  if (found == 0)
-    {
-      dbg_log (_("No sockets with data found !"));
-      return -1;
-    }
-
-  if (debug_flag > 2)
-    dbg_log (_("handle_new_request uses socket %d"), i);
+       dbs[cnt].array = (struct hashentry **)
+         calloc (dbs[cnt].module, sizeof (struct hashentry *));
+       if (dbs[cnt].array == NULL)
+         error (EXIT_FAILURE, errno, "while allocating cache");
 
-  /* Read from it.  */
-  nbytes = read (sock[i], *reqp, sizeof (request_header));
-  if (nbytes != sizeof (request_header))
-    {
-      /* Handle non-data read cases.  */
-      if (nbytes == 0)
-       {
-         /* Close socket down.  */
-         if (debug_flag > 2)
-           dbg_log (_("Real close socket %d|%d"), i, sock[i]);
-
-         pthread_mutex_lock (&sock_lock);
-         read_polls[i].fd = -1;
-         close (sock[i]);
-         sock[i] = 0;
-         --socks_active;
-         pthread_mutex_unlock (&sock_lock);
-       }
-      else
-       if (nbytes < 0)
+       if (dbs[cnt].check_file)
          {
-           dbg_log (_("Read(%d|%d) error on get request: %s"),
-                    i, sock[i], strerror (errno));
-           exit (1);
-         }
-       else
-         dbg_log (_("Read, data < request buf size, ignoring data"));
+           /* We need the modification date of the file.  */
+           struct stat st;
 
-      return -1;
-    }
-  else
-    {
-      *key = malloc ((*reqp)->key_len + 1);
-      /* Read the key from it */
-      nbytes = read (sock[i], *key, (*reqp)->key_len);
-      if (nbytes != (*reqp)->key_len)
-       {
-         /* Handle non-data read cases.  */
-         if (nbytes == 0)
-           {
-             /* Close socket down.  */
-             if (debug_flag > 2)
-               dbg_log (_("Real close socket %d|%d"), i, sock[i]);
-
-             pthread_mutex_lock (&sock_lock);
-             read_polls[i].fd = -1;
-             close (sock[i]);
-             sock[i] = 0;
-             --socks_active;
-             pthread_mutex_unlock (&sock_lock);
-           }
-         else
-           if (nbytes < 0)
+           if (stat (dbs[cnt].filename, &st) < 0)
              {
-               perror (_("Read() error on get request"));
-               return 0;
+               char buf[128];
+               /* We cannot stat() the file, disable file checking.  */
+               dbg_log (_("cannot stat() file `%s': %s"),
+                        dbs[cnt].filename,
+                        strerror_r (errno, buf, sizeof (buf)));
+               dbs[cnt].check_file = 0;
              }
            else
-             fputs (_("Read, data < request buf size, ignoring data"),
-                    stderr);
-
-         free (*key);
-         return -1;
-       }
-      else
-       {
-         /* Ok, have a live one, A real data req buf has been obtained.  */
-         (*key)[(*reqp)->key_len] = '\0';
-         **connp = i;
-         return 0;
-       }
-    }
-}
-
-void
-get_request (int *conn, request_header *req, char **key)
-{
-  int done = 0;
-  int nr;
-
-  if (debug_flag)
-    dbg_log ("get_request");
-
-  /* loop, processing new connection requests until a client buffer
-     is read in on an existing connection.  */
-  while (!done)
-    {
-      /* Poll active connections.  */
-      nr = poll (read_polls, MAX_NUM_CONNECTIONS, -1);
-      if (nr <= 0)
-       {
-         perror (_("Poll new reads"));
-         exit (1);
-       }
-      if (read_polls[0].revents & (POLLRDNORM|POLLERR|POLLHUP|POLLNVAL))
-       /* Handle the case of a new connection request on the named socket.  */
-       handle_new_connection ();
-      else
-       {
-         /* Read data from client specific descriptor.  */
-         if (handle_new_request (&conn, &req, key) == 0)
-           done = 1;
-       }
-    } /* While not_done.  */
-}
-
-void
-init_sockets (void)
-{
-  struct sockaddr_un sock_addr;
-  int i;
-
-  /* Initialize the connections db.  */
-  socks_active = 0;
-
-  /* Initialize the poll array. */
-  for (i = 0; i < MAX_NUM_CONNECTIONS; i++)
-    read_polls[i].fd = -1;
+             dbs[cnt].file_mtime = st.st_mtime;
+         }
+      }
 
   /* Create the socket.  */
-  sock[0] = socket (AF_UNIX, SOCK_STREAM, 0);
-  if (sock[0] < 0)
+  sock = socket (AF_UNIX, SOCK_STREAM, 0);
+  if (sock < 0)
     {
-      perror (_("cannot create socket"));
+      dbg_log (_("cannot open socket: %s"), strerror (errno));
       exit (1);
     }
   /* Bind a name to the socket.  */
   sock_addr.sun_family = AF_UNIX;
   strcpy (sock_addr.sun_path, _PATH_NSCDSOCKET);
-  if (bind (sock[0], (struct sockaddr *) &sock_addr, sizeof (sock_addr)) < 0)
+  if (bind (sock, (struct sockaddr *) &sock_addr, sizeof (sock_addr)) < 0)
     {
       dbg_log ("%s: %s", _PATH_NSCDSOCKET, strerror (errno));
       exit (1);
     }
+
   /* Set permissions for the socket.  */
   chmod (_PATH_NSCDSOCKET, 0666);
 
   /* Set the socket up to accept connections.  */
-  if (listen (sock[0], MAX_NUM_CONNECTIONS) < 0)
+  if (listen (sock, SOMAXCONN) < 0)
     {
-      perror (_("cannot enable socket to accept connections"));
+      dbg_log (_("cannot enable socket to accept connections: %s"),
+              strerror (errno));
       exit (1);
     }
-
-  /* Add the socket to the server's set of active sockets.  */
-  read_polls[0].fd = sock[0];
-  read_polls[0].events = POLLRDNORM;
-  ++socks_active;
 }
 
+
+/* Close the connections.  */
 void
-pw_send_answer (int conn, struct passwd *pwd)
+close_sockets (void)
 {
-  struct iovec vec[6];
-  pw_response_header resp;
-  size_t total_len;
-  int nblocks;
+  close (sock);
+}
 
-  resp.version = NSCD_VERSION;
-  if (pwd != NULL)
-    {
-      resp.found = 1;
-      resp.pw_name_len = strlen (pwd->pw_name);
-      resp.pw_passwd_len = strlen (pwd->pw_passwd);
-      resp.pw_uid = pwd->pw_uid;
-      resp.pw_gid = pwd->pw_gid;
-      resp.pw_gecos_len = strlen (pwd->pw_gecos);
-      resp.pw_dir_len = strlen (pwd->pw_dir);
-      resp.pw_shell_len = strlen (pwd->pw_shell);
-    }
-  else
-    {
-      resp.found = 0;
-      resp.pw_name_len = 0;
-      resp.pw_passwd_len = 0;
-      resp.pw_uid = -1;
-      resp.pw_gid = -1;
-      resp.pw_gecos_len = 0;
-      resp.pw_dir_len = 0;
-      resp.pw_shell_len = 0;
-    }
-  if (sock[conn] == 0)
+
+/* Handle new request.  */
+static void
+handle_request (int fd, request_header *req, void *key)
+{
+  if (debug_level > 0)
+    dbg_log (_("handle_requests: request received (Version = %d)"),
+            req->version);
+
+  if (req->version != NSCD_VERSION)
     {
-      dbg_log (_("bad connection id on send response [%d|%d]"),
-              conn, sock[conn]);
+      dbg_log (_("\
+cannot handle old request version %d; current version is %d"),
+              req->version, NSCD_VERSION);
       return;
     }
 
-  /* Add response header.  */
-  vec[0].iov_base = &resp;
-  vec[0].iov_len = sizeof (pw_response_header);
-  total_len = sizeof (pw_response_header);
-  nblocks = 1;
-
-  if (resp.found)
+  if (req->type >= GETPWBYNAME && req->type <= LASTDBREQ)
     {
-      /* Add pw_name.  */
-      vec[1].iov_base = pwd->pw_name;
-      vec[1].iov_len = resp.pw_name_len;
-      total_len += resp.pw_name_len;
-      /* Add pw_passwd.  */
-      vec[2].iov_base = pwd->pw_passwd;
-      vec[2].iov_len = resp.pw_passwd_len;
-      total_len += resp.pw_passwd_len;
-      /* Add pw_gecos.  */
-      vec[3].iov_base = pwd->pw_gecos;
-      vec[3].iov_len = resp.pw_gecos_len;
-      total_len += resp.pw_gecos_len;
-      /* Add pw_dir.  */
-      vec[4].iov_base = pwd->pw_dir;
-      vec[4].iov_len = resp.pw_dir_len;
-      total_len += resp.pw_dir_len;
-      /* Add pw_shell.  */
-      vec[5].iov_base = pwd->pw_shell;
-      vec[5].iov_len = resp.pw_shell_len;
-      total_len += resp.pw_shell_len;
-
-      nblocks = 6;
-    }
+      struct hashentry *cached;
+      struct database *db = &dbs[serv2db[req->type]];
 
-  /* Send all the data.  */
-  if (writev (sock[conn], vec, nblocks) != total_len)
-    dbg_log (_("write incomplete on send passwd answer: %s"),
-            strerror (errno));
-}
+      if (debug_level > 0)
+       dbg_log ("\t%s (%s)", serv2str[req->type], key);
 
-void
-pw_send_disabled (int conn)
-{
-  pw_response_header resp;
-
-  resp.version = NSCD_VERSION;
-  resp.found = -1;
-  resp.pw_name_len = 0;
-  resp.pw_passwd_len = 0;
-  resp.pw_uid = -1;
-  resp.pw_gid = -1;
-  resp.pw_gecos_len = 0;
-  resp.pw_dir_len = 0;
-  resp.pw_shell_len = 0;
-
-  if (sock[conn] == 0)
-    {
-      dbg_log (_("bad connection id on send response [%d|%d]"),
-              conn, sock[conn]);
-      return;
-    }
+      /* Is this service enabled?  */
+      if (!db->enabled)
+       {
+         /* No sent the prepared record.  */
+         if (TEMP_FAILURE_RETRY (write (fd, db->disabled_iov->iov_base,
+                                        db->disabled_iov->iov_len))
+             != db->disabled_iov->iov_len)
+           {
+             /* We have problems sending the result.  */
+             char buf[256];
+             dbg_log (_("cannot write result: %s"),
+                      strerror_r (errno, buf, sizeof (buf)));
+           }
 
-  /* Send response header.  */
-  if (write (sock[conn], &resp, sizeof (pw_response_header))
-      != sizeof (pw_response_header))
-    dbg_log (_("write incomplete on send response: %s"), strerror (errno));
-}
+         return;
+       }
 
-void
-gr_send_answer (int conn, struct group *grp)
-{
-  struct iovec *vec;
-  size_t *len;
-  gr_response_header resp;
-  size_t total_len, sum;
-  int nblocks;
-  size_t maxiov;
-
-  resp.version = NSCD_VERSION;
-  if (grp != NULL)
-    {
-      resp.found = 1;
-      resp.gr_name_len = strlen (grp->gr_name);
-      resp.gr_passwd_len = strlen (grp->gr_passwd);
-      resp.gr_gid = grp->gr_gid;
-      resp.gr_mem_len = 0;
-      while (grp->gr_mem[resp.gr_mem_len])
-       ++resp.gr_mem_len;
+      /* Be sure we can read the data.  */
+      pthread_rwlock_rdlock (&db->lock);
+
+      /* See whether we can handle it from the cache.  */
+      cached = (struct hashentry *) cache_search (req->type, key, req->key_len,
+                                                 db);
+      if (cached != NULL)
+       {
+         /* Hurray it's in the cache.  */
+         if (TEMP_FAILURE_RETRY (write (fd, cached->packet, cached->total))
+             != cached->total)
+           {
+             /* 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 (debug_level > 0)
+      dbg_log ("\t%s", serv2str[req->type]);
+
+  /* Handle the request.  */
+  switch (req->type)
     {
-      resp.found = 0;
-      resp.gr_name_len = 0;
-      resp.gr_passwd_len = 0;
-      resp.gr_gid = -1;
-      resp.gr_mem_len = 0;
-    }
-  if (sock[conn] == 0)
-    {
-      dbg_log (_("bad connection id on send response [%d|%d]"),
-              conn, sock[conn]);
-      return;
+    case GETPWBYNAME:
+      addpwbyname (&dbs[serv2db[req->type]], fd, req, key);
+      break;
+
+    case GETPWBYUID:
+      addpwbyuid (&dbs[serv2db[req->type]], fd, req, key);
+      break;
+
+    case GETGRBYNAME:
+      addgrbyname (&dbs[serv2db[req->type]], fd, req, key);
+      break;
+
+    case GETGRBYGID:
+      addgrbygid (&dbs[serv2db[req->type]], fd, req, key);
+      break;
+
+    case GETHOSTBYNAME:
+      addhstbyname (&dbs[serv2db[req->type]], fd, req, key);
+      break;
+
+    case GETHOSTBYNAMEv6:
+      addhstbynamev6 (&dbs[serv2db[req->type]], fd, req, key);
+      break;
+
+    case GETHOSTBYADDR:
+      addhstbyaddr (&dbs[serv2db[req->type]], fd, req, key);
+      break;
+
+    case GETHOSTBYADDRv6:
+      addhstbyaddrv6 (&dbs[serv2db[req->type]], fd, req, key);
+      break;
+
+    case GETSTAT:
+      send_stats (fd, dbs);
+      break;
+
+    case SHUTDOWN:
+      termination_handler (0);
+      break;
+
+    default:
+      abort ();
     }
+}
+
 
-  /* We have no fixed number of records so allocate the IOV here.  */
-  vec = alloca ((3 + 1 + resp.gr_mem_len) * sizeof (struct iovec));
-  len = alloca (resp.gr_mem_len * sizeof (size_t));
+/* This is the main loop.  It is replicated in different threads but the
+   `poll' call makes sure only one thread handles an incoming connection.  */
+static void *
+__attribute__ ((__noreturn__))
+nscd_run (void *p)
+{
+  int my_number = (int) p;
+  struct pollfd conn;
+  int run_prune = my_number < lastdb && dbs[my_number].enabled;
+  time_t now = time (NULL);
+  time_t next_prune = now + 15;
+  int timeout = run_prune ? 1000 * (next_prune - now) : -1;
 
-  /* Add response header.  */
-  vec[0].iov_base = &resp;
-  vec[0].iov_len = sizeof (gr_response_header);
-  total_len = sizeof (gr_response_header);
-  nblocks = 1;
+  conn.fd = sock;
+  conn.events = POLLRDNORM;
 
-  if (resp.found)
+  while (1)
     {
-      unsigned int l = 0;
-
-      /* Add gr_name.  */
-      vec[1].iov_base = grp->gr_name;
-      vec[1].iov_len = resp.gr_name_len;
-      total_len += resp.gr_name_len;
-      /* Add gr_passwd.  */
-      vec[2].iov_base = grp->gr_passwd;
-      vec[2].iov_len = resp.gr_passwd_len;
-      total_len += resp.gr_passwd_len;
-      nblocks = 3;
-
-      if (grp->gr_mem[l])
+      int nr = poll (&conn, 1, timeout);
+
+      if (nr == 0)
+       {
+         /* The `poll' call timed out.  It's time to clean up the cache.  */
+         assert (my_number < lastdb);
+         now = time (NULL);
+         prune_cache (&dbs[my_number], now);
+         next_prune = now + 15;
+         timeout = 1000 * (next_prune - now);
+         continue;
+       }
+
+      /* We have a new incoming connection.  */
+      if (conn.revents & (POLLRDNORM|POLLERR|POLLHUP|POLLNVAL))
        {
-         vec[3].iov_base = len;
-         vec[3].iov_len = resp.gr_mem_len * sizeof (size_t);
-         total_len += resp.gr_mem_len * sizeof (size_t);
-         nblocks = 4;
+         /* Accept the connection.  */
+         int fd = accept (conn.fd, NULL, NULL);
+         request_header req;
+         char buf[256];
 
-         do
+         if (fd < 0)
            {
-             len[l] = strlen (grp->gr_mem[l]);
+             dbg_log (_("while accepting connection: %s"),
+                      strerror_r (errno, buf, sizeof (buf)));
+             continue;
+           }
 
-             vec[nblocks].iov_base = grp->gr_mem[l];
-             vec[nblocks].iov_len = len[l];
-             total_len += len[l];
+         /* Now read the request.  */
+         if (TEMP_FAILURE_RETRY (read (fd, &req, sizeof (req)))
+             != sizeof (req))
+           {
+             dbg_log (_("short read while reading request: %s"),
+                      strerror_r (errno, buf, sizeof (buf)));
+             close (fd);
+             continue;
+           }
 
-             ++nblocks;
+         /* 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 (req.key_len < 0 || req.key_len > 1024)
+           {
+             dbg_log (_("key length in request to long: %Zd"), req.key_len);
+             close (fd);
+             continue;
+           }
+         else
+           {
+             /* Get the key.  */
+             char keybuf[req.key_len];
+
+             if (TEMP_FAILURE_RETRY (read (fd, keybuf, req.key_len))
+                 != req.key_len)
+               {
+                 dbg_log (_("short read while reading request key: %s"),
+                          strerror_r (errno, buf, sizeof (buf)));
+                 close (fd);
+                 continue;
+               }
+
+             /* Phew, we got all the data, now process it.  */
+             handle_request (fd, &req, keybuf);
+
+             /* We are done.  */
+             close (fd);
            }
-         while (grp->gr_mem[++l]);
        }
-    }
 
-#ifdef UIO_MAXIOV
-  maxiov = UIO_MAXIOV;
-#else
-  maxiov = sysconf (_SC_UIO_MAXIOV);
-#endif
-
-  /* Send all the data.  */
-  sum = 0;
-  while (nblocks > maxiov)
-    {
-      sum += writev (sock[conn], vec, maxiov);
-      vec += maxiov;
-      nblocks -= maxiov;
+      if (run_prune)
+       {
+         now = time (NULL);
+         timeout = now < next_prune ? 1000 * (next_prune - now) : 0;
+       }
     }
-  if (sum + writev (sock[conn], vec, nblocks) != total_len)
-    dbg_log (_("write incomplete on send group answer: %s"),
-            strerror (errno));
 }
 
+
+/* Start all the threads we want.  The initial process is thread no. 1.  */
 void
-gr_send_disabled (int conn)
+start_threads (void)
 {
-  gr_response_header resp;
+  int i;
+  pthread_attr_t attr;
+  pthread_t th;
 
-  resp.version = NSCD_VERSION;
-  resp.found = -1;
-  resp.gr_name_len = 0;
-  resp.gr_passwd_len = 0;
-  resp.gr_gid = -1;
-  resp.gr_mem_len = 0;
+  pthread_attr_init (&attr);
+  pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
 
-  if (sock[conn] == 0)
-    {
-      dbg_log (_("bad connection id on send gr_disabled response [%d|%d]"),
-              conn, sock[conn]);
-      return;
-    }
+  /* We allow less than LASTDB threads only for debugging.  */
+  if (debug_level == 0)
+    nthreads = MAX (nthreads, lastdb);
 
-  /* Send response header.  */
-  if (write (sock[conn], &resp, sizeof (gr_response_header))
-      != sizeof (gr_response_header))
-    dbg_log (_("write incomplete on send gr_disabled response: %s"),
-            strerror (errno));
-}
+  for (i = 1; i < nthreads; ++i)
+    pthread_create (&th, &attr, nscd_run, (void *) i);
 
-void
-stat_send (int conn, stat_response_header *resp)
-{
-  if (sock[conn] == 0)
-    {
-      dbg_log (_("bad connection id on send stat response [%d|%d]"),
-              conn, sock[conn]);
-      return;
-    }
+  pthread_attr_destroy (&attr);
 
-  /* send response header.  */
-  if (write (sock[conn], resp, sizeof (stat_response_header))
-      != sizeof (stat_response_header))
-    dbg_log (_("write incomplete on send stat response: %s"),
-            strerror (errno));
+  nscd_run ((void *) 0);
 }
index b2b8b3e31cae8c05e359bed219ac1d93e74cb906..21997cdcf4981ce612ef373b213be17480bc9ed0 100644 (file)
@@ -28,8 +28,8 @@
    if in debug mode and no debug file, we write the messages to stderr,
    else to syslog.  */
 
-FILE *dbgout = NULL;
-int debug_flag = 0;
+FILE *dbgout;
+int debug_level;
 
 int
 set_logfile (const char *logfile)
@@ -47,7 +47,7 @@ dbg_log (const char *fmt,...)
   va_start (ap, fmt);
   vsnprintf (msg2, sizeof (msg), fmt, ap);
 
-  if (debug_flag)
+  if (debug_level > 0)
     {
       snprintf (msg, sizeof (msg), "%d: %s\n", getpid (), msg2);
       if (dbgout)
index c3d1dc4559cbcdda0edf8ccabb32971cb61bb1f1..5aeff89f6a50e603fbe48f284b721937d39bfe09 100644 (file)
 #ifndef _DBG_LOG_H
 #define _DBG_LOG_H     1
 
-extern int debug_flag;
+extern int debug_level;
 
-extern void dbg_log (const char *, ...);
+extern void dbg_log (const char *str, ...);
+
+extern int set_logfile (const char *logfile);
 
 #endif
diff --git a/nscd/gethstbyad_r.c b/nscd/gethstbyad_r.c
new file mode 100644 (file)
index 0000000..c22044a
--- /dev/null
@@ -0,0 +1,31 @@
+/* Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#include <netdb.h>
+
+
+#define LOOKUP_TYPE    struct hostent
+#define FUNCTION_NAME  gethostbyaddr
+#define DATABASE_NAME  hosts
+#define ADD_PARAMS     const char *addr, int len, int type
+#define ADD_VARIABLES  addr, len, type
+#define NEED_H_ERRNO   1
+#define NEED__RES      1
+
+#include "../nss/getXXbyYY_r.c"
diff --git a/nscd/gethstbynm2_r.c b/nscd/gethstbynm2_r.c
new file mode 100644 (file)
index 0000000..b9b3a71
--- /dev/null
@@ -0,0 +1,39 @@
+/* Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#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  gethostbyname2
+#define DATABASE_NAME  hosts
+#define ADD_PARAMS     const char *name, int af
+#define ADD_VARIABLES  name, af
+#define NEED_H_ERRNO   1
+
+#define HANDLE_DIGITS_DOTS     1
+#define HAVE_LOOKUP_BUFFER     1
+#define HAVE_AF                        1
+
+#include "../nss/getXXbyYY_r.c"
index f34780ac4e08a508c1f5a375f9249f510349c573..1645d71e4192e0e769e2aeb53a22598324574ff9 100644 (file)
@@ -1,6 +1,7 @@
-/* Copyright (c) 1998 Free Software Foundation, Inc.
+/* Cache handling for group lookup.
+   Copyright (C) 1998 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
-   Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1998.
+   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 Library General Public License as
    You should have received a copy of the GNU Library General Public
    License along with the GNU C Library; see the file COPYING.LIB.  If not,
    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-   Boston, MA 02111-1307, USA. */
+   Boston, MA 02111-1307, USA.  */
 
+#include <assert.h>
 #include <errno.h>
+#include <error.h>
 #include <grp.h>
-#include <pthread.h>
+#include <stddef.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
-#include <rpcsvc/nis.h>
-#include <sys/types.h>
 
-#include "dbg_log.h"
 #include "nscd.h"
+#include "dbg_log.h"
 
-static unsigned long modulo = 211;
-static unsigned long postimeout = 3600;
-static unsigned long negtimeout = 60;
-
-static unsigned long poshit = 0;
-static unsigned long posmiss = 0;
-static unsigned long neghit = 0;
-static unsigned long negmiss = 0;
-
-struct grphash
+/* This is the standard reply in case the service is disabled.  */
+static const gr_response_header disabled =
 {
-  time_t create;
-  struct grphash *next;
-  struct group *grp;
+  version: NSCD_VERSION,
+  found: -1,
+  gr_name_len: 0,
+  gr_passwd_len: 0,
+  gr_gid: -1,
+  gr_mem_cnt: 0,
 };
-typedef struct grphash grphash;
 
-struct gidhash
+/* This is the struct describing how to write this record.  */
+const struct iovec grp_iov_disabled =
 {
-  struct gidhash *next;
-  struct group *grptr;
+  iov_base: (void *) &disabled,
+  iov_len: sizeof (disabled)
 };
-typedef struct gidhash gidhash;
 
-struct neghash
+
+/* This is the standard reply in case we haven't found the dataset.  */
+static const gr_response_header notfound =
 {
-  time_t create;
-  struct neghash *next;
-  char *key;
+  version: NSCD_VERSION,
+  found: 0,
+  gr_name_len: 0,
+  gr_passwd_len: 0,
+  gr_gid: -1,
+  gr_mem_cnt: 0,
 };
-typedef struct neghash neghash;
-
-static grphash *grptbl;
-static gidhash *gidtbl;
-static neghash *negtbl;
-
-static pthread_rwlock_t grplock = PTHREAD_RWLOCK_INITIALIZER;
-static pthread_rwlock_t neglock = PTHREAD_RWLOCK_INITIALIZER;
 
-static void *grptable_update (void *);
-static void *negtable_update (void *);
-
-void
-get_gr_stat (stat_response_header *stat)
+/* This is the struct describing how to write this record.  */
+static const struct iovec iov_notfound =
 {
-  stat->gr_poshit = poshit;
-  stat->gr_posmiss = posmiss;
-  stat->gr_neghit = neghit;
-  stat->gr_negmiss = negmiss;
-  stat->gr_size = modulo;
-  stat->gr_posttl = postimeout;
-  stat->gr_negttl = negtimeout;
-}
+  iov_base: (void *) &notfound,
+  iov_len: sizeof (notfound)
+};
 
-void
-set_grp_modulo (unsigned long mod)
-{
-  modulo = mod;
-}
 
-void
-set_pos_grp_ttl (unsigned long ttl)
+struct groupdata
 {
-  postimeout = ttl;
-}
+  gr_response_header resp;
+  char strdata[0];
+};
 
-void
-set_neg_grp_ttl (unsigned long ttl)
-{
-  negtimeout = ttl;
-}
 
-int
-cache_grpinit ()
+static void
+cache_addgr (struct database *db, int fd, request_header *req, void *key,
+            struct group *grp)
 {
-  pthread_attr_t attr;
-  pthread_t thread;
+  ssize_t total;
+  ssize_t written;
+  time_t t = time (NULL);
 
-  grptbl = calloc (modulo, sizeof (grphash));
-  if (grptbl == NULL)
-    return -1;
-  gidtbl = calloc (modulo, sizeof (grphash));
-  if (gidtbl == NULL)
-    return -1;
-  negtbl = calloc (modulo, sizeof (neghash));
-  if (negtbl == NULL)
-    return -1;
-
-  pthread_attr_init (&attr);
-  pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
-
-  pthread_create (&thread, NULL, grptable_update, &attr);
-  pthread_create (&thread, NULL, negtable_update, &attr);
-
-  pthread_attr_destroy (&attr);
-
-  return 0;
-}
-
-static struct group *
-save_grp (struct group *src)
-{
-  struct group *dest;
-  unsigned long int l;
-  size_t tlen;
-  size_t name_len = strlen (src->gr_name) + 1;
-  size_t passwd_len = strlen (src->gr_passwd) + 1;
-  char *cp;
-
-  /* How many members does this group have?  */
-  l = tlen = 0;
-  while (src->gr_mem[l] != NULL)
-    tlen += strlen (src->gr_mem[l++]) + 1;
-
-  dest = malloc (sizeof (struct group) + (l + 1) * sizeof (char *)
-                + name_len + passwd_len + tlen);
-  if (dest == NULL)
-    return NULL;
-
-  dest->gr_mem = (char **) (dest + 1);
-  cp = (char *) (dest->gr_mem + l + 1);
-
-  dest->gr_name = cp;
-  cp = mempcpy (cp, src->gr_name, name_len);
-  dest->gr_passwd = cp;
-  cp = mempcpy (cp, src->gr_passwd, passwd_len);
-  dest->gr_gid = src->gr_gid;
-
-  l = 0;
-  while (src->gr_mem[l] != NULL)
+  if (grp == NULL)
     {
-      dest->gr_mem[l] = cp;
-      cp = stpcpy (cp, src->gr_mem[l]) + 1;
-      ++l;
-    }
-  dest->gr_mem[l] = NULL;
+      /* We have no data.  This means we send the standard reply for this
+        case.  */
+      void *copy;
 
-  return dest;
-}
+      total = sizeof (notfound);
 
-static void
-free_grp (struct group *src)
-{
-  free (src);
-}
+      written = writev (fd, &iov_notfound, 1);
 
-static int
-add_cache (struct group *grp)
-{
-  grphash *work;
-  gidhash *gidwork;
-  unsigned long int hash = __nis_hash (grp->gr_name,
-                                      strlen (grp->gr_name)) % modulo;
+      copy = malloc (req->key_len);
+      if (copy == NULL)
+       error (EXIT_FAILURE, errno, _("while allocating key copy"));
+      memcpy (copy, key, req->key_len);
 
-  if (debug_flag)
-    dbg_log (_("grp_add_cache (%s)"), grp->gr_name);
+      /* Compute the timeout time.  */
+      t += db->negtimeout;
 
-  work = &grptbl[hash];
+      /* Now get the lock to safely insert the records.  */
+      pthread_rwlock_rdlock (&db->lock);
 
-  if (grptbl[hash].grp == NULL)
-    grptbl[hash].grp = save_grp (grp);
-  else
-    {
-      while (work->next != NULL)
-       work = work->next;
+      cache_add (req->type, copy, req->key_len, &iov_notfound,
+                sizeof (notfound), (void *) -1, 0, t, db);
 
-      work->next = calloc (1, sizeof (grphash));
-      work->next->grp = save_grp (grp);
-      work = work->next;
+      pthread_rwlock_unlock (&db->lock);
     }
-
-  time (&work->create);
-  gidwork = &gidtbl[grp->gr_gid % modulo];
-  if (gidwork->grptr == NULL)
-    gidwork->grptr = work->grp;
   else
     {
-      while (gidwork->next != NULL)
-       gidwork = gidwork->next;
+      /* Determine the I/O structure.  */
+      struct groupdata *data;
+      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;
+      size_t *gr_mem_len;
+      size_t gr_mem_len_total = 0;
+      char *gr_name;
+      char *cp;
+      char buf[12];
+      ssize_t n;
+      size_t cnt;
+
+      /* We need this to insert the `bygid' entry.  */
+      n = snprintf (buf, sizeof (buf), "%d", grp->gr_gid) + 1;
+
+      /* Determine the length of all members.  */
+      while (grp->gr_mem[gr_mem_cnt])
+       ++gr_mem_cnt;
+      gr_mem_len = (size_t *) alloca (gr_mem_cnt * sizeof (size_t));
+      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];
+       }
 
-      gidwork->next = calloc (1, sizeof (gidhash));
-      gidwork->next->grptr = work->grp;
-    }
+      /* We allocate all data in one memory block: the iov vector,
+        the response header and the dataset itself.  */
+      total = (sizeof (struct groupdata)
+              + gr_mem_cnt * sizeof (size_t)
+              + gr_name_len + gr_passwd_len + gr_mem_len_total);
+      data = (struct groupdata *) malloc (total + n);
+      if (data == NULL)
+       /* There is no reason to go on.  */
+       error (EXIT_FAILURE, errno, _("while allocating cache entry"));
 
-  return 0;
-}
+      data->resp.found = 1;
+      data->resp.gr_name_len = gr_name_len;
+      data->resp.gr_passwd_len = gr_passwd_len;
+      data->resp.gr_gid = grp->gr_gid;
+      data->resp.gr_mem_cnt = gr_mem_cnt;
 
-static struct group *
-cache_search_name (const char *name)
-{
-  grphash *work;
-  unsigned long int hash = __nis_hash (name, strlen(name)) % modulo;
+      cp = data->strdata;
 
-  work = &grptbl[hash];
+      /* This is the member string length array.  */
+      cp = mempcpy (cp, gr_mem_len, gr_mem_cnt * sizeof (size_t));
+      gr_name = cp = mempcpy (cp, grp->gr_name, gr_name_len);
+      cp = mempcpy (cp, grp->gr_passwd, gr_passwd_len);
 
-  while (work->grp != NULL)
-    {
-      if (strcmp (work->grp->gr_name, name) == 0)
-       return work->grp;
-      if (work->next != NULL)
-       work = work->next;
-      else
-       return NULL;
-    }
-  return NULL;
-}
-
-static struct group *
-cache_search_gid (gid_t gid)
-{
-  gidhash *work;
+      for (cnt = 0; cnt < gr_mem_cnt; ++cnt)
+       cp = mempcpy (cp, grp->gr_mem[cnt], gr_mem_len[cnt]);
 
-  work = &gidtbl[gid % modulo];
+      /* Finally the stringified GID value.  */
+      memcpy (cp, buf, n);
 
-  while (work->grptr != NULL)
-    {
-      if (work->grptr->gr_gid == gid)
-       return work->grptr;
-      if (work->next != NULL)
-       work = work->next;
-      else
-       return NULL;
-    }
-  return NULL;
-}
+      /* Write the result.  */
+      written = write (fd, &data->resp, total);
 
-static int
-add_negcache (char *key)
-{
-  neghash *work;
-  unsigned long int hash = __nis_hash (key, strlen (key)) % modulo;
+      /* Compute the timeout time.  */
+      t += db->postimeout;
 
-  if (debug_flag)
-    dbg_log (_("grp_add_netgache (%s|%ld)"), key, hash);
+      /* Now get the lock to safely insert the records.  */
+      pthread_rwlock_rdlock (&db->lock);
 
-  work = &negtbl[hash];
+      /* We have to add the value for both, byname and byuid.  */
+      cache_add (GETGRBYNAME, gr_name, gr_name_len, data,
+                total, data, 0, t, db);
 
-  if (negtbl[hash].key == NULL)
-    {
-      negtbl[hash].key = strdup (key);
-      negtbl[hash].next = NULL;
-    }
-  else
-    {
-      while (work->next != NULL)
-       work = work->next;
+      cache_add (GETGRBYGID, cp, n, data, total, data, 1, t, db);
 
-      work->next = calloc (1, sizeof (neghash));
-      work->next->key = strdup (key);
-      work = work->next;
+      pthread_rwlock_unlock (&db->lock);
     }
 
-  time (&work->create);
-  return 0;
-}
-
-static int
-cache_search_neg (const char *key)
-{
-  neghash *work;
-  unsigned long int hash = __nis_hash (key, strlen (key)) % modulo;
-
-  if (debug_flag)
-    dbg_log (_("grp_cache_search_neg (%s|%ld)"), key, hash);
-
-  work = &negtbl[hash];
-
-  while (work->key != NULL)
+  if (written != total)
     {
-      if (strcmp (work->key, key) == 0)
-       return 1;
-      if (work->next != NULL)
-       work = work->next;
-      else
-       return 0;
+      char buf[256];
+      dbg_log (_("short write in %s: %s"),  __FUNCTION__,
+              strerror_r (errno, buf, sizeof (buf)));
     }
-  return 0;
 }
 
-void *
-cache_getgrnam (void *v_param)
-{
-  param_t *param = (param_t *)v_param;
-  struct group *grp;
 
-  pthread_rwlock_rdlock (&grplock);
-  grp = cache_search_name (param->key);
+void
+addgrbyname (struct database *db, int fd, request_header *req, void *key)
+{
+  /* 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 = 256;
+  char *buffer = alloca (buflen);
+  struct group resultbuf;
+  struct group *grp;
 
-  /* I don't like it to hold the read only lock longer, but it is
-     necessary to avoid to much malloc/free/strcpy.  */
+  if (debug_level > 0)
+    dbg_log (_("Haven't found \"%s\" in group cache!"), key);
 
-  if (grp != NULL)
+  while (getgrnam_r (key, &resultbuf, buffer, buflen, &grp) != 0
+        && errno == ERANGE)
     {
-      if (debug_flag)
-       dbg_log (_("Found \"%s\" in cache !"), param->key);
-
-      ++poshit;
-      gr_send_answer (param->conn, grp);
-      close_socket (param->conn);
-
-      pthread_rwlock_unlock (&grplock);
+      errno = 0;
+      buflen += 256;
+      buffer = alloca (buflen);
     }
-  else
-    {
-      int status;
-      int buflen = 1024;
-      char *buffer = calloc (1, buflen);
-      struct group resultbuf;
-
-      if (debug_flag)
-       dbg_log (_("Doesn't found \"%s\" in cache !"), param->key);
-
-      pthread_rwlock_unlock (&grplock);
-
-      pthread_rwlock_rdlock (&neglock);
-      status = cache_search_neg (param->key);
-      pthread_rwlock_unlock (&neglock);
 
-      if (status == 0)
-       {
-         while (buffer != NULL
-                && (getgrnam_r (param->key, &resultbuf, buffer, buflen, &grp)
-                    != 0)
-                && errno == ERANGE)
-           {
-             errno = 0;
-             buflen += 1024;
-             buffer = realloc (buffer, buflen);
-           }
-
-         if (buffer != NULL && grp != NULL)
-           {
-             struct group *tmp;
-
-             ++poshit;
-             pthread_rwlock_wrlock (&grplock);
-             /* While we are waiting on the lock, somebody else could
-                add this entry.  */
-             tmp = cache_search_name (param->key);
-             if (tmp == NULL)
-               add_cache (grp);
-             pthread_rwlock_unlock (&grplock);
-           }
-         else
-           {
-             pthread_rwlock_wrlock (&neglock);
-             add_negcache (param->key);
-             ++negmiss;
-             pthread_rwlock_unlock (&neglock);
-           }
-       }
-      else
-       ++neghit;
-
-      gr_send_answer (param->conn, grp);
-      close_socket (param->conn);
-      if (buffer != NULL)
-       free (buffer);
-    }
-  free (param->key);
-  free (param);
-  return NULL;
-}
-
-void *
-cache_gr_disabled (void *v_param)
-{
-  param_t *param = (param_t *)v_param;
-
-  if (debug_flag)
-    dbg_log (_("\tgroup cache is disabled\n"));
-
-  gr_send_disabled (param->conn);
-  return NULL;
+  cache_addgr (db, fd, req, key, grp);
 }
 
-void *
-cache_getgrgid (void *v_param)
-{
-  param_t *param = (param_t *)v_param;
-  struct group *grp, resultbuf;
-  gid_t gid = strtol (param->key, NULL, 10);
-
-  pthread_rwlock_rdlock (&grplock);
-  grp = cache_search_gid (gid);
-
-  /* I don't like it to hold the read only lock longer, but it is
-     necessary to avoid to much malloc/free/strcpy.  */
-
-  if (grp != NULL)
-    {
-      if (debug_flag)
-       dbg_log (_("Found \"%d\" in cache !"), gid);
 
-      ++poshit;
-      gr_send_answer (param->conn, grp);
-      close_socket (param->conn);
-
-      pthread_rwlock_unlock (&grplock);
-    }
-  else
-    {
-      int buflen = 1024;
-      char *buffer = malloc (buflen);
-      int status;
-
-      if (debug_flag)
-       dbg_log (_("Doesn't found \"%d\" in cache !"), gid);
-
-      pthread_rwlock_unlock (&grplock);
-
-      pthread_rwlock_rdlock (&neglock);
-      status = cache_search_neg (param->key);
-      pthread_rwlock_unlock (&neglock);
-
-      if (status == 0)
-        {
-         while (buffer != NULL
-                && (getgrgid_r (gid, &resultbuf, buffer, buflen, &grp) != 0)
-                && errno == ERANGE)
-           {
-             errno = 0;
-             buflen += 1024;
-             buffer = realloc (buffer, buflen);
-           }
-
-         if (buffer != NULL && grp != NULL)
-           {
-             struct group *tmp;
-
-             ++posmiss;
-             pthread_rwlock_wrlock (&grplock);
-             /* While we are waiting on the lock, somebody else could
-                add this entry.  */
-             tmp = cache_search_gid (gid);
-             if (tmp == NULL)
-               add_cache (grp);
-             pthread_rwlock_unlock (&grplock);
-           }
-         else
-           {
-             ++negmiss;
-             pthread_rwlock_wrlock (&neglock);
-             add_negcache (param->key);
-             pthread_rwlock_unlock (&neglock);
-           }
-       }
-      else
-       ++neghit;
-
-      gr_send_answer (param->conn, grp);
-      close_socket (param->conn);
-      if (buffer != NULL)
-       free (buffer);
-    }
-  free (param->key);
-  free (param);
-  return NULL;
-}
-
-static void *
-grptable_update (void *v)
-{
-  time_t now;
-  int i;
+void
+addgrbygid (struct database *db, int fd, request_header *req, void *key)
+{
+  /* 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 = 256;
+  char *buffer = alloca (buflen);
+  struct group resultbuf;
+  struct group *grp;
+  gid_t gid = atol (key);
 
-  sleep (20);
+  if (debug_level > 0)
+    dbg_log (_("Haven't found \"%d\" in group cache!"), gid);
 
-  while (!do_shutdown)
+  while (getgrgid_r (gid, &resultbuf, buffer, buflen, &grp) != 0
+        && errno == ERANGE)
     {
-      if (debug_flag > 2)
-       dbg_log (_("(grptable_update) Wait for write lock!"));
-
-      pthread_rwlock_wrlock (&grplock);
-
-      if (debug_flag > 2)
-       dbg_log (_("(grptable_update) Have write lock"));
-
-      time (&now);
-      for (i = 0; i < modulo; ++i)
-       {
-         grphash *work = &grptbl[i];
-
-         while (work && work->grp)
-           {
-             if ((now - work->create) >= postimeout)
-               {
-                 gidhash *uh = &gidtbl[work->grp->gr_gid % modulo];
-
-                 if (debug_flag)
-                   dbg_log (_("Give \"%s\" free"), work->grp->gr_name);
-
-                 while (uh && uh->grptr)
-                   {
-                     if (uh->grptr->gr_gid == work->grp->gr_gid)
-                       {
-                         if (debug_flag > 3)
-                           dbg_log (_("Give gid for \"%s\" free"),
-                                    work->grp->gr_name);
-                         if (uh->next != NULL)
-                           {
-                             gidhash *tmp = uh->next;
-                             uh->grptr = tmp->grptr;
-                             uh->next = tmp->next;
-                             free (tmp);
-                           }
-                         else
-                           uh->grptr = NULL;
-                       }
-                     uh = uh->next;
-                   }
-
-                 free_grp (work->grp);
-                 if (work->next != NULL)
-                   {
-                     grphash *tmp = work->next;
-                     work->create = tmp->create;
-                     work->next = tmp->next;
-                     work->grp = tmp->grp;
-                     free (tmp);
-                   }
-                 else
-                   work->grp = NULL;
-               }
-             work = work->next;
-           }
-       }
-      if (debug_flag > 2)
-       dbg_log (_("(grptable_update) Release wait lock"));
-      pthread_rwlock_unlock (&grplock);
-      sleep (20);
+      errno = 0;
+      buflen += 256;
+      buffer = alloca (buflen);
     }
-  return NULL;
-}
-
-static void *
-negtable_update (void *v)
-{
-  time_t now;
-  int i;
-
-  sleep (30);
 
-  while (!do_shutdown)
-    {
-      if (debug_flag > 2)
-       dbg_log (_("(neggrptable_update) Wait for write lock!"));
-
-      pthread_rwlock_wrlock (&neglock);
-
-      if (debug_flag > 2)
-       dbg_log (_("(neggrptable_update) Have write lock"));
-
-      time (&now);
-      for (i = 0; i < modulo; ++i)
-       {
-         neghash *work = &negtbl[i];
-
-         while (work && work->key)
-           {
-             if ((now - work->create) >= negtimeout)
-               {
-                 if (debug_flag)
-                   dbg_log (_("Give \"%s\" free"), work->key);
-
-                 free (work->key);
-
-                 if (work->next != NULL)
-                   {
-                     neghash *tmp = work->next;
-                     work->create = tmp->create;
-                     work->next = tmp->next;
-                     work->key = tmp->key;
-                     free (tmp);
-                   }
-                 else
-                   work->key = NULL;
-               }
-             work = work->next;
-           }
-       }
-      if (debug_flag > 2)
-       dbg_log (_("(neggrptable_update) Release wait lock"));
-      pthread_rwlock_unlock (&neglock);
-      sleep (10);
-    }
-  return NULL;
+  cache_addgr (db, fd, req, key, grp);
 }
diff --git a/nscd/hstcache.c b/nscd/hstcache.c
new file mode 100644 (file)
index 0000000..4e3af9c
--- /dev/null
@@ -0,0 +1,405 @@
+/* Cache handling for host lookup.
+   Copyright (C) 1998 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 Library General Public License as
+   published by the Free Software Foundation; either version 2 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  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 <error.h>
+#include <netdb.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+
+#include "nscd.h"
+#include "dbg_log.h"
+
+/* Get implementation for some internal functions.  */
+#include "../resolv/mapv4v6addr.h"
+
+
+/* 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 struct describing how to write this record.  */
+static const struct iovec iov_notfound =
+{
+  iov_base: (void *) &notfound,
+  iov_len: sizeof (notfound)
+};
+
+
+struct hostdata
+{
+  hst_response_header resp;
+  char strdata[0];
+};
+
+
+static void
+cache_addhst (struct database *db, int fd, request_header *req, void *key,
+             struct hostent *hst)
+{
+  ssize_t total;
+  ssize_t written;
+  time_t t = time (NULL);
+
+  if (hst == NULL)
+    {
+      /* We have no data.  This means we send the standard reply for this
+        case.  */
+      void *copy;
+
+      total = sizeof (notfound);
+
+      written = writev (fd, &iov_notfound, 1);
+
+      copy = malloc (req->key_len);
+      if (copy == NULL)
+       error (EXIT_FAILURE, errno, _("while allocating key copy"));
+      memcpy (copy, key, req->key_len);
+
+      /* Compute the timeout time.  */
+      t += db->negtimeout;
+
+      /* Now get the lock to safely insert the records.  */
+      pthread_rwlock_rdlock (&db->lock);
+
+      cache_add (req->type, copy, req->key_len, &iov_notfound,
+                sizeof (notfound), (void *) -1, 0, t, db);
+
+      pthread_rwlock_unlock (&db->lock);
+    }
+  else
+    {
+      /* Determine the I/O structure.  */
+      struct hostdata *data;
+      size_t h_name_len = strlen (hst->h_name) + 1;
+      size_t h_aliases_cnt;
+      size_t *h_aliases_len;
+      size_t h_addr_list_cnt;
+      int addr_list_type;
+      char *addresses;
+      char *aliases;
+      char *key_copy = NULL;
+      char *cp;
+      size_t cnt;
+
+      /* 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 = alloca (h_aliases_cnt * sizeof (size_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;
+      for (cnt = 0; hst->h_addr_list[cnt]; ++cnt)
+       ++h_addr_list_cnt;
+
+      /* We allocate all data in one memory block: the iov vector,
+        the response header and the dataset itself.  */
+      total += (sizeof (struct hostdata)
+               + h_name_len
+               + h_aliases_cnt * sizeof (size_t)
+               + h_addr_list_cnt * (hst->h_length
+                                    + (hst->h_length == INADDRSZ
+                                       ? IN6ADDRSZ : 0)));
+
+      data = (struct hostdata *) malloc (total + req->key_len);
+      if (data == NULL)
+       /* There is no reason to go on.  */
+       error (EXIT_FAILURE, errno, _("while allocating cache entry"));
+
+      data->resp.found = 1;
+      data->resp.h_name_len = h_name_len;
+      data->resp.h_aliases_cnt = h_aliases_cnt;
+      data->resp.h_addrtype = hst->h_addrtype;
+      data->resp.h_length = hst->h_length;
+      data->resp.h_addr_list_cnt = h_addr_list_cnt;
+      data->resp.error = NETDB_SUCCESS;
+
+      cp = data->strdata;
+
+      cp = mempcpy (cp, hst->h_name, h_name_len);
+      cp = mempcpy (cp, h_aliases_len, h_aliases_cnt * sizeof (size_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);
+
+      /* And the generated IPv6 addresses if necessary.  */
+      if (hst->h_length == INADDRSZ)
+       {
+         /* Generate the IPv6 addresses.  */
+         for (cnt = 0; cnt < h_addr_list_cnt; cp += IN6ADDRSZ, ++cnt)
+           map_v4v6_address (hst->h_addr_list[cnt], cp);
+       }
+
+      /* 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 == data->strdata + total - sizeof (hst_response_header));
+
+      /* 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.  */
+      if (req->type == GETHOSTBYNAME || req->type == GETHOSTBYNAMEv6)
+       key_copy = memcpy (cp, key, req->key_len);
+
+      /* 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.  */
+      written = write (fd, data, total);
+
+      addr_list_type = (hst->h_length == INADDRSZ
+                       ? GETHOSTBYADDR : GETHOSTBYADDRv6);
+
+      /* Compute the timeout time.  */
+      t += db->postimeout;
+
+      /* Now get the lock to safely insert the records.  */
+      pthread_rwlock_rdlock (&db->lock);
+
+      /* First add all the aliases.  */
+      for (cnt = 0; cnt < h_aliases_cnt; ++cnt)
+       {
+         if (addr_list_type == GETHOSTBYADDR)
+           cache_add (GETHOSTBYNAME, aliases, h_aliases_len[cnt], data, total,
+                      data, 0, t, db);
+
+         cache_add (GETHOSTBYNAMEv6, aliases, h_aliases_len[cnt], data, total,
+                    data, 0, t, db);
+
+         aliases += h_aliases_len[cnt];
+       }
+
+      /* Next the normal addresses.  */
+      for (cnt = 0; cnt < h_addr_list_cnt; ++cnt)
+       {
+         cache_add (addr_list_type, addresses, hst->h_length, data, total,
+                    data, 0, t, db);
+         addresses += hst->h_length;
+       }
+
+      /* If necessary the IPv6 addresses.  */
+      if (addr_list_type == GETHOSTBYADDR)
+       for (cnt = 0; cnt < h_addr_list_cnt; ++cnt)
+         {
+           cache_add (GETHOSTBYADDRv6, addresses, IN6ADDRSZ, data, total,
+                      data, 0, t, db);
+           addresses += IN6ADDRSZ;
+         }
+
+      /* If necessary add the key for this request.  */
+      if (req->type == GETHOSTBYNAME || req->type == GETHOSTBYNAMEv6)
+       {
+         if (addr_list_type == GETHOSTBYADDR)
+           cache_add (GETHOSTBYNAME, key_copy, req->key_len, data, total,
+                      data, 0, t, db);
+         cache_add (GETHOSTBYNAMEv6, key_copy, req->key_len, data,
+                    total, data, 0, t, db);
+       }
+
+      /* And finally the name.  We mark this as the last entry.  */
+      if (addr_list_type == GETHOSTBYADDR)
+       cache_add (GETHOSTBYNAME, data->strdata, h_name_len, data, total, data,
+                  0, t, db);
+      cache_add (GETHOSTBYNAMEv6, data->strdata, h_name_len, data,
+                total, data, 1, t, db);
+
+      pthread_rwlock_unlock (&db->lock);
+    }
+
+  if (written != total)
+    {
+      char buf[256];
+      dbg_log (_("short write in %s: %s"),  __FUNCTION__,
+              strerror_r (errno, buf, sizeof (buf)));
+    }
+}
+
+
+void
+addhstbyname (struct database *db, int fd, request_header *req, void *key)
+{
+  /* 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 = 512;
+  char *buffer = alloca (buflen);
+  struct hostent resultbuf;
+  struct hostent *hst;
+
+  if (debug_level > 0)
+    dbg_log (_("Haven't found \"%s\" in hosts cache!"), key);
+
+  while (gethostbyname2_r (key, AF_INET, &resultbuf, buffer, buflen, &hst,
+                          &h_errno) != 0
+        && h_errno == NETDB_INTERNAL
+        && errno == ERANGE)
+    {
+      errno = 0;
+      buflen += 256;
+      buffer = alloca (buflen);
+    }
+
+  cache_addhst (db, fd, req, key, hst);
+}
+
+
+void
+addhstbyaddr (struct database *db, int fd, request_header *req, void *key)
+{
+  /* 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 = 512;
+  char *buffer = alloca (buflen);
+  struct hostent resultbuf;
+  struct hostent *hst;
+
+  if (debug_level > 0)
+    {
+      char buf[64];
+      dbg_log (_("Haven't found \"%s\" in hosts cache!"),
+              inet_ntop (AF_INET, key, buf, sizeof (buf)));
+    }
+
+  while (gethostbyaddr_r (key, INADDRSZ, AF_INET, &resultbuf, buffer, buflen,
+                         &hst, &h_errno) != 0
+        && h_errno == NETDB_INTERNAL
+        && errno == ERANGE)
+    {
+      errno = 0;
+      buflen += 256;
+      buffer = alloca (buflen);
+    }
+
+  cache_addhst (db, fd, req, key, hst);
+}
+
+
+void
+addhstbynamev6 (struct database *db, int fd, request_header *req, void *key)
+{
+  /* 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 = 512;
+  char *buffer = alloca (buflen);
+  struct hostent resultbuf;
+  struct hostent *hst;
+
+  if (debug_level > 0)
+    dbg_log (_("Haven't found \"%s\" in hosts cache!"), key);
+
+  while (gethostbyname2_r (key, AF_INET6, &resultbuf, buffer, buflen, &hst,
+                          &h_errno) != 0
+        && h_errno == NETDB_INTERNAL
+        && errno == ERANGE)
+    {
+      errno = 0;
+      buflen += 256;
+      buffer = alloca (buflen);
+    }
+
+  cache_addhst (db, fd, req, key, hst);
+}
+
+
+void
+addhstbyaddrv6 (struct database *db, int fd, request_header *req, void *key)
+{
+  /* 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 = 512;
+  char *buffer = alloca (buflen);
+  struct hostent resultbuf;
+  struct hostent *hst;
+
+  if (debug_level > 0)
+    {
+      char buf[64];
+      dbg_log (_("Haven't found \"%s\" in hosts cache!"),
+              inet_ntop (AF_INET6, key, buf, sizeof (buf)));
+    }
+
+  while (gethostbyaddr_r (key, IN6ADDRSZ, AF_INET6, &resultbuf, buffer, buflen,
+                         &hst, &h_errno) != 0
+        && h_errno == NETDB_INTERNAL
+        && errno == ERANGE)
+    {
+      errno = 0;
+      buflen += 256;
+      buffer = alloca (buflen);
+    }
+
+  cache_addhst (db, fd, req, key, hst);
+}
index 9ddbb5f54ef6a9f210e0835cc7bdc06a4113b5cb..d6d2c6497d71a8159f7ca782e3d734471d9f3b7e 100644 (file)
    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA. */
 
-/* nscd - Name Service Cache Daemon. Caches passwd and group.  */
+/* nscd - Name Service Cache Daemon. Caches passwd, group, and hosts.  */
 
 #include <argp.h>
+#include <assert.h>
 #include <errno.h>
 #include <error.h>
 #include <libintl.h>
@@ -61,12 +62,10 @@ int do_shutdown = 0;
 int disabled_passwd = 0;
 int disabled_group = 0;
 int go_background = 1;
-const char *conffile = _PATH_NSCDCONF;
+static const char *conffile = _PATH_NSCDCONF;
 
-static void termination_handler (int signum);
 static int check_pid (const char *file);
 static int write_pid (const char *file);
-static void handle_requests (void);
 
 /* Name and version of program.  */
 static void print_version (FILE *stream, struct argp_state *state);
@@ -79,6 +78,7 @@ static const struct argp_option options[] =
     N_("Read configuration data from NAME") },
   { "debug", 'd', NULL, 0,
     N_("Do not fork and display messages on the current tty") },
+  { "nthreads", 't', N_("NUMBER"), 0, N_("Start NUMBER threads") },
   { "shutdown", 'K', NULL, 0, N_("Shut the server down") },
   { "statistic", 'g', NULL, 0, N_("Print current configuration statistic") },
   { NULL, 0, NULL, 0, NULL }
@@ -118,10 +118,7 @@ main (int argc, char **argv)
 
   /* Check if we are already running. */
   if (check_pid (_PATH_NSCDPID))
-    {
-      fputs (_("already running"), stderr);
-      exit (EXIT_FAILURE);
-    }
+    error (EXIT_FAILURE, 0, _("already running"));
 
   /* Behave like a daemon.  */
   if (go_background)
@@ -144,7 +141,7 @@ main (int argc, char **argv)
       if (write_pid (_PATH_NSCDPID) < 0)
         dbg_log ("%s: %s", _PATH_NSCDPID, strerror (errno));
 
-      /* Ignore job control signals */
+      /* Ignore job control signals */
       signal (SIGTTOU, SIG_IGN);
       signal (SIGTTIN, SIG_IGN);
       signal (SIGTSTP, SIG_IGN);
@@ -155,21 +152,14 @@ main (int argc, char **argv)
   signal (SIGTERM, termination_handler);
   signal (SIGPIPE, SIG_IGN);
 
-  /* Cleanup files created by a previous `bind' */
+  /* Cleanup files created by a previous `bind' */
   unlink (_PATH_NSCDSOCKET);
 
-  nscd_parse_file (conffile);
+  /* Init databases.  */
+  nscd_init (conffile);
 
-  /* Create first sockets */
-  init_sockets ();
-  /* Init databases */
-  if ((cache_pwdinit () < 0) || (cache_grpinit () < 0))
-    {
-      fputs (_("Not enough memory\n"), stderr);
-      return 1;
-    }
   /* Handle incoming requests */
-  handle_requests ();
+  start_threads ();
 
   return 0;
 }
@@ -182,20 +172,19 @@ parse_opt (int key, char *arg, struct argp_state *state)
   switch (key)
     {
     case 'd':
-      debug_flag = 1;
+      ++debug_level;
       go_background = 0;
       break;
+
     case 'f':
       conffile = arg;
       break;
+
     case 'K':
       if (getuid () != 0)
-       {
-         printf (_("Only root is allowed to use this option!\n\n"));
-         exit (EXIT_FAILURE);
-       }
+       error (EXIT_FAILURE, 0, _("Only root is allowed to use this option!"));
       {
-       int sock = __nscd_open_socket ();
+       int sock = nscd_open_socket ();
        request_header req;
        ssize_t nbytes;
 
@@ -205,19 +194,24 @@ parse_opt (int key, char *arg, struct argp_state *state)
        req.version = NSCD_VERSION;
        req.type = SHUTDOWN;
        req.key_len = 0;
-       nbytes = write (sock, &req, sizeof (request_header));
+       nbytes = TEMP_FAILURE_RETRY (write (sock, &req,
+                                           sizeof (request_header)));
        close (sock);
-       if (nbytes != req.key_len)
-         exit (EXIT_FAILURE);
-       else
-         exit (EXIT_SUCCESS);
+       exit (nbytes != sizeof (request_header) ? EXIT_FAILURE : EXIT_SUCCESS);
       }
+
     case 'g':
-      print_stat ();
-      exit (EXIT_SUCCESS);
+      receive_print_stats ();
+      /* Does not return.  */
+
+    case 't':
+      nthreads = atol (arg);
+      break;
+
     default:
       return ARGP_ERR_UNKNOWN;
     }
+
   return 0;
 }
 
@@ -231,13 +225,14 @@ 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\
 "), "1998");
-  fprintf (stream, gettext ("Written by %s.\n"), "Thorsten Kukuk");
+  fprintf (stream, gettext ("Written by %s.\n"),
+          "Thorsten Kukuk and Ulrich Drepper");
 }
 
 
-/* Create a socket connected to a name. */
+/* Create a socket connected to a name.  */
 int
-__nscd_open_socket (void)
+nscd_open_socket (void)
 {
   struct sockaddr_un addr;
   int sock;
@@ -247,6 +242,7 @@ __nscd_open_socket (void)
     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)
     {
@@ -258,12 +254,12 @@ __nscd_open_socket (void)
 }
 
 /* Cleanup.  */
-static void
+void
 termination_handler (int signum)
 {
   close_sockets ();
 
-  /* Clean up the files created by `bind'.  */
+  /* Clean up the file created by `bind'.  */
   unlink (_PATH_NSCDSOCKET);
 
   /* Clean up pid file.  */
@@ -282,11 +278,12 @@ check_pid (const char *file)
   if (fp)
     {
       pid_t pid;
+      int n;
 
-      fscanf (fp, "%d", &pid);
+      n = fscanf (fp, "%d", &pid);
       fclose (fp);
 
-      if (kill (pid, 0) == 0)
+      if (n != 1 || kill (pid, 0) == 0)
         return 1;
     }
 
@@ -305,176 +302,10 @@ write_pid (const char *file)
     return -1;
 
   fprintf (fp, "%d\n", getpid ());
-  if (ferror (fp))
+  if (fflush (fp) || ferror (fp))
     return -1;
 
   fclose (fp);
 
   return 0;
 }
-
-/* Type of the lookup function for netname2user.  */
-typedef int (*pwbyname_function) (const char *name, struct passwd *pw,
-                                  char *buffer, size_t buflen);
-
-/* Handle incoming requests.  */
-static
-void handle_requests (void)
-{
-  request_header req;
-  int conn; /* Handle on which connection (client) the request came from.  */
-  int done = 0;
-  char *key;
-  pthread_attr_t th_attr;
-
-  /* We will create all threads detached.  Therefore prepare an attribute
-     now.  */
-  pthread_attr_init (&th_attr);
-  pthread_attr_setdetachstate (&th_attr, PTHREAD_CREATE_DETACHED);
-
-  while (!done)
-    {
-      key = NULL;
-      get_request (&conn, &req, &key);
-      if (debug_flag)
-       dbg_log (_("handle_requests: request received (Version = %d)"),
-                req.version);
-      switch (req.type)
-       {
-       case GETPWBYNAME:
-         {
-           param_t *param = malloc (sizeof (param_t));
-           pthread_t thread;
-           int status;
-
-           if (debug_flag)
-             dbg_log ("\tGETPWBYNAME (%s)", key);
-           param->key = key;
-           param->conn = conn;
-           if (disabled_passwd)
-             status = pthread_create (&thread, &th_attr, cache_pw_disabled,
-                                      (void *)param);
-           else
-             status = pthread_create (&thread, &th_attr, cache_getpwnam,
-                                      (void *)param);
-           if (status != 0)
-             {
-               dbg_log (_("Creation of thread failed: %s"), strerror (errno));
-               close_socket (conn);
-             }
-           pthread_detach (thread);
-         }
-         break;
-       case GETPWBYUID:
-         {
-           param_t *param = malloc (sizeof (param_t));
-           pthread_t thread;
-           int status;
-
-           if (debug_flag)
-             dbg_log ("\tGETPWBYUID (%s)", key);
-           param->key = key;
-           param->conn = conn;
-           if (disabled_passwd)
-             status = pthread_create (&thread, &th_attr, cache_pw_disabled,
-                             (void *)param);
-           else
-             status = pthread_create (&thread, &th_attr, cache_getpwuid,
-                                      (void *)param);
-           if (status != 0)
-             {
-               dbg_log (_("Creation of thread failed: %s"), strerror (errno));
-               close_socket (conn);
-             }
-         }
-         break;
-       case GETGRBYNAME:
-         {
-           param_t *param = malloc (sizeof (param_t));
-           pthread_t thread;
-           int status;
-
-           if (debug_flag)
-             dbg_log ("\tGETGRBYNAME (%s)", key);
-           param->key = key;
-           param->conn = conn;
-           if (disabled_group)
-             status = pthread_create (&thread, &th_attr, cache_gr_disabled,
-                                      (void *)param);
-           else
-             status = pthread_create (&thread, &th_attr, cache_getgrnam,
-                                      (void *)param);
-           if (status != 0)
-             {
-               dbg_log (_("Creation of thread failed: %s"), strerror (errno));
-               close_socket (conn);
-             }
-         }
-         break;
-       case GETGRBYGID:
-         {
-           param_t *param = malloc (sizeof (param_t));
-           pthread_t thread;
-           int status;
-
-           if (debug_flag)
-             dbg_log ("\tGETGRBYGID (%s)", key);
-           param->key = key;
-           param->conn = conn;
-           if (disabled_group)
-             status = pthread_create (&thread, &th_attr, cache_gr_disabled,
-                                      (void *)param);
-           else
-             status = pthread_create (&thread, &th_attr, cache_getgrgid,
-                                      (void *)param);
-           if (status != 0)
-             {
-               dbg_log (_("Creation of thread failed: %s"), strerror (errno));
-               close_socket (conn);
-             }
-         }
-         break;
-       case GETHOSTBYNAME:
-         /* Not yetimplemented.  */
-         close_socket (conn);
-         break;
-       case GETHOSTBYADDR:
-         /* Not yet implemented. */
-         close_socket (conn);
-         break;
-       case SHUTDOWN:
-         do_shutdown = 1;
-         close_socket (0);
-         close_socket (conn);
-         /* Clean up the files created by `bind'.  */
-         unlink (_PATH_NSCDSOCKET);
-         /* Clean up pid file.  */
-         unlink (_PATH_NSCDPID);
-         done = 1;
-         break;
-       case GETSTAT:
-         {
-           stat_response_header resp;
-
-           if (debug_flag)
-             dbg_log ("\tGETSTAT");
-
-           get_pw_stat (&resp);
-           get_gr_stat (&resp);
-           resp.debug_level = debug_flag;
-           resp.pw_enabled = !disabled_passwd;
-           resp.gr_enabled = !disabled_group;
-
-           stat_send (conn, &resp);
-
-           close_socket (conn);
-         }
-         break;
-       default:
-         dbg_log (_("Unknown request (%d)"), req.type);
-         break;
-       }
-    }
-
-  pthread_attr_destroy (&th_attr);
-}
index 5d8c7f31ac4fb6c641ebd634daa0f163022ec1c2..5e327e86fe09fa3b7f4f0578f75efb717fa874f8 100644 (file)
@@ -6,25 +6,36 @@
 # Legal entries are:
 #
 #      logfile                 <file>
-#       enable-cache           <service> <yes|no>
 #      debug-level             <level>
+#
+#       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>
 #
 # Currently supported cache names (services): passwd, group
 #
 
 
 #      logfile                 /var/adm/nscd.log
-#      enable-cache            hosts           no
 
        debug-level             0
 
+       enable-cache            passwd          yes
        positive-time-to-live   passwd          600
        negative-time-to-live   passwd          20
        suggested-size          passwd          211
+       check-files             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
+
+       enable-cache            hosts           yes
+       positive-time-to-live   hosts           3600
+       negative-time-to-live   hosts           20
+       suggested-size          hosts           211
+       check-files             hosts           yes
index 48355426199e24768dce65c716408e5406befded..bc8150aca12726a9be9cb85b7dd8267b0cab570e 100644 (file)
    Boston, MA 02111-1307, USA. */
 
 #ifndef _NSCD_H
-#define _NSCD_H 1
+#define _NSCD_H        1
+
+#include <pthread.h>
+#include <time.h>
+#include <sys/uio.h>
 
-#include <grp.h>
-#include <pwd.h>
 
 /* Version number of the daemon interface */
-#define NSCD_VERSION 1
+#define NSCD_VERSION 2
+
+/* Path of the file where the PID of the running system is stored.  */
+#define _PATH_NSCDPID   "/var/run/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"
+
 
-/* How many threads do we spawn maximal ? */
-#define MAX_NUM_CONNECTIONS 16
+/* Handle databases.  */
+typedef enum
+{
+  pwddb,
+  grpdb,
+  hstdb,
+  lastdb
+} dbtype;
 
-/* Services provided */
+
+/* Available services.  */
 typedef enum
 {
   GETPWBYNAME,
@@ -37,22 +56,67 @@ typedef enum
   GETGRBYNAME,
   GETGRBYGID,
   GETHOSTBYNAME,
+  GETHOSTBYNAMEv6,
   GETHOSTBYADDR,
-  SHUTDOWN,            /* Shut the server down */
-  GETSTAT               /* Get the server statistic */
+  GETHOSTBYADDRv6,
+  LASTDBREQ = GETHOSTBYADDRv6,
+  SHUTDOWN,            /* Shut the server down.  */
+  GETSTAT,             /* Get the server statistic.  */
+  LASTREQ,
 } request_type;
 
+
+/* Structure for one hash table entry.  */
+struct hashentry
+{
+  request_type type;           /* Which type of dataset.  */
+  size_t len;                  /* Length of key.  */
+  void *key;                   /* Pointer to key.  */
+  struct hashentry *next;      /* Next entry in this hash bucket list.  */
+  time_t timeout;              /* Time when this entry becomes invalid.  */
+  ssize_t total;               /* Number of bytes in PACKET.  */
+  const void *packet;          /* Records for the result.  */
+  void *data;                  /* The malloc()ed respond record.  */
+  int last;                    /* Nonzero if DATA should be free()d.  */
+  struct hashentry *dellist;   /* Next record to be deleted.  */
+};
+
+/* Structure describing one database.  */
+struct database
+{
+  pthread_rwlock_t lock;
+
+  int enabled;
+  int check_file;
+  const char *filename;
+  time_t file_mtime;
+  size_t module;
+
+  const struct iovec *disabled_iov;
+
+  unsigned long int postimeout;
+  unsigned long int negtimeout;
+
+  unsigned long int poshit;
+  unsigned long int neghit;
+  unsigned long int posmiss;
+  unsigned long int negmiss;
+
+  struct hashentry **array;
+};
+
+
 /* Header common to all requests */
 typedef struct
 {
-  /* Version number of the daemon interface */
-  int version;
-  /* Service requested */
-  request_type type;
-  /* key len */
-  ssize_t key_len;
+  int version;         /* Version number of the daemon interface.  */
+  request_type type;   /* Service requested.  */
+  ssize_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
 {
   int version;
@@ -66,6 +130,9 @@ typedef struct
   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
 {
   int version;
@@ -73,77 +140,82 @@ typedef struct
   ssize_t gr_name_len;
   ssize_t gr_passwd_len;
   gid_t gr_gid;
-  ssize_t gr_mem_len;
+  ssize_t gr_mem_cnt;
 } gr_response_header;
 
-typedef struct
-{
-  int debug_level;
-  int pw_enabled;
-  unsigned long pw_poshit;
-  unsigned long pw_posmiss;
-  unsigned long pw_neghit;
-  unsigned long pw_negmiss;
-  unsigned long pw_size;
-  unsigned long pw_posttl;
-  unsigned long pw_negttl;
-  int gr_enabled;
-  unsigned long gr_poshit;
-  unsigned long gr_posmiss;
-  unsigned long gr_neghit;
-  unsigned long gr_negmiss;
-  unsigned long gr_size;
-  unsigned long gr_posttl;
-  unsigned long gr_negttl;
-} stat_response_header;
-
-#define _PATH_NSCDPID   "/var/run/nscd.pid"
-#define _PATH_NSCDSOCKET "/var/run/.nscd_socket"
-#define _PATH_NSCDCONF  "/etc/nscd.conf"
 
+/* 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
 {
-  char *key;
-  int conn;
-} param_t;
-
-extern int  do_shutdown; /* 1 if we should quit the programm.  */
-extern int  disabled_passwd;
-extern int  disabled_group;
-
-extern int  nscd_parse_file __P ((const char *fname));
-extern int  set_logfile __P ((const char *logfile));
-extern void set_pos_pwd_ttl __P ((unsigned long));
-extern void set_neg_pwd_ttl __P ((unsigned long));
-extern void set_pos_grp_ttl __P ((unsigned long));
-extern void set_neg_grp_ttl __P ((unsigned long));
-extern void set_pwd_modulo __P ((unsigned long));
-extern void set_grp_modulo __P ((unsigned long));
-
-extern void init_sockets __P ((void));
-extern void close_socket __P ((int conn));
-extern void close_sockets __P ((void));
-extern void get_request __P ((int *conn, request_header *req, char **key));
-extern void pw_send_answer __P ((int conn, struct passwd *pwd));
-extern void pw_send_disabled __P ((int conn));
-extern void gr_send_answer __P ((int conn, struct group *grp));
-extern void gr_send_disabled __P ((int conn));
-
-extern int  cache_pwdinit __P ((void));
-extern void *cache_getpwnam __P ((void *param));
-extern void *cache_getpwuid __P ((void *param));
-extern void *cache_pw_disabled __P ((void *param));
-
-extern int  cache_grpinit __P ((void));
-extern void *cache_getgrnam __P ((void *param));
-extern void *cache_getgrgid __P ((void *param));
-extern void *cache_gr_disabled __P ((void *param));
-
-extern int __nscd_open_socket __P ((void));
-
-extern void get_pw_stat __P ((stat_response_header *resp));
-extern void get_gr_stat __P ((stat_response_header *resp));
-extern void print_stat __P ((void));
-extern void stat_send __P ((int conn, stat_response_header *resp));
-
-#endif
+  int version;
+  int found;
+  ssize_t h_name_len;
+  ssize_t h_aliases_cnt;
+  int h_addrtype;
+  int h_length;
+  ssize_t h_addr_list_cnt;
+  int error;
+} hst_response_header;
+
+/* Global variables.  */
+extern const char *dbnames[lastdb];
+extern const char *serv2str[LASTREQ];
+
+extern const struct iovec pwd_iov_disabled;
+extern const struct iovec grp_iov_disabled;
+extern const struct iovec hst_iov_disabled;
+
+/* Number of threads to run.  */
+extern int nthreads;
+
+
+/* Prototypes for global functions.  */
+
+/* nscd.c */
+extern void termination_handler (int signum);
+extern int nscd_open_socket (void);
+
+/* connections.c */
+extern void nscd_init (const char *conffile);
+extern void close_sockets (void);
+extern void start_threads (void);
+
+/* nscd_conf.c */
+extern int nscd_parse_file (const char *fname, struct database dbs[lastdb]);
+
+/* nscd_stat.c */
+extern void send_stats (int fd, struct database dbs[lastdb]);
+extern int receive_print_stats (void);
+
+/* cache.c */
+extern struct hashentry *cache_search (int type, void *key, size_t len,
+                                      struct database *table);
+extern void cache_add (int type, void *key, size_t len,
+                      const void *packet, size_t iovtotal, void *data,
+                      int last, time_t t, struct database *table);
+extern void prune_cache (struct database *table, time_t now);
+
+/* pwdcache.c */
+extern void addpwbyname (struct database *db, int fd, request_header *req,
+                        void *key);
+extern void addpwbyuid (struct database *db, int fd, request_header *req,
+                       void *key);
+
+/* grpcache.c */
+extern void addgrbyname (struct database *db, int fd, request_header *req,
+                        void *key);
+extern void addgrbygid (struct database *db, int fd, request_header *req,
+                       void *key);
+
+/* hstcache.c */
+extern void addhstbyname (struct database *db, int fd, request_header *req,
+                         void *key);
+extern void addhstbyaddr (struct database *db, int fd, request_header *req,
+                         void *key);
+extern void addhstbynamev6 (struct database *db, int fd, request_header *req,
+                           void *key);
+extern void addhstbyaddrv6 (struct database *db, int fd, request_header *req,
+                           void *key);
+
+#endif /* nscd.h */
index 917fe022ff2c778b7e6eea26c9191617842bfa53..6ac1677a8bfcdd5ec50297f99fd86b77baabe83b 100644 (file)
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/param.h>
 #include <sys/types.h>
 
 #include "dbg_log.h"
 #include "nscd.h"
 
+/* Names of the databases.  */
+const char *dbnames[lastdb] =
+{
+  [pwddb] = "passwd",
+  [grpdb] = "group",
+  [hstdb] = "hosts"
+};
+
 int
-nscd_parse_file (const char *fname)
+nscd_parse_file (const char *fname, struct database dbs[lastdb])
 {
   FILE *fp;
   char *line, *cp, *entry, *arg1, *arg2;
   size_t len;
+  int cnt;
 
   /* Open the configuration file.  */
   fp = fopen (fname, "r");
@@ -92,41 +102,64 @@ nscd_parse_file (const char *fname)
 
       if (strcmp (entry, "positive-time-to-live") == 0)
        {
-         if (strcmp (arg1, "passwd") == 0)
-           set_pos_pwd_ttl (atol (arg2));
-         else if (strcmp (arg1, "group") == 0)
-           set_pos_grp_ttl (atol (arg2));
-         else
+         for (cnt = 0; cnt < lastdb; ++cnt)
+           if (strcmp (arg1, dbnames[cnt]) == 0)
+             {
+               dbs[cnt].postimeout = atol (arg2);
+               break;
+             }
+         if (cnt == lastdb)
            dbg_log ("server %s is not supported\n", arg1);
        }
       else if (strcmp (entry, "negative-time-to-live") == 0)
        {
-         if (strcmp (arg1, "passwd") == 0)
-           set_neg_pwd_ttl (atol (arg2));
-         else if (strcmp (arg1, "group") == 0)
-           set_neg_grp_ttl (atol (arg2));
-         else
-           dbg_log (_("service %s is not supported"), arg1);
+         for (cnt = 0; cnt < lastdb; ++cnt)
+           if (strcmp (arg1, dbnames[cnt]) == 0)
+             {
+               dbs[cnt].negtimeout = atol (arg2);
+               break;
+             }
+         if (cnt == lastdb)
+           dbg_log ("server %s is not supported\n", arg1);
        }
       else if (strcmp (entry, "suggested-size") == 0)
        {
-         if (strcmp (arg1, "passwd") == 0)
-           set_pwd_modulo (atol (arg2));
-         else if (strcmp (arg1, "group") == 0)
-           set_grp_modulo (atol (arg2));
-         else
-           dbg_log (_("service %s is not supported"), arg1);
+         for (cnt = 0; cnt < lastdb; ++cnt)
+           if (strcmp (arg1, dbnames[cnt]) == 0)
+             {
+               dbs[cnt].module = atol (arg2);
+               break;
+             }
+         if (cnt == lastdb)
+           dbg_log ("server %s is not supported\n", arg1);
+       }
+      else if (strcmp (entry, "enable-cache") == 0)
+       {
+         for (cnt = 0; cnt < lastdb; ++cnt)
+           if (strcmp (arg1, dbnames[cnt]) == 0)
+             {
+               if (strcmp (arg2, "no") == 0)
+                 dbs[cnt].enabled = 0;
+               else if (strcmp (arg2, "yes") == 0)
+                 dbs[cnt].enabled = 1;
+               break;
+             }
+         if (cnt == lastdb)
+           dbg_log ("server %s is not supported\n", arg1);
        }
-      else if (strcmp (entry, "enable-cache") ==0)
+      else if (strcmp (entry, "check-files") == 0)
        {
-         if (strcmp (arg1, "passwd") == 0
-             && strcmp (arg2, "no") == 0)
-           disabled_passwd = 1;
-         else if (strcmp (arg1, "group") == 0
-                  && strcmp (arg2, "no") == 0)
-           disabled_group = 1;
-         else
-           dbg_log (_("service %s is not supported"), arg1);
+         for (cnt = 0; cnt < lastdb; ++cnt)
+           if (strcmp (arg1, dbnames[cnt]) == 0)
+             {
+               if (strcmp (arg2, "no") == 0)
+                 dbs[cnt].check_file = 0;
+               else if (strcmp (arg2, "yes") == 0)
+                 dbs[cnt].check_file = 1;
+               break;
+             }
+         if (cnt == lastdb)
+           dbg_log ("server %s is not supported\n", arg1);
        }
       else if (strcmp (entry, "logfile") == 0)
        {
@@ -137,7 +170,12 @@ nscd_parse_file (const char *fname)
        {
          int level = atoi (arg1);
          if (level > 0)
-           debug_flag = level;
+           debug_level = level;
+       }
+      else if (strcmp (entry, "threads") == 0)
+       {
+         if (nthreads == -1)
+           nthreads = MAX (atol (arg1), lastdb);
        }
       else
        dbg_log (_("Unknown option: %s %s %s"), entry, arg1, arg2);
index ec4f5a12973c2095a5f56c92ae99fa8ed0a1ce58..f27da0256930f0088a50b544bd852462bccb4c17 100644 (file)
 
 int __nss_not_use_nscd_group;
 
-static int __nscd_getgr_r (const char *key, request_type type,
-                          struct group *resultbuf, char *buffer,
-                          size_t buflen);
+static int nscd_getgr_r (const char *key, size_t keylen, request_type type,
+                        struct group *resultbuf, char *buffer,
+                        size_t buflen);
+
 
 int
 __nscd_getgrnam_r (const char *name, struct group *resultbuf, char *buffer,
                   size_t buflen)
 {
-  if (name == NULL)
-    return 1;
-
-  return __nscd_getgr_r (name, GETGRBYNAME, resultbuf, buffer, buflen);
+  return nscd_getgr_r (name, strlen (name) + 1, GETGRBYNAME, resultbuf,
+                      buffer, buflen);
 }
 
+
 int
 __nscd_getgrgid_r (gid_t gid, struct group *resultbuf, char *buffer,
                   size_t buflen)
 {
-  char *p = buffer;
-  int plen;
+  char buf[12];
+  size_t n;
 
-  plen = __snprintf (buffer, buflen, "%d", gid);
-  if (plen == -1)
-    {
-      __set_errno (ERANGE);
-      return -1;
-    }
-  p = buffer + plen + 1;
+  n = __snprintf (buf, sizeof (buf), "%d", gid) + 1;
 
-  return __nscd_getgr_r (buffer, GETGRBYGID, resultbuf, p, buflen - plen -1);
+  return nscd_getgr_r (buf, n, GETGRBYGID, resultbuf, buffer, buflen);
 }
 
+
 /* Create a socket connected to a name. */
 static int
-nscd_open_socket (void)
+open_socket (void)
 {
   struct sockaddr_un addr;
   int sock;
@@ -91,16 +86,16 @@ nscd_open_socket (void)
   return sock;
 }
 
+
 static int
-__nscd_getgr_r (const char *key, request_type type, struct group *resultbuf,
-               char *buffer, size_t buflen)
+nscd_getgr_r (const char *key, size_t keylen, request_type type,
+             struct group *resultbuf, char *buffer, size_t buflen)
 {
-  int sock = nscd_open_socket ();
+  int sock = open_socket ();
   request_header req;
   gr_response_header gr_resp;
   ssize_t nbytes;
-  size_t maxiov;
-  size_t sum;
+  struct iovec vec[2];
 
   if (sock == -1)
     {
@@ -110,16 +105,14 @@ __nscd_getgr_r (const char *key, request_type type, struct group *resultbuf,
 
   req.version = NSCD_VERSION;
   req.type = type;
-  req.key_len = strlen (key);
-  nbytes = __write (sock, &req, sizeof (request_header));
-  if (nbytes != sizeof (request_header))
-    {
-      __close (sock);
-      return 1;
-    }
+  req.key_len = keylen;
+
+  vec[0].iov_base = &req;
+  vec[0].iov_len = sizeof (request_header);
+  vec[1].iov_base = (void *) key;
+  vec[1].iov_len = keylen;
 
-  nbytes = __write (sock, key, req.key_len);
-  if (nbytes != req.key_len)
+  if (__writev (sock, vec, 2) != sizeof (request_header) + keylen)
     {
       __close (sock);
       return 1;
@@ -142,118 +135,79 @@ __nscd_getgr_r (const char *key, request_type type, struct group *resultbuf,
 
   if (gr_resp.found == 1)
     {
-      struct iovec *vec;
       size_t *len;
       char *p = buffer;
-      int nblocks;
       size_t total_len;
       uintptr_t align;
+      size_t cnt;
 
-      /* A first check whether the buffer is sufficently large is possible.  */
-      if (buflen < gr_resp.gr_name_len + 1 + gr_resp.gr_passwd_len + 1)
+      /* Now allocate the buffer the array for the group members.  We must
+        align the pointer.  */
+      align = ((__alignof__ (char *) - (p - ((char *) 0)))
+              & (__alignof__ (char *) - 1));
+      if (buflen < (align + (1 + gr_resp.gr_mem_cnt) * sizeof (char *)
+                   + gr_resp.gr_name_len + gr_resp.gr_passwd_len))
        {
+       no_room:
          __set_errno (ERANGE);
          __close (sock);
          return -1;
        }
 
-      /* Allocate the IOVEC.  */
-      vec = alloca ((2 + gr_resp.gr_mem_len) * sizeof (struct iovec));
-      len = alloca (gr_resp.gr_mem_len * sizeof (size_t));
+      p += align;
+      resultbuf->gr_mem = (char **) p;
+      p += (1 + gr_resp.gr_mem_cnt) * sizeof (char *);
+      buflen -= align + (1 + gr_resp.gr_mem_cnt) * sizeof (char *);
 
-      vec[0].iov_base = resultbuf->gr_name = p;
-      vec[0].iov_len = gr_resp.gr_name_len;
-      total_len = gr_resp.gr_name_len;
-      p += gr_resp.gr_name_len + 1;
+      /* Set pointers for strings.  */
+      resultbuf->gr_name = p;
+      p += gr_resp.gr_name_len;
+      resultbuf->gr_passwd = p;
+      p += gr_resp.gr_passwd_len;
 
-      vec[1].iov_base = resultbuf->gr_passwd = p;
-      vec[1].iov_len = gr_resp.gr_passwd_len;
-      total_len += gr_resp.gr_passwd_len;
-      p += gr_resp.gr_passwd_len + 1;
-      buflen -= total_len;
-      nblocks = 2;
+      /* Fill in what we know now.  */
+      resultbuf->gr_gid = gr_resp.gr_gid;
 
-      if (gr_resp.gr_mem_len > 0)
-       {
-         vec[2].iov_base = len;
-         vec[2].iov_len = gr_resp.gr_mem_len * sizeof (size_t);
-         total_len += gr_resp.gr_mem_len * sizeof (size_t);
-         nblocks = 3;
-       }
+      /* Allocate array to store lengths.  */
+      len = alloca (gr_resp.gr_mem_cnt * sizeof (size_t));
+
+      total_len = gr_resp.gr_mem_cnt * sizeof (size_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;
+
+      buflen -= total_len;
 
       /* Get this data.  */
-      if (__readv (sock, vec, nblocks) != total_len)
+      if (__readv (sock, vec, 2) != total_len)
        {
          __close (sock);
          return 1;
        }
 
-      /* Now we know the sizes.  First terminate the strings we just read. */
-      resultbuf->gr_name[gr_resp.gr_name_len] = '\0';
-      resultbuf->gr_passwd[gr_resp.gr_passwd_len] = '\0';
-
-      resultbuf->gr_gid = gr_resp.gr_gid;
+      /* Clear the terminating entry.  */
+      resultbuf->gr_mem[gr_resp.gr_mem_cnt] = NULL;
 
-      /* Now allocate the buffer the array for the group members.  We must
-        align the pointer.  */
-      align = ((__alignof__ (char *) - (p - ((char *) 0)))
-              & (__alignof__ (char *) - 1));
-      if (align + (1 + gr_resp.gr_mem_len) * sizeof (char *) > buflen)
+      /* Prepare reading the group members.  */
+      total_len = 0;
+      for (cnt = 0; cnt < gr_resp.gr_mem_cnt; ++cnt)
        {
-         __set_errno (ERANGE);
-         __close (sock);
-         return -1;
+         resultbuf->gr_mem[cnt] = p;
+         total_len += len[cnt];
+         p += len[cnt];
        }
-      p += align;
-      resultbuf->gr_mem = (char **) p;
-      p += (1 + gr_resp.gr_mem_len) * sizeof (char *);
-      buflen -= align + (1 + gr_resp.gr_mem_len) * sizeof (char *);
 
-      resultbuf->gr_mem[gr_resp.gr_mem_len] = NULL;
+      if (total_len > buflen)
+       goto no_room;
 
-      if (gr_resp.gr_mem_len > 0)
+      if (__read (sock, resultbuf->gr_mem[0], total_len) != total_len)
        {
-         /* Prepare reading the group members.  */
-         size_t i;
-
-         total_len = 0;
-         for (i = 0; i < gr_resp.gr_mem_len; ++i)
-           {
-             if (len[i] >= buflen)
-               {
-                 __set_errno (ERANGE);
-                 __close (sock);
-                 return -1;
-               }
-
-             vec[i].iov_base = resultbuf->gr_mem[i] = p;
-             vec[i].iov_len = len[i];
-             total_len += len[i];
-             buflen -= len[i];
-             p += len[i];
-             *p++ = '\0';
-           }
-
-#ifdef UIO_MAXIOV
-         maxiov = UIO_MAXIOV;
-#else
-         maxiov = sysconf (_SC_UIO_MAXIOV);
-#endif
-
-         sum = 0;
-         while (i > maxiov)
-           {
-             sum += __readv (sock, vec, maxiov);
-             vec += maxiov;
-             i -= maxiov;
-           }
-
-         if (sum + __readv (sock, vec, i) != total_len)
-           {
-             __close (sock);
-             return -1;
-           }
+         __close (sock);
+         return -1;
        }
+
       __close (sock);
       return 0;
     }
diff --git a/nscd/nscd_gethst_r.c b/nscd/nscd_gethst_r.c
new file mode 100644 (file)
index 0000000..f78b104
--- /dev/null
@@ -0,0 +1,302 @@
+/* Copyright (C) 1998 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 Library General Public License as
+   published by the Free Software Foundation; either version 2 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#include <errno.h>
+#include <netdb.h>
+#include <resolv.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <arpa/nameser.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+
+#include "nscd.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, int *h_errnop);
+
+
+int
+__nscd_gethostbyname_r (const char *name, struct hostent *resultbuf,
+                       char *buffer, size_t buflen, int *h_errnop)
+{
+  request_type reqtype;
+
+  reqtype = (_res.options & RES_USE_INET6) ? GETHOSTBYNAMEv6 : GETHOSTBYNAME;
+
+  return nscd_gethst_r (name, strlen (name) + 1, reqtype, resultbuf,
+                       buffer, buflen, h_errnop);
+}
+
+
+int
+__nscd_gethostbyname2_r (const char *name, int af, struct hostent *resultbuf,
+                        char *buffer, size_t buflen, int *h_errnop)
+{
+  request_type reqtype;
+
+  reqtype = af == AF_INET6 ? GETHOSTBYNAMEv6 : GETHOSTBYNAME;
+
+  return nscd_gethst_r (name, strlen (name) + 1, reqtype, resultbuf,
+                       buffer, buflen, h_errnop);
+}
+
+
+int
+__nscd_gethostbyaddr_r (const char *addr, int len, int type,
+                       struct hostent *resultbuf, char *buffer, size_t buflen,
+                       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,
+                       h_errnop);
+}
+
+
+/* Create a socket connected to a name. */
+static int
+open_socket (void)
+{
+  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)
+    {
+      __close (sock);
+      __set_errno (saved_errno);
+      return -1;
+    }
+
+  return sock;
+}
+
+
+static int
+nscd_gethst_r (const char *key, size_t keylen, request_type type,
+              struct hostent *resultbuf, char *buffer, size_t buflen,
+              int *h_errnop)
+{
+  int sock = open_socket ();
+  hst_response_header hst_resp;
+  request_header req;
+  ssize_t nbytes;
+
+  if (sock == -1)
+    {
+      __nss_not_use_nscd_group = 1;
+      return 1;
+    }
+
+  req.version = NSCD_VERSION;
+  req.type = type;
+  req.key_len = keylen;
+  nbytes = __write (sock, &req, sizeof (request_header));
+  if (nbytes != sizeof (request_header))
+    {
+      __close (sock);
+      return 1;
+    }
+
+  nbytes = __write (sock, key, req.key_len);
+  if (nbytes != req.key_len)
+    {
+      __close (sock);
+      return 1;
+    }
+
+  nbytes = __read (sock, &hst_resp, sizeof (hst_response_header));
+  if (nbytes != sizeof (hst_response_header))
+    {
+      __close (sock);
+      return 1;
+    }
+
+  if (hst_resp.found == -1)
+    {
+      /* The daemon does not cache this database.  */
+      __close (sock);
+      __nss_not_use_nscd_hosts = 1;
+      return 1;
+    }
+
+  if (hst_resp.found == 1)
+    {
+      struct iovec vec[4];
+      size_t *aliases_len;
+      char *cp = buffer;
+      uintptr_t align;
+      size_t total_len;
+      ssize_t cnt;
+      char *ignore;
+      int n;
+
+      /* A first check whether the buffer is sufficently large is possible.  */
+      /* Now allocate the buffer the array for the group members.  We must
+        align the pointer.  */
+      align = ((__alignof__ (char *) - (cp - ((char *) 0)))
+              & (__alignof__ (char *) - 1));
+      if (buflen < (align + hst_resp.h_name_len
+                   + ((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:
+         __set_errno (ERANGE);
+         __close (sock);
+         return -1;
+       }
+      cp += align;
+
+      /* 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;
+      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 (size_t));
+      vec[1].iov_base = aliases_len;
+      vec[1].iov_len = hst_resp.h_aliases_cnt * sizeof (size_t);
+
+      total_len = (hst_resp.h_name_len
+                  + hst_resp.h_aliases_cnt * sizeof (size_t));
+
+      n = 2;
+      if (type == GETHOSTBYADDR || type == GETHOSTBYNAME)
+       {
+         vec[2].iov_base = cp;
+         vec[2].iov_len = hst_resp.h_addr_list_cnt * INADDRSZ;
+
+         ignore = alloca (hst_resp.h_addr_list_cnt * IN6ADDRSZ);
+         vec[3].iov_base = ignore;
+         vec[3].iov_len = hst_resp.h_addr_list_cnt * IN6ADDRSZ;
+
+         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 + IN6ADDRSZ);
+
+         n = 4;
+       }
+      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;
+
+             total_len += hst_resp.h_addr_list_cnt * INADDRSZ;
+
+             n = 3;
+           }
+
+         vec[n].iov_base = cp;
+         vec[n].iov_len = hst_resp.h_addr_list_cnt * IN6ADDRSZ;
+
+         for (cnt = 0; cnt < hst_resp.h_addr_list_cnt; ++cnt)
+           {
+             resultbuf->h_addr_list[cnt] = cp;
+             cp += IN6ADDRSZ;
+           }
+
+         resultbuf->h_addrtype = AF_INET6;
+         resultbuf->h_length = IN6ADDRSZ;
+
+         total_len += hst_resp.h_addr_list_cnt * IN6ADDRSZ;
+
+         ++n;
+       }
+      resultbuf->h_addr_list[cnt] = NULL;
+
+      if (__readv (sock, vec, n) != total_len)
+       {
+         __close (sock);
+         return 1;
+       }
+
+      /*  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;
+
+      /* See whether this would exceed the buffer capacity.  */
+      if (cp > buffer + buflen)
+       goto no_room;
+
+      /* And finally read the aliases.  */
+      if (__read (sock, resultbuf->h_aliases[0], total_len) != total_len)
+       {
+         __close (sock);
+         return 1;
+       }
+
+      __close (sock);
+      return 0;
+    }
+  else
+    {
+      /* Store the error number.  */
+      *h_errnop = hst_resp.error;
+
+      __close (sock);
+      return -1;
+    }
+}
index a24d33b78963dd50025401df21090825b8840fb2..83baafcc2af23bf15633d88fa55fb95106b02865 100644 (file)
@@ -31,9 +31,9 @@
 
 int __nss_not_use_nscd_passwd;
 
-static int __nscd_getpw_r (const char *key, request_type type,
-                          struct passwd *resultbuf, char *buffer,
-                          size_t buflen);
+static int nscd_getpw_r (const char *key, request_type type,
+                        struct passwd *resultbuf, char *buffer,
+                        size_t buflen);
 
 int
 __nscd_getpwnam_r (const char *name, struct passwd *resultbuf, char *buffer,
@@ -42,7 +42,7 @@ __nscd_getpwnam_r (const char *name, struct passwd *resultbuf, char *buffer,
   if (name == NULL)
     return 1;
 
-  return __nscd_getpw_r (name, GETPWBYNAME, resultbuf, buffer, buflen);
+  return nscd_getpw_r (name, GETPWBYNAME, resultbuf, buffer, buflen);
 }
 
 int
@@ -60,12 +60,12 @@ __nscd_getpwuid_r (uid_t uid, struct passwd *resultbuf, char *buffer,
     }
   p = buffer + plen + 1;
 
-  return __nscd_getpw_r (buffer, GETPWBYUID, resultbuf, p, buflen - plen -1);
+  return nscd_getpw_r (buffer, GETPWBYUID, resultbuf, p, buflen - plen - 1);
 }
 
 /* Create a socket connected to a name. */
 static int
-nscd_open_socket (void)
+open_socket (void)
 {
   struct sockaddr_un addr;
   int sock;
@@ -91,10 +91,10 @@ nscd_open_socket (void)
 }
 
 static int
-__nscd_getpw_r (const char *key, request_type type, struct passwd *resultbuf,
-               char *buffer, size_t buflen)
+nscd_getpw_r (const char *key, request_type type, struct passwd *resultbuf,
+             char *buffer, size_t buflen)
 {
-  int sock = nscd_open_socket ();
+  int sock = open_socket ();
   request_header req;
   pw_response_header pw_resp;
   ssize_t nbytes;
@@ -107,7 +107,7 @@ __nscd_getpw_r (const char *key, request_type type, struct passwd *resultbuf,
 
   req.version = NSCD_VERSION;
   req.type = type;
-  req.key_len = strlen (key);
+  req.key_len = strlen (key) + 1;
   nbytes = __write (sock, &req, sizeof (request_header));
   if (nbytes != sizeof (request_header))
     {
@@ -139,68 +139,42 @@ __nscd_getpw_r (const char *key, request_type type, struct passwd *resultbuf,
 
   if (pw_resp.found == 1)
     {
-      struct iovec vec[5];
       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 (buflen < (pw_resp.pw_name_len + 1 + pw_resp.pw_passwd_len + 1
-                   + pw_resp.pw_gecos_len + 1 + pw_resp.pw_dir_len + 1
-                   + pw_resp.pw_shell_len + 1))
+      if (buflen < total)
        {
          __set_errno (ERANGE);
          __close (sock);
          return -1;
        }
 
+      /* Set the information we already have.  */
+      resultbuf->pw_uid = pw_resp.pw_uid;
+      resultbuf->pw_gid = pw_resp.pw_gid;
+
       /* get pw_name */
-      vec[0].iov_base = p;
-      vec[0].iov_len = pw_resp.pw_name_len;
-      p += pw_resp.pw_name_len + 1;
-      buflen -= (pw_resp.pw_name_len + 1);
+      resultbuf->pw_name = p;
+      p += pw_resp.pw_name_len;
       /* get pw_passwd */
-      vec[1].iov_base = p;
-      vec[1].iov_len = pw_resp.pw_passwd_len;
-      p += pw_resp.pw_passwd_len + 1;
-      buflen -= (pw_resp.pw_passwd_len + 1);
+      resultbuf->pw_passwd = p;
+      p += pw_resp.pw_passwd_len;
       /* get pw_gecos */
-      vec[2].iov_base = p;
-      vec[2].iov_len = pw_resp.pw_gecos_len;
-      p += pw_resp.pw_gecos_len + 1;
-      buflen -= (pw_resp.pw_gecos_len + 1);
+      resultbuf->pw_gecos = p;
+      p += pw_resp.pw_gecos_len;
       /* get pw_dir */
-      vec[3].iov_base = p;
-      vec[3].iov_len = pw_resp.pw_dir_len;
-      p += pw_resp.pw_dir_len + 1;
-      buflen -= (pw_resp.pw_dir_len + 1);
+      resultbuf->pw_dir = p;
+      p += pw_resp.pw_dir_len;
       /* get pw_pshell */
-      vec[4].iov_base = p;
-      vec[4].iov_len = pw_resp.pw_shell_len;
-      p += pw_resp.pw_shell_len + 1;
-      buflen -= (pw_resp.pw_shell_len + 1);
-
-      nbytes = __readv (sock, vec, 5);
-      if (nbytes !=  (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))
-       {
-         __close (sock);
-         return 1;
-       }
+      resultbuf->pw_shell = p;
 
-      resultbuf->pw_name = vec[0].iov_base;
-      resultbuf->pw_name[pw_resp.pw_name_len] = '\0';
-      resultbuf->pw_passwd = vec[1].iov_base;
-      resultbuf->pw_passwd[pw_resp.pw_passwd_len] = '\0';
-      resultbuf->pw_uid = pw_resp.pw_uid;
-      resultbuf->pw_gid = pw_resp.pw_gid;
-      resultbuf->pw_gecos = vec[2].iov_base;
-      resultbuf->pw_gecos[pw_resp.pw_gecos_len] = '\0';
-      resultbuf->pw_dir = vec[3].iov_base;
-      resultbuf->pw_dir[pw_resp.pw_dir_len] = '\0';
-      resultbuf->pw_shell = vec[4].iov_base;
-      resultbuf->pw_shell[pw_resp.pw_shell_len] = '\0';
+      nbytes = __read (sock, buffer, total);
 
       __close (sock);
-      return 0;
+
+      return nbytes == total ? 0 : 1;
     }
   else
     {
index f82f86c1abd7b1c0b57303a02d9f3d29e2e4667b..760b57de76e924a596b82bf306d8b3b6be8b9c1a 100644 (file)
 #define _NSCD_PROTO_H 1
 
 #include <grp.h>
+#include <netdb.h>
 #include <pwd.h>
 
 /* Variables for communication between NSCD handler functions and NSS.  */
 extern int __nss_not_use_nscd_passwd;
 extern int __nss_not_use_nscd_group;
+extern int __nss_not_use_nscd_hosts;
 
 extern int __nscd_getpwnam_r __P ((const char *name, struct passwd *resultbuf,
                                   char *buffer, size_t buflen));
@@ -35,5 +37,17 @@ extern int __nscd_getgrnam_r __P ((const char *name, struct group *resultbuf,
                                   char *buffer, size_t buflen));
 extern int __nscd_getgrgid_r __P ((uid_t uid, struct group *resultbuf,
                                   char *buffer,  size_t buflen));
+extern int __nscd_gethostbyname_r __P ((const char *name,
+                                       struct hostent *resultbuf,
+                                       char *buffer, size_t buflen,
+                                       int *h_errnop));
+extern int __nscd_gethostbyname2_r __P ((const char *name, int af,
+                                        struct hostent *resultbuf,
+                                        char *buffer, size_t buflen,
+                                        int *h_errnop));
+extern int __nscd_gethostbyaddr_r __P ((const char *addr, int len, int type,
+                                       struct hostent *resultbuf,
+                                       char *buffer, size_t buflen,
+                                       int *h_errnop));
 
 #endif /* _NSCD_PROTO_H */
index d8182885ac9231c712c9fdada65cd9290bf6a92d..f9d21aeb01ba5b817947218cbad787c2dae990f3 100644 (file)
    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA. */
 
+#include <errno.h>
+#include <error.h>
+#include <langinfo.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <unistd.h>
-#include <sys/types.h>
+
 #include "nscd.h"
+#include "dbg_log.h"
+
+/* 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;
+  size_t module;
+
+  unsigned long int postimeout;
+  unsigned long int negtimeout;
+
+  unsigned long int poshit;
+  unsigned long int neghit;
+  unsigned long int posmiss;
+  unsigned long int negmiss;
+};
+
+/* Record for transmitting statistics.  */
+struct statdata
+{
+  char version[sizeof (compilation)];
+  int debug_level;
+  int ndbs;
+  struct dbstat dbs[lastdb];
+};
+
 
 void
-print_stat (void)
+send_stats (int fd, struct database dbs[lastdb])
 {
-  int sock = __nscd_open_socket ();
-  request_header req;
-  stat_response_header resp;
-  ssize_t nbytes;
+  struct statdata data;
+  int cnt;
+
+  memcpy (data.version, compilation, sizeof (compilation));
+  data.debug_level = debug_level;
+  data.ndbs = lastdb;
+
+  for (cnt = 0; cnt < lastdb; ++cnt)
+    {
+      data.dbs[cnt].enabled = dbs[cnt].enabled;
+      data.dbs[cnt].check_file = dbs[cnt].check_file;
+      data.dbs[cnt].module = dbs[cnt].module;
+      data.dbs[cnt].postimeout = dbs[cnt].postimeout;
+      data.dbs[cnt].negtimeout = dbs[cnt].negtimeout;
+      data.dbs[cnt].poshit = dbs[cnt].poshit;
+      data.dbs[cnt].neghit = dbs[cnt].neghit;
+      data.dbs[cnt].posmiss = dbs[cnt].posmiss;
+      data.dbs[cnt].negmiss = dbs[cnt].negmiss;
+    }
 
-  if (sock == -1)
+  if (TEMP_FAILURE_RETRY (write (fd, &data, sizeof (data))) != sizeof (data))
     {
-      fputs (_("nscd not running!\n"), stdout);
-      exit (EXIT_FAILURE);
+      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;
 
+  /* 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 = write (sock, &req, sizeof (request_header));
+  nbytes = TEMP_FAILURE_RETRY (write (fd, &req, sizeof (request_header)));
   if (nbytes != sizeof (request_header))
     {
-      perror (_("write incomplete"));
-      close (sock);
-      exit (EXIT_FAILURE);
+      int err = errno;
+      close (fd);
+      error (EXIT_FAILURE, err, _("write incomplete"));
     }
 
-  nbytes = read (sock, &resp, sizeof (stat_response_header));
-  if (nbytes != sizeof (stat_response_header))
+  /* 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))
     {
-      perror (_("read incomplete"));
-      close (sock);
-      exit (EXIT_FAILURE);
+      /* Not the right version.  */
+      int err = errno;
+      close (fd);
+      error (EXIT_FAILURE, err, _("cannot read statistics data"));
     }
 
-  close (sock);
-
-  printf (_("nscd configuration:\n\n"));
-  printf (_("%12d  server debug level\n\n"), resp.debug_level);
-
-  printf (_("passwd cache:\n\n"));
-  printf (_("%12s  cache is enabled\n"), resp.pw_enabled ? _("Yes") : _("No"));
-  printf (_("%12ld  cache hits on positive entries\n"), resp.pw_poshit);
-  printf (_("%12ld  cache hits on negative entries\n"), resp.pw_neghit);
-  printf (_("%12ld  cache misses on positive entries\n"), resp.pw_posmiss);
-  printf (_("%12ld  cache misses on negative entries\n"), resp.pw_negmiss);
-  printf (_("%12ld  suggested size\n"), resp.pw_size);
-  printf (_("%12ld  seconds time to live for positive entries\n"),
-         resp.pw_posttl);
-  printf (_("%12ld  seconds time to live for negative entries\n\n"),
-         resp.pw_negttl);
-
-  printf (_("group cache:\n\n"));
-  printf (_("%12s  cache is enabled\n"), resp.gr_enabled ? _("Yes") : _("No"));
-  printf (_("%12ld  cache hits on positive entries\n"), resp.gr_poshit);
-  printf (_("%12ld  cache hits on negative entries\n"), resp.gr_neghit);
-  printf (_("%12ld  cache misses on positive entries\n"), resp.gr_posmiss);
-  printf (_("%12ld  cache misses on negative entries\n"), resp.gr_negmiss);
-  printf (_("%12ld  suggested size\n"), resp.gr_size);
-  printf (_("%12ld  seconds time to live for positive entries\n"),
-         resp.gr_posttl);
-  printf (_("%12ld  seconds time to live for negative entries\n"),
-         resp.gr_negttl);
+  printf (_("nscd configuration:\n\n%15d  server debug level\n"),
+         data.debug_level);
+
+  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 = nl_langinfo (data.dbs[i].enabled ? YESSTR : NOSTR);
+      const char *check_file = nl_langinfo (data.dbs[i].check_file
+                                           ? 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 ? _("     yes") : _("      no");
+      if (check_file[0] == '\0')
+       check_file = data.dbs[i].check_file ? _("     yes") : _("      no");
+
+      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"
+               "%15Zd  suggested size\n"
+               "%15ld  seconds time to live for positive entries\n"
+               "%15ld  seconds time to live for negative entries\n"
+               "%15ld  cache hits on positive entries\n"
+               "%15ld  cache hits on negative entries\n"
+               "%15ld  cache misses on positive entries\n"
+               "%15ld  cache misses on negative entries\n"
+               "%15ld%% cache hit rate\n"
+               "%15s  check /etc/%s for changes\n"),
+             dbnames[i], enabled,
+             data.dbs[i].module,
+             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,
+             check_file, dbnames[i]);
+    }
+
+  close (fd);
+
+  exit (0);
 }
index 85b4fe9bdedadfb315b3136f7d8c786c68772ce7..5bf89a75bacc7806038bd8b8532c91cdb8548750 100644 (file)
@@ -1,6 +1,7 @@
-/* Copyright (c) 1998 Free Software Foundation, Inc.
+/* Cache handling for passwd lookup.
+   Copyright (C) 1998 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
-   Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1998.
+   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 Library General Public License as
    You should have received a copy of the GNU Library General Public
    License along with the GNU C Library; see the file COPYING.LIB.  If not,
    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-   Boston, MA 02111-1307, USA. */
+   Boston, MA 02111-1307, USA.  */
 
 #include <errno.h>
-#include <malloc.h>
-#include <pthread.h>
+#include <error.h>
 #include <pwd.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
+#include <time.h>
 #include <unistd.h>
-#include <rpcsvc/nis.h>
-#include <sys/types.h>
 
-#include "dbg_log.h"
 #include "nscd.h"
+#include "dbg_log.h"
 
-static unsigned long int modulo = 211;
-static unsigned long int postimeout = 600;
-static unsigned long int negtimeout = 20;
-
-static unsigned long int poshit = 0;
-static unsigned long int posmiss = 0;
-static unsigned long int neghit = 0;
-static unsigned long int negmiss = 0;
-
-struct pwdhash
-{
-  time_t create;
-  struct pwdhash *next;
-  struct passwd *pwd;
-};
-typedef struct pwdhash pwdhash;
-
-struct uidhash
-{
-  struct uidhash *next;
-  struct passwd *pwptr;
+/* 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
 };
-typedef struct uidhash uidhash;
 
-struct neghash
+/* This is the struct describing how to write this record.  */
+const struct iovec pwd_iov_disabled =
 {
-  time_t create;
-  struct neghash *next;
-  char *key;
+  iov_base: (void *) &disabled,
+  iov_len: sizeof (disabled)
 };
-typedef struct neghash neghash;
-
-static pwdhash *pwdtbl;
-static uidhash *uidtbl;
-static neghash *negtbl;
 
-static pthread_rwlock_t pwdlock = PTHREAD_RWLOCK_INITIALIZER;
-static pthread_rwlock_t neglock = PTHREAD_RWLOCK_INITIALIZER;
 
-static void *pwdtable_update (void *);
-static void *negtable_update (void *);
-
-void
-get_pw_stat (stat_response_header *stat)
+/* This is the standard reply in case we haven't found the dataset.  */
+static const pw_response_header notfound =
 {
-  stat->pw_poshit = poshit;
-  stat->pw_posmiss = posmiss;
-  stat->pw_neghit = neghit;
-  stat->pw_negmiss = negmiss;
-  stat->pw_size = modulo;
-  stat->pw_posttl = postimeout;
-  stat->pw_negttl = negtimeout;
-}
-
-void
-set_pwd_modulo (unsigned long int mod)
-{
-  modulo = mod;
-}
+  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
+};
 
-void
-set_pos_pwd_ttl (unsigned long int ttl)
+/* This is the struct describing how to write this record.  */
+static const struct iovec iov_notfound =
 {
-  postimeout = ttl;
-}
+  iov_base: (void *) &notfound,
+  iov_len: sizeof (notfound)
+};
 
-void
-set_neg_pwd_ttl (unsigned long int ttl)
-{
-  negtimeout = ttl;
-}
 
-int
-cache_pwdinit ()
+struct passwddata
 {
-  pthread_attr_t attr;
-  pthread_t thread;
-
-  pwdtbl = calloc (modulo, sizeof (pwdhash));
-  if (pwdtbl == NULL)
-    return -1;
-  uidtbl = calloc (modulo, sizeof (uidhash));
-  if (uidtbl == NULL)
-    return -1;
-  negtbl = calloc (modulo, sizeof (neghash));
-  if (negtbl == NULL)
-    return -1;
-
-  pthread_attr_init (&attr);
-  pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
-
-  pthread_create (&thread, NULL, pwdtable_update, &attr);
-  pthread_create (&thread, NULL, negtable_update, &attr);
+  pw_response_header resp;
+  char strdata[0];
+};
 
-  pthread_attr_destroy (&attr);
-
-  return 0;
-}
-
-static struct passwd *
-save_pwd (struct passwd *src)
-{
-  struct passwd *dest;
-  size_t name_len = strlen (src->pw_name) + 1;
-  size_t passwd_len = strlen (src->pw_gecos) + 1;
-  size_t gecos_len = strlen (src->pw_dir) + 1;
-  size_t dir_len = strlen (src->pw_dir) + 1;
-  size_t shell_len = strlen (src->pw_shell) + 1;
-  char *cp;
-
-  dest = malloc (sizeof (struct passwd)
-                + name_len + passwd_len + gecos_len + dir_len + shell_len);
-  if (dest == NULL)
-    return NULL;
-
-  cp = (char *) (dest + 1);
-  dest->pw_name = cp;
-  cp = mempcpy (cp, src->pw_name, name_len);
-  dest->pw_passwd = cp;
-  cp = mempcpy (cp, src->pw_passwd, passwd_len);
-  dest->pw_uid = src->pw_uid;
-  dest->pw_gid = src->pw_gid;
-  dest->pw_gecos = cp;
-  cp = mempcpy (cp, src->pw_gecos, gecos_len);
-  dest->pw_dir = cp;
-  cp = mempcpy (cp, src->pw_dir, dir_len);
-  dest->pw_shell = cp;
-  mempcpy (cp, src->pw_shell, shell_len);
-
-  return dest;
-}
 
 static void
-free_pwd (struct passwd *src)
-{
-  free (src);
-}
-
-static int
-add_cache (struct passwd *pwd)
+cache_addpw (struct database *db, int fd, request_header *req, void *key,
+            struct passwd *pwd)
 {
-  pwdhash *work;
-  uidhash *uidwork;
-  unsigned long int hash = __nis_hash (pwd->pw_name,
-                                      strlen (pwd->pw_name)) % modulo;
-
-  if (debug_flag)
-    dbg_log (_("pwd_add_cache (%s)"), pwd->pw_name);
-
-  work = &pwdtbl[hash];
-
-  if (pwdtbl[hash].pwd == NULL)
-    pwdtbl[hash].pwd = save_pwd (pwd);
-  else
-    {
-      while (work->next != NULL)
-       work = work->next;
-
-      work->next = calloc (1, sizeof (pwdhash));
-      work->next->pwd = save_pwd (pwd);
-      work = work->next;
-    }
-  /* Set a pointer from the pwuid hash table to the pwname hash table */
-  time (&work->create);
-  uidwork = &uidtbl[pwd->pw_uid % modulo];
-  if (uidwork->pwptr == NULL)
-    uidwork->pwptr = work->pwd;
-  else
-   {
-      while (uidwork->next != NULL)
-       uidwork = uidwork->next;
-
-      uidwork->next = calloc (1, sizeof (uidhash));
-      uidwork->next->pwptr = work->pwd;
-    }
-  return 0;
-}
-
-static struct passwd *
-cache_search_name (const char *name)
-{
-  pwdhash *work;
-  unsigned long int hash = __nis_hash (name, strlen (name)) % modulo;
-
-  work = &pwdtbl[hash];
+  ssize_t total;
+  ssize_t written;
+  time_t t = time (NULL);
 
-  while (work->pwd != NULL)
+  if (pwd == NULL)
     {
-      if (strcmp (work->pwd->pw_name, name) == 0)
-       return work->pwd;
-      if (work->next != NULL)
-       work = work->next;
-      else
-       return NULL;
-    }
-  return NULL;
-}
+      /* We have no data.  This means we send the standard reply for this
+        case.  */
+      void *copy;
 
-static struct passwd *
-cache_search_uid (uid_t uid)
-{
-  uidhash *work;
+      total = sizeof (notfound);
 
-  work = &uidtbl[uid % modulo];
+      written = writev (fd, &iov_notfound, 1);
 
-  while (work->pwptr != NULL)
-    {
-      if (work->pwptr->pw_uid == uid)
-       return work->pwptr;
-      if (work->next != NULL)
-       work = work->next;
-      else
-       return NULL;
-    }
-  return NULL;
-}
+      copy = malloc (req->key_len);
+      if (copy == NULL)
+       error (EXIT_FAILURE, errno, _("while allocating key copy"));
+      memcpy (copy, key, req->key_len);
 
-static int
-add_negcache (char *key)
-{
-  neghash *work;
-  unsigned long int hash = __nis_hash (key, strlen (key)) % modulo;
+      /* Compute the timeout time.  */
+      t += db->negtimeout;
 
-  if (debug_flag)
-    dbg_log (_("pwd_add_netgache (%s|%ld)"), key, hash);
+      /* Now get the lock to safely insert the records.  */
+      pthread_rwlock_rdlock (&db->lock);
 
-  work = &negtbl[hash];
+      cache_add (req->type, copy, req->key_len, &iov_notfound,
+                sizeof (notfound), (void *) -1, 0, t, db);
 
-  if (negtbl[hash].key == NULL)
-    {
-      negtbl[hash].key = strdup (key);
-      negtbl[hash].next = NULL;
+      pthread_rwlock_unlock (&db->lock);
     }
   else
     {
-      while (work->next != NULL)
-       work = work->next;
-
-      work->next = calloc (1, sizeof (neghash));
-      work->next->key = strdup (key);
-      work = work->next;
+      /* Determine the I/O structure.  */
+      struct passwddata *data;
+      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;
+      char buf[12];
+      ssize_t n;
+
+      /* We need this to insert the `byuid' entry.  */
+      n = snprintf (buf, sizeof (buf), "%d", pwd->pw_uid) + 1;
+
+      /* We allocate all data in one memory block: the iov vector,
+        the response header and the dataset itself.  */
+      total = (sizeof (struct passwddata) + pw_name_len + pw_passwd_len
+              + pw_gecos_len + pw_dir_len + pw_shell_len);
+      data = (struct passwddata *) malloc (total + n);
+      if (data == NULL)
+       /* There is no reason to go on.  */
+       error (EXIT_FAILURE, errno, _("while allocating cache entry"));
+
+      data->resp.found = 1;
+      data->resp.pw_name_len = pw_name_len;
+      data->resp.pw_passwd_len = pw_passwd_len;
+      data->resp.pw_uid = pwd->pw_uid;
+      data->resp.pw_gid = pwd->pw_gid;
+      data->resp.pw_gecos_len = pw_gecos_len;
+      data->resp.pw_dir_len = pw_dir_len;
+      data->resp.pw_shell_len = pw_shell_len;
+
+      cp = data->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);
+
+      /* 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.  */
+      written = write (fd, &data->resp, total);
+
+      /* Compute the timeout time.  */
+      t += db->postimeout;
+
+      /* Now get the lock to safely insert the records.  */
+      pthread_rwlock_rdlock (&db->lock);
+
+      /* We have to add the value for both, byname and byuid.  */
+      cache_add (GETPWBYNAME, data->strdata, pw_name_len, data,
+                total, data, 0, t, db);
+
+      cache_add (GETPWBYUID, cp, n, data, total, data, 1, t, db);
+
+      pthread_rwlock_unlock (&db->lock);
     }
 
-  time (&work->create);
-
-  return 0;
-}
-
-static int
-cache_search_neg (const char *key)
-{
-  neghash *work;
-  unsigned long int hash = __nis_hash (key, strlen (key)) % modulo;
-
-  if (debug_flag)
-    dbg_log (_("pwd_cache_search_neg (%s|%ld)"), key, hash);
-
-  work = &negtbl[hash];
-
-  while (work->key != NULL)
+  if (written != total)
     {
-      if (strcmp (work->key, key) == 0)
-       return 1;
-      if (work->next != NULL)
-       work = work->next;
-      else
-       return 0;
+      char buf[256];
+      dbg_log (_("short write in %s: %s"),  __FUNCTION__,
+              strerror_r (errno, buf, sizeof (buf)));
     }
-  return 0;
 }
 
-void *
-cache_getpwnam (void *v_param)
-{
-  struct passwd *pwd;
-  param_t *param = (param_t *)v_param;
-
-  pthread_rwlock_rdlock (&pwdlock);
-  pwd = cache_search_name (param->key);
 
-  /* I don't like it to hold the read only lock longer, but it is
-     necessary to avoid to much malloc/free/strcpy.  */
-
-  if (pwd != NULL)
-    {
-      if (debug_flag)
-       dbg_log (_("Found \"%s\" in cache !"), param->key);
+void
+addpwbyname (struct database *db, int fd, request_header *req, void *key)
+{
+  /* 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 = 256;
+  char *buffer = alloca (buflen);
+  struct passwd resultbuf;
+  struct passwd *pwd;
 
-      ++poshit;
-      pw_send_answer (param->conn, pwd);
-      close_socket (param->conn);
+  if (debug_level > 0)
+    dbg_log (_("Haven't found \"%s\" in password cache!"), key);
 
-      pthread_rwlock_unlock (&pwdlock);
-    }
-  else
+  while (getpwnam_r (key, &resultbuf, buffer, buflen, &pwd) != 0
+        && errno == ERANGE)
     {
-      int status;
-      int buflen = 1024;
-      char *buffer = calloc (1, buflen);
-      struct passwd resultbuf;
-
-      if (debug_flag)
-       dbg_log (_("Doesn't found \"%s\" in cache !"), param->key);
-
-      pthread_rwlock_unlock (&pwdlock);
-
-      pthread_rwlock_rdlock (&neglock);
-      status = cache_search_neg (param->key);
-      pthread_rwlock_unlock (&neglock);
-
-      if (status == 0)
-       {
-         while (buffer != NULL
-                && (getpwnam_r (param->key, &resultbuf, buffer, buflen, &pwd)
-                    != 0)
-                && errno == ERANGE)
-           {
-             errno = 0;
-             buflen += 1024;
-             buffer = realloc (buffer, buflen);
-           }
-
-         if (buffer != NULL && pwd != NULL)
-           {
-             struct passwd *tmp;
-
-             ++posmiss;
-             pthread_rwlock_wrlock (&pwdlock);
-             /* While we are waiting on the lock, somebody else could
-                add this entry.  */
-             tmp = cache_search_name (param->key);
-             if (tmp == NULL)
-               add_cache (pwd);
-             pthread_rwlock_unlock (&pwdlock);
-           }
-         else
-           {
-             ++negmiss;
-             pthread_rwlock_wrlock (&neglock);
-             add_negcache (param->key);
-             pthread_rwlock_unlock (&neglock);
-           }
-       }
-      else
-       ++neghit;
-      pw_send_answer (param->conn, pwd);
-      close_socket (param->conn);
-      if (buffer != NULL)
-       free (buffer);
+      errno = 0;
+      buflen += 256;
+      buffer = alloca (buflen);
     }
-  free (param->key);
-  free (param);
-  return NULL;
-}
-
-void *
-cache_pw_disabled (void *v_param)
-{
-  param_t *param = (param_t *)v_param;
-
-  if (debug_flag)
-    dbg_log (_("\tpasswd cache is disabled\n"));
 
-  pw_send_disabled (param->conn);
-  return NULL;
+  cache_addpw (db, fd, req, key, pwd);
 }
 
-void *
-cache_getpwuid (void *v_param)
-{
-  param_t *param = (param_t *)v_param;
-  struct passwd *pwd, resultbuf;
-  uid_t uid = strtol (param->key, NULL, 10);
-
-  pthread_rwlock_rdlock (&pwdlock);
-  pwd = cache_search_uid (uid);
-
-  /* I don't like it to hold the read only lock longer, but it is
-     necessary to avoid to much malloc/free/strcpy.  */
 
-  if (pwd != NULL)
-    {
-      if (debug_flag)
-       dbg_log (_("Found \"%d\" in cache !"), uid);
-
-      ++poshit;
-      pw_send_answer (param->conn, pwd);
-      close_socket (param->conn);
-
-      pthread_rwlock_unlock (&pwdlock);
-    }
-  else
-    {
-      int buflen = 1024;
-      char *buffer = malloc (buflen);
-      int status;
-
-      if (debug_flag)
-       dbg_log (_("Doesn't found \"%d\" in cache !"), uid);
-
-      pthread_rwlock_unlock (&pwdlock);
-
-      pthread_rwlock_rdlock (&neglock);
-      status = cache_search_neg (param->key);
-      pthread_rwlock_unlock (&neglock);
-
-      if (status == 0)
-        {
-         while (buffer != NULL
-                && (getpwuid_r (uid, &resultbuf, buffer, buflen, &pwd) != 0)
-                && errno == ERANGE)
-           {
-             errno = 0;
-             buflen += 1024;
-             buffer = realloc (buffer, buflen);
-           }
-
-         if (buffer != NULL && pwd != NULL)
-           {
-             struct passwd *tmp;
-
-             ++posmiss;
-             pthread_rwlock_wrlock (&pwdlock);
-             /* While we are waiting on the lock, somebody else could
-                add this entry.  */
-             tmp = cache_search_uid (uid);
-             if (tmp == NULL)
-               add_cache (pwd);
-             pthread_rwlock_unlock (&pwdlock);
-           }
-         else
-           {
-             ++negmiss;
-             pthread_rwlock_wrlock (&neglock);
-             add_negcache (param->key);
-             pthread_rwlock_unlock (&neglock);
-           }
-       }
-      else
-       ++neghit;
-
-      pw_send_answer (param->conn, pwd);
-      close_socket (param->conn);
-      if (buffer != NULL)
-       free (buffer);
-    }
-  free (param->key);
-  free (param);
-  return NULL;
-}
-
-static void *
-pwdtable_update (void *v)
-{
-  time_t now;
-  int i;
+void
+addpwbyuid (struct database *db, int fd, request_header *req, void *key)
+{
+  /* 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 = 256;
+  char *buffer = alloca (buflen);
+  struct passwd resultbuf;
+  struct passwd *pwd;
+  uid_t uid = atol (key);
 
-  sleep (20);
+  if (debug_level > 0)
+    dbg_log (_("Haven't found \"%d\" in password cache!"), uid);
 
-  while (!do_shutdown)
+  while (getpwuid_r (uid, &resultbuf, buffer, buflen, &pwd) != 0
+        && errno == ERANGE)
     {
-      if (debug_flag > 2)
-       dbg_log (_("(pwdtable_update) Wait for write lock!"));
-
-      pthread_rwlock_wrlock (&pwdlock);
-
-      if (debug_flag > 2)
-       dbg_log (_("(pwdtable_update) Have write lock"));
-
-      time (&now);
-      for (i = 0; i < modulo; ++i)
-       {
-         pwdhash *work = &pwdtbl[i];
-
-         while (work && work->pwd)
-           {
-             if ((now - work->create) >= postimeout)
-               {
-                 uidhash *uh = &uidtbl[work->pwd->pw_uid % modulo];
-
-                 if (debug_flag)
-                   dbg_log (_("Give \"%s\" free"), work->pwd->pw_name);
-
-                 while (uh != NULL && uh->pwptr)
-                   {
-                     if (uh->pwptr->pw_uid == work->pwd->pw_uid)
-                       {
-                         if (debug_flag)
-                           dbg_log (_("Give uid for \"%s\" free"),
-                                    work->pwd->pw_name);
-                         if (uh->next != NULL)
-                           {
-                             uidhash *tmp = uh->next;
-                             uh->pwptr = tmp->pwptr;
-                             uh->next = tmp->next;
-                             free (tmp);
-                           }
-                         else
-                           uh->pwptr = NULL;
-                       }
-                     uh = uh->next;
-                   }
-
-                 free_pwd (work->pwd);
-                 if (work->next != NULL)
-                   {
-                     pwdhash *tmp = work->next;
-                     work->create = tmp->create;
-                     work->next = tmp->next;
-                     work->pwd = tmp->pwd;
-                     free (tmp);
-                   }
-                 else
-                   work->pwd = NULL;
-               }
-             work = work->next;
-           }
-       }
-      if (debug_flag > 2)
-       dbg_log (_("(pwdtable_update) Release wait lock"));
-      pthread_rwlock_unlock (&pwdlock);
-      sleep (20);
+      errno = 0;
+      buflen += 256;
+      buffer = alloca (buflen);
     }
-  return NULL;
-}
-
-static void *
-negtable_update (void *v)
-{
-  time_t now;
-  int i;
 
-  sleep (30);
-
-  while (!do_shutdown)
-    {
-      if (debug_flag > 2)
-       dbg_log (_("(negpwdtable_update) Wait for write lock!"));
-
-      pthread_rwlock_wrlock (&neglock);
-
-      if (debug_flag > 2)
-       dbg_log (_("(negpwdtable_update) Have write lock"));
-
-      time (&now);
-      for (i = 0; i < modulo; ++i)
-       {
-         neghash *work = &negtbl[i];
-
-         while (work && work->key)
-           {
-             if ((now - work->create) >= negtimeout)
-               {
-                 if (debug_flag)
-                   dbg_log (_("Give \"%s\" free"), work->key);
-
-                 free (work->key);
-
-                 if (work->next != NULL)
-                   {
-                     neghash *tmp = work->next;
-                     work->create = tmp->create;
-                     work->next = tmp->next;
-                     work->key = tmp->key;
-                     free (tmp);
-                   }
-                 else
-                   work->key = NULL;
-               }
-             work = work->next;
-           }
-       }
-      if (debug_flag > 2)
-       dbg_log (_("(negpwdtable_update) Release wait lock"));
-
-      pthread_rwlock_unlock (&neglock);
-      sleep (10);
-    }
-  return NULL;
+  cache_addpw (db, fd, req, key, pwd);
 }
index 217f4da7a23af31161fe7ea351b362b965f88173..80b9ca87dd67acab428a9b6666c5c47e96f03ffa 100644 (file)
@@ -1,7 +1,7 @@
 libc {
   GLIBC_2.0 {
      # functions used in other libraries
-    __nss_passwd_lookup; __nss_group_lookup; __nss_next;
+    __nss_passwd_lookup; __nss_group_lookup; __nss_hosts_lookup; __nss_next;
     _nss_files_parse_grent; _nss_files_parse_pwent; _nss_files_parse_spent;
     __nss_database_lookup; __nss_configure_lookup;
   }
index 0df916b1edea1a5e38f2bad70c4c736dbec0dcda..21baad9474ef51bc57282898a508091e0fd73a89 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+/* Copyright (C) 1996, 1997, 1998 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
@@ -117,7 +117,6 @@ FUNCTION_NAME (ADD_PARAMS)
             process gets a chance for a normal termination.  */
          save = errno;
          free (buffer);
-         buffer = NULL;
          __set_errno (save);
        }
       buffer = new_buf;
index 0f1206762b13fbddd6f6060f1ec0049f276dba90..3befede20fe358ef526b6b5cea32b62259773be2 100644 (file)
@@ -19,7 +19,9 @@
 
 #include <errno.h>
 #include "nsswitch.h"
-#include <nscd/nscd_proto.h>
+#ifdef USE_NSCD
+# include <nscd/nscd_proto.h>
+#endif
 
 /*******************************************************************\
 |* Here we assume several symbols to be defined:                  *|
index ca0e3634223b0a6e81c6b65d06f293976c83b84c..30453f88c8914fa82d97383bc6acf0810bc2ad48 100644 (file)
@@ -28,7 +28,6 @@ routines := fgetpwent getpw putpwent \
 
 include ../Rules
 
-# We can later add the names of other thread packages here.
 ifeq ($(have-thread-library),yes)
 
 CFLAGS-getpwuid_r.c = -DUSE_NSCD=1
index ed4be6733758a58895cc6bb453de9570912516bf..4d46384ca7acd906f765397375f71411dafca1a5 100644 (file)
@@ -120,11 +120,12 @@ typedef union querybuf
 static enum nss_status getanswer_r (const querybuf *answer, int anslen,
                                    const char *qname, int qtype,
                                    struct hostent *result, char *buffer,
-                                   size_t buflen, int *h_errnop);
+                                   size_t buflen, int *errnop, int *h_errnop);
 
 enum nss_status
 _nss_dns_gethostbyname2_r (const char *name, int af, struct hostent *result,
-                          char *buffer, size_t buflen, int *h_errnop)
+                          char *buffer, size_t buflen, int *errnop,
+                          int *h_errnop)
 {
   querybuf host_buffer;
   int size, type, n;
@@ -141,7 +142,7 @@ _nss_dns_gethostbyname2_r (const char *name, int af, struct hostent *result,
     break;
   default:
     *h_errnop = NETDB_INTERNAL;
-    __set_errno (EAFNOSUPPORT);
+    *errnop = EAFNOSUPPORT;
     return NSS_STATUS_UNAVAIL;
   }
 
@@ -160,26 +161,28 @@ _nss_dns_gethostbyname2_r (const char *name, int af, struct hostent *result,
   if (n < 0)
     {
       *h_errnop = h_errno;
+      *errnop = errno;
       return errno == ECONNREFUSED ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND;
     }
 
   return getanswer_r (&host_buffer, n, name, type, result, buffer, buflen,
-                     h_errnop);
+                     errnop, h_errnop);
 }
 
 
 enum nss_status
 _nss_dns_gethostbyname_r (const char *name, struct hostent *result,
-                         char *buffer, size_t buflen, int *h_errnop)
+                         char *buffer, size_t buflen, int *errnop,
+                         int *h_errnop)
 {
   enum nss_status status = NSS_STATUS_NOTFOUND;
 
   if (_res.options & RES_USE_INET6)
     status = _nss_dns_gethostbyname2_r (name, AF_INET6, result, buffer,
-                                       buflen, h_errnop);
+                                       buflen, errnop, h_errnop);
   if (status == NSS_STATUS_NOTFOUND)
     status = _nss_dns_gethostbyname2_r (name, AF_INET, result, buffer,
-                                       buflen, h_errnop);
+                                       buflen, errnop, h_errnop);
 
   return status;
 }
@@ -188,7 +191,7 @@ _nss_dns_gethostbyname_r (const char *name, struct hostent *result,
 enum nss_status
 _nss_dns_gethostbyaddr_r (const char *addr, int len, int af,
                          struct hostent *result, char *buffer, size_t buflen,
-                         int *h_errnop)
+                         int *errnop, int *h_errnop)
 {
   static const u_char mapped[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0xff,0xff };
   static const u_char tunnelled[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0,0 };
@@ -224,13 +227,13 @@ _nss_dns_gethostbyaddr_r (const char *addr, int len, int af,
       size = IN6ADDRSZ;
       break;
     default:
-      __set_errno (EAFNOSUPPORT);
+      *errnop = EAFNOSUPPORT;
       *h_errnop = NETDB_INTERNAL;
       return NSS_STATUS_UNAVAIL;
     }
   if (size != len)
     {
-      __set_errno (EAFNOSUPPORT);
+      *errnop = EAFNOSUPPORT;
       *h_errnop = NETDB_INTERNAL;
       return NSS_STATUS_UNAVAIL;
     }
@@ -256,14 +259,16 @@ _nss_dns_gethostbyaddr_r (const char *addr, int len, int af,
   if (n < 0)
     {
       *h_errnop = h_errno;
+      *errnop = errno;
       return errno == ECONNREFUSED ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND;
     }
 
   status = getanswer_r (&host_buffer, n, qbuf, T_PTR, result, buffer, buflen,
-                       h_errnop);
+                       errnop, h_errnop);
   if (status != NSS_STATUS_SUCCESS)
     {
       *h_errnop = h_errno;
+      *errnop = errno;
       return status;
     }
 
@@ -292,7 +297,7 @@ _nss_dns_gethostbyaddr_r (const char *addr, int len, int af,
 static enum nss_status
 getanswer_r (const querybuf *answer, int anslen, const char *qname, int qtype,
             struct hostent *result, char *buffer, size_t buflen,
-            int *h_errnop)
+            int *errnop, int *h_errnop)
 {
   struct host_data
   {
@@ -344,7 +349,17 @@ getanswer_r (const querybuf *answer, int anslen, const char *qname, int qtype,
   n = dn_expand (answer->buf, end_of_message, cp, bp, linebuflen);
   if (n < 0 || (*name_ok) (bp) == 0)
     {
-      *h_errnop = NO_RECOVERY;
+      if (errno == EMSGSIZE)
+       {
+         /* There is not enough room in the input buffer.  */
+         *errnop = ERANGE;
+         *h_errnop = NETDB_INTERNAL;
+       }
+      else
+       {
+         *errnop = errno;
+         *h_errnop = NO_RECOVERY;
+       }
       return NSS_STATUS_UNAVAIL;
     }
   cp += n + QFIXEDSZ;
@@ -358,7 +373,7 @@ getanswer_r (const querybuf *answer, int anslen, const char *qname, int qtype,
       n = strlen (bp) + 1;             /* for the \0 */
       if (n >= MAXHOSTNAMELEN)
        {
-         __set_h_errno (NO_RECOVERY);
+         *h_errnop = NO_RECOVERY;
          return NSS_STATUS_TRYAGAIN;
        }
       result->h_name = bp;
index e9a4b91e9d21a74afbdb4bb992ab872eff32270f..6faeec33e425a1dabbc068a225a95485a7a2747e 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+/* Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Extended from original form by Ulrich Drepper <drepper@cygnus.com>, 1996.
 
@@ -104,7 +104,7 @@ static enum nss_status getanswer_r (const querybuf *answer, int anslen,
 
 enum nss_status
 _nss_dns_getnetbyname_r (const char *name, struct netent *result,
-                        char *buffer, size_t buflen)
+                        char *buffer, size_t buflen, int *errnop)
 {
   /* Return entry for network with NAME.  */
   querybuf net_buffer;
@@ -115,11 +115,14 @@ _nss_dns_getnetbyname_r (const char *name, struct netent *result,
   anslen = res_search (qbuf, C_IN, T_PTR, (u_char *) &net_buffer,
                       sizeof (querybuf));
   if (anslen < 0)
-    /* Nothing found.  */
-    return (errno == ECONNREFUSED
-           || errno == EPFNOSUPPORT
-           || errno == EAFNOSUPPORT)
-      ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND;
+    {
+      /* Nothing found.  */
+      *errnop = errno;
+      return (errno == ECONNREFUSED
+             || errno == EPFNOSUPPORT
+             || errno == EAFNOSUPPORT)
+       ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND;
+    }
 
   return getanswer_r (&net_buffer, anslen, result, buffer, buflen, BYNAME);
 }
@@ -127,7 +130,7 @@ _nss_dns_getnetbyname_r (const char *name, struct netent *result,
 
 enum nss_status
 _nss_dns_getnetbyaddr_r (long net, int type, struct netent *result,
-                        char *buffer, size_t buflen)
+                        char *buffer, size_t buflen, int *errnop)
 {
   /* Return entry for network with NAME.  */
   enum nss_status status;
@@ -170,11 +173,14 @@ _nss_dns_getnetbyaddr_r (long net, int type, struct netent *result,
   anslen = res_query (qbuf, C_IN, T_PTR, (u_char *) &net_buffer,
                      sizeof (querybuf));
   if (anslen < 0)
-    /* Nothing found.  */
-    return (errno == ECONNREFUSED
-           || errno == EPFNOSUPPORT
-           || errno == EAFNOSUPPORT)
-      ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND;
+    {
+      /* Nothing found.  */
+      *errnop = errno;
+      return (errno == ECONNREFUSED
+             || errno == EPFNOSUPPORT
+             || errno == EAFNOSUPPORT)
+       ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND;
+    }
 
   status = getanswer_r (&net_buffer, anslen, result, buffer, buflen, BYADDR);
   if (status == NSS_STATUS_SUCCESS)
diff --git a/sysdeps/unix/sysv/linux/m68k/setegid.c b/sysdeps/unix/sysv/linux/m68k/setegid.c
new file mode 100644 (file)
index 0000000..2e3a54c
--- /dev/null
@@ -0,0 +1 @@
+#include <sysdeps/unix/sysv/linux/i386/setegid.c>
diff --git a/sysdeps/unix/sysv/linux/m68k/seteuid.c b/sysdeps/unix/sysv/linux/m68k/seteuid.c
new file mode 100644 (file)
index 0000000..18e41d0
--- /dev/null
@@ -0,0 +1 @@
+#include <sysdeps/unix/sysv/linux/i386/seteuid.c>
diff --git a/sysdeps/unix/sysv/linux/m68k/setfsgid.c b/sysdeps/unix/sysv/linux/m68k/setfsgid.c
new file mode 100644 (file)
index 0000000..0886712
--- /dev/null
@@ -0,0 +1 @@
+#include <sysdeps/unix/sysv/linux/i386/setfsgid.c>
diff --git a/sysdeps/unix/sysv/linux/m68k/setfsuid.c b/sysdeps/unix/sysv/linux/m68k/setfsuid.c
new file mode 100644 (file)
index 0000000..b478595
--- /dev/null
@@ -0,0 +1 @@
+s
diff --git a/sysdeps/unix/sysv/linux/m68k/setgid.c b/sysdeps/unix/sysv/linux/m68k/setgid.c
new file mode 100644 (file)
index 0000000..377021d
--- /dev/null
@@ -0,0 +1 @@
+#include <sysdeps/unix/sysv/linux/i386/setgid.c>
diff --git a/sysdeps/unix/sysv/linux/m68k/setresgid.c b/sysdeps/unix/sysv/linux/m68k/setresgid.c
new file mode 100644 (file)
index 0000000..daca1a4
--- /dev/null
@@ -0,0 +1 @@
+#include <sysdeps/unix/sysv/linux/i386/setresgid.c>
diff --git a/sysdeps/unix/sysv/linux/m68k/setresuid.c b/sysdeps/unix/sysv/linux/m68k/setresuid.c
new file mode 100644 (file)
index 0000000..3aeabe9
--- /dev/null
@@ -0,0 +1 @@
+#include <sysdeps/unix/sysv/linux/i386/setresuid.c>
diff --git a/sysdeps/unix/sysv/linux/m68k/setuid.c b/sysdeps/unix/sysv/linux/m68k/setuid.c
new file mode 100644 (file)
index 0000000..de39437
--- /dev/null
@@ -0,0 +1 @@
+#include <sysdeps/unix/sysv/linux/i386/setuid.c>