From 67479a700e3bd2e52980c00ac35c888589ac0a36 Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Sun, 18 Oct 1998 15:16:22 +0000 Subject: Update. 1998-10-18 Ulrich Drepper * 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. --- nscd/Makefile | 5 +- nscd/cache.c | 247 +++++++++++++++++ nscd/connections.c | 771 +++++++++++++++++++++------------------------------ nscd/dbg_log.c | 6 +- nscd/dbg_log.h | 6 +- nscd/gethstbyad_r.c | 31 +++ nscd/gethstbynm2_r.c | 39 +++ nscd/grpcache.c | 698 +++++++++++----------------------------------- nscd/hstcache.c | 405 +++++++++++++++++++++++++++ nscd/nscd.c | 245 +++------------- nscd/nscd.conf | 15 +- nscd/nscd.h | 240 ++++++++++------ nscd/nscd_conf.c | 94 +++++-- nscd/nscd_getgr_r.c | 186 +++++-------- nscd/nscd_gethst_r.c | 302 ++++++++++++++++++++ nscd/nscd_getpw_r.c | 86 ++---- nscd/nscd_proto.h | 14 + nscd/nscd_stat.c | 181 +++++++++--- nscd/pwdcache.c | 713 ++++++++++++----------------------------------- 19 files changed, 2208 insertions(+), 2076 deletions(-) create mode 100644 nscd/cache.c create mode 100644 nscd/gethstbyad_r.c create mode 100644 nscd/gethstbynm2_r.c create mode 100644 nscd/hstcache.c create mode 100644 nscd/nscd_gethst_r.c (limited to 'nscd') 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 , 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 , 1998. + Contributed by Ulrich Drepper , 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 +#include #include -#include -#include -#include +#include #include -#include -#include #include #include +#include #include #include #include -#include -#include #include #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 , 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 + + +#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 , 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 +#include +#include +#include +#include +#include + + +#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 , 1998. + Contributed by Ulrich Drepper , 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 #include +#include #include -#include +#include +#include #include #include #include -#include -#include -#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 *) ¬found, + 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 , 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 *) ¬found, + 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 +#include #include #include #include @@ -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 -# enable-cache # debug-level +# +# enable-cache # positive-time-to-live