aboutsummaryrefslogtreecommitdiff
path: root/nscd/connections.c
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/connections.c
parent6cde0c6047769a661b8cf7e4f93842914a4bb54f (diff)
downloadglibc-67479a700e3bd2e52980c00ac35c888589ac0a36.tar
glibc-67479a700e3bd2e52980c00ac35c888589ac0a36.tar.gz
glibc-67479a700e3bd2e52980c00ac35c888589ac0a36.tar.bz2
glibc-67479a700e3bd2e52980c00ac35c888589ac0a36.zip
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/connections.c')
-rw-r--r--nscd/connections.c771
1 files changed, 314 insertions, 457 deletions
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);
}