diff options
Diffstat (limited to 'nscd')
-rw-r--r-- | nscd/Makefile | 5 | ||||
-rw-r--r-- | nscd/cache.c | 247 | ||||
-rw-r--r-- | nscd/connections.c | 771 | ||||
-rw-r--r-- | nscd/dbg_log.c | 6 | ||||
-rw-r--r-- | nscd/dbg_log.h | 6 | ||||
-rw-r--r-- | nscd/gethstbyad_r.c | 31 | ||||
-rw-r--r-- | nscd/gethstbynm2_r.c | 39 | ||||
-rw-r--r-- | nscd/grpcache.c | 698 | ||||
-rw-r--r-- | nscd/hstcache.c | 405 | ||||
-rw-r--r-- | nscd/nscd.c | 245 | ||||
-rw-r--r-- | nscd/nscd.conf | 15 | ||||
-rw-r--r-- | nscd/nscd.h | 240 | ||||
-rw-r--r-- | nscd/nscd_conf.c | 94 | ||||
-rw-r--r-- | nscd/nscd_getgr_r.c | 186 | ||||
-rw-r--r-- | nscd/nscd_gethst_r.c | 302 | ||||
-rw-r--r-- | nscd/nscd_getpw_r.c | 86 | ||||
-rw-r--r-- | nscd/nscd_proto.h | 14 | ||||
-rw-r--r-- | nscd/nscd_stat.c | 181 | ||||
-rw-r--r-- | nscd/pwdcache.c | 713 |
19 files changed, 2208 insertions, 2076 deletions
diff --git a/nscd/Makefile b/nscd/Makefile index f00b2f0..ed5c0ba 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 0000000..e957a57 --- /dev/null +++ b/nscd/cache.c @@ -0,0 +1,247 @@ +/* Copyright (c) 1998 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include <atomicity.h> +#include <errno.h> +#include <error.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <rpcsvc/nis.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/uio.h> + +#include "nscd.h" +#include "dbg_log.h" + +/* Search the cache for a matching entry and return it when found. If + this fails search the negative cache and return (void *) -1 if this + search was successful. Otherwise return NULL. + + This function must be called with the read-lock held. */ +struct hashentry * +cache_search (int type, void *key, size_t len, struct database *table) +{ + unsigned long int hash = __nis_hash (key, len) % table->module; + struct hashentry *work; + + work = table->array[hash]; + + while (work != NULL) + { + if (type == work->type + && len == work->len && memcmp (key, work->key, len) == 0) + { + /* We found the entry. Increment the appropriate counter. */ + if (work->data == (void *) -1) + ++table->neghit; + else + ++table->poshit; + + return work; + } + + work = work->next; + } + + return NULL; +} + +/* Add a new entry to the cache. The return value is zero if the function + call was successful. + + This function must be called with the read-lock held. + + We modify the table but we nevertheless only acquire a read-lock. + This is ok since we use operations which would be safe even without + locking, given that the `prune_cache' function never runs. Using + the readlock reduces the chance of conflicts. */ +void +cache_add (int type, void *key, size_t len, const void *packet, size_t total, + void *data, int last, time_t t, struct database *table) +{ + unsigned long int hash = __nis_hash (key, len) % table->module; + struct hashentry *newp; + + newp = malloc (sizeof (struct hashentry)); + if (newp == NULL) + error (EXIT_FAILURE, errno, _("while allocating hash table entry")); + + newp->type = type; + newp->len = len; + newp->key = key; + newp->data = data; + newp->timeout = t; + newp->packet = packet; + newp->total = total; + + newp->last = last; + + /* Put the new entry in the first position. */ + do + newp->next = table->array[hash]; + while (! compare_and_swap ((volatile long int *) &table->array[hash], + (long int) newp->next, (long int) newp)); + + /* Update the statistics. */ + if (data == (void *) -1) + ++table->negmiss; + else if (last) + ++table->posmiss; +} + +/* Walk through the table and remove all entries which lifetime ended. + + We have a problem here. To actually remove the entries we must get + the write-lock. But since we want to keep the time we have the + lock as short as possible we cannot simply acquire the lock when we + start looking for timedout entries. + + Therefore we do it in two stages: first we look for entries which + must be invalidated and remember them. Then we get the lock and + actually remove them. This is complicated by the way we have to + free the data structures since some hash table entries share the same + data. + + This function must be called with the write-lock held. */ +void +prune_cache (struct database *table, time_t now) +{ + size_t cnt = table->module; + int mark[cnt]; + int anything = 0; + size_t first = cnt + 1; + size_t last = 0; + + /* If we check for the modification of the underlying file we invalidate + the entries also in this case. */ + if (table->check_file) + { + struct stat st; + + if (stat (table->filename, &st) < 0) + { + char buf[128]; + /* We cannot stat() the file, disable file checking. */ + dbg_log (_("cannot stat() file `%s': %s"), + table->filename, strerror_r (errno, buf, sizeof (buf))); + table->check_file = 0; + } + else + { + if (st.st_mtime != table->file_mtime) + /* The file changed. Invalidate all entries. */ + now = LONG_MAX; + } + } + + /* We run through the table and find values which are not valid anymore. + + Note that for the initial step, finding the entries to be removed, + we don't need to get any lock. It is at all timed assured that the + linked lists are set up correctly and that no second thread prunes + the cache. */ + do + { + struct hashentry *runp = table->array[--cnt]; + + mark[cnt] = 0; + + while (runp != NULL) + { + if (runp->timeout < now) + { + ++mark[cnt]; + anything = 1; + first = MIN (first, cnt); + last = MAX (last, cnt); + } + runp = runp->next; + } + } + while (cnt > 0); + + if (anything) + { + struct hashentry *head = NULL; + + /* Now we have to get the write lock since we are about to modify + the table. */ + pthread_rwlock_wrlock (&table->lock); + + while (first <= last) + { + if (mark[first] > 0) + { + struct hashentry *runp; + + while (table->array[first]->timeout < now) + { + table->array[first]->dellist = head; + head = table->array[first]; + table->array[first] = head->next; + if (--mark[first] == 0) + break; + } + + runp = table->array[first]; + while (mark[first] > 0) + { + if (runp->next->timeout < now) + { + runp->next->dellist = head; + head = runp->next; + runp->next = head->next; + --mark[first]; + } + else + runp = runp->next; + } + } + ++first; + } + + /* It's all done. */ + pthread_rwlock_unlock (&table->lock); + + /* And another run to free the data. */ + do + { + struct hashentry *old = head; + + if (debug_level > 0) + dbg_log ("remove %s entry \"%s\"", + serv2str[old->type], + old->last + ? old->key : old->data == (void *) -1 ? old->key : "???"); + + /* Free the data structures. */ + if (old->data == (void *) -1) + free (old->key); + else if (old->last) + free (old->data); + + head = head->dellist; + + free (old); + } + while (head != NULL); + } +} diff --git a/nscd/connections.c b/nscd/connections.c index 4cf397d..8e6839a 100644 --- a/nscd/connections.c +++ b/nscd/connections.c @@ -1,6 +1,7 @@ -/* Copyright (c) 1998 Free Software Foundation, Inc. +/* Inner loops of cache daemon. + Copyright (C) 1998 Free Software Foundation, Inc. This file is part of the GNU C Library. - Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1998. + Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as @@ -15,551 +16,407 @@ You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ + Boston, MA 02111-1307, USA. */ -#include <errno.h> +#include <assert.h> #include <error.h> -#include <fcntl.h> -#include <libintl.h> -#include <locale.h> +#include <errno.h> #include <pthread.h> -#include <pwd.h> -#include <stdio.h> #include <stdlib.h> #include <unistd.h> +#include <sys/param.h> #include <sys/poll.h> #include <sys/socket.h> #include <sys/stat.h> -#include <sys/types.h> -#include <sys/uio.h> #include <sys/un.h> #include "nscd.h" #include "dbg_log.h" -/* Socket 0 in the array is named and exported into the file namespace - as a connection point for clients. There's a one to one - correspondence between sock[i] and read_polls[i]. */ -static int sock[MAX_NUM_CONNECTIONS]; -static int socks_active; -static struct pollfd read_polls[MAX_NUM_CONNECTIONS]; -static pthread_mutex_t sock_lock = PTHREAD_MUTEX_INITIALIZER; - -/* Cleanup. */ -void -close_sockets (void) +/* Mapping of request type to database. */ +static const dbtype serv2db[LASTDBREQ + 1] = { - int i; - - if (debug_flag) - dbg_log (_("close_sockets called")); - - pthread_mutex_lock (&sock_lock); - - /* Close sockets. */ - for (i = 0; i < MAX_NUM_CONNECTIONS; ++i) - if (sock[i] != 0) - { - if (close (sock[i])) - dbg_log (_("socket [%d|%d] close: %s"), i, sock[i], strerror (errno)); - - sock[i] = 0; - read_polls[i].fd = -1; - --socks_active; - } - - pthread_mutex_unlock (&sock_lock); -} - -void -close_socket (int conn) + [GETPWBYNAME] = pwddb, + [GETPWBYUID] = pwddb, + [GETGRBYNAME] = grpdb, + [GETGRBYGID] = grpdb, + [GETHOSTBYNAME] = hstdb, + [GETHOSTBYNAMEv6] = hstdb, + [GETHOSTBYADDR] = hstdb, + [GETHOSTBYADDRv6] = hstdb, +}; + +/* Map request type to a string. */ +const char *serv2str[LASTREQ] = { - if (debug_flag > 2) - dbg_log (_("close socket (%d|%d)"), conn, sock[conn]); + [GETPWBYNAME] = "GETPWBYNAME", + [GETPWBYUID] = "GETPWBYUID", + [GETGRBYNAME] = "GETGRBYNAME", + [GETGRBYGID] = "GETGRBYGID", + [GETHOSTBYNAME] = "GETHOSTBYNAME", + [GETHOSTBYNAMEv6] = "GETHOSTBYNAMEv6", + [GETHOSTBYADDR] = "GETHOSTBYADDR", + [GETHOSTBYADDRv6] = "GETHOSTBYADDRv6", + [SHUTDOWN] = "SHUTDOWN", + [GETSTAT] = "GETSTAT" +}; + +/* The control data structures for the services. */ +static struct database dbs[lastdb] = +{ + [pwddb] = { + lock: PTHREAD_RWLOCK_INITIALIZER, + enabled: 1, + check_file: 1, + filename: "/etc/passwd", + module: 211, + disabled_iov: &pwd_iov_disabled + }, + [grpdb] = { + lock: PTHREAD_RWLOCK_INITIALIZER, + enabled: 1, + check_file: 1, + filename: "/etc/group", + module: 211, + disabled_iov: &grp_iov_disabled + }, + [hstdb] = { + lock: PTHREAD_RWLOCK_INITIALIZER, + enabled: 1, + check_file: 1, + filename: "/etc/hosts", + module: 211, + disabled_iov: &hst_iov_disabled + } +}; - pthread_mutex_lock (&sock_lock); +/* Number of threads to use. */ +int nthreads = -1; - close (sock[conn]); - sock[conn] = 0; - read_polls[conn].fd = -1; - --socks_active; +/* Socket for incoming connections. */ +static int sock; - pthread_mutex_unlock (&sock_lock); -} -/* Local routine, assigns a socket to a new connection request. */ -static void -handle_new_connection (void) +/* Initialize database information structures. */ +void +nscd_init (const char *conffile) { - int i; - - if (debug_flag > 2) - dbg_log (_("handle_new_connection")); - - pthread_mutex_lock (&sock_lock); + struct sockaddr_un sock_addr; + size_t cnt; - if (socks_active < MAX_NUM_CONNECTIONS) - /* Find a free socket entry to use. */ - for (i = 1; i < MAX_NUM_CONNECTIONS; ++i) - { - if (sock[i] == 0) - { - if ((sock[i] = accept (sock[0], NULL, NULL)) < 0) - { - dbg_log (_("socket accept: %s"), strerror (errno)); - return; - } - ++socks_active; - read_polls[i].fd = sock[i]; - read_polls[i].events = POLLRDNORM; - if (debug_flag > 2) - dbg_log (_("handle_new_connection used socket %d|%d"), i, - sock[i]); - break; - } - } - else + /* Read the configuration file. */ + if (nscd_parse_file (conffile, dbs) != 0) { - int black_widow_sock; - dbg_log (_("Supported number of simultaneous connections exceeded")); - dbg_log (_("Ignoring client connect request")); - /* There has to be a better way to ignore a connection request,.. - when I get my hands on a sockets wiz I'll modify this. */ - black_widow_sock = accept (sock[0], NULL, NULL); - close (black_widow_sock); + /* We couldn't read the configuration file. Disable all services + by shutting down the srever. */ + dbg_log (_("cannot read configuration file; this is fatal")); + exit (1); } - pthread_mutex_unlock (&sock_lock); -} - -/* Local routine, reads a request off a socket indicated by read_polls. */ -static int -handle_new_request (int **connp, request_header **reqp, char **key) -{ - ssize_t nbytes; - int i, found = 0; + if (nthreads == -1) + /* No configuration for this value, assume a default. */ + nthreads = 2 * lastdb; - if (debug_flag) - dbg_log ("handle_new_request"); - - /* Find the descriptor. */ - for (i = 1; i < MAX_NUM_CONNECTIONS; ++i) { - if (read_polls[i].fd >= 0 - && read_polls[i].revents & (POLLRDNORM|POLLERR|POLLNVAL)) - { - found = i; - break; - } - if (read_polls[i].fd >= 0 && (read_polls[i].revents & POLLHUP)) + for (cnt = 0; cnt < lastdb; ++cnt) + if (dbs[cnt].enabled) { - /* Don't close the socket, we still need to send data. Just - stop polling for more data now. */ - read_polls[i].fd = -1; - } - } + pthread_rwlock_init (&dbs[cnt].lock, NULL); - if (found == 0) - { - dbg_log (_("No sockets with data found !")); - return -1; - } - - if (debug_flag > 2) - dbg_log (_("handle_new_request uses socket %d"), i); + dbs[cnt].array = (struct hashentry **) + calloc (dbs[cnt].module, sizeof (struct hashentry *)); + if (dbs[cnt].array == NULL) + error (EXIT_FAILURE, errno, "while allocating cache"); - /* Read from it. */ - nbytes = read (sock[i], *reqp, sizeof (request_header)); - if (nbytes != sizeof (request_header)) - { - /* Handle non-data read cases. */ - if (nbytes == 0) - { - /* Close socket down. */ - if (debug_flag > 2) - dbg_log (_("Real close socket %d|%d"), i, sock[i]); - - pthread_mutex_lock (&sock_lock); - read_polls[i].fd = -1; - close (sock[i]); - sock[i] = 0; - --socks_active; - pthread_mutex_unlock (&sock_lock); - } - else - if (nbytes < 0) + if (dbs[cnt].check_file) { - dbg_log (_("Read(%d|%d) error on get request: %s"), - i, sock[i], strerror (errno)); - exit (1); - } - else - dbg_log (_("Read, data < request buf size, ignoring data")); + /* We need the modification date of the file. */ + struct stat st; - return -1; - } - else - { - *key = malloc ((*reqp)->key_len + 1); - /* Read the key from it */ - nbytes = read (sock[i], *key, (*reqp)->key_len); - if (nbytes != (*reqp)->key_len) - { - /* Handle non-data read cases. */ - if (nbytes == 0) - { - /* Close socket down. */ - if (debug_flag > 2) - dbg_log (_("Real close socket %d|%d"), i, sock[i]); - - pthread_mutex_lock (&sock_lock); - read_polls[i].fd = -1; - close (sock[i]); - sock[i] = 0; - --socks_active; - pthread_mutex_unlock (&sock_lock); - } - else - if (nbytes < 0) + if (stat (dbs[cnt].filename, &st) < 0) { - perror (_("Read() error on get request")); - return 0; + char buf[128]; + /* We cannot stat() the file, disable file checking. */ + dbg_log (_("cannot stat() file `%s': %s"), + dbs[cnt].filename, + strerror_r (errno, buf, sizeof (buf))); + dbs[cnt].check_file = 0; } else - fputs (_("Read, data < request buf size, ignoring data"), - stderr); - - free (*key); - return -1; - } - else - { - /* Ok, have a live one, A real data req buf has been obtained. */ - (*key)[(*reqp)->key_len] = '\0'; - **connp = i; - return 0; - } - } -} - -void -get_request (int *conn, request_header *req, char **key) -{ - int done = 0; - int nr; - - if (debug_flag) - dbg_log ("get_request"); - - /* loop, processing new connection requests until a client buffer - is read in on an existing connection. */ - while (!done) - { - /* Poll active connections. */ - nr = poll (read_polls, MAX_NUM_CONNECTIONS, -1); - if (nr <= 0) - { - perror (_("Poll new reads")); - exit (1); - } - if (read_polls[0].revents & (POLLRDNORM|POLLERR|POLLHUP|POLLNVAL)) - /* Handle the case of a new connection request on the named socket. */ - handle_new_connection (); - else - { - /* Read data from client specific descriptor. */ - if (handle_new_request (&conn, &req, key) == 0) - done = 1; - } - } /* While not_done. */ -} - -void -init_sockets (void) -{ - struct sockaddr_un sock_addr; - int i; - - /* Initialize the connections db. */ - socks_active = 0; - - /* Initialize the poll array. */ - for (i = 0; i < MAX_NUM_CONNECTIONS; i++) - read_polls[i].fd = -1; + dbs[cnt].file_mtime = st.st_mtime; + } + } /* Create the socket. */ - sock[0] = socket (AF_UNIX, SOCK_STREAM, 0); - if (sock[0] < 0) + sock = socket (AF_UNIX, SOCK_STREAM, 0); + if (sock < 0) { - perror (_("cannot create socket")); + dbg_log (_("cannot open socket: %s"), strerror (errno)); exit (1); } /* Bind a name to the socket. */ sock_addr.sun_family = AF_UNIX; strcpy (sock_addr.sun_path, _PATH_NSCDSOCKET); - if (bind (sock[0], (struct sockaddr *) &sock_addr, sizeof (sock_addr)) < 0) + if (bind (sock, (struct sockaddr *) &sock_addr, sizeof (sock_addr)) < 0) { dbg_log ("%s: %s", _PATH_NSCDSOCKET, strerror (errno)); exit (1); } + /* Set permissions for the socket. */ chmod (_PATH_NSCDSOCKET, 0666); /* Set the socket up to accept connections. */ - if (listen (sock[0], MAX_NUM_CONNECTIONS) < 0) + if (listen (sock, SOMAXCONN) < 0) { - perror (_("cannot enable socket to accept connections")); + dbg_log (_("cannot enable socket to accept connections: %s"), + strerror (errno)); exit (1); } - - /* Add the socket to the server's set of active sockets. */ - read_polls[0].fd = sock[0]; - read_polls[0].events = POLLRDNORM; - ++socks_active; } + +/* Close the connections. */ void -pw_send_answer (int conn, struct passwd *pwd) +close_sockets (void) { - struct iovec vec[6]; - pw_response_header resp; - size_t total_len; - int nblocks; + close (sock); +} - resp.version = NSCD_VERSION; - if (pwd != NULL) - { - resp.found = 1; - resp.pw_name_len = strlen (pwd->pw_name); - resp.pw_passwd_len = strlen (pwd->pw_passwd); - resp.pw_uid = pwd->pw_uid; - resp.pw_gid = pwd->pw_gid; - resp.pw_gecos_len = strlen (pwd->pw_gecos); - resp.pw_dir_len = strlen (pwd->pw_dir); - resp.pw_shell_len = strlen (pwd->pw_shell); - } - else - { - resp.found = 0; - resp.pw_name_len = 0; - resp.pw_passwd_len = 0; - resp.pw_uid = -1; - resp.pw_gid = -1; - resp.pw_gecos_len = 0; - resp.pw_dir_len = 0; - resp.pw_shell_len = 0; - } - if (sock[conn] == 0) + +/* Handle new request. */ +static void +handle_request (int fd, request_header *req, void *key) +{ + if (debug_level > 0) + dbg_log (_("handle_requests: request received (Version = %d)"), + req->version); + + if (req->version != NSCD_VERSION) { - dbg_log (_("bad connection id on send response [%d|%d]"), - conn, sock[conn]); + dbg_log (_("\ +cannot handle old request version %d; current version is %d"), + req->version, NSCD_VERSION); return; } - /* Add response header. */ - vec[0].iov_base = &resp; - vec[0].iov_len = sizeof (pw_response_header); - total_len = sizeof (pw_response_header); - nblocks = 1; - - if (resp.found) + if (req->type >= GETPWBYNAME && req->type <= LASTDBREQ) { - /* Add pw_name. */ - vec[1].iov_base = pwd->pw_name; - vec[1].iov_len = resp.pw_name_len; - total_len += resp.pw_name_len; - /* Add pw_passwd. */ - vec[2].iov_base = pwd->pw_passwd; - vec[2].iov_len = resp.pw_passwd_len; - total_len += resp.pw_passwd_len; - /* Add pw_gecos. */ - vec[3].iov_base = pwd->pw_gecos; - vec[3].iov_len = resp.pw_gecos_len; - total_len += resp.pw_gecos_len; - /* Add pw_dir. */ - vec[4].iov_base = pwd->pw_dir; - vec[4].iov_len = resp.pw_dir_len; - total_len += resp.pw_dir_len; - /* Add pw_shell. */ - vec[5].iov_base = pwd->pw_shell; - vec[5].iov_len = resp.pw_shell_len; - total_len += resp.pw_shell_len; - - nblocks = 6; - } + struct hashentry *cached; + struct database *db = &dbs[serv2db[req->type]]; - /* Send all the data. */ - if (writev (sock[conn], vec, nblocks) != total_len) - dbg_log (_("write incomplete on send passwd answer: %s"), - strerror (errno)); -} + if (debug_level > 0) + dbg_log ("\t%s (%s)", serv2str[req->type], key); -void -pw_send_disabled (int conn) -{ - pw_response_header resp; - - resp.version = NSCD_VERSION; - resp.found = -1; - resp.pw_name_len = 0; - resp.pw_passwd_len = 0; - resp.pw_uid = -1; - resp.pw_gid = -1; - resp.pw_gecos_len = 0; - resp.pw_dir_len = 0; - resp.pw_shell_len = 0; - - if (sock[conn] == 0) - { - dbg_log (_("bad connection id on send response [%d|%d]"), - conn, sock[conn]); - return; - } + /* Is this service enabled? */ + if (!db->enabled) + { + /* No sent the prepared record. */ + if (TEMP_FAILURE_RETRY (write (fd, db->disabled_iov->iov_base, + db->disabled_iov->iov_len)) + != db->disabled_iov->iov_len) + { + /* We have problems sending the result. */ + char buf[256]; + dbg_log (_("cannot write result: %s"), + strerror_r (errno, buf, sizeof (buf))); + } - /* Send response header. */ - if (write (sock[conn], &resp, sizeof (pw_response_header)) - != sizeof (pw_response_header)) - dbg_log (_("write incomplete on send response: %s"), strerror (errno)); -} + return; + } -void -gr_send_answer (int conn, struct group *grp) -{ - struct iovec *vec; - size_t *len; - gr_response_header resp; - size_t total_len, sum; - int nblocks; - size_t maxiov; - - resp.version = NSCD_VERSION; - if (grp != NULL) - { - resp.found = 1; - resp.gr_name_len = strlen (grp->gr_name); - resp.gr_passwd_len = strlen (grp->gr_passwd); - resp.gr_gid = grp->gr_gid; - resp.gr_mem_len = 0; - while (grp->gr_mem[resp.gr_mem_len]) - ++resp.gr_mem_len; + /* Be sure we can read the data. */ + pthread_rwlock_rdlock (&db->lock); + + /* See whether we can handle it from the cache. */ + cached = (struct hashentry *) cache_search (req->type, key, req->key_len, + db); + if (cached != NULL) + { + /* Hurray it's in the cache. */ + if (TEMP_FAILURE_RETRY (write (fd, cached->packet, cached->total)) + != cached->total) + { + /* We have problems sending the result. */ + char buf[256]; + dbg_log (_("cannot write result: %s"), + strerror_r (errno, buf, sizeof (buf))); + } + + pthread_rwlock_unlock (&db->lock); + + return; + } + + pthread_rwlock_unlock (&db->lock); } else + if (debug_level > 0) + dbg_log ("\t%s", serv2str[req->type]); + + /* Handle the request. */ + switch (req->type) { - resp.found = 0; - resp.gr_name_len = 0; - resp.gr_passwd_len = 0; - resp.gr_gid = -1; - resp.gr_mem_len = 0; - } - if (sock[conn] == 0) - { - dbg_log (_("bad connection id on send response [%d|%d]"), - conn, sock[conn]); - return; + case GETPWBYNAME: + addpwbyname (&dbs[serv2db[req->type]], fd, req, key); + break; + + case GETPWBYUID: + addpwbyuid (&dbs[serv2db[req->type]], fd, req, key); + break; + + case GETGRBYNAME: + addgrbyname (&dbs[serv2db[req->type]], fd, req, key); + break; + + case GETGRBYGID: + addgrbygid (&dbs[serv2db[req->type]], fd, req, key); + break; + + case GETHOSTBYNAME: + addhstbyname (&dbs[serv2db[req->type]], fd, req, key); + break; + + case GETHOSTBYNAMEv6: + addhstbynamev6 (&dbs[serv2db[req->type]], fd, req, key); + break; + + case GETHOSTBYADDR: + addhstbyaddr (&dbs[serv2db[req->type]], fd, req, key); + break; + + case GETHOSTBYADDRv6: + addhstbyaddrv6 (&dbs[serv2db[req->type]], fd, req, key); + break; + + case GETSTAT: + send_stats (fd, dbs); + break; + + case SHUTDOWN: + termination_handler (0); + break; + + default: + abort (); } +} + - /* We have no fixed number of records so allocate the IOV here. */ - vec = alloca ((3 + 1 + resp.gr_mem_len) * sizeof (struct iovec)); - len = alloca (resp.gr_mem_len * sizeof (size_t)); +/* This is the main loop. It is replicated in different threads but the + `poll' call makes sure only one thread handles an incoming connection. */ +static void * +__attribute__ ((__noreturn__)) +nscd_run (void *p) +{ + int my_number = (int) p; + struct pollfd conn; + int run_prune = my_number < lastdb && dbs[my_number].enabled; + time_t now = time (NULL); + time_t next_prune = now + 15; + int timeout = run_prune ? 1000 * (next_prune - now) : -1; - /* Add response header. */ - vec[0].iov_base = &resp; - vec[0].iov_len = sizeof (gr_response_header); - total_len = sizeof (gr_response_header); - nblocks = 1; + conn.fd = sock; + conn.events = POLLRDNORM; - if (resp.found) + while (1) { - unsigned int l = 0; - - /* Add gr_name. */ - vec[1].iov_base = grp->gr_name; - vec[1].iov_len = resp.gr_name_len; - total_len += resp.gr_name_len; - /* Add gr_passwd. */ - vec[2].iov_base = grp->gr_passwd; - vec[2].iov_len = resp.gr_passwd_len; - total_len += resp.gr_passwd_len; - nblocks = 3; - - if (grp->gr_mem[l]) + int nr = poll (&conn, 1, timeout); + + if (nr == 0) + { + /* The `poll' call timed out. It's time to clean up the cache. */ + assert (my_number < lastdb); + now = time (NULL); + prune_cache (&dbs[my_number], now); + next_prune = now + 15; + timeout = 1000 * (next_prune - now); + continue; + } + + /* We have a new incoming connection. */ + if (conn.revents & (POLLRDNORM|POLLERR|POLLHUP|POLLNVAL)) { - vec[3].iov_base = len; - vec[3].iov_len = resp.gr_mem_len * sizeof (size_t); - total_len += resp.gr_mem_len * sizeof (size_t); - nblocks = 4; + /* Accept the connection. */ + int fd = accept (conn.fd, NULL, NULL); + request_header req; + char buf[256]; - do + if (fd < 0) { - len[l] = strlen (grp->gr_mem[l]); + dbg_log (_("while accepting connection: %s"), + strerror_r (errno, buf, sizeof (buf))); + continue; + } - vec[nblocks].iov_base = grp->gr_mem[l]; - vec[nblocks].iov_len = len[l]; - total_len += len[l]; + /* Now read the request. */ + if (TEMP_FAILURE_RETRY (read (fd, &req, sizeof (req))) + != sizeof (req)) + { + dbg_log (_("short read while reading request: %s"), + strerror_r (errno, buf, sizeof (buf))); + close (fd); + continue; + } - ++nblocks; + /* It should not be possible to crash the nscd with a silly + request (i.e., a terribly large key. We limit the size + to 1kb. */ + if (req.key_len < 0 || req.key_len > 1024) + { + dbg_log (_("key length in request to long: %Zd"), req.key_len); + close (fd); + continue; + } + else + { + /* Get the key. */ + char keybuf[req.key_len]; + + if (TEMP_FAILURE_RETRY (read (fd, keybuf, req.key_len)) + != req.key_len) + { + dbg_log (_("short read while reading request key: %s"), + strerror_r (errno, buf, sizeof (buf))); + close (fd); + continue; + } + + /* Phew, we got all the data, now process it. */ + handle_request (fd, &req, keybuf); + + /* We are done. */ + close (fd); } - while (grp->gr_mem[++l]); } - } -#ifdef UIO_MAXIOV - maxiov = UIO_MAXIOV; -#else - maxiov = sysconf (_SC_UIO_MAXIOV); -#endif - - /* Send all the data. */ - sum = 0; - while (nblocks > maxiov) - { - sum += writev (sock[conn], vec, maxiov); - vec += maxiov; - nblocks -= maxiov; + if (run_prune) + { + now = time (NULL); + timeout = now < next_prune ? 1000 * (next_prune - now) : 0; + } } - if (sum + writev (sock[conn], vec, nblocks) != total_len) - dbg_log (_("write incomplete on send group answer: %s"), - strerror (errno)); } + +/* Start all the threads we want. The initial process is thread no. 1. */ void -gr_send_disabled (int conn) +start_threads (void) { - gr_response_header resp; + int i; + pthread_attr_t attr; + pthread_t th; - resp.version = NSCD_VERSION; - resp.found = -1; - resp.gr_name_len = 0; - resp.gr_passwd_len = 0; - resp.gr_gid = -1; - resp.gr_mem_len = 0; + pthread_attr_init (&attr); + pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); - if (sock[conn] == 0) - { - dbg_log (_("bad connection id on send gr_disabled response [%d|%d]"), - conn, sock[conn]); - return; - } + /* We allow less than LASTDB threads only for debugging. */ + if (debug_level == 0) + nthreads = MAX (nthreads, lastdb); - /* Send response header. */ - if (write (sock[conn], &resp, sizeof (gr_response_header)) - != sizeof (gr_response_header)) - dbg_log (_("write incomplete on send gr_disabled response: %s"), - strerror (errno)); -} + for (i = 1; i < nthreads; ++i) + pthread_create (&th, &attr, nscd_run, (void *) i); -void -stat_send (int conn, stat_response_header *resp) -{ - if (sock[conn] == 0) - { - dbg_log (_("bad connection id on send stat response [%d|%d]"), - conn, sock[conn]); - return; - } + pthread_attr_destroy (&attr); - /* send response header. */ - if (write (sock[conn], resp, sizeof (stat_response_header)) - != sizeof (stat_response_header)) - dbg_log (_("write incomplete on send stat response: %s"), - strerror (errno)); + nscd_run ((void *) 0); } diff --git a/nscd/dbg_log.c b/nscd/dbg_log.c index b2b8b3e..21997cd 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 c3d1dc4..5aeff89 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 0000000..c22044a --- /dev/null +++ b/nscd/gethstbyad_r.c @@ -0,0 +1,31 @@ +/* Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include <netdb.h> + + +#define LOOKUP_TYPE struct hostent +#define FUNCTION_NAME gethostbyaddr +#define DATABASE_NAME hosts +#define ADD_PARAMS const char *addr, int len, int type +#define ADD_VARIABLES addr, len, type +#define NEED_H_ERRNO 1 +#define NEED__RES 1 + +#include "../nss/getXXbyYY_r.c" diff --git a/nscd/gethstbynm2_r.c b/nscd/gethstbynm2_r.c new file mode 100644 index 0000000..b9b3a71 --- /dev/null +++ b/nscd/gethstbynm2_r.c @@ -0,0 +1,39 @@ +/* Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include <ctype.h> +#include <errno.h> +#include <netdb.h> +#include <string.h> +#include <arpa/inet.h> +#include <netinet/in.h> + + +#define LOOKUP_TYPE struct hostent +#define FUNCTION_NAME gethostbyname2 +#define DATABASE_NAME hosts +#define ADD_PARAMS const char *name, int af +#define ADD_VARIABLES name, af +#define NEED_H_ERRNO 1 + +#define HANDLE_DIGITS_DOTS 1 +#define HAVE_LOOKUP_BUFFER 1 +#define HAVE_AF 1 + +#include "../nss/getXXbyYY_r.c" diff --git a/nscd/grpcache.c b/nscd/grpcache.c index f34780a..1645d71 100644 --- a/nscd/grpcache.c +++ b/nscd/grpcache.c @@ -1,6 +1,7 @@ -/* Copyright (c) 1998 Free Software Foundation, Inc. +/* Cache handling for group lookup. + Copyright (C) 1998 Free Software Foundation, Inc. This file is part of the GNU C Library. - Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1998. + Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as @@ -15,605 +16,234 @@ You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ + Boston, MA 02111-1307, USA. */ +#include <assert.h> #include <errno.h> +#include <error.h> #include <grp.h> -#include <pthread.h> +#include <stddef.h> +#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> -#include <rpcsvc/nis.h> -#include <sys/types.h> -#include "dbg_log.h" #include "nscd.h" +#include "dbg_log.h" -static unsigned long modulo = 211; -static unsigned long postimeout = 3600; -static unsigned long negtimeout = 60; - -static unsigned long poshit = 0; -static unsigned long posmiss = 0; -static unsigned long neghit = 0; -static unsigned long negmiss = 0; - -struct grphash +/* This is the standard reply in case the service is disabled. */ +static const gr_response_header disabled = { - time_t create; - struct grphash *next; - struct group *grp; + version: NSCD_VERSION, + found: -1, + gr_name_len: 0, + gr_passwd_len: 0, + gr_gid: -1, + gr_mem_cnt: 0, }; -typedef struct grphash grphash; -struct gidhash +/* This is the struct describing how to write this record. */ +const struct iovec grp_iov_disabled = { - struct gidhash *next; - struct group *grptr; + iov_base: (void *) &disabled, + iov_len: sizeof (disabled) }; -typedef struct gidhash gidhash; -struct neghash + +/* This is the standard reply in case we haven't found the dataset. */ +static const gr_response_header notfound = { - time_t create; - struct neghash *next; - char *key; + version: NSCD_VERSION, + found: 0, + gr_name_len: 0, + gr_passwd_len: 0, + gr_gid: -1, + gr_mem_cnt: 0, }; -typedef struct neghash neghash; - -static grphash *grptbl; -static gidhash *gidtbl; -static neghash *negtbl; - -static pthread_rwlock_t grplock = PTHREAD_RWLOCK_INITIALIZER; -static pthread_rwlock_t neglock = PTHREAD_RWLOCK_INITIALIZER; -static void *grptable_update (void *); -static void *negtable_update (void *); - -void -get_gr_stat (stat_response_header *stat) +/* This is the struct describing how to write this record. */ +static const struct iovec iov_notfound = { - stat->gr_poshit = poshit; - stat->gr_posmiss = posmiss; - stat->gr_neghit = neghit; - stat->gr_negmiss = negmiss; - stat->gr_size = modulo; - stat->gr_posttl = postimeout; - stat->gr_negttl = negtimeout; -} + iov_base: (void *) ¬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 0000000..4e3af9c --- /dev/null +++ b/nscd/hstcache.c @@ -0,0 +1,405 @@ +/* Cache handling for host lookup. + Copyright (C) 1998 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include <assert.h> +#include <errno.h> +#include <error.h> +#include <netdb.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <arpa/inet.h> + +#include "nscd.h" +#include "dbg_log.h" + +/* Get implementation for some internal functions. */ +#include "../resolv/mapv4v6addr.h" + + +/* This is the standard reply in case the service is disabled. */ +static const hst_response_header disabled = +{ + version: NSCD_VERSION, + found: -1, + h_name_len: 0, + h_aliases_cnt: 0, + h_addrtype: -1, + h_length: -1, + h_addr_list_cnt: 0, + error: NETDB_INTERNAL +}; + +/* This is the struct describing how to write this record. */ +const struct iovec hst_iov_disabled = +{ + iov_base: (void *) &disabled, + iov_len: sizeof (disabled) +}; + + +/* This is the standard reply in case we haven't found the dataset. */ +static const hst_response_header notfound = +{ + version: NSCD_VERSION, + found: 0, + h_name_len: 0, + h_aliases_cnt: 0, + h_addrtype: -1, + h_length: -1, + h_addr_list_cnt: 0, + error: HOST_NOT_FOUND +}; + +/* This is the struct describing how to write this record. */ +static const struct iovec iov_notfound = +{ + iov_base: (void *) ¬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 9ddbb5f..d6d2c64 100644 --- a/nscd/nscd.c +++ b/nscd/nscd.c @@ -17,9 +17,10 @@ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* nscd - Name Service Cache Daemon. Caches passwd and group. */ +/* nscd - Name Service Cache Daemon. Caches passwd, group, and hosts. */ #include <argp.h> +#include <assert.h> #include <errno.h> #include <error.h> #include <libintl.h> @@ -61,12 +62,10 @@ int do_shutdown = 0; int disabled_passwd = 0; int disabled_group = 0; int go_background = 1; -const char *conffile = _PATH_NSCDCONF; +static const char *conffile = _PATH_NSCDCONF; -static void termination_handler (int signum); static int check_pid (const char *file); static int write_pid (const char *file); -static void handle_requests (void); /* Name and version of program. */ static void print_version (FILE *stream, struct argp_state *state); @@ -79,6 +78,7 @@ static const struct argp_option options[] = N_("Read configuration data from NAME") }, { "debug", 'd', NULL, 0, N_("Do not fork and display messages on the current tty") }, + { "nthreads", 't', N_("NUMBER"), 0, N_("Start NUMBER threads") }, { "shutdown", 'K', NULL, 0, N_("Shut the server down") }, { "statistic", 'g', NULL, 0, N_("Print current configuration statistic") }, { NULL, 0, NULL, 0, NULL } @@ -118,10 +118,7 @@ main (int argc, char **argv) /* Check if we are already running. */ if (check_pid (_PATH_NSCDPID)) - { - fputs (_("already running"), stderr); - exit (EXIT_FAILURE); - } + error (EXIT_FAILURE, 0, _("already running")); /* Behave like a daemon. */ if (go_background) @@ -144,7 +141,7 @@ main (int argc, char **argv) if (write_pid (_PATH_NSCDPID) < 0) dbg_log ("%s: %s", _PATH_NSCDPID, strerror (errno)); - /* Ignore job control signals */ + /* Ignore job control signals. */ signal (SIGTTOU, SIG_IGN); signal (SIGTTIN, SIG_IGN); signal (SIGTSTP, SIG_IGN); @@ -155,21 +152,14 @@ main (int argc, char **argv) signal (SIGTERM, termination_handler); signal (SIGPIPE, SIG_IGN); - /* Cleanup files created by a previous `bind' */ + /* Cleanup files created by a previous `bind'. */ unlink (_PATH_NSCDSOCKET); - nscd_parse_file (conffile); + /* Init databases. */ + nscd_init (conffile); - /* Create first sockets */ - init_sockets (); - /* Init databases */ - if ((cache_pwdinit () < 0) || (cache_grpinit () < 0)) - { - fputs (_("Not enough memory\n"), stderr); - return 1; - } /* Handle incoming requests */ - handle_requests (); + start_threads (); return 0; } @@ -182,20 +172,19 @@ parse_opt (int key, char *arg, struct argp_state *state) switch (key) { case 'd': - debug_flag = 1; + ++debug_level; go_background = 0; break; + case 'f': conffile = arg; break; + case 'K': if (getuid () != 0) - { - printf (_("Only root is allowed to use this option!\n\n")); - exit (EXIT_FAILURE); - } + error (EXIT_FAILURE, 0, _("Only root is allowed to use this option!")); { - int sock = __nscd_open_socket (); + int sock = nscd_open_socket (); request_header req; ssize_t nbytes; @@ -205,19 +194,24 @@ parse_opt (int key, char *arg, struct argp_state *state) req.version = NSCD_VERSION; req.type = SHUTDOWN; req.key_len = 0; - nbytes = write (sock, &req, sizeof (request_header)); + nbytes = TEMP_FAILURE_RETRY (write (sock, &req, + sizeof (request_header))); close (sock); - if (nbytes != req.key_len) - exit (EXIT_FAILURE); - else - exit (EXIT_SUCCESS); + exit (nbytes != sizeof (request_header) ? EXIT_FAILURE : EXIT_SUCCESS); } + case 'g': - print_stat (); - exit (EXIT_SUCCESS); + receive_print_stats (); + /* Does not return. */ + + case 't': + nthreads = atol (arg); + break; + default: return ARGP_ERR_UNKNOWN; } + return 0; } @@ -231,13 +225,14 @@ Copyright (C) %s Free Software Foundation, Inc.\n\ This is free software; see the source for copying conditions. There is NO\n\ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ "), "1998"); - fprintf (stream, gettext ("Written by %s.\n"), "Thorsten Kukuk"); + fprintf (stream, gettext ("Written by %s.\n"), + "Thorsten Kukuk and Ulrich Drepper"); } -/* Create a socket connected to a name. */ +/* Create a socket connected to a name. */ int -__nscd_open_socket (void) +nscd_open_socket (void) { struct sockaddr_un addr; int sock; @@ -247,6 +242,7 @@ __nscd_open_socket (void) return -1; addr.sun_family = AF_UNIX; + assert (sizeof (addr.sun_path) >= sizeof (_PATH_NSCDSOCKET)); strcpy (addr.sun_path, _PATH_NSCDSOCKET); if (connect (sock, (struct sockaddr *) &addr, sizeof (addr)) < 0) { @@ -258,12 +254,12 @@ __nscd_open_socket (void) } /* Cleanup. */ -static void +void termination_handler (int signum) { close_sockets (); - /* Clean up the files created by `bind'. */ + /* Clean up the file created by `bind'. */ unlink (_PATH_NSCDSOCKET); /* Clean up pid file. */ @@ -282,11 +278,12 @@ check_pid (const char *file) if (fp) { pid_t pid; + int n; - fscanf (fp, "%d", &pid); + n = fscanf (fp, "%d", &pid); fclose (fp); - if (kill (pid, 0) == 0) + if (n != 1 || kill (pid, 0) == 0) return 1; } @@ -305,176 +302,10 @@ write_pid (const char *file) return -1; fprintf (fp, "%d\n", getpid ()); - if (ferror (fp)) + if (fflush (fp) || ferror (fp)) return -1; fclose (fp); return 0; } - -/* Type of the lookup function for netname2user. */ -typedef int (*pwbyname_function) (const char *name, struct passwd *pw, - char *buffer, size_t buflen); - -/* Handle incoming requests. */ -static -void handle_requests (void) -{ - request_header req; - int conn; /* Handle on which connection (client) the request came from. */ - int done = 0; - char *key; - pthread_attr_t th_attr; - - /* We will create all threads detached. Therefore prepare an attribute - now. */ - pthread_attr_init (&th_attr); - pthread_attr_setdetachstate (&th_attr, PTHREAD_CREATE_DETACHED); - - while (!done) - { - key = NULL; - get_request (&conn, &req, &key); - if (debug_flag) - dbg_log (_("handle_requests: request received (Version = %d)"), - req.version); - switch (req.type) - { - case GETPWBYNAME: - { - param_t *param = malloc (sizeof (param_t)); - pthread_t thread; - int status; - - if (debug_flag) - dbg_log ("\tGETPWBYNAME (%s)", key); - param->key = key; - param->conn = conn; - if (disabled_passwd) - status = pthread_create (&thread, &th_attr, cache_pw_disabled, - (void *)param); - else - status = pthread_create (&thread, &th_attr, cache_getpwnam, - (void *)param); - if (status != 0) - { - dbg_log (_("Creation of thread failed: %s"), strerror (errno)); - close_socket (conn); - } - pthread_detach (thread); - } - break; - case GETPWBYUID: - { - param_t *param = malloc (sizeof (param_t)); - pthread_t thread; - int status; - - if (debug_flag) - dbg_log ("\tGETPWBYUID (%s)", key); - param->key = key; - param->conn = conn; - if (disabled_passwd) - status = pthread_create (&thread, &th_attr, cache_pw_disabled, - (void *)param); - else - status = pthread_create (&thread, &th_attr, cache_getpwuid, - (void *)param); - if (status != 0) - { - dbg_log (_("Creation of thread failed: %s"), strerror (errno)); - close_socket (conn); - } - } - break; - case GETGRBYNAME: - { - param_t *param = malloc (sizeof (param_t)); - pthread_t thread; - int status; - - if (debug_flag) - dbg_log ("\tGETGRBYNAME (%s)", key); - param->key = key; - param->conn = conn; - if (disabled_group) - status = pthread_create (&thread, &th_attr, cache_gr_disabled, - (void *)param); - else - status = pthread_create (&thread, &th_attr, cache_getgrnam, - (void *)param); - if (status != 0) - { - dbg_log (_("Creation of thread failed: %s"), strerror (errno)); - close_socket (conn); - } - } - break; - case GETGRBYGID: - { - param_t *param = malloc (sizeof (param_t)); - pthread_t thread; - int status; - - if (debug_flag) - dbg_log ("\tGETGRBYGID (%s)", key); - param->key = key; - param->conn = conn; - if (disabled_group) - status = pthread_create (&thread, &th_attr, cache_gr_disabled, - (void *)param); - else - status = pthread_create (&thread, &th_attr, cache_getgrgid, - (void *)param); - if (status != 0) - { - dbg_log (_("Creation of thread failed: %s"), strerror (errno)); - close_socket (conn); - } - } - break; - case GETHOSTBYNAME: - /* Not yetimplemented. */ - close_socket (conn); - break; - case GETHOSTBYADDR: - /* Not yet implemented. */ - close_socket (conn); - break; - case SHUTDOWN: - do_shutdown = 1; - close_socket (0); - close_socket (conn); - /* Clean up the files created by `bind'. */ - unlink (_PATH_NSCDSOCKET); - /* Clean up pid file. */ - unlink (_PATH_NSCDPID); - done = 1; - break; - case GETSTAT: - { - stat_response_header resp; - - if (debug_flag) - dbg_log ("\tGETSTAT"); - - get_pw_stat (&resp); - get_gr_stat (&resp); - resp.debug_level = debug_flag; - resp.pw_enabled = !disabled_passwd; - resp.gr_enabled = !disabled_group; - - stat_send (conn, &resp); - - close_socket (conn); - } - break; - default: - dbg_log (_("Unknown request (%d)"), req.type); - break; - } - } - - pthread_attr_destroy (&th_attr); -} diff --git a/nscd/nscd.conf b/nscd/nscd.conf index 5d8c7f3..5e327e8 100644 --- a/nscd/nscd.conf +++ b/nscd/nscd.conf @@ -6,25 +6,36 @@ # Legal entries are: # # logfile <file> -# enable-cache <service> <yes|no> # debug-level <level> +# +# enable-cache <service> <yes|no> # positive-time-to-live <service> <time in seconds> # negative-time-to-live <service> <time in seconds> # suggested-size <service> <prime number> +# check-files <service> <yes|no> # # Currently supported cache names (services): passwd, group # # logfile /var/adm/nscd.log -# enable-cache hosts no debug-level 0 + enable-cache passwd yes positive-time-to-live passwd 600 negative-time-to-live passwd 20 suggested-size passwd 211 + check-files passwd yes + enable-cache group yes positive-time-to-live group 3600 negative-time-to-live group 60 suggested-size group 211 + check-files group yes + + enable-cache hosts yes + positive-time-to-live hosts 3600 + negative-time-to-live hosts 20 + suggested-size hosts 211 + check-files hosts yes diff --git a/nscd/nscd.h b/nscd/nscd.h index 4835542..bc8150a 100644 --- a/nscd/nscd.h +++ b/nscd/nscd.h @@ -18,18 +18,37 @@ Boston, MA 02111-1307, USA. */ #ifndef _NSCD_H -#define _NSCD_H 1 +#define _NSCD_H 1 + +#include <pthread.h> +#include <time.h> +#include <sys/uio.h> -#include <grp.h> -#include <pwd.h> /* Version number of the daemon interface */ -#define NSCD_VERSION 1 +#define NSCD_VERSION 2 + +/* Path of the file where the PID of the running system is stored. */ +#define _PATH_NSCDPID "/var/run/nscd.pid" + +/* Path for the Unix domain socket. */ +#define _PATH_NSCDSOCKET "/var/run/.nscd_socket" + +/* Path for the configuration file. */ +#define _PATH_NSCDCONF "/etc/nscd.conf" + -/* How many threads do we spawn maximal ? */ -#define MAX_NUM_CONNECTIONS 16 +/* Handle databases. */ +typedef enum +{ + pwddb, + grpdb, + hstdb, + lastdb +} dbtype; -/* Services provided */ + +/* Available services. */ typedef enum { GETPWBYNAME, @@ -37,22 +56,67 @@ typedef enum GETGRBYNAME, GETGRBYGID, GETHOSTBYNAME, + GETHOSTBYNAMEv6, GETHOSTBYADDR, - SHUTDOWN, /* Shut the server down */ - GETSTAT /* Get the server statistic */ + GETHOSTBYADDRv6, + LASTDBREQ = GETHOSTBYADDRv6, + SHUTDOWN, /* Shut the server down. */ + GETSTAT, /* Get the server statistic. */ + LASTREQ, } request_type; + +/* Structure for one hash table entry. */ +struct hashentry +{ + request_type type; /* Which type of dataset. */ + size_t len; /* Length of key. */ + void *key; /* Pointer to key. */ + struct hashentry *next; /* Next entry in this hash bucket list. */ + time_t timeout; /* Time when this entry becomes invalid. */ + ssize_t total; /* Number of bytes in PACKET. */ + const void *packet; /* Records for the result. */ + void *data; /* The malloc()ed respond record. */ + int last; /* Nonzero if DATA should be free()d. */ + struct hashentry *dellist; /* Next record to be deleted. */ +}; + +/* Structure describing one database. */ +struct database +{ + pthread_rwlock_t lock; + + int enabled; + int check_file; + const char *filename; + time_t file_mtime; + size_t module; + + const struct iovec *disabled_iov; + + unsigned long int postimeout; + unsigned long int negtimeout; + + unsigned long int poshit; + unsigned long int neghit; + unsigned long int posmiss; + unsigned long int negmiss; + + struct hashentry **array; +}; + + /* Header common to all requests */ typedef struct { - /* Version number of the daemon interface */ - int version; - /* Service requested */ - request_type type; - /* key len */ - ssize_t key_len; + int version; /* Version number of the daemon interface. */ + request_type type; /* Service requested. */ + ssize_t key_len; /* Key length. */ } request_header; + +/* Structure sent in reply to password query. Note that this struct is + sent also if the service is disabled or there is no record found. */ typedef struct { int version; @@ -66,6 +130,9 @@ typedef struct ssize_t pw_shell_len; } pw_response_header; + +/* Structure sent in reply to group query. Note that this struct is + sent also if the service is disabled or there is no record found. */ typedef struct { int version; @@ -73,77 +140,82 @@ typedef struct ssize_t gr_name_len; ssize_t gr_passwd_len; gid_t gr_gid; - ssize_t gr_mem_len; + ssize_t gr_mem_cnt; } gr_response_header; -typedef struct -{ - int debug_level; - int pw_enabled; - unsigned long pw_poshit; - unsigned long pw_posmiss; - unsigned long pw_neghit; - unsigned long pw_negmiss; - unsigned long pw_size; - unsigned long pw_posttl; - unsigned long pw_negttl; - int gr_enabled; - unsigned long gr_poshit; - unsigned long gr_posmiss; - unsigned long gr_neghit; - unsigned long gr_negmiss; - unsigned long gr_size; - unsigned long gr_posttl; - unsigned long gr_negttl; -} stat_response_header; - -#define _PATH_NSCDPID "/var/run/nscd.pid" -#define _PATH_NSCDSOCKET "/var/run/.nscd_socket" -#define _PATH_NSCDCONF "/etc/nscd.conf" +/* Structure sent in reply to host query. Note that this struct is + sent also if the service is disabled or there is no record found. */ typedef struct { - char *key; - int conn; -} param_t; - -extern int do_shutdown; /* 1 if we should quit the programm. */ -extern int disabled_passwd; -extern int disabled_group; - -extern int nscd_parse_file __P ((const char *fname)); -extern int set_logfile __P ((const char *logfile)); -extern void set_pos_pwd_ttl __P ((unsigned long)); -extern void set_neg_pwd_ttl __P ((unsigned long)); -extern void set_pos_grp_ttl __P ((unsigned long)); -extern void set_neg_grp_ttl __P ((unsigned long)); -extern void set_pwd_modulo __P ((unsigned long)); -extern void set_grp_modulo __P ((unsigned long)); - -extern void init_sockets __P ((void)); -extern void close_socket __P ((int conn)); -extern void close_sockets __P ((void)); -extern void get_request __P ((int *conn, request_header *req, char **key)); -extern void pw_send_answer __P ((int conn, struct passwd *pwd)); -extern void pw_send_disabled __P ((int conn)); -extern void gr_send_answer __P ((int conn, struct group *grp)); -extern void gr_send_disabled __P ((int conn)); - -extern int cache_pwdinit __P ((void)); -extern void *cache_getpwnam __P ((void *param)); -extern void *cache_getpwuid __P ((void *param)); -extern void *cache_pw_disabled __P ((void *param)); - -extern int cache_grpinit __P ((void)); -extern void *cache_getgrnam __P ((void *param)); -extern void *cache_getgrgid __P ((void *param)); -extern void *cache_gr_disabled __P ((void *param)); - -extern int __nscd_open_socket __P ((void)); - -extern void get_pw_stat __P ((stat_response_header *resp)); -extern void get_gr_stat __P ((stat_response_header *resp)); -extern void print_stat __P ((void)); -extern void stat_send __P ((int conn, stat_response_header *resp)); - -#endif + int version; + int found; + ssize_t h_name_len; + ssize_t h_aliases_cnt; + int h_addrtype; + int h_length; + ssize_t h_addr_list_cnt; + int error; +} hst_response_header; + +/* Global variables. */ +extern const char *dbnames[lastdb]; +extern const char *serv2str[LASTREQ]; + +extern const struct iovec pwd_iov_disabled; +extern const struct iovec grp_iov_disabled; +extern const struct iovec hst_iov_disabled; + +/* Number of threads to run. */ +extern int nthreads; + + +/* Prototypes for global functions. */ + +/* nscd.c */ +extern void termination_handler (int signum); +extern int nscd_open_socket (void); + +/* connections.c */ +extern void nscd_init (const char *conffile); +extern void close_sockets (void); +extern void start_threads (void); + +/* nscd_conf.c */ +extern int nscd_parse_file (const char *fname, struct database dbs[lastdb]); + +/* nscd_stat.c */ +extern void send_stats (int fd, struct database dbs[lastdb]); +extern int receive_print_stats (void); + +/* cache.c */ +extern struct hashentry *cache_search (int type, void *key, size_t len, + struct database *table); +extern void cache_add (int type, void *key, size_t len, + const void *packet, size_t iovtotal, void *data, + int last, time_t t, struct database *table); +extern void prune_cache (struct database *table, time_t now); + +/* pwdcache.c */ +extern void addpwbyname (struct database *db, int fd, request_header *req, + void *key); +extern void addpwbyuid (struct database *db, int fd, request_header *req, + void *key); + +/* grpcache.c */ +extern void addgrbyname (struct database *db, int fd, request_header *req, + void *key); +extern void addgrbygid (struct database *db, int fd, request_header *req, + void *key); + +/* hstcache.c */ +extern void addhstbyname (struct database *db, int fd, request_header *req, + void *key); +extern void addhstbyaddr (struct database *db, int fd, request_header *req, + void *key); +extern void addhstbynamev6 (struct database *db, int fd, request_header *req, + void *key); +extern void addhstbyaddrv6 (struct database *db, int fd, request_header *req, + void *key); + +#endif /* nscd.h */ diff --git a/nscd/nscd_conf.c b/nscd/nscd_conf.c index 917fe02..6ac1677 100644 --- a/nscd/nscd_conf.c +++ b/nscd/nscd_conf.c @@ -22,17 +22,27 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/param.h> #include <sys/types.h> #include "dbg_log.h" #include "nscd.h" +/* Names of the databases. */ +const char *dbnames[lastdb] = +{ + [pwddb] = "passwd", + [grpdb] = "group", + [hstdb] = "hosts" +}; + int -nscd_parse_file (const char *fname) +nscd_parse_file (const char *fname, struct database dbs[lastdb]) { FILE *fp; char *line, *cp, *entry, *arg1, *arg2; size_t len; + int cnt; /* Open the configuration file. */ fp = fopen (fname, "r"); @@ -92,41 +102,64 @@ nscd_parse_file (const char *fname) if (strcmp (entry, "positive-time-to-live") == 0) { - if (strcmp (arg1, "passwd") == 0) - set_pos_pwd_ttl (atol (arg2)); - else if (strcmp (arg1, "group") == 0) - set_pos_grp_ttl (atol (arg2)); - else + for (cnt = 0; cnt < lastdb; ++cnt) + if (strcmp (arg1, dbnames[cnt]) == 0) + { + dbs[cnt].postimeout = atol (arg2); + break; + } + if (cnt == lastdb) dbg_log ("server %s is not supported\n", arg1); } else if (strcmp (entry, "negative-time-to-live") == 0) { - if (strcmp (arg1, "passwd") == 0) - set_neg_pwd_ttl (atol (arg2)); - else if (strcmp (arg1, "group") == 0) - set_neg_grp_ttl (atol (arg2)); - else - dbg_log (_("service %s is not supported"), arg1); + for (cnt = 0; cnt < lastdb; ++cnt) + if (strcmp (arg1, dbnames[cnt]) == 0) + { + dbs[cnt].negtimeout = atol (arg2); + break; + } + if (cnt == lastdb) + dbg_log ("server %s is not supported\n", arg1); } else if (strcmp (entry, "suggested-size") == 0) { - if (strcmp (arg1, "passwd") == 0) - set_pwd_modulo (atol (arg2)); - else if (strcmp (arg1, "group") == 0) - set_grp_modulo (atol (arg2)); - else - dbg_log (_("service %s is not supported"), arg1); + for (cnt = 0; cnt < lastdb; ++cnt) + if (strcmp (arg1, dbnames[cnt]) == 0) + { + dbs[cnt].module = atol (arg2); + break; + } + if (cnt == lastdb) + dbg_log ("server %s is not supported\n", arg1); + } + else if (strcmp (entry, "enable-cache") == 0) + { + for (cnt = 0; cnt < lastdb; ++cnt) + if (strcmp (arg1, dbnames[cnt]) == 0) + { + if (strcmp (arg2, "no") == 0) + dbs[cnt].enabled = 0; + else if (strcmp (arg2, "yes") == 0) + dbs[cnt].enabled = 1; + break; + } + if (cnt == lastdb) + dbg_log ("server %s is not supported\n", arg1); } - else if (strcmp (entry, "enable-cache") ==0) + else if (strcmp (entry, "check-files") == 0) { - if (strcmp (arg1, "passwd") == 0 - && strcmp (arg2, "no") == 0) - disabled_passwd = 1; - else if (strcmp (arg1, "group") == 0 - && strcmp (arg2, "no") == 0) - disabled_group = 1; - else - dbg_log (_("service %s is not supported"), arg1); + for (cnt = 0; cnt < lastdb; ++cnt) + if (strcmp (arg1, dbnames[cnt]) == 0) + { + if (strcmp (arg2, "no") == 0) + dbs[cnt].check_file = 0; + else if (strcmp (arg2, "yes") == 0) + dbs[cnt].check_file = 1; + break; + } + if (cnt == lastdb) + dbg_log ("server %s is not supported\n", arg1); } else if (strcmp (entry, "logfile") == 0) { @@ -137,7 +170,12 @@ nscd_parse_file (const char *fname) { int level = atoi (arg1); if (level > 0) - debug_flag = level; + debug_level = level; + } + else if (strcmp (entry, "threads") == 0) + { + if (nthreads == -1) + nthreads = MAX (atol (arg1), lastdb); } else dbg_log (_("Unknown option: %s %s %s"), entry, arg1, arg2); diff --git a/nscd/nscd_getgr_r.c b/nscd/nscd_getgr_r.c index ec4f5a1..f27da02 100644 --- a/nscd/nscd_getgr_r.c +++ b/nscd/nscd_getgr_r.c @@ -32,41 +32,36 @@ int __nss_not_use_nscd_group; -static int __nscd_getgr_r (const char *key, request_type type, - struct group *resultbuf, char *buffer, - size_t buflen); +static int nscd_getgr_r (const char *key, size_t keylen, request_type type, + struct group *resultbuf, char *buffer, + size_t buflen); + int __nscd_getgrnam_r (const char *name, struct group *resultbuf, char *buffer, size_t buflen) { - if (name == NULL) - return 1; - - return __nscd_getgr_r (name, GETGRBYNAME, resultbuf, buffer, buflen); + return nscd_getgr_r (name, strlen (name) + 1, GETGRBYNAME, resultbuf, + buffer, buflen); } + int __nscd_getgrgid_r (gid_t gid, struct group *resultbuf, char *buffer, size_t buflen) { - char *p = buffer; - int plen; + char buf[12]; + size_t n; - plen = __snprintf (buffer, buflen, "%d", gid); - if (plen == -1) - { - __set_errno (ERANGE); - return -1; - } - p = buffer + plen + 1; + n = __snprintf (buf, sizeof (buf), "%d", gid) + 1; - return __nscd_getgr_r (buffer, GETGRBYGID, resultbuf, p, buflen - plen -1); + return nscd_getgr_r (buf, n, GETGRBYGID, resultbuf, buffer, buflen); } + /* Create a socket connected to a name. */ static int -nscd_open_socket (void) +open_socket (void) { struct sockaddr_un addr; int sock; @@ -91,16 +86,16 @@ nscd_open_socket (void) return sock; } + static int -__nscd_getgr_r (const char *key, request_type type, struct group *resultbuf, - char *buffer, size_t buflen) +nscd_getgr_r (const char *key, size_t keylen, request_type type, + struct group *resultbuf, char *buffer, size_t buflen) { - int sock = nscd_open_socket (); + int sock = open_socket (); request_header req; gr_response_header gr_resp; ssize_t nbytes; - size_t maxiov; - size_t sum; + struct iovec vec[2]; if (sock == -1) { @@ -110,16 +105,14 @@ __nscd_getgr_r (const char *key, request_type type, struct group *resultbuf, req.version = NSCD_VERSION; req.type = type; - req.key_len = strlen (key); - nbytes = __write (sock, &req, sizeof (request_header)); - if (nbytes != sizeof (request_header)) - { - __close (sock); - return 1; - } + req.key_len = keylen; + + vec[0].iov_base = &req; + vec[0].iov_len = sizeof (request_header); + vec[1].iov_base = (void *) key; + vec[1].iov_len = keylen; - nbytes = __write (sock, key, req.key_len); - if (nbytes != req.key_len) + if (__writev (sock, vec, 2) != sizeof (request_header) + keylen) { __close (sock); return 1; @@ -142,118 +135,79 @@ __nscd_getgr_r (const char *key, request_type type, struct group *resultbuf, if (gr_resp.found == 1) { - struct iovec *vec; size_t *len; char *p = buffer; - int nblocks; size_t total_len; uintptr_t align; + size_t cnt; - /* A first check whether the buffer is sufficently large is possible. */ - if (buflen < gr_resp.gr_name_len + 1 + gr_resp.gr_passwd_len + 1) + /* Now allocate the buffer the array for the group members. We must + align the pointer. */ + align = ((__alignof__ (char *) - (p - ((char *) 0))) + & (__alignof__ (char *) - 1)); + if (buflen < (align + (1 + gr_resp.gr_mem_cnt) * sizeof (char *) + + gr_resp.gr_name_len + gr_resp.gr_passwd_len)) { + no_room: __set_errno (ERANGE); __close (sock); return -1; } - /* Allocate the IOVEC. */ - vec = alloca ((2 + gr_resp.gr_mem_len) * sizeof (struct iovec)); - len = alloca (gr_resp.gr_mem_len * sizeof (size_t)); + p += align; + resultbuf->gr_mem = (char **) p; + p += (1 + gr_resp.gr_mem_cnt) * sizeof (char *); + buflen -= align + (1 + gr_resp.gr_mem_cnt) * sizeof (char *); - vec[0].iov_base = resultbuf->gr_name = p; - vec[0].iov_len = gr_resp.gr_name_len; - total_len = gr_resp.gr_name_len; - p += gr_resp.gr_name_len + 1; + /* Set pointers for strings. */ + resultbuf->gr_name = p; + p += gr_resp.gr_name_len; + resultbuf->gr_passwd = p; + p += gr_resp.gr_passwd_len; - vec[1].iov_base = resultbuf->gr_passwd = p; - vec[1].iov_len = gr_resp.gr_passwd_len; - total_len += gr_resp.gr_passwd_len; - p += gr_resp.gr_passwd_len + 1; - buflen -= total_len; - nblocks = 2; + /* Fill in what we know now. */ + resultbuf->gr_gid = gr_resp.gr_gid; - if (gr_resp.gr_mem_len > 0) - { - vec[2].iov_base = len; - vec[2].iov_len = gr_resp.gr_mem_len * sizeof (size_t); - total_len += gr_resp.gr_mem_len * sizeof (size_t); - nblocks = 3; - } + /* Allocate array to store lengths. */ + len = alloca (gr_resp.gr_mem_cnt * sizeof (size_t)); + + total_len = gr_resp.gr_mem_cnt * sizeof (size_t); + vec[0].iov_base = len; + vec[0].iov_len = total_len; + vec[1].iov_base = resultbuf->gr_name; + vec[1].iov_len = gr_resp.gr_name_len + gr_resp.gr_passwd_len; + total_len += gr_resp.gr_name_len + gr_resp.gr_passwd_len; + + buflen -= total_len; /* Get this data. */ - if (__readv (sock, vec, nblocks) != total_len) + if (__readv (sock, vec, 2) != total_len) { __close (sock); return 1; } - /* Now we know the sizes. First terminate the strings we just read. */ - resultbuf->gr_name[gr_resp.gr_name_len] = '\0'; - resultbuf->gr_passwd[gr_resp.gr_passwd_len] = '\0'; - - resultbuf->gr_gid = gr_resp.gr_gid; + /* Clear the terminating entry. */ + resultbuf->gr_mem[gr_resp.gr_mem_cnt] = NULL; - /* Now allocate the buffer the array for the group members. We must - align the pointer. */ - align = ((__alignof__ (char *) - (p - ((char *) 0))) - & (__alignof__ (char *) - 1)); - if (align + (1 + gr_resp.gr_mem_len) * sizeof (char *) > buflen) + /* Prepare reading the group members. */ + total_len = 0; + for (cnt = 0; cnt < gr_resp.gr_mem_cnt; ++cnt) { - __set_errno (ERANGE); - __close (sock); - return -1; + resultbuf->gr_mem[cnt] = p; + total_len += len[cnt]; + p += len[cnt]; } - p += align; - resultbuf->gr_mem = (char **) p; - p += (1 + gr_resp.gr_mem_len) * sizeof (char *); - buflen -= align + (1 + gr_resp.gr_mem_len) * sizeof (char *); - resultbuf->gr_mem[gr_resp.gr_mem_len] = NULL; + if (total_len > buflen) + goto no_room; - if (gr_resp.gr_mem_len > 0) + if (__read (sock, resultbuf->gr_mem[0], total_len) != total_len) { - /* Prepare reading the group members. */ - size_t i; - - total_len = 0; - for (i = 0; i < gr_resp.gr_mem_len; ++i) - { - if (len[i] >= buflen) - { - __set_errno (ERANGE); - __close (sock); - return -1; - } - - vec[i].iov_base = resultbuf->gr_mem[i] = p; - vec[i].iov_len = len[i]; - total_len += len[i]; - buflen -= len[i]; - p += len[i]; - *p++ = '\0'; - } - -#ifdef UIO_MAXIOV - maxiov = UIO_MAXIOV; -#else - maxiov = sysconf (_SC_UIO_MAXIOV); -#endif - - sum = 0; - while (i > maxiov) - { - sum += __readv (sock, vec, maxiov); - vec += maxiov; - i -= maxiov; - } - - if (sum + __readv (sock, vec, i) != total_len) - { - __close (sock); - return -1; - } + __close (sock); + return -1; } + __close (sock); return 0; } diff --git a/nscd/nscd_gethst_r.c b/nscd/nscd_gethst_r.c new file mode 100644 index 0000000..f78b104 --- /dev/null +++ b/nscd/nscd_gethst_r.c @@ -0,0 +1,302 @@ +/* Copyright (C) 1998 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include <errno.h> +#include <netdb.h> +#include <resolv.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <arpa/nameser.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <sys/un.h> + +#include "nscd.h" +#include "nscd_proto.h" + +int __nss_not_use_nscd_hosts; + +static int nscd_gethst_r (const char *key, size_t keylen, request_type type, + struct hostent *resultbuf, char *buffer, + size_t buflen, int *h_errnop); + + +int +__nscd_gethostbyname_r (const char *name, struct hostent *resultbuf, + char *buffer, size_t buflen, int *h_errnop) +{ + request_type reqtype; + + reqtype = (_res.options & RES_USE_INET6) ? GETHOSTBYNAMEv6 : GETHOSTBYNAME; + + return nscd_gethst_r (name, strlen (name) + 1, reqtype, resultbuf, + buffer, buflen, h_errnop); +} + + +int +__nscd_gethostbyname2_r (const char *name, int af, struct hostent *resultbuf, + char *buffer, size_t buflen, int *h_errnop) +{ + request_type reqtype; + + reqtype = af == AF_INET6 ? GETHOSTBYNAMEv6 : GETHOSTBYNAME; + + return nscd_gethst_r (name, strlen (name) + 1, reqtype, resultbuf, + buffer, buflen, h_errnop); +} + + +int +__nscd_gethostbyaddr_r (const char *addr, int len, int type, + struct hostent *resultbuf, char *buffer, size_t buflen, + int *h_errnop) +{ + request_type reqtype; + + if (!((len == INADDRSZ && type == AF_INET) + || (len == IN6ADDRSZ && type == AF_INET6))) + /* LEN and TYPE do not match. */ + return 1; + + reqtype = type == AF_INET6 ? GETHOSTBYADDRv6 : GETHOSTBYADDR; + + return nscd_gethst_r (addr, len, reqtype, resultbuf, buffer, buflen, + h_errnop); +} + + +/* Create a socket connected to a name. */ +static int +open_socket (void) +{ + struct sockaddr_un addr; + int sock; + int saved_errno = errno; + + sock = __socket (PF_UNIX, SOCK_STREAM, 0); + if (sock < 0) + { + __set_errno (saved_errno); + return -1; + } + + addr.sun_family = AF_UNIX; + strcpy (addr.sun_path, _PATH_NSCDSOCKET); + if (__connect (sock, (struct sockaddr *) &addr, sizeof (addr)) < 0) + { + __close (sock); + __set_errno (saved_errno); + return -1; + } + + return sock; +} + + +static int +nscd_gethst_r (const char *key, size_t keylen, request_type type, + struct hostent *resultbuf, char *buffer, size_t buflen, + int *h_errnop) +{ + int sock = open_socket (); + hst_response_header hst_resp; + request_header req; + ssize_t nbytes; + + if (sock == -1) + { + __nss_not_use_nscd_group = 1; + return 1; + } + + req.version = NSCD_VERSION; + req.type = type; + req.key_len = keylen; + nbytes = __write (sock, &req, sizeof (request_header)); + if (nbytes != sizeof (request_header)) + { + __close (sock); + return 1; + } + + nbytes = __write (sock, key, req.key_len); + if (nbytes != req.key_len) + { + __close (sock); + return 1; + } + + nbytes = __read (sock, &hst_resp, sizeof (hst_response_header)); + if (nbytes != sizeof (hst_response_header)) + { + __close (sock); + return 1; + } + + if (hst_resp.found == -1) + { + /* The daemon does not cache this database. */ + __close (sock); + __nss_not_use_nscd_hosts = 1; + return 1; + } + + if (hst_resp.found == 1) + { + struct iovec vec[4]; + size_t *aliases_len; + char *cp = buffer; + uintptr_t align; + size_t total_len; + ssize_t cnt; + char *ignore; + int n; + + /* A first check whether the buffer is sufficently large is possible. */ + /* Now allocate the buffer the array for the group members. We must + align the pointer. */ + align = ((__alignof__ (char *) - (cp - ((char *) 0))) + & (__alignof__ (char *) - 1)); + if (buflen < (align + hst_resp.h_name_len + + ((hst_resp.h_aliases_cnt + hst_resp.h_addr_list_cnt + 2) + * sizeof (char *)) + + hst_resp.h_addr_list_cnt * (type == AF_INET + ? INADDRSZ : IN6ADDRSZ))) + { + no_room: + __set_errno (ERANGE); + __close (sock); + return -1; + } + cp += align; + + /* Prepare the result as far as we can. */ + resultbuf->h_aliases = (char **) cp; + cp += (hst_resp.h_aliases_cnt + 1) * sizeof (char *); + resultbuf->h_addr_list = (char **) cp; + cp += (hst_resp.h_addr_list_cnt + 1) * sizeof (char *); + + resultbuf->h_name = cp; + cp += hst_resp.h_name_len; + vec[0].iov_base = resultbuf->h_name; + vec[0].iov_len = hst_resp.h_name_len; + + aliases_len = alloca (hst_resp.h_aliases_cnt * sizeof (size_t)); + vec[1].iov_base = aliases_len; + vec[1].iov_len = hst_resp.h_aliases_cnt * sizeof (size_t); + + total_len = (hst_resp.h_name_len + + hst_resp.h_aliases_cnt * sizeof (size_t)); + + n = 2; + if (type == GETHOSTBYADDR || type == GETHOSTBYNAME) + { + vec[2].iov_base = cp; + vec[2].iov_len = hst_resp.h_addr_list_cnt * INADDRSZ; + + ignore = alloca (hst_resp.h_addr_list_cnt * IN6ADDRSZ); + vec[3].iov_base = ignore; + vec[3].iov_len = hst_resp.h_addr_list_cnt * IN6ADDRSZ; + + for (cnt = 0; cnt < hst_resp.h_addr_list_cnt; ++cnt) + { + resultbuf->h_addr_list[cnt] = cp; + cp += INADDRSZ; + } + + resultbuf->h_addrtype = AF_INET; + resultbuf->h_length = INADDRSZ; + + total_len += hst_resp.h_addr_list_cnt * (INADDRSZ + IN6ADDRSZ); + + n = 4; + } + else + { + if (hst_resp.h_length == INADDRSZ) + { + ignore = alloca (hst_resp.h_addr_list_cnt * INADDRSZ); + vec[2].iov_base = ignore; + vec[2].iov_len = hst_resp.h_addr_list_cnt * INADDRSZ; + + total_len += hst_resp.h_addr_list_cnt * INADDRSZ; + + n = 3; + } + + vec[n].iov_base = cp; + vec[n].iov_len = hst_resp.h_addr_list_cnt * IN6ADDRSZ; + + for (cnt = 0; cnt < hst_resp.h_addr_list_cnt; ++cnt) + { + resultbuf->h_addr_list[cnt] = cp; + cp += IN6ADDRSZ; + } + + resultbuf->h_addrtype = AF_INET6; + resultbuf->h_length = IN6ADDRSZ; + + total_len += hst_resp.h_addr_list_cnt * IN6ADDRSZ; + + ++n; + } + resultbuf->h_addr_list[cnt] = NULL; + + if (__readv (sock, vec, n) != total_len) + { + __close (sock); + return 1; + } + + /* Now we also can read the aliases. */ + total_len = 0; + for (cnt = 0; cnt < hst_resp.h_aliases_cnt; ++cnt) + { + resultbuf->h_aliases[cnt] = cp; + cp += aliases_len[cnt]; + total_len += aliases_len[cnt]; + } + resultbuf->h_aliases[cnt] = NULL; + + /* See whether this would exceed the buffer capacity. */ + if (cp > buffer + buflen) + goto no_room; + + /* And finally read the aliases. */ + if (__read (sock, resultbuf->h_aliases[0], total_len) != total_len) + { + __close (sock); + return 1; + } + + __close (sock); + return 0; + } + else + { + /* Store the error number. */ + *h_errnop = hst_resp.error; + + __close (sock); + return -1; + } +} diff --git a/nscd/nscd_getpw_r.c b/nscd/nscd_getpw_r.c index a24d33b..83baafc 100644 --- a/nscd/nscd_getpw_r.c +++ b/nscd/nscd_getpw_r.c @@ -31,9 +31,9 @@ int __nss_not_use_nscd_passwd; -static int __nscd_getpw_r (const char *key, request_type type, - struct passwd *resultbuf, char *buffer, - size_t buflen); +static int nscd_getpw_r (const char *key, request_type type, + struct passwd *resultbuf, char *buffer, + size_t buflen); int __nscd_getpwnam_r (const char *name, struct passwd *resultbuf, char *buffer, @@ -42,7 +42,7 @@ __nscd_getpwnam_r (const char *name, struct passwd *resultbuf, char *buffer, if (name == NULL) return 1; - return __nscd_getpw_r (name, GETPWBYNAME, resultbuf, buffer, buflen); + return nscd_getpw_r (name, GETPWBYNAME, resultbuf, buffer, buflen); } int @@ -60,12 +60,12 @@ __nscd_getpwuid_r (uid_t uid, struct passwd *resultbuf, char *buffer, } p = buffer + plen + 1; - return __nscd_getpw_r (buffer, GETPWBYUID, resultbuf, p, buflen - plen -1); + return nscd_getpw_r (buffer, GETPWBYUID, resultbuf, p, buflen - plen - 1); } /* Create a socket connected to a name. */ static int -nscd_open_socket (void) +open_socket (void) { struct sockaddr_un addr; int sock; @@ -91,10 +91,10 @@ nscd_open_socket (void) } static int -__nscd_getpw_r (const char *key, request_type type, struct passwd *resultbuf, - char *buffer, size_t buflen) +nscd_getpw_r (const char *key, request_type type, struct passwd *resultbuf, + char *buffer, size_t buflen) { - int sock = nscd_open_socket (); + int sock = open_socket (); request_header req; pw_response_header pw_resp; ssize_t nbytes; @@ -107,7 +107,7 @@ __nscd_getpw_r (const char *key, request_type type, struct passwd *resultbuf, req.version = NSCD_VERSION; req.type = type; - req.key_len = strlen (key); + req.key_len = strlen (key) + 1; nbytes = __write (sock, &req, sizeof (request_header)); if (nbytes != sizeof (request_header)) { @@ -139,68 +139,42 @@ __nscd_getpw_r (const char *key, request_type type, struct passwd *resultbuf, if (pw_resp.found == 1) { - struct iovec vec[5]; char *p = buffer; + size_t total = (pw_resp.pw_name_len + pw_resp.pw_passwd_len + + pw_resp.pw_gecos_len + pw_resp.pw_dir_len + + pw_resp.pw_shell_len); - if (buflen < (pw_resp.pw_name_len + 1 + pw_resp.pw_passwd_len + 1 - + pw_resp.pw_gecos_len + 1 + pw_resp.pw_dir_len + 1 - + pw_resp.pw_shell_len + 1)) + if (buflen < total) { __set_errno (ERANGE); __close (sock); return -1; } + /* Set the information we already have. */ + resultbuf->pw_uid = pw_resp.pw_uid; + resultbuf->pw_gid = pw_resp.pw_gid; + /* get pw_name */ - vec[0].iov_base = p; - vec[0].iov_len = pw_resp.pw_name_len; - p += pw_resp.pw_name_len + 1; - buflen -= (pw_resp.pw_name_len + 1); + resultbuf->pw_name = p; + p += pw_resp.pw_name_len; /* get pw_passwd */ - vec[1].iov_base = p; - vec[1].iov_len = pw_resp.pw_passwd_len; - p += pw_resp.pw_passwd_len + 1; - buflen -= (pw_resp.pw_passwd_len + 1); + resultbuf->pw_passwd = p; + p += pw_resp.pw_passwd_len; /* get pw_gecos */ - vec[2].iov_base = p; - vec[2].iov_len = pw_resp.pw_gecos_len; - p += pw_resp.pw_gecos_len + 1; - buflen -= (pw_resp.pw_gecos_len + 1); + resultbuf->pw_gecos = p; + p += pw_resp.pw_gecos_len; /* get pw_dir */ - vec[3].iov_base = p; - vec[3].iov_len = pw_resp.pw_dir_len; - p += pw_resp.pw_dir_len + 1; - buflen -= (pw_resp.pw_dir_len + 1); + resultbuf->pw_dir = p; + p += pw_resp.pw_dir_len; /* get pw_pshell */ - vec[4].iov_base = p; - vec[4].iov_len = pw_resp.pw_shell_len; - p += pw_resp.pw_shell_len + 1; - buflen -= (pw_resp.pw_shell_len + 1); - - nbytes = __readv (sock, vec, 5); - if (nbytes != (pw_resp.pw_name_len + pw_resp.pw_passwd_len - + pw_resp.pw_gecos_len + pw_resp.pw_dir_len - + pw_resp.pw_shell_len)) - { - __close (sock); - return 1; - } + resultbuf->pw_shell = p; - resultbuf->pw_name = vec[0].iov_base; - resultbuf->pw_name[pw_resp.pw_name_len] = '\0'; - resultbuf->pw_passwd = vec[1].iov_base; - resultbuf->pw_passwd[pw_resp.pw_passwd_len] = '\0'; - resultbuf->pw_uid = pw_resp.pw_uid; - resultbuf->pw_gid = pw_resp.pw_gid; - resultbuf->pw_gecos = vec[2].iov_base; - resultbuf->pw_gecos[pw_resp.pw_gecos_len] = '\0'; - resultbuf->pw_dir = vec[3].iov_base; - resultbuf->pw_dir[pw_resp.pw_dir_len] = '\0'; - resultbuf->pw_shell = vec[4].iov_base; - resultbuf->pw_shell[pw_resp.pw_shell_len] = '\0'; + nbytes = __read (sock, buffer, total); __close (sock); - return 0; + + return nbytes == total ? 0 : 1; } else { diff --git a/nscd/nscd_proto.h b/nscd/nscd_proto.h index f82f86c..760b57d 100644 --- a/nscd/nscd_proto.h +++ b/nscd/nscd_proto.h @@ -21,11 +21,13 @@ #define _NSCD_PROTO_H 1 #include <grp.h> +#include <netdb.h> #include <pwd.h> /* Variables for communication between NSCD handler functions and NSS. */ extern int __nss_not_use_nscd_passwd; extern int __nss_not_use_nscd_group; +extern int __nss_not_use_nscd_hosts; extern int __nscd_getpwnam_r __P ((const char *name, struct passwd *resultbuf, char *buffer, size_t buflen)); @@ -35,5 +37,17 @@ extern int __nscd_getgrnam_r __P ((const char *name, struct group *resultbuf, char *buffer, size_t buflen)); extern int __nscd_getgrgid_r __P ((uid_t uid, struct group *resultbuf, char *buffer, size_t buflen)); +extern int __nscd_gethostbyname_r __P ((const char *name, + struct hostent *resultbuf, + char *buffer, size_t buflen, + int *h_errnop)); +extern int __nscd_gethostbyname2_r __P ((const char *name, int af, + struct hostent *resultbuf, + char *buffer, size_t buflen, + int *h_errnop)); +extern int __nscd_gethostbyaddr_r __P ((const char *addr, int len, int type, + struct hostent *resultbuf, + char *buffer, size_t buflen, + int *h_errnop)); #endif /* _NSCD_PROTO_H */ diff --git a/nscd/nscd_stat.c b/nscd/nscd_stat.c index d818288..f9d21ae 100644 --- a/nscd/nscd_stat.c +++ b/nscd/nscd_stat.c @@ -17,71 +17,160 @@ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include <errno.h> +#include <error.h> +#include <langinfo.h> #include <stdio.h> #include <stdlib.h> +#include <string.h> #include <unistd.h> -#include <sys/types.h> + #include "nscd.h" +#include "dbg_log.h" + +/* We use this to make sure the receiver is the same. */ +static const char compilation[21] = __DATE__ " " __TIME__; + +/* Statistic data for one database. */ +struct dbstat +{ + int enabled; + int check_file; + size_t module; + + unsigned long int postimeout; + unsigned long int negtimeout; + + unsigned long int poshit; + unsigned long int neghit; + unsigned long int posmiss; + unsigned long int negmiss; +}; + +/* Record for transmitting statistics. */ +struct statdata +{ + char version[sizeof (compilation)]; + int debug_level; + int ndbs; + struct dbstat dbs[lastdb]; +}; + void -print_stat (void) +send_stats (int fd, struct database dbs[lastdb]) { - int sock = __nscd_open_socket (); - request_header req; - stat_response_header resp; - ssize_t nbytes; + struct statdata data; + int cnt; + + memcpy (data.version, compilation, sizeof (compilation)); + data.debug_level = debug_level; + data.ndbs = lastdb; + + for (cnt = 0; cnt < lastdb; ++cnt) + { + data.dbs[cnt].enabled = dbs[cnt].enabled; + data.dbs[cnt].check_file = dbs[cnt].check_file; + data.dbs[cnt].module = dbs[cnt].module; + data.dbs[cnt].postimeout = dbs[cnt].postimeout; + data.dbs[cnt].negtimeout = dbs[cnt].negtimeout; + data.dbs[cnt].poshit = dbs[cnt].poshit; + data.dbs[cnt].neghit = dbs[cnt].neghit; + data.dbs[cnt].posmiss = dbs[cnt].posmiss; + data.dbs[cnt].negmiss = dbs[cnt].negmiss; + } - if (sock == -1) + if (TEMP_FAILURE_RETRY (write (fd, &data, sizeof (data))) != sizeof (data)) { - fputs (_("nscd not running!\n"), stdout); - exit (EXIT_FAILURE); + char buf[256]; + dbg_log (_("cannot write statistics: %s"), + strerror_r (errno, buf, sizeof (buf))); } +} + + +int +receive_print_stats (void) +{ + struct statdata data; + request_header req; + ssize_t nbytes; + int fd; + int i; + /* Open a socket to the running nscd. */ + fd = nscd_open_socket (); + if (fd == -1) + error (EXIT_FAILURE, 0, _("nscd not running!\n")); + + /* Send the request. */ req.version = NSCD_VERSION; req.type = GETSTAT; req.key_len = 0; - nbytes = write (sock, &req, sizeof (request_header)); + nbytes = TEMP_FAILURE_RETRY (write (fd, &req, sizeof (request_header))); if (nbytes != sizeof (request_header)) { - perror (_("write incomplete")); - close (sock); - exit (EXIT_FAILURE); + int err = errno; + close (fd); + error (EXIT_FAILURE, err, _("write incomplete")); } - nbytes = read (sock, &resp, sizeof (stat_response_header)); - if (nbytes != sizeof (stat_response_header)) + /* Read as much data as we expect. */ + if (TEMP_FAILURE_RETRY (read (fd, &data, sizeof (data))) != sizeof (data) + || (memcmp (data.version, compilation, sizeof (compilation)) != 0 + /* Yes, this is an assignment! */ + && errno == EINVAL)) { - perror (_("read incomplete")); - close (sock); - exit (EXIT_FAILURE); + /* Not the right version. */ + int err = errno; + close (fd); + error (EXIT_FAILURE, err, _("cannot read statistics data")); } - close (sock); - - printf (_("nscd configuration:\n\n")); - printf (_("%12d server debug level\n\n"), resp.debug_level); - - printf (_("passwd cache:\n\n")); - printf (_("%12s cache is enabled\n"), resp.pw_enabled ? _("Yes") : _("No")); - printf (_("%12ld cache hits on positive entries\n"), resp.pw_poshit); - printf (_("%12ld cache hits on negative entries\n"), resp.pw_neghit); - printf (_("%12ld cache misses on positive entries\n"), resp.pw_posmiss); - printf (_("%12ld cache misses on negative entries\n"), resp.pw_negmiss); - printf (_("%12ld suggested size\n"), resp.pw_size); - printf (_("%12ld seconds time to live for positive entries\n"), - resp.pw_posttl); - printf (_("%12ld seconds time to live for negative entries\n\n"), - resp.pw_negttl); - - printf (_("group cache:\n\n")); - printf (_("%12s cache is enabled\n"), resp.gr_enabled ? _("Yes") : _("No")); - printf (_("%12ld cache hits on positive entries\n"), resp.gr_poshit); - printf (_("%12ld cache hits on negative entries\n"), resp.gr_neghit); - printf (_("%12ld cache misses on positive entries\n"), resp.gr_posmiss); - printf (_("%12ld cache misses on negative entries\n"), resp.gr_negmiss); - printf (_("%12ld suggested size\n"), resp.gr_size); - printf (_("%12ld seconds time to live for positive entries\n"), - resp.gr_posttl); - printf (_("%12ld seconds time to live for negative entries\n"), - resp.gr_negttl); + printf (_("nscd configuration:\n\n%15d server debug level\n"), + data.debug_level); + + for (i = 0; i < lastdb; ++i) + { + unsigned long int hit = data.dbs[i].poshit + data.dbs[i].neghit; + unsigned long int all = hit + data.dbs[i].posmiss + data.dbs[i].negmiss; + const char *enabled = nl_langinfo (data.dbs[i].enabled ? YESSTR : NOSTR); + const char *check_file = nl_langinfo (data.dbs[i].check_file + ? YESSTR : NOSTR); + + if (enabled[0] == '\0') + /* The locale does not provide this information so we have to + translate it ourself. Since we should avoid short translation + terms we artifically increase the length. */ + enabled = data.dbs[i].enabled ? _(" yes") : _(" no"); + if (check_file[0] == '\0') + check_file = data.dbs[i].check_file ? _(" yes") : _(" no"); + + if (all == 0) + /* If nothing happened so far report a 0% hit rate. */ + all = 1; + + printf (_("\n%s cache:\n\n" + "%15s cache is enabled\n" + "%15Zd suggested size\n" + "%15ld seconds time to live for positive entries\n" + "%15ld seconds time to live for negative entries\n" + "%15ld cache hits on positive entries\n" + "%15ld cache hits on negative entries\n" + "%15ld cache misses on positive entries\n" + "%15ld cache misses on negative entries\n" + "%15ld%% cache hit rate\n" + "%15s check /etc/%s for changes\n"), + dbnames[i], enabled, + data.dbs[i].module, + data.dbs[i].postimeout, data.dbs[i].negtimeout, + data.dbs[i].poshit, data.dbs[i].neghit, + data.dbs[i].posmiss, data.dbs[i].negmiss, + (100 * hit) / all, + check_file, dbnames[i]); + } + + close (fd); + + exit (0); } diff --git a/nscd/pwdcache.c b/nscd/pwdcache.c index 85b4fe9..5bf89a7 100644 --- a/nscd/pwdcache.c +++ b/nscd/pwdcache.c @@ -1,6 +1,7 @@ -/* Copyright (c) 1998 Free Software Foundation, Inc. +/* Cache handling for passwd lookup. + Copyright (C) 1998 Free Software Foundation, Inc. This file is part of the GNU C Library. - Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1998. + Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as @@ -15,597 +16,231 @@ You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ + Boston, MA 02111-1307, USA. */ #include <errno.h> -#include <malloc.h> -#include <pthread.h> +#include <error.h> #include <pwd.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> #include <string.h> +#include <time.h> #include <unistd.h> -#include <rpcsvc/nis.h> -#include <sys/types.h> -#include "dbg_log.h" #include "nscd.h" +#include "dbg_log.h" -static unsigned long int modulo = 211; -static unsigned long int postimeout = 600; -static unsigned long int negtimeout = 20; - -static unsigned long int poshit = 0; -static unsigned long int posmiss = 0; -static unsigned long int neghit = 0; -static unsigned long int negmiss = 0; - -struct pwdhash -{ - time_t create; - struct pwdhash *next; - struct passwd *pwd; -}; -typedef struct pwdhash pwdhash; - -struct uidhash -{ - struct uidhash *next; - struct passwd *pwptr; +/* This is the standard reply in case the service is disabled. */ +static const pw_response_header disabled = +{ + version: NSCD_VERSION, + found: -1, + pw_name_len: 0, + pw_passwd_len: 0, + pw_uid: -1, + pw_gid: -1, + pw_gecos_len: 0, + pw_dir_len: 0, + pw_shell_len: 0 }; -typedef struct uidhash uidhash; -struct neghash +/* This is the struct describing how to write this record. */ +const struct iovec pwd_iov_disabled = { - time_t create; - struct neghash *next; - char *key; + iov_base: (void *) &disabled, + iov_len: sizeof (disabled) }; -typedef struct neghash neghash; - -static pwdhash *pwdtbl; -static uidhash *uidtbl; -static neghash *negtbl; -static pthread_rwlock_t pwdlock = PTHREAD_RWLOCK_INITIALIZER; -static pthread_rwlock_t neglock = PTHREAD_RWLOCK_INITIALIZER; -static void *pwdtable_update (void *); -static void *negtable_update (void *); - -void -get_pw_stat (stat_response_header *stat) +/* This is the standard reply in case we haven't found the dataset. */ +static const pw_response_header notfound = { - stat->pw_poshit = poshit; - stat->pw_posmiss = posmiss; - stat->pw_neghit = neghit; - stat->pw_negmiss = negmiss; - stat->pw_size = modulo; - stat->pw_posttl = postimeout; - stat->pw_negttl = negtimeout; -} - -void -set_pwd_modulo (unsigned long int mod) -{ - modulo = mod; -} + version: NSCD_VERSION, + found: 0, + pw_name_len: 0, + pw_passwd_len: 0, + pw_uid: -1, + pw_gid: -1, + pw_gecos_len: 0, + pw_dir_len: 0, + pw_shell_len: 0 +}; -void -set_pos_pwd_ttl (unsigned long int ttl) +/* This is the struct describing how to write this record. */ +static const struct iovec iov_notfound = { - postimeout = ttl; -} + iov_base: (void *) ¬found, + iov_len: sizeof (notfound) +}; -void -set_neg_pwd_ttl (unsigned long int ttl) -{ - negtimeout = ttl; -} -int -cache_pwdinit () +struct passwddata { - pthread_attr_t attr; - pthread_t thread; - - pwdtbl = calloc (modulo, sizeof (pwdhash)); - if (pwdtbl == NULL) - return -1; - uidtbl = calloc (modulo, sizeof (uidhash)); - if (uidtbl == NULL) - return -1; - negtbl = calloc (modulo, sizeof (neghash)); - if (negtbl == NULL) - return -1; - - pthread_attr_init (&attr); - pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); - - pthread_create (&thread, NULL, pwdtable_update, &attr); - pthread_create (&thread, NULL, negtable_update, &attr); + pw_response_header resp; + char strdata[0]; +}; - pthread_attr_destroy (&attr); - - return 0; -} - -static struct passwd * -save_pwd (struct passwd *src) -{ - struct passwd *dest; - size_t name_len = strlen (src->pw_name) + 1; - size_t passwd_len = strlen (src->pw_gecos) + 1; - size_t gecos_len = strlen (src->pw_dir) + 1; - size_t dir_len = strlen (src->pw_dir) + 1; - size_t shell_len = strlen (src->pw_shell) + 1; - char *cp; - - dest = malloc (sizeof (struct passwd) - + name_len + passwd_len + gecos_len + dir_len + shell_len); - if (dest == NULL) - return NULL; - - cp = (char *) (dest + 1); - dest->pw_name = cp; - cp = mempcpy (cp, src->pw_name, name_len); - dest->pw_passwd = cp; - cp = mempcpy (cp, src->pw_passwd, passwd_len); - dest->pw_uid = src->pw_uid; - dest->pw_gid = src->pw_gid; - dest->pw_gecos = cp; - cp = mempcpy (cp, src->pw_gecos, gecos_len); - dest->pw_dir = cp; - cp = mempcpy (cp, src->pw_dir, dir_len); - dest->pw_shell = cp; - mempcpy (cp, src->pw_shell, shell_len); - - return dest; -} static void -free_pwd (struct passwd *src) -{ - free (src); -} - -static int -add_cache (struct passwd *pwd) +cache_addpw (struct database *db, int fd, request_header *req, void *key, + struct passwd *pwd) { - pwdhash *work; - uidhash *uidwork; - unsigned long int hash = __nis_hash (pwd->pw_name, - strlen (pwd->pw_name)) % modulo; - - if (debug_flag) - dbg_log (_("pwd_add_cache (%s)"), pwd->pw_name); - - work = &pwdtbl[hash]; - - if (pwdtbl[hash].pwd == NULL) - pwdtbl[hash].pwd = save_pwd (pwd); - else - { - while (work->next != NULL) - work = work->next; - - work->next = calloc (1, sizeof (pwdhash)); - work->next->pwd = save_pwd (pwd); - work = work->next; - } - /* Set a pointer from the pwuid hash table to the pwname hash table */ - time (&work->create); - uidwork = &uidtbl[pwd->pw_uid % modulo]; - if (uidwork->pwptr == NULL) - uidwork->pwptr = work->pwd; - else - { - while (uidwork->next != NULL) - uidwork = uidwork->next; - - uidwork->next = calloc (1, sizeof (uidhash)); - uidwork->next->pwptr = work->pwd; - } - return 0; -} - -static struct passwd * -cache_search_name (const char *name) -{ - pwdhash *work; - unsigned long int hash = __nis_hash (name, strlen (name)) % modulo; - - work = &pwdtbl[hash]; + ssize_t total; + ssize_t written; + time_t t = time (NULL); - while (work->pwd != NULL) + if (pwd == NULL) { - if (strcmp (work->pwd->pw_name, name) == 0) - return work->pwd; - if (work->next != NULL) - work = work->next; - else - return NULL; - } - return NULL; -} + /* We have no data. This means we send the standard reply for this + case. */ + void *copy; -static struct passwd * -cache_search_uid (uid_t uid) -{ - uidhash *work; + total = sizeof (notfound); - work = &uidtbl[uid % modulo]; + written = writev (fd, &iov_notfound, 1); - while (work->pwptr != NULL) - { - if (work->pwptr->pw_uid == uid) - return work->pwptr; - if (work->next != NULL) - work = work->next; - else - return NULL; - } - return NULL; -} + copy = malloc (req->key_len); + if (copy == NULL) + error (EXIT_FAILURE, errno, _("while allocating key copy")); + memcpy (copy, key, req->key_len); -static int -add_negcache (char *key) -{ - neghash *work; - unsigned long int hash = __nis_hash (key, strlen (key)) % modulo; + /* Compute the timeout time. */ + t += db->negtimeout; - if (debug_flag) - dbg_log (_("pwd_add_netgache (%s|%ld)"), key, hash); + /* Now get the lock to safely insert the records. */ + pthread_rwlock_rdlock (&db->lock); - work = &negtbl[hash]; + cache_add (req->type, copy, req->key_len, &iov_notfound, + sizeof (notfound), (void *) -1, 0, t, db); - if (negtbl[hash].key == NULL) - { - negtbl[hash].key = strdup (key); - negtbl[hash].next = NULL; + pthread_rwlock_unlock (&db->lock); } else { - while (work->next != NULL) - work = work->next; - - work->next = calloc (1, sizeof (neghash)); - work->next->key = strdup (key); - work = work->next; + /* Determine the I/O structure. */ + struct passwddata *data; + size_t pw_name_len = strlen (pwd->pw_name) + 1; + size_t pw_passwd_len = strlen (pwd->pw_passwd) + 1; + size_t pw_gecos_len = strlen (pwd->pw_gecos) + 1; + size_t pw_dir_len = strlen (pwd->pw_dir) + 1; + size_t pw_shell_len = strlen (pwd->pw_shell) + 1; + char *cp; + char buf[12]; + ssize_t n; + + /* We need this to insert the `byuid' entry. */ + n = snprintf (buf, sizeof (buf), "%d", pwd->pw_uid) + 1; + + /* We allocate all data in one memory block: the iov vector, + the response header and the dataset itself. */ + total = (sizeof (struct passwddata) + pw_name_len + pw_passwd_len + + pw_gecos_len + pw_dir_len + pw_shell_len); + data = (struct passwddata *) malloc (total + n); + if (data == NULL) + /* There is no reason to go on. */ + error (EXIT_FAILURE, errno, _("while allocating cache entry")); + + data->resp.found = 1; + data->resp.pw_name_len = pw_name_len; + data->resp.pw_passwd_len = pw_passwd_len; + data->resp.pw_uid = pwd->pw_uid; + data->resp.pw_gid = pwd->pw_gid; + data->resp.pw_gecos_len = pw_gecos_len; + data->resp.pw_dir_len = pw_dir_len; + data->resp.pw_shell_len = pw_shell_len; + + cp = data->strdata; + + /* Copy the strings over into the buffer. */ + cp = mempcpy (cp, pwd->pw_name, pw_name_len); + cp = mempcpy (cp, pwd->pw_passwd, pw_passwd_len); + cp = mempcpy (cp, pwd->pw_gecos, pw_gecos_len); + cp = mempcpy (cp, pwd->pw_dir, pw_dir_len); + cp = mempcpy (cp, pwd->pw_shell, pw_shell_len); + + /* Finally the stringified UID value. */ + memcpy (cp, buf, n); + + /* We write the dataset before inserting it to the database + since while inserting this thread might block and so would + unnecessarily let the receiver wait. */ + written = write (fd, &data->resp, total); + + /* Compute the timeout time. */ + t += db->postimeout; + + /* Now get the lock to safely insert the records. */ + pthread_rwlock_rdlock (&db->lock); + + /* We have to add the value for both, byname and byuid. */ + cache_add (GETPWBYNAME, data->strdata, pw_name_len, data, + total, data, 0, t, db); + + cache_add (GETPWBYUID, cp, n, data, total, data, 1, t, db); + + pthread_rwlock_unlock (&db->lock); } - time (&work->create); - - return 0; -} - -static int -cache_search_neg (const char *key) -{ - neghash *work; - unsigned long int hash = __nis_hash (key, strlen (key)) % modulo; - - if (debug_flag) - dbg_log (_("pwd_cache_search_neg (%s|%ld)"), key, hash); - - work = &negtbl[hash]; - - while (work->key != NULL) + if (written != total) { - if (strcmp (work->key, key) == 0) - return 1; - if (work->next != NULL) - work = work->next; - else - return 0; + char buf[256]; + dbg_log (_("short write in %s: %s"), __FUNCTION__, + strerror_r (errno, buf, sizeof (buf))); } - return 0; } -void * -cache_getpwnam (void *v_param) -{ - struct passwd *pwd; - param_t *param = (param_t *)v_param; - - pthread_rwlock_rdlock (&pwdlock); - pwd = cache_search_name (param->key); - /* I don't like it to hold the read only lock longer, but it is - necessary to avoid to much malloc/free/strcpy. */ - - if (pwd != NULL) - { - if (debug_flag) - dbg_log (_("Found \"%s\" in cache !"), param->key); +void +addpwbyname (struct database *db, int fd, request_header *req, void *key) +{ + /* Search for the entry matching the key. Please note that we don't + look again in the table whether the dataset is now available. We + simply insert it. It does not matter if it is in there twice. The + pruning function only will look at the timestamp. */ + int buflen = 256; + char *buffer = alloca (buflen); + struct passwd resultbuf; + struct passwd *pwd; - ++poshit; - pw_send_answer (param->conn, pwd); - close_socket (param->conn); + if (debug_level > 0) + dbg_log (_("Haven't found \"%s\" in password cache!"), key); - pthread_rwlock_unlock (&pwdlock); - } - else + while (getpwnam_r (key, &resultbuf, buffer, buflen, &pwd) != 0 + && errno == ERANGE) { - int status; - int buflen = 1024; - char *buffer = calloc (1, buflen); - struct passwd resultbuf; - - if (debug_flag) - dbg_log (_("Doesn't found \"%s\" in cache !"), param->key); - - pthread_rwlock_unlock (&pwdlock); - - pthread_rwlock_rdlock (&neglock); - status = cache_search_neg (param->key); - pthread_rwlock_unlock (&neglock); - - if (status == 0) - { - while (buffer != NULL - && (getpwnam_r (param->key, &resultbuf, buffer, buflen, &pwd) - != 0) - && errno == ERANGE) - { - errno = 0; - buflen += 1024; - buffer = realloc (buffer, buflen); - } - - if (buffer != NULL && pwd != NULL) - { - struct passwd *tmp; - - ++posmiss; - pthread_rwlock_wrlock (&pwdlock); - /* While we are waiting on the lock, somebody else could - add this entry. */ - tmp = cache_search_name (param->key); - if (tmp == NULL) - add_cache (pwd); - pthread_rwlock_unlock (&pwdlock); - } - else - { - ++negmiss; - pthread_rwlock_wrlock (&neglock); - add_negcache (param->key); - pthread_rwlock_unlock (&neglock); - } - } - else - ++neghit; - pw_send_answer (param->conn, pwd); - close_socket (param->conn); - if (buffer != NULL) - free (buffer); + errno = 0; + buflen += 256; + buffer = alloca (buflen); } - free (param->key); - free (param); - return NULL; -} - -void * -cache_pw_disabled (void *v_param) -{ - param_t *param = (param_t *)v_param; - - if (debug_flag) - dbg_log (_("\tpasswd cache is disabled\n")); - pw_send_disabled (param->conn); - return NULL; + cache_addpw (db, fd, req, key, pwd); } -void * -cache_getpwuid (void *v_param) -{ - param_t *param = (param_t *)v_param; - struct passwd *pwd, resultbuf; - uid_t uid = strtol (param->key, NULL, 10); - - pthread_rwlock_rdlock (&pwdlock); - pwd = cache_search_uid (uid); - - /* I don't like it to hold the read only lock longer, but it is - necessary to avoid to much malloc/free/strcpy. */ - if (pwd != NULL) - { - if (debug_flag) - dbg_log (_("Found \"%d\" in cache !"), uid); - - ++poshit; - pw_send_answer (param->conn, pwd); - close_socket (param->conn); - - pthread_rwlock_unlock (&pwdlock); - } - else - { - int buflen = 1024; - char *buffer = malloc (buflen); - int status; - - if (debug_flag) - dbg_log (_("Doesn't found \"%d\" in cache !"), uid); - - pthread_rwlock_unlock (&pwdlock); - - pthread_rwlock_rdlock (&neglock); - status = cache_search_neg (param->key); - pthread_rwlock_unlock (&neglock); - - if (status == 0) - { - while (buffer != NULL - && (getpwuid_r (uid, &resultbuf, buffer, buflen, &pwd) != 0) - && errno == ERANGE) - { - errno = 0; - buflen += 1024; - buffer = realloc (buffer, buflen); - } - - if (buffer != NULL && pwd != NULL) - { - struct passwd *tmp; - - ++posmiss; - pthread_rwlock_wrlock (&pwdlock); - /* While we are waiting on the lock, somebody else could - add this entry. */ - tmp = cache_search_uid (uid); - if (tmp == NULL) - add_cache (pwd); - pthread_rwlock_unlock (&pwdlock); - } - else - { - ++negmiss; - pthread_rwlock_wrlock (&neglock); - add_negcache (param->key); - pthread_rwlock_unlock (&neglock); - } - } - else - ++neghit; - - pw_send_answer (param->conn, pwd); - close_socket (param->conn); - if (buffer != NULL) - free (buffer); - } - free (param->key); - free (param); - return NULL; -} - -static void * -pwdtable_update (void *v) -{ - time_t now; - int i; +void +addpwbyuid (struct database *db, int fd, request_header *req, void *key) +{ + /* Search for the entry matching the key. Please note that we don't + look again in the table whether the dataset is now available. We + simply insert it. It does not matter if it is in there twice. The + pruning function only will look at the timestamp. */ + int buflen = 256; + char *buffer = alloca (buflen); + struct passwd resultbuf; + struct passwd *pwd; + uid_t uid = atol (key); - sleep (20); + if (debug_level > 0) + dbg_log (_("Haven't found \"%d\" in password cache!"), uid); - while (!do_shutdown) + while (getpwuid_r (uid, &resultbuf, buffer, buflen, &pwd) != 0 + && errno == ERANGE) { - if (debug_flag > 2) - dbg_log (_("(pwdtable_update) Wait for write lock!")); - - pthread_rwlock_wrlock (&pwdlock); - - if (debug_flag > 2) - dbg_log (_("(pwdtable_update) Have write lock")); - - time (&now); - for (i = 0; i < modulo; ++i) - { - pwdhash *work = &pwdtbl[i]; - - while (work && work->pwd) - { - if ((now - work->create) >= postimeout) - { - uidhash *uh = &uidtbl[work->pwd->pw_uid % modulo]; - - if (debug_flag) - dbg_log (_("Give \"%s\" free"), work->pwd->pw_name); - - while (uh != NULL && uh->pwptr) - { - if (uh->pwptr->pw_uid == work->pwd->pw_uid) - { - if (debug_flag) - dbg_log (_("Give uid for \"%s\" free"), - work->pwd->pw_name); - if (uh->next != NULL) - { - uidhash *tmp = uh->next; - uh->pwptr = tmp->pwptr; - uh->next = tmp->next; - free (tmp); - } - else - uh->pwptr = NULL; - } - uh = uh->next; - } - - free_pwd (work->pwd); - if (work->next != NULL) - { - pwdhash *tmp = work->next; - work->create = tmp->create; - work->next = tmp->next; - work->pwd = tmp->pwd; - free (tmp); - } - else - work->pwd = NULL; - } - work = work->next; - } - } - if (debug_flag > 2) - dbg_log (_("(pwdtable_update) Release wait lock")); - pthread_rwlock_unlock (&pwdlock); - sleep (20); + errno = 0; + buflen += 256; + buffer = alloca (buflen); } - return NULL; -} - -static void * -negtable_update (void *v) -{ - time_t now; - int i; - sleep (30); - - while (!do_shutdown) - { - if (debug_flag > 2) - dbg_log (_("(negpwdtable_update) Wait for write lock!")); - - pthread_rwlock_wrlock (&neglock); - - if (debug_flag > 2) - dbg_log (_("(negpwdtable_update) Have write lock")); - - time (&now); - for (i = 0; i < modulo; ++i) - { - neghash *work = &negtbl[i]; - - while (work && work->key) - { - if ((now - work->create) >= negtimeout) - { - if (debug_flag) - dbg_log (_("Give \"%s\" free"), work->key); - - free (work->key); - - if (work->next != NULL) - { - neghash *tmp = work->next; - work->create = tmp->create; - work->next = tmp->next; - work->key = tmp->key; - free (tmp); - } - else - work->key = NULL; - } - work = work->next; - } - } - if (debug_flag > 2) - dbg_log (_("(negpwdtable_update) Release wait lock")); - - pthread_rwlock_unlock (&neglock); - sleep (10); - } - return NULL; + cache_addpw (db, fd, req, key, pwd); } |