summaryrefslogtreecommitdiff
path: root/nscd
diff options
context:
space:
mode:
authorUlrich Drepper <drepper@redhat.com>1998-10-18 15:16:22 +0000
committerUlrich Drepper <drepper@redhat.com>1998-10-18 15:16:22 +0000
commit67479a700e3bd2e52980c00ac35c888589ac0a36 (patch)
tree2a13dea0fbd27ba19d0b19d5849699128d495806 /nscd
parent6cde0c6047769a661b8cf7e4f93842914a4bb54f (diff)
downloadglibc-67479a700e3bd2e52980c00ac35c888589ac0a36.tar.gz
Update.
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.
Diffstat (limited to 'nscd')
-rw-r--r--nscd/Makefile5
-rw-r--r--nscd/cache.c247
-rw-r--r--nscd/connections.c771
-rw-r--r--nscd/dbg_log.c6
-rw-r--r--nscd/dbg_log.h6
-rw-r--r--nscd/gethstbyad_r.c31
-rw-r--r--nscd/gethstbynm2_r.c39
-rw-r--r--nscd/grpcache.c698
-rw-r--r--nscd/hstcache.c405
-rw-r--r--nscd/nscd.c245
-rw-r--r--nscd/nscd.conf15
-rw-r--r--nscd/nscd.h240
-rw-r--r--nscd/nscd_conf.c94
-rw-r--r--nscd/nscd_getgr_r.c186
-rw-r--r--nscd/nscd_gethst_r.c302
-rw-r--r--nscd/nscd_getpw_r.c86
-rw-r--r--nscd/nscd_proto.h14
-rw-r--r--nscd/nscd_stat.c181
-rw-r--r--nscd/pwdcache.c713
19 files changed, 2208 insertions, 2076 deletions
diff --git a/nscd/Makefile b/nscd/Makefile
index f00b2f0b3e..ed5c0ba9c5 100644
--- a/nscd/Makefile
+++ b/nscd/Makefile
@@ -21,12 +21,13 @@
#
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
index 0000000000..e957a577c0
--- /dev/null
+++ b/nscd/cache.c
@@ -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);
+ }
+}
diff --git a/nscd/connections.c b/nscd/connections.c
index 4cf397d201..8e6839a157 100644
--- a/nscd/connections.c
+++ b/nscd/connections.c
@@ -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
@@ -15,551 +16,407 @@
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);
}
diff --git a/nscd/dbg_log.c b/nscd/dbg_log.c
index b2b8b3e31c..21997cdcf4 100644
--- a/nscd/dbg_log.c
+++ b/nscd/dbg_log.c
@@ -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)
diff --git a/nscd/dbg_log.h b/nscd/dbg_log.h
index c3d1dc4559..5aeff89f6a 100644
--- a/nscd/dbg_log.h
+++ b/nscd/dbg_log.h
@@ -20,8 +20,10 @@
#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
index 0000000000..c22044aac1
--- /dev/null
+++ b/nscd/gethstbyad_r.c
@@ -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
index 0000000000..b9b3a7149f
--- /dev/null
+++ b/nscd/gethstbynm2_r.c
@@ -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"
diff --git a/nscd/grpcache.c b/nscd/grpcache.c
index f34780ac4e..1645d71e41 100644
--- a/nscd/grpcache.c
+++ b/nscd/grpcache.c
@@ -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
@@ -15,605 +16,234 @@
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
index 0000000000..4e3af9ca40
--- /dev/null
+++ b/nscd/hstcache.c
@@ -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);
+}
diff --git a/nscd/nscd.c b/nscd/nscd.c
index 9ddbb5f54e..d6d2c6497d 100644
--- a/nscd/nscd.c
+++ b/nscd/nscd.c
@@ -17,9 +17,10 @@
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);
-}
diff --git a/nscd/nscd.conf b/nscd/nscd.conf
index 5d8c7f31ac..5e327e86fe 100644
--- a/nscd/nscd.conf
+++ b/nscd/nscd.conf
@@ -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
diff --git a/nscd/nscd.h b/nscd/nscd.h
index 4835542619..bc8150aca1 100644
--- a/nscd/nscd.h
+++ b/nscd/nscd.h
@@ -18,18 +18,37 @@
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 */
diff --git a/nscd/nscd_conf.c b/nscd/nscd_conf.c
index 917fe022ff..6ac1677a8b 100644
--- a/nscd/nscd_conf.c
+++ b/nscd/nscd_conf.c
@@ -22,17 +22,27 @@
#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);
diff --git a/nscd/nscd_getgr_r.c b/nscd/nscd_getgr_r.c
index ec4f5a1297..f27da02569 100644
--- a/nscd/nscd_getgr_r.c
+++ b/nscd/nscd_getgr_r.c
@@ -32,41 +32,36 @@
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
index 0000000000..f78b104103
--- /dev/null
+++ b/nscd/nscd_gethst_r.c
@@ -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;
+ }
+}
diff --git a/nscd/nscd_getpw_r.c b/nscd/nscd_getpw_r.c
index a24d33b789..83baafcc2a 100644
--- a/nscd/nscd_getpw_r.c
+++ b/nscd/nscd_getpw_r.c
@@ -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
{
diff --git a/nscd/nscd_proto.h b/nscd/nscd_proto.h
index f82f86c1ab..760b57de76 100644
--- a/nscd/nscd_proto.h
+++ b/nscd/nscd_proto.h
@@ -21,11 +21,13 @@
#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 */
diff --git a/nscd/nscd_stat.c b/nscd/nscd_stat.c
index d8182885ac..f9d21aeb01 100644
--- a/nscd/nscd_stat.c
+++ b/nscd/nscd_stat.c
@@ -17,71 +17,160 @@
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);
}
diff --git a/nscd/pwdcache.c b/nscd/pwdcache.c
index 85b4fe9bde..5bf89a75ba 100644
--- a/nscd/pwdcache.c
+++ b/nscd/pwdcache.c
@@ -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
@@ -15,597 +16,231 @@
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);
}