1998-10-18  Ulrich Drepper  <drepper@cygnus.com>

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

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

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

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

	* nss/Versions: Add __nss_hosts_lookup.

	* nss/getXXbyYY.c: Remove unneeded assignment.

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

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

View File

@ -1,3 +1,56 @@
1998-10-18 Ulrich Drepper <drepper@cygnus.com>
* resolv/nss_dns/dns-host.c: Add missing errnop parameter to the
NSS functions.
* resolv/nss_dns/dns-network.c: Likewise.
* grp/Makefile: Don't search for linuxhtreads in add-ons, use
have-thread-library to determine whether threads are available.
* pwd/Makefile: Remove wrong comment.
* inet/Makefile: Define CFLAGS-gethstbyad_r.c, CFLAGS-gethstbynm_r.c,
and CFLAGS-gethstbynm2_r.c to -DUSE_NSCD=1.
* locale/C-messages.c: Define default strings for YESTR and NOSTR.
* nss/Versions: Add __nss_hosts_lookup.
* nss/getXXbyYY.c: Remove unneeded assignment.
* nss/getXXbyYY_r.c: Include nscd/nscd_proto.h only if needed.
Almost complete rewrite of the NSCD to make it smaller, faster,
add more functionnality and make it easier to extend.
* nscd/Makfile (routines): Add nscd_gethst_r.
(nscd-modules): Add hstcache, gethstbyad_r, gethstbynm2_r, and cache.
* nscd/cache.c: New file.
* nscd/gethstbyad_r.c: New file.
* nscd/gethstbynm2_r.c: New file.
* nscd/hstcache.c: New file.
* nscd/nscd_gethst_r.c: New file.
* nscd/connections.c: Rewritten. Don't start new thread for every
new connection. Use a fixed set of threads which handle all
connections and also the cache cleanup.
* nscd/grpcache.c: Rewritten to use generic cache handling functions
in cache.c.
* nscd/nscd.c: Recognize new parameter nthreads. Adjust initialization
for rewrite. Remove handle_requests function.
* nscd/nscd.h (NSCD_VERSION): Bump to 2.
Define new data structure for the new unified cache and the host
database entries.
* nscd/nscd_conf.c: Rewrite parsing partly to allow adding of more
databases easily. Recognize check-files and threads definitions.
* nscd/nscd.conf: Add definition of enable-cache and check-files to
passwd and group definitions. Add new set of definitions for hosts.
* nscd/nscd_getgr_r.c: Rewrite for new protocol.
* nscd/nscd_getpw_r.c: Likewise.
* nscd/nscd_proto.h: Add prototype for host database functions.
* nscd/nscd_stat.c: Rewrite to simplify printing of information
for many databases.
* nscd/dbg_log.c: Remove unnecessary variable initializations.
Global variable debug_flag is renamed to dbg_level.
* nscd/dbg_log.h: Declare set_logfile.
1998-10-16 Ulrich Drepper <drepper@cygnus.com> 1998-10-16 Ulrich Drepper <drepper@cygnus.com>
* sysdeps/unix/sysv/linux/bits/fcntl.h: Add dummy definition of * sysdeps/unix/sysv/linux/bits/fcntl.h: Add dummy definition of

View File

@ -149,3 +149,13 @@ contact <bug-glibc@gnu.org>.
{ {
... use the plain file if it exists, otherwise the db ... ... use the plain file if it exists, otherwise the db ...
} }
[23] The `strptime' function needs to be completed. This includes among
other things that it must get teached about timezones. The solution
envisioned is to extract the timezones from the ADO timezone
specifications. Special care must be given names which are used
multiple times. Here the precedence should (probably) be according
to the geograhical distance. E.g., the timezone EST should be
treated as the `Eastern Australia Time' instead of the US `Eastern
Standard Time' if the current TZ variable is set to, say,
Australia/Canberra or if the current locale is en_AU.

View File

@ -30,8 +30,7 @@ tests := testgrp
include ../Rules include ../Rules
# We can later add the names of other thread packages here. ifeq ($(have-thread-library),yes)
ifneq (,$(findstring linuxthreads,$(add-ons)))
CFLAGS-getgrgid_r.c = -DUSE_NSCD=1 CFLAGS-getgrgid_r.c = -DUSE_NSCD=1
CFLAGS-getgrnam_r.c = -DUSE_NSCD=1 CFLAGS-getgrnam_r.c = -DUSE_NSCD=1

View File

@ -54,3 +54,11 @@ CFLAGS-rexec.c = -w
CFLAGS-ruserpass.c = -w CFLAGS-ruserpass.c = -w
include ../Rules include ../Rules
ifeq ($(have-thread-library),yes)
CFLAGS-gethstbyad_r.c = -DUSE_NSCD=1
CFLAGS-gethstbynm_r.c = -DUSE_NSCD=1
CFLAGS-gethstbynm2_r.c = -DUSE_NSCD=1
endif

View File

@ -1,4 +1,4 @@
/* Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc. /* Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
This file is part of the GNU C Library. This file is part of the GNU C Library.
Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995. Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
@ -33,7 +33,7 @@ const struct locale_data _nl_C_LC_MESSAGES =
{ {
{ string: "^[yY]" }, { string: "^[yY]" },
{ string: "^[nN]" }, { string: "^[nN]" },
{ string: "" }, { string: "yes" },
{ string: "" } { string: "no" }
} }
}; };

View File

@ -21,12 +21,13 @@
# #
subdir := nscd subdir := nscd
routines := nscd_getpw_r nscd_getgr_r routines := nscd_getpw_r nscd_getgr_r nscd_gethst_r
include ../Makeconfig include ../Makeconfig
nscd-modules := nscd connections pwdcache getpwnam_r getpwuid_r grpcache \ 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) ifeq ($(have-thread-library),yes)

247
nscd/cache.c Normal file
View File

@ -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);
}
}

View File

@ -1,6 +1,7 @@
/* Copyright (c) 1998 Free Software Foundation, Inc. /* Inner loops of cache daemon.
Copyright (C) 1998 Free Software Foundation, Inc.
This file is part of the GNU C Library. 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 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 modify it under the terms of the GNU Library General Public License as
@ -17,549 +18,405 @@
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 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 <error.h>
#include <fcntl.h> #include <errno.h>
#include <libintl.h>
#include <locale.h>
#include <pthread.h> #include <pthread.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <sys/param.h>
#include <sys/poll.h> #include <sys/poll.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/un.h> #include <sys/un.h>
#include "nscd.h" #include "nscd.h"
#include "dbg_log.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 /* Mapping of request type to database. */
correspondence between sock[i] and read_polls[i]. */ static const dbtype serv2db[LASTDBREQ + 1] =
static int sock[MAX_NUM_CONNECTIONS]; {
static int socks_active; [GETPWBYNAME] = pwddb,
static struct pollfd read_polls[MAX_NUM_CONNECTIONS]; [GETPWBYUID] = pwddb,
static pthread_mutex_t sock_lock = PTHREAD_MUTEX_INITIALIZER; [GETGRBYNAME] = grpdb,
[GETGRBYGID] = grpdb,
[GETHOSTBYNAME] = hstdb,
[GETHOSTBYNAMEv6] = hstdb,
[GETHOSTBYADDR] = hstdb,
[GETHOSTBYADDRv6] = hstdb,
};
/* Map request type to a string. */
const char *serv2str[LASTREQ] =
{
[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
}
};
/* Number of threads to use. */
int nthreads = -1;
/* Socket for incoming connections. */
static int sock;
/* Cleanup. */ /* Initialize database information structures. */
void void
close_sockets (void) nscd_init (const char *conffile)
{
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)
{
if (debug_flag > 2)
dbg_log (_("close socket (%d|%d)"), conn, sock[conn]);
pthread_mutex_lock (&sock_lock);
close (sock[conn]);
sock[conn] = 0;
read_polls[conn].fd = -1;
--socks_active;
pthread_mutex_unlock (&sock_lock);
}
/* Local routine, assigns a socket to a new connection request. */
static void
handle_new_connection (void)
{
int i;
if (debug_flag > 2)
dbg_log (_("handle_new_connection"));
pthread_mutex_lock (&sock_lock);
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
{
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);
}
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 (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))
{
/* Don't close the socket, we still need to send data. Just
stop polling for more data now. */
read_polls[i].fd = -1;
}
}
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);
/* 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)
{
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"));
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)
{
perror (_("Read() error on get request"));
return 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; struct sockaddr_un sock_addr;
int i; size_t cnt;
/* Initialize the connections db. */ /* Read the configuration file. */
socks_active = 0; if (nscd_parse_file (conffile, dbs) != 0)
{
/* 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);
}
if (nthreads == -1)
/* No configuration for this value, assume a default. */
nthreads = 2 * lastdb;
/* Initialize the poll array. */ for (cnt = 0; cnt < lastdb; ++cnt)
for (i = 0; i < MAX_NUM_CONNECTIONS; i++) if (dbs[cnt].enabled)
read_polls[i].fd = -1; {
pthread_rwlock_init (&dbs[cnt].lock, NULL);
dbs[cnt].array = (struct hashentry **)
calloc (dbs[cnt].module, sizeof (struct hashentry *));
if (dbs[cnt].array == NULL)
error (EXIT_FAILURE, errno, "while allocating cache");
if (dbs[cnt].check_file)
{
/* We need the modification date of the file. */
struct stat st;
if (stat (dbs[cnt].filename, &st) < 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
dbs[cnt].file_mtime = st.st_mtime;
}
}
/* Create the socket. */ /* Create the socket. */
sock[0] = socket (AF_UNIX, SOCK_STREAM, 0); sock = socket (AF_UNIX, SOCK_STREAM, 0);
if (sock[0] < 0) if (sock < 0)
{ {
perror (_("cannot create socket")); dbg_log (_("cannot open socket: %s"), strerror (errno));
exit (1); exit (1);
} }
/* Bind a name to the socket. */ /* Bind a name to the socket. */
sock_addr.sun_family = AF_UNIX; sock_addr.sun_family = AF_UNIX;
strcpy (sock_addr.sun_path, _PATH_NSCDSOCKET); 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)); dbg_log ("%s: %s", _PATH_NSCDSOCKET, strerror (errno));
exit (1); exit (1);
} }
/* Set permissions for the socket. */ /* Set permissions for the socket. */
chmod (_PATH_NSCDSOCKET, 0666); chmod (_PATH_NSCDSOCKET, 0666);
/* Set the socket up to accept connections. */ /* 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); 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;
} }
void
pw_send_answer (int conn, struct passwd *pwd)
{
struct iovec vec[6];
pw_response_header resp;
size_t total_len;
int nblocks;
resp.version = NSCD_VERSION; /* Close the connections. */
if (pwd != NULL) void
close_sockets (void)
{
close (sock);
}
/* 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)
{ {
resp.found = 1; dbg_log (_("\
resp.pw_name_len = strlen (pwd->pw_name); cannot handle old request version %d; current version is %d"),
resp.pw_passwd_len = strlen (pwd->pw_passwd); req->version, NSCD_VERSION);
resp.pw_uid = pwd->pw_uid; return;
resp.pw_gid = pwd->pw_gid; }
resp.pw_gecos_len = strlen (pwd->pw_gecos);
resp.pw_dir_len = strlen (pwd->pw_dir); if (req->type >= GETPWBYNAME && req->type <= LASTDBREQ)
resp.pw_shell_len = strlen (pwd->pw_shell); {
struct hashentry *cached;
struct database *db = &dbs[serv2db[req->type]];
if (debug_level > 0)
dbg_log ("\t%s (%s)", serv2str[req->type], key);
/* 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)));
}
return;
}
/* 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)
{
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 ();
}
}
/* 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;
conn.fd = sock;
conn.events = POLLRDNORM;
while (1)
{
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))
{
/* Accept the connection. */
int fd = accept (conn.fd, NULL, NULL);
request_header req;
char buf[256];
if (fd < 0)
{
dbg_log (_("while accepting connection: %s"),
strerror_r (errno, buf, sizeof (buf)));
continue;
}
/* 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;
}
/* 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 else
{ {
resp.found = 0; /* Get the key. */
resp.pw_name_len = 0; char keybuf[req.key_len];
resp.pw_passwd_len = 0;
resp.pw_uid = -1; if (TEMP_FAILURE_RETRY (read (fd, keybuf, req.key_len))
resp.pw_gid = -1; != req.key_len)
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]"), dbg_log (_("short read while reading request key: %s"),
conn, sock[conn]); strerror_r (errno, buf, sizeof (buf)));
return; close (fd);
continue;
} }
/* Add response header. */ /* Phew, we got all the data, now process it. */
vec[0].iov_base = &resp; handle_request (fd, &req, keybuf);
vec[0].iov_len = sizeof (pw_response_header);
total_len = sizeof (pw_response_header);
nblocks = 1;
if (resp.found) /* We are done. */
close (fd);
}
}
if (run_prune)
{ {
/* Add pw_name. */ now = time (NULL);
vec[1].iov_base = pwd->pw_name; timeout = now < next_prune ? 1000 * (next_prune - now) : 0;
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;
} }
/* Send all the data. */
if (writev (sock[conn], vec, nblocks) != total_len)
dbg_log (_("write incomplete on send passwd answer: %s"),
strerror (errno));
} }
/* Start all the threads we want. The initial process is thread no. 1. */
void void
pw_send_disabled (int conn) start_threads (void)
{ {
pw_response_header resp; int i;
pthread_attr_t attr;
pthread_t th;
resp.version = NSCD_VERSION; pthread_attr_init (&attr);
resp.found = -1; pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
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) /* We allow less than LASTDB threads only for debugging. */
{ if (debug_level == 0)
dbg_log (_("bad connection id on send response [%d|%d]"), nthreads = MAX (nthreads, lastdb);
conn, sock[conn]);
return;
}
/* Send response header. */ for (i = 1; i < nthreads; ++i)
if (write (sock[conn], &resp, sizeof (pw_response_header)) pthread_create (&th, &attr, nscd_run, (void *) i);
!= sizeof (pw_response_header))
dbg_log (_("write incomplete on send response: %s"), strerror (errno)); pthread_attr_destroy (&attr);
}
nscd_run ((void *) 0);
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;
}
else
{
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;
}
/* 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));
/* Add response header. */
vec[0].iov_base = &resp;
vec[0].iov_len = sizeof (gr_response_header);
total_len = sizeof (gr_response_header);
nblocks = 1;
if (resp.found)
{
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])
{
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;
do
{
len[l] = strlen (grp->gr_mem[l]);
vec[nblocks].iov_base = grp->gr_mem[l];
vec[nblocks].iov_len = len[l];
total_len += len[l];
++nblocks;
}
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 (sum + writev (sock[conn], vec, nblocks) != total_len)
dbg_log (_("write incomplete on send group answer: %s"),
strerror (errno));
}
void
gr_send_disabled (int conn)
{
gr_response_header resp;
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;
if (sock[conn] == 0)
{
dbg_log (_("bad connection id on send gr_disabled response [%d|%d]"),
conn, sock[conn]);
return;
}
/* 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));
}
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;
}
/* 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));
} }

View File

@ -28,8 +28,8 @@
if in debug mode and no debug file, we write the messages to stderr, if in debug mode and no debug file, we write the messages to stderr,
else to syslog. */ else to syslog. */
FILE *dbgout = NULL; FILE *dbgout;
int debug_flag = 0; int debug_level;
int int
set_logfile (const char *logfile) set_logfile (const char *logfile)
@ -47,7 +47,7 @@ dbg_log (const char *fmt,...)
va_start (ap, fmt); va_start (ap, fmt);
vsnprintf (msg2, sizeof (msg), fmt, ap); vsnprintf (msg2, sizeof (msg), fmt, ap);
if (debug_flag) if (debug_level > 0)
{ {
snprintf (msg, sizeof (msg), "%d: %s\n", getpid (), msg2); snprintf (msg, sizeof (msg), "%d: %s\n", getpid (), msg2);
if (dbgout) if (dbgout)

View File

@ -20,8 +20,10 @@
#ifndef _DBG_LOG_H #ifndef _DBG_LOG_H
#define _DBG_LOG_H 1 #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 #endif

31
nscd/gethstbyad_r.c Normal file
View File

@ -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"

39
nscd/gethstbynm2_r.c Normal file
View File

@ -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"

View File

@ -1,6 +1,7 @@
/* Copyright (c) 1998 Free Software Foundation, Inc. /* Cache handling for group lookup.
Copyright (C) 1998 Free Software Foundation, Inc.
This file is part of the GNU C Library. 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 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 modify it under the terms of the GNU Library General Public License as
@ -17,603 +18,232 @@
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 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 <errno.h>
#include <error.h>
#include <grp.h> #include <grp.h>
#include <pthread.h> #include <stddef.h>
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <rpcsvc/nis.h>
#include <sys/types.h>
#include "dbg_log.h"
#include "nscd.h" #include "nscd.h"
#include "dbg_log.h"
static unsigned long modulo = 211; /* This is the standard reply in case the service is disabled. */
static unsigned long postimeout = 3600; static const gr_response_header disabled =
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
{ {
time_t create; version: NSCD_VERSION,
struct grphash *next; found: -1,
struct group *grp; 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; iov_base: (void *) &disabled,
struct group *grptr; 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; version: NSCD_VERSION,
struct neghash *next; found: 0,
char *key; gr_name_len: 0,
gr_passwd_len: 0,
gr_gid: -1,
gr_mem_cnt: 0,
}; };
typedef struct neghash neghash;
static grphash *grptbl; /* This is the struct describing how to write this record. */
static gidhash *gidtbl; static const struct iovec iov_notfound =
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)
{ {
stat->gr_poshit = poshit; iov_base: (void *) &notfound,
stat->gr_posmiss = posmiss; iov_len: sizeof (notfound)
stat->gr_neghit = neghit; };
stat->gr_negmiss = negmiss;
stat->gr_size = modulo;
stat->gr_posttl = postimeout;
stat->gr_negttl = negtimeout;
}
void
set_grp_modulo (unsigned long mod) struct groupdata
{ {
modulo = mod; gr_response_header resp;
} char strdata[0];
};
void
set_pos_grp_ttl (unsigned long ttl)
{
postimeout = ttl;
}
void
set_neg_grp_ttl (unsigned long ttl)
{
negtimeout = ttl;
}
int
cache_grpinit ()
{
pthread_attr_t attr;
pthread_t thread;
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)
{
dest->gr_mem[l] = cp;
cp = stpcpy (cp, src->gr_mem[l]) + 1;
++l;
}
dest->gr_mem[l] = NULL;
return dest;
}
static void static void
free_grp (struct group *src) cache_addgr (struct database *db, int fd, request_header *req, void *key,
struct group *grp)
{ {
free (src); ssize_t total;
} ssize_t written;
time_t t = time (NULL);
static int if (grp == NULL)
add_cache (struct group *grp)
{
grphash *work;
gidhash *gidwork;
unsigned long int hash = __nis_hash (grp->gr_name,
strlen (grp->gr_name)) % modulo;
if (debug_flag)
dbg_log (_("grp_add_cache (%s)"), grp->gr_name);
work = &grptbl[hash];
if (grptbl[hash].grp == NULL)
grptbl[hash].grp = save_grp (grp);
else
{ {
while (work->next != NULL) /* We have no data. This means we send the standard reply for this
work = work->next; case. */
void *copy;
work->next = calloc (1, sizeof (grphash)); total = sizeof (notfound);
work->next->grp = save_grp (grp);
work = work->next;
}
time (&work->create); written = writev (fd, &iov_notfound, 1);
gidwork = &gidtbl[grp->gr_gid % modulo];
if (gidwork->grptr == NULL)
gidwork->grptr = work->grp;
else
{
while (gidwork->next != NULL)
gidwork = gidwork->next;
gidwork->next = calloc (1, sizeof (gidhash)); copy = malloc (req->key_len);
gidwork->next->grptr = work->grp; if (copy == NULL)
} error (EXIT_FAILURE, errno, _("while allocating key copy"));
memcpy (copy, key, req->key_len);
return 0; /* Compute the timeout time. */
} t += db->negtimeout;
static struct group * /* Now get the lock to safely insert the records. */
cache_search_name (const char *name) pthread_rwlock_rdlock (&db->lock);
{
grphash *work;
unsigned long int hash = __nis_hash (name, strlen(name)) % modulo;
work = &grptbl[hash]; cache_add (req->type, copy, req->key_len, &iov_notfound,
sizeof (notfound), (void *) -1, 0, t, db);
while (work->grp != NULL) pthread_rwlock_unlock (&db->lock);
{
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;
work = &gidtbl[gid % modulo];
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;
}
static int
add_negcache (char *key)
{
neghash *work;
unsigned long int hash = __nis_hash (key, strlen (key)) % modulo;
if (debug_flag)
dbg_log (_("grp_add_netgache (%s|%ld)"), key, hash);
work = &negtbl[hash];
if (negtbl[hash].key == NULL)
{
negtbl[hash].key = strdup (key);
negtbl[hash].next = NULL;
} }
else else
{ {
while (work->next != NULL) /* Determine the I/O structure. */
work = work->next; 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;
work->next = calloc (1, sizeof (neghash)); /* We need this to insert the `bygid' entry. */
work->next->key = strdup (key); n = snprintf (buf, sizeof (buf), "%d", grp->gr_gid) + 1;
work = work->next;
}
time (&work->create); /* Determine the length of all members. */
return 0; while (grp->gr_mem[gr_mem_cnt])
} ++gr_mem_cnt;
gr_mem_len = (size_t *) alloca (gr_mem_cnt * sizeof (size_t));
static int for (gr_mem_cnt = 0; grp->gr_mem[gr_mem_cnt]; ++gr_mem_cnt)
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 (strcmp (work->key, key) == 0) gr_mem_len[gr_mem_cnt] = strlen (grp->gr_mem[gr_mem_cnt]) + 1;
return 1; gr_mem_len_total += gr_mem_len[gr_mem_cnt];
if (work->next != NULL) }
work = work->next;
else /* We allocate all data in one memory block: the iov vector,
return 0; 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"));
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;
cp = data->strdata;
/* 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);
for (cnt = 0; cnt < gr_mem_cnt; ++cnt)
cp = mempcpy (cp, grp->gr_mem[cnt], gr_mem_len[cnt]);
/* Finally the stringified GID value. */
memcpy (cp, buf, n);
/* Write the result. */
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 (GETGRBYNAME, gr_name, gr_name_len, data,
total, data, 0, t, db);
cache_add (GETGRBYGID, cp, n, 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)));
} }
return 0;
} }
void *
cache_getgrnam (void *v_param) void
addgrbyname (struct database *db, int fd, request_header *req, void *key)
{ {
param_t *param = (param_t *)v_param; /* 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; struct group *grp;
pthread_rwlock_rdlock (&grplock); if (debug_level > 0)
grp = cache_search_name (param->key); dbg_log (_("Haven't found \"%s\" in group cache!"), key);
/* I don't like it to hold the read only lock longer, but it is while (getgrnam_r (key, &resultbuf, buffer, buflen, &grp) != 0
necessary to avoid to much malloc/free/strcpy. */ && errno == ERANGE)
if (grp != NULL)
{ {
if (debug_flag) errno = 0;
dbg_log (_("Found \"%s\" in cache !"), param->key); buflen += 256;
buffer = alloca (buflen);
++poshit;
gr_send_answer (param->conn, grp);
close_socket (param->conn);
pthread_rwlock_unlock (&grplock);
} }
else
{ cache_addgr (db, fd, req, key, grp);
int status; }
int buflen = 1024;
char *buffer = calloc (1, buflen);
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 resultbuf;
struct group *grp;
gid_t gid = atol (key);
if (debug_flag) if (debug_level > 0)
dbg_log (_("Doesn't found \"%s\" in cache !"), param->key); dbg_log (_("Haven't found \"%d\" in group cache!"), gid);
pthread_rwlock_unlock (&grplock); while (getgrgid_r (gid, &resultbuf, buffer, buflen, &grp) != 0
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 == ERANGE)
{ {
errno = 0; errno = 0;
buflen += 1024; buflen += 256;
buffer = realloc (buffer, buflen); buffer = alloca (buflen);
} }
if (buffer != NULL && grp != NULL) cache_addgr (db, fd, req, key, grp);
{
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;
}
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;
sleep (20);
while (!do_shutdown)
{
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);
}
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;
} }

405
nscd/hstcache.c Normal file
View File

@ -0,0 +1,405 @@
/* Cache handling for host lookup.
Copyright (C) 1998 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#include <assert.h>
#include <errno.h>
#include <error.h>
#include <netdb.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <arpa/inet.h>
#include "nscd.h"
#include "dbg_log.h"
/* Get implementation for some internal functions. */
#include "../resolv/mapv4v6addr.h"
/* This is the standard reply in case the service is disabled. */
static const hst_response_header disabled =
{
version: NSCD_VERSION,
found: -1,
h_name_len: 0,
h_aliases_cnt: 0,
h_addrtype: -1,
h_length: -1,
h_addr_list_cnt: 0,
error: NETDB_INTERNAL
};
/* This is the struct describing how to write this record. */
const struct iovec hst_iov_disabled =
{
iov_base: (void *) &disabled,
iov_len: sizeof (disabled)
};
/* This is the standard reply in case we haven't found the dataset. */
static const hst_response_header notfound =
{
version: NSCD_VERSION,
found: 0,
h_name_len: 0,
h_aliases_cnt: 0,
h_addrtype: -1,
h_length: -1,
h_addr_list_cnt: 0,
error: HOST_NOT_FOUND
};
/* This is the struct describing how to write this record. */
static const struct iovec iov_notfound =
{
iov_base: (void *) &notfound,
iov_len: sizeof (notfound)
};
struct hostdata
{
hst_response_header resp;
char strdata[0];
};
static void
cache_addhst (struct database *db, int fd, request_header *req, void *key,
struct hostent *hst)
{
ssize_t total;
ssize_t written;
time_t t = time (NULL);
if (hst == NULL)
{
/* We have no data. This means we send the standard reply for this
case. */
void *copy;
total = sizeof (notfound);
written = writev (fd, &iov_notfound, 1);
copy = malloc (req->key_len);
if (copy == NULL)
error (EXIT_FAILURE, errno, _("while allocating key copy"));
memcpy (copy, key, req->key_len);
/* Compute the timeout time. */
t += db->negtimeout;
/* Now get the lock to safely insert the records. */
pthread_rwlock_rdlock (&db->lock);
cache_add (req->type, copy, req->key_len, &iov_notfound,
sizeof (notfound), (void *) -1, 0, t, db);
pthread_rwlock_unlock (&db->lock);
}
else
{
/* Determine the I/O structure. */
struct hostdata *data;
size_t h_name_len = strlen (hst->h_name) + 1;
size_t h_aliases_cnt;
size_t *h_aliases_len;
size_t h_addr_list_cnt;
int addr_list_type;
char *addresses;
char *aliases;
char *key_copy = NULL;
char *cp;
size_t cnt;
/* Determine the number of aliases. */
h_aliases_cnt = 0;
for (cnt = 0; hst->h_aliases[cnt] != NULL; ++cnt)
++h_aliases_cnt;
/* Determine the length of all aliases. */
h_aliases_len = alloca (h_aliases_cnt * sizeof (size_t));
total = 0;
for (cnt = 0; cnt < h_aliases_cnt; ++cnt)
{
h_aliases_len[cnt] = strlen (hst->h_aliases[cnt]) + 1;
total += h_aliases_len[cnt];
}
/* Determine the number of addresses. */
h_addr_list_cnt = 0;
for (cnt = 0; hst->h_addr_list[cnt]; ++cnt)
++h_addr_list_cnt;
/* We allocate all data in one memory block: the iov vector,
the response header and the dataset itself. */
total += (sizeof (struct hostdata)
+ h_name_len
+ h_aliases_cnt * sizeof (size_t)
+ h_addr_list_cnt * (hst->h_length
+ (hst->h_length == INADDRSZ
? IN6ADDRSZ : 0)));
data = (struct hostdata *) malloc (total + req->key_len);
if (data == NULL)
/* There is no reason to go on. */
error (EXIT_FAILURE, errno, _("while allocating cache entry"));
data->resp.found = 1;
data->resp.h_name_len = h_name_len;
data->resp.h_aliases_cnt = h_aliases_cnt;
data->resp.h_addrtype = hst->h_addrtype;
data->resp.h_length = hst->h_length;
data->resp.h_addr_list_cnt = h_addr_list_cnt;
data->resp.error = NETDB_SUCCESS;
cp = data->strdata;
cp = mempcpy (cp, hst->h_name, h_name_len);
cp = mempcpy (cp, h_aliases_len, h_aliases_cnt * sizeof (size_t));
/* The normal addresses first. */
addresses = cp;
for (cnt = 0; cnt < h_addr_list_cnt; ++cnt)
cp = mempcpy (cp, hst->h_addr_list[cnt], hst->h_length);
/* And the generated IPv6 addresses if necessary. */
if (hst->h_length == INADDRSZ)
{
/* Generate the IPv6 addresses. */
for (cnt = 0; cnt < h_addr_list_cnt; cp += IN6ADDRSZ, ++cnt)
map_v4v6_address (hst->h_addr_list[cnt], cp);
}
/* Then the aliases. */
aliases = cp;
for (cnt = 0; cnt < h_aliases_cnt; ++cnt)
cp = mempcpy (cp, hst->h_aliases[cnt], h_aliases_len[cnt]);
assert (cp == data->strdata + total - sizeof (hst_response_header));
/* If we are adding a GETHOSTBYNAME{,v6} entry we must be prepared
that the answer we get from the NSS does not contain the key
itself. This is the case if the resolver is used and the name
is extended by the domainnames from /etc/resolv.conf. Therefore
we explicitly add the name here. */
if (req->type == GETHOSTBYNAME || req->type == GETHOSTBYNAMEv6)
key_copy = memcpy (cp, key, req->key_len);
/* We write the dataset before inserting it to the database
since while inserting this thread might block and so would
unnecessarily let the receiver wait. */
written = write (fd, data, total);
addr_list_type = (hst->h_length == INADDRSZ
? GETHOSTBYADDR : GETHOSTBYADDRv6);
/* Compute the timeout time. */
t += db->postimeout;
/* Now get the lock to safely insert the records. */
pthread_rwlock_rdlock (&db->lock);
/* First add all the aliases. */
for (cnt = 0; cnt < h_aliases_cnt; ++cnt)
{
if (addr_list_type == GETHOSTBYADDR)
cache_add (GETHOSTBYNAME, aliases, h_aliases_len[cnt], data, total,
data, 0, t, db);
cache_add (GETHOSTBYNAMEv6, aliases, h_aliases_len[cnt], data, total,
data, 0, t, db);
aliases += h_aliases_len[cnt];
}
/* Next the normal addresses. */
for (cnt = 0; cnt < h_addr_list_cnt; ++cnt)
{
cache_add (addr_list_type, addresses, hst->h_length, data, total,
data, 0, t, db);
addresses += hst->h_length;
}
/* If necessary the IPv6 addresses. */
if (addr_list_type == GETHOSTBYADDR)
for (cnt = 0; cnt < h_addr_list_cnt; ++cnt)
{
cache_add (GETHOSTBYADDRv6, addresses, IN6ADDRSZ, data, total,
data, 0, t, db);
addresses += IN6ADDRSZ;
}
/* If necessary add the key for this request. */
if (req->type == GETHOSTBYNAME || req->type == GETHOSTBYNAMEv6)
{
if (addr_list_type == GETHOSTBYADDR)
cache_add (GETHOSTBYNAME, key_copy, req->key_len, data, total,
data, 0, t, db);
cache_add (GETHOSTBYNAMEv6, key_copy, req->key_len, data,
total, data, 0, t, db);
}
/* And finally the name. We mark this as the last entry. */
if (addr_list_type == GETHOSTBYADDR)
cache_add (GETHOSTBYNAME, data->strdata, h_name_len, data, total, data,
0, t, db);
cache_add (GETHOSTBYNAMEv6, data->strdata, h_name_len, data,
total, data, 1, t, db);
pthread_rwlock_unlock (&db->lock);
}
if (written != total)
{
char buf[256];
dbg_log (_("short write in %s: %s"), __FUNCTION__,
strerror_r (errno, buf, sizeof (buf)));
}
}
void
addhstbyname (struct database *db, int fd, request_header *req, void *key)
{
/* Search for the entry matching the key. Please note that we don't
look again in the table whether the dataset is now available. We
simply insert it. It does not matter if it is in there twice. The
pruning function only will look at the timestamp. */
int buflen = 512;
char *buffer = alloca (buflen);
struct hostent resultbuf;
struct hostent *hst;
if (debug_level > 0)
dbg_log (_("Haven't found \"%s\" in hosts cache!"), key);
while (gethostbyname2_r (key, AF_INET, &resultbuf, buffer, buflen, &hst,
&h_errno) != 0
&& h_errno == NETDB_INTERNAL
&& errno == ERANGE)
{
errno = 0;
buflen += 256;
buffer = alloca (buflen);
}
cache_addhst (db, fd, req, key, hst);
}
void
addhstbyaddr (struct database *db, int fd, request_header *req, void *key)
{
/* Search for the entry matching the key. Please note that we don't
look again in the table whether the dataset is now available. We
simply insert it. It does not matter if it is in there twice. The
pruning function only will look at the timestamp. */
int buflen = 512;
char *buffer = alloca (buflen);
struct hostent resultbuf;
struct hostent *hst;
if (debug_level > 0)
{
char buf[64];
dbg_log (_("Haven't found \"%s\" in hosts cache!"),
inet_ntop (AF_INET, key, buf, sizeof (buf)));
}
while (gethostbyaddr_r (key, INADDRSZ, AF_INET, &resultbuf, buffer, buflen,
&hst, &h_errno) != 0
&& h_errno == NETDB_INTERNAL
&& errno == ERANGE)
{
errno = 0;
buflen += 256;
buffer = alloca (buflen);
}
cache_addhst (db, fd, req, key, hst);
}
void
addhstbynamev6 (struct database *db, int fd, request_header *req, void *key)
{
/* Search for the entry matching the key. Please note that we don't
look again in the table whether the dataset is now available. We
simply insert it. It does not matter if it is in there twice. The
pruning function only will look at the timestamp. */
int buflen = 512;
char *buffer = alloca (buflen);
struct hostent resultbuf;
struct hostent *hst;
if (debug_level > 0)
dbg_log (_("Haven't found \"%s\" in hosts cache!"), key);
while (gethostbyname2_r (key, AF_INET6, &resultbuf, buffer, buflen, &hst,
&h_errno) != 0
&& h_errno == NETDB_INTERNAL
&& errno == ERANGE)
{
errno = 0;
buflen += 256;
buffer = alloca (buflen);
}
cache_addhst (db, fd, req, key, hst);
}
void
addhstbyaddrv6 (struct database *db, int fd, request_header *req, void *key)
{
/* Search for the entry matching the key. Please note that we don't
look again in the table whether the dataset is now available. We
simply insert it. It does not matter if it is in there twice. The
pruning function only will look at the timestamp. */
int buflen = 512;
char *buffer = alloca (buflen);
struct hostent resultbuf;
struct hostent *hst;
if (debug_level > 0)
{
char buf[64];
dbg_log (_("Haven't found \"%s\" in hosts cache!"),
inet_ntop (AF_INET6, key, buf, sizeof (buf)));
}
while (gethostbyaddr_r (key, IN6ADDRSZ, AF_INET6, &resultbuf, buffer, buflen,
&hst, &h_errno) != 0
&& h_errno == NETDB_INTERNAL
&& errno == ERANGE)
{
errno = 0;
buflen += 256;
buffer = alloca (buflen);
}
cache_addhst (db, fd, req, key, hst);
}

View File

@ -17,9 +17,10 @@
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */ 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 <argp.h>
#include <assert.h>
#include <errno.h> #include <errno.h>
#include <error.h> #include <error.h>
#include <libintl.h> #include <libintl.h>
@ -61,12 +62,10 @@ int do_shutdown = 0;
int disabled_passwd = 0; int disabled_passwd = 0;
int disabled_group = 0; int disabled_group = 0;
int go_background = 1; 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 check_pid (const char *file);
static int write_pid (const char *file); static int write_pid (const char *file);
static void handle_requests (void);
/* Name and version of program. */ /* Name and version of program. */
static void print_version (FILE *stream, struct argp_state *state); 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") }, N_("Read configuration data from NAME") },
{ "debug", 'd', NULL, 0, { "debug", 'd', NULL, 0,
N_("Do not fork and display messages on the current tty") }, 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") }, { "shutdown", 'K', NULL, 0, N_("Shut the server down") },
{ "statistic", 'g', NULL, 0, N_("Print current configuration statistic") }, { "statistic", 'g', NULL, 0, N_("Print current configuration statistic") },
{ NULL, 0, NULL, 0, NULL } { NULL, 0, NULL, 0, NULL }
@ -118,10 +118,7 @@ main (int argc, char **argv)
/* Check if we are already running. */ /* Check if we are already running. */
if (check_pid (_PATH_NSCDPID)) if (check_pid (_PATH_NSCDPID))
{ error (EXIT_FAILURE, 0, _("already running"));
fputs (_("already running"), stderr);
exit (EXIT_FAILURE);
}
/* Behave like a daemon. */ /* Behave like a daemon. */
if (go_background) if (go_background)
@ -144,7 +141,7 @@ main (int argc, char **argv)
if (write_pid (_PATH_NSCDPID) < 0) if (write_pid (_PATH_NSCDPID) < 0)
dbg_log ("%s: %s", _PATH_NSCDPID, strerror (errno)); dbg_log ("%s: %s", _PATH_NSCDPID, strerror (errno));
/* Ignore job control signals */ /* Ignore job control signals. */
signal (SIGTTOU, SIG_IGN); signal (SIGTTOU, SIG_IGN);
signal (SIGTTIN, SIG_IGN); signal (SIGTTIN, SIG_IGN);
signal (SIGTSTP, SIG_IGN); signal (SIGTSTP, SIG_IGN);
@ -155,21 +152,14 @@ main (int argc, char **argv)
signal (SIGTERM, termination_handler); signal (SIGTERM, termination_handler);
signal (SIGPIPE, SIG_IGN); signal (SIGPIPE, SIG_IGN);
/* Cleanup files created by a previous `bind' */ /* Cleanup files created by a previous `bind'. */
unlink (_PATH_NSCDSOCKET); 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 incoming requests */
handle_requests (); start_threads ();
return 0; return 0;
} }
@ -182,20 +172,19 @@ parse_opt (int key, char *arg, struct argp_state *state)
switch (key) switch (key)
{ {
case 'd': case 'd':
debug_flag = 1; ++debug_level;
go_background = 0; go_background = 0;
break; break;
case 'f': case 'f':
conffile = arg; conffile = arg;
break; break;
case 'K': case 'K':
if (getuid () != 0) if (getuid () != 0)
error (EXIT_FAILURE, 0, _("Only root is allowed to use this option!"));
{ {
printf (_("Only root is allowed to use this option!\n\n")); int sock = nscd_open_socket ();
exit (EXIT_FAILURE);
}
{
int sock = __nscd_open_socket ();
request_header req; request_header req;
ssize_t nbytes; ssize_t nbytes;
@ -205,19 +194,24 @@ parse_opt (int key, char *arg, struct argp_state *state)
req.version = NSCD_VERSION; req.version = NSCD_VERSION;
req.type = SHUTDOWN; req.type = SHUTDOWN;
req.key_len = 0; req.key_len = 0;
nbytes = write (sock, &req, sizeof (request_header)); nbytes = TEMP_FAILURE_RETRY (write (sock, &req,
sizeof (request_header)));
close (sock); close (sock);
if (nbytes != req.key_len) exit (nbytes != sizeof (request_header) ? EXIT_FAILURE : EXIT_SUCCESS);
exit (EXIT_FAILURE);
else
exit (EXIT_SUCCESS);
} }
case 'g': case 'g':
print_stat (); receive_print_stats ();
exit (EXIT_SUCCESS); /* Does not return. */
case 't':
nthreads = atol (arg);
break;
default: default:
return ARGP_ERR_UNKNOWN; return ARGP_ERR_UNKNOWN;
} }
return 0; 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\ 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\ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
"), "1998"); "), "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 int
__nscd_open_socket (void) nscd_open_socket (void)
{ {
struct sockaddr_un addr; struct sockaddr_un addr;
int sock; int sock;
@ -247,6 +242,7 @@ __nscd_open_socket (void)
return -1; return -1;
addr.sun_family = AF_UNIX; addr.sun_family = AF_UNIX;
assert (sizeof (addr.sun_path) >= sizeof (_PATH_NSCDSOCKET));
strcpy (addr.sun_path, _PATH_NSCDSOCKET); strcpy (addr.sun_path, _PATH_NSCDSOCKET);
if (connect (sock, (struct sockaddr *) &addr, sizeof (addr)) < 0) if (connect (sock, (struct sockaddr *) &addr, sizeof (addr)) < 0)
{ {
@ -258,12 +254,12 @@ __nscd_open_socket (void)
} }
/* Cleanup. */ /* Cleanup. */
static void void
termination_handler (int signum) termination_handler (int signum)
{ {
close_sockets (); close_sockets ();
/* Clean up the files created by `bind'. */ /* Clean up the file created by `bind'. */
unlink (_PATH_NSCDSOCKET); unlink (_PATH_NSCDSOCKET);
/* Clean up pid file. */ /* Clean up pid file. */
@ -282,11 +278,12 @@ check_pid (const char *file)
if (fp) if (fp)
{ {
pid_t pid; pid_t pid;
int n;
fscanf (fp, "%d", &pid); n = fscanf (fp, "%d", &pid);
fclose (fp); fclose (fp);
if (kill (pid, 0) == 0) if (n != 1 || kill (pid, 0) == 0)
return 1; return 1;
} }
@ -305,176 +302,10 @@ write_pid (const char *file)
return -1; return -1;
fprintf (fp, "%d\n", getpid ()); fprintf (fp, "%d\n", getpid ());
if (ferror (fp)) if (fflush (fp) || ferror (fp))
return -1; return -1;
fclose (fp); fclose (fp);
return 0; 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);
}

View File

@ -6,25 +6,36 @@
# Legal entries are: # Legal entries are:
# #
# logfile <file> # logfile <file>
# enable-cache <service> <yes|no>
# debug-level <level> # debug-level <level>
#
# enable-cache <service> <yes|no>
# positive-time-to-live <service> <time in seconds> # positive-time-to-live <service> <time in seconds>
# negative-time-to-live <service> <time in seconds> # negative-time-to-live <service> <time in seconds>
# suggested-size <service> <prime number> # suggested-size <service> <prime number>
# check-files <service> <yes|no>
# #
# Currently supported cache names (services): passwd, group # Currently supported cache names (services): passwd, group
# #
# logfile /var/adm/nscd.log # logfile /var/adm/nscd.log
# enable-cache hosts no
debug-level 0 debug-level 0
enable-cache passwd yes
positive-time-to-live passwd 600 positive-time-to-live passwd 600
negative-time-to-live passwd 20 negative-time-to-live passwd 20
suggested-size passwd 211 suggested-size passwd 211
check-files passwd yes
enable-cache group yes
positive-time-to-live group 3600 positive-time-to-live group 3600
negative-time-to-live group 60 negative-time-to-live group 60
suggested-size group 211 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

View File

@ -20,16 +20,35 @@
#ifndef _NSCD_H #ifndef _NSCD_H
#define _NSCD_H 1 #define _NSCD_H 1
#include <grp.h> #include <pthread.h>
#include <pwd.h> #include <time.h>
#include <sys/uio.h>
/* Version number of the daemon interface */ /* Version number of the daemon interface */
#define NSCD_VERSION 1 #define NSCD_VERSION 2
/* How many threads do we spawn maximal ? */ /* Path of the file where the PID of the running system is stored. */
#define MAX_NUM_CONNECTIONS 16 #define _PATH_NSCDPID "/var/run/nscd.pid"
/* Services provided */ /* Path for the Unix domain socket. */
#define _PATH_NSCDSOCKET "/var/run/.nscd_socket"
/* Path for the configuration file. */
#define _PATH_NSCDCONF "/etc/nscd.conf"
/* Handle databases. */
typedef enum
{
pwddb,
grpdb,
hstdb,
lastdb
} dbtype;
/* Available services. */
typedef enum typedef enum
{ {
GETPWBYNAME, GETPWBYNAME,
@ -37,22 +56,67 @@ typedef enum
GETGRBYNAME, GETGRBYNAME,
GETGRBYGID, GETGRBYGID,
GETHOSTBYNAME, GETHOSTBYNAME,
GETHOSTBYNAMEv6,
GETHOSTBYADDR, GETHOSTBYADDR,
SHUTDOWN, /* Shut the server down */ GETHOSTBYADDRv6,
GETSTAT /* Get the server statistic */ LASTDBREQ = GETHOSTBYADDRv6,
SHUTDOWN, /* Shut the server down. */
GETSTAT, /* Get the server statistic. */
LASTREQ,
} request_type; } 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 */ /* Header common to all requests */
typedef struct typedef struct
{ {
/* Version number of the daemon interface */ int version; /* Version number of the daemon interface. */
int version; request_type type; /* Service requested. */
/* Service requested */ ssize_t key_len; /* Key length. */
request_type type;
/* key len */
ssize_t key_len;
} request_header; } 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 typedef struct
{ {
int version; int version;
@ -66,6 +130,9 @@ typedef struct
ssize_t pw_shell_len; ssize_t pw_shell_len;
} pw_response_header; } 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 typedef struct
{ {
int version; int version;
@ -73,77 +140,82 @@ typedef struct
ssize_t gr_name_len; ssize_t gr_name_len;
ssize_t gr_passwd_len; ssize_t gr_passwd_len;
gid_t gr_gid; gid_t gr_gid;
ssize_t gr_mem_len; ssize_t gr_mem_cnt;
} gr_response_header; } gr_response_header;
/* 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 typedef struct
{ {
int debug_level; int version;
int pw_enabled; int found;
unsigned long pw_poshit; ssize_t h_name_len;
unsigned long pw_posmiss; ssize_t h_aliases_cnt;
unsigned long pw_neghit; int h_addrtype;
unsigned long pw_negmiss; int h_length;
unsigned long pw_size; ssize_t h_addr_list_cnt;
unsigned long pw_posttl; int error;
unsigned long pw_negttl; } hst_response_header;
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" /* Global variables. */
#define _PATH_NSCDSOCKET "/var/run/.nscd_socket" extern const char *dbnames[lastdb];
#define _PATH_NSCDCONF "/etc/nscd.conf" extern const char *serv2str[LASTREQ];
typedef struct extern const struct iovec pwd_iov_disabled;
{ extern const struct iovec grp_iov_disabled;
char *key; extern const struct iovec hst_iov_disabled;
int conn;
} param_t;
extern int do_shutdown; /* 1 if we should quit the programm. */ /* Number of threads to run. */
extern int disabled_passwd; extern int nthreads;
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)); /* Prototypes for global functions. */
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)); /* nscd.c */
extern void *cache_getpwnam __P ((void *param)); extern void termination_handler (int signum);
extern void *cache_getpwuid __P ((void *param)); extern int nscd_open_socket (void);
extern void *cache_pw_disabled __P ((void *param));
extern int cache_grpinit __P ((void)); /* connections.c */
extern void *cache_getgrnam __P ((void *param)); extern void nscd_init (const char *conffile);
extern void *cache_getgrgid __P ((void *param)); extern void close_sockets (void);
extern void *cache_gr_disabled __P ((void *param)); extern void start_threads (void);
extern int __nscd_open_socket __P ((void)); /* nscd_conf.c */
extern int nscd_parse_file (const char *fname, struct database dbs[lastdb]);
extern void get_pw_stat __P ((stat_response_header *resp)); /* nscd_stat.c */
extern void get_gr_stat __P ((stat_response_header *resp)); extern void send_stats (int fd, struct database dbs[lastdb]);
extern void print_stat __P ((void)); extern int receive_print_stats (void);
extern void stat_send __P ((int conn, stat_response_header *resp));
#endif /* 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 */

View File

@ -22,17 +22,27 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/param.h>
#include <sys/types.h> #include <sys/types.h>
#include "dbg_log.h" #include "dbg_log.h"
#include "nscd.h" #include "nscd.h"
/* Names of the databases. */
const char *dbnames[lastdb] =
{
[pwddb] = "passwd",
[grpdb] = "group",
[hstdb] = "hosts"
};
int int
nscd_parse_file (const char *fname) nscd_parse_file (const char *fname, struct database dbs[lastdb])
{ {
FILE *fp; FILE *fp;
char *line, *cp, *entry, *arg1, *arg2; char *line, *cp, *entry, *arg1, *arg2;
size_t len; size_t len;
int cnt;
/* Open the configuration file. */ /* Open the configuration file. */
fp = fopen (fname, "r"); fp = fopen (fname, "r");
@ -92,41 +102,64 @@ nscd_parse_file (const char *fname)
if (strcmp (entry, "positive-time-to-live") == 0) if (strcmp (entry, "positive-time-to-live") == 0)
{ {
if (strcmp (arg1, "passwd") == 0) for (cnt = 0; cnt < lastdb; ++cnt)
set_pos_pwd_ttl (atol (arg2)); if (strcmp (arg1, dbnames[cnt]) == 0)
else if (strcmp (arg1, "group") == 0) {
set_pos_grp_ttl (atol (arg2)); dbs[cnt].postimeout = atol (arg2);
else break;
}
if (cnt == lastdb)
dbg_log ("server %s is not supported\n", arg1); dbg_log ("server %s is not supported\n", arg1);
} }
else if (strcmp (entry, "negative-time-to-live") == 0) else if (strcmp (entry, "negative-time-to-live") == 0)
{ {
if (strcmp (arg1, "passwd") == 0) for (cnt = 0; cnt < lastdb; ++cnt)
set_neg_pwd_ttl (atol (arg2)); if (strcmp (arg1, dbnames[cnt]) == 0)
else if (strcmp (arg1, "group") == 0) {
set_neg_grp_ttl (atol (arg2)); dbs[cnt].negtimeout = atol (arg2);
else break;
dbg_log (_("service %s is not supported"), arg1); }
if (cnt == lastdb)
dbg_log ("server %s is not supported\n", arg1);
} }
else if (strcmp (entry, "suggested-size") == 0) else if (strcmp (entry, "suggested-size") == 0)
{ {
if (strcmp (arg1, "passwd") == 0) for (cnt = 0; cnt < lastdb; ++cnt)
set_pwd_modulo (atol (arg2)); if (strcmp (arg1, dbnames[cnt]) == 0)
else if (strcmp (arg1, "group") == 0)
set_grp_modulo (atol (arg2));
else
dbg_log (_("service %s is not supported"), arg1);
}
else if (strcmp (entry, "enable-cache") ==0)
{ {
if (strcmp (arg1, "passwd") == 0 dbs[cnt].module = atol (arg2);
&& strcmp (arg2, "no") == 0) break;
disabled_passwd = 1; }
else if (strcmp (arg1, "group") == 0 if (cnt == lastdb)
&& strcmp (arg2, "no") == 0) dbg_log ("server %s is not supported\n", arg1);
disabled_group = 1; }
else else if (strcmp (entry, "enable-cache") == 0)
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].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, "check-files") == 0)
{
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) else if (strcmp (entry, "logfile") == 0)
{ {
@ -137,7 +170,12 @@ nscd_parse_file (const char *fname)
{ {
int level = atoi (arg1); int level = atoi (arg1);
if (level > 0) if (level > 0)
debug_flag = level; debug_level = level;
}
else if (strcmp (entry, "threads") == 0)
{
if (nthreads == -1)
nthreads = MAX (atol (arg1), lastdb);
} }
else else
dbg_log (_("Unknown option: %s %s %s"), entry, arg1, arg2); dbg_log (_("Unknown option: %s %s %s"), entry, arg1, arg2);

View File

@ -32,41 +32,36 @@
int __nss_not_use_nscd_group; int __nss_not_use_nscd_group;
static int __nscd_getgr_r (const char *key, request_type type, static int nscd_getgr_r (const char *key, size_t keylen, request_type type,
struct group *resultbuf, char *buffer, struct group *resultbuf, char *buffer,
size_t buflen); size_t buflen);
int int
__nscd_getgrnam_r (const char *name, struct group *resultbuf, char *buffer, __nscd_getgrnam_r (const char *name, struct group *resultbuf, char *buffer,
size_t buflen) size_t buflen)
{ {
if (name == NULL) return nscd_getgr_r (name, strlen (name) + 1, GETGRBYNAME, resultbuf,
return 1; buffer, buflen);
return __nscd_getgr_r (name, GETGRBYNAME, resultbuf, buffer, buflen);
} }
int int
__nscd_getgrgid_r (gid_t gid, struct group *resultbuf, char *buffer, __nscd_getgrgid_r (gid_t gid, struct group *resultbuf, char *buffer,
size_t buflen) size_t buflen)
{ {
char *p = buffer; char buf[12];
int plen; size_t n;
plen = __snprintf (buffer, buflen, "%d", gid); n = __snprintf (buf, sizeof (buf), "%d", gid) + 1;
if (plen == -1)
{
__set_errno (ERANGE);
return -1;
}
p = buffer + plen + 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. */ /* Create a socket connected to a name. */
static int static int
nscd_open_socket (void) open_socket (void)
{ {
struct sockaddr_un addr; struct sockaddr_un addr;
int sock; int sock;
@ -91,16 +86,16 @@ nscd_open_socket (void)
return sock; return sock;
} }
static int static int
__nscd_getgr_r (const char *key, request_type type, struct group *resultbuf, nscd_getgr_r (const char *key, size_t keylen, request_type type,
char *buffer, size_t buflen) struct group *resultbuf, char *buffer, size_t buflen)
{ {
int sock = nscd_open_socket (); int sock = open_socket ();
request_header req; request_header req;
gr_response_header gr_resp; gr_response_header gr_resp;
ssize_t nbytes; ssize_t nbytes;
size_t maxiov; struct iovec vec[2];
size_t sum;
if (sock == -1) if (sock == -1)
{ {
@ -110,16 +105,14 @@ __nscd_getgr_r (const char *key, request_type type, struct group *resultbuf,
req.version = NSCD_VERSION; req.version = NSCD_VERSION;
req.type = type; req.type = type;
req.key_len = strlen (key); 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); vec[0].iov_base = &req;
if (nbytes != req.key_len) vec[0].iov_len = sizeof (request_header);
vec[1].iov_base = (void *) key;
vec[1].iov_len = keylen;
if (__writev (sock, vec, 2) != sizeof (request_header) + keylen)
{ {
__close (sock); __close (sock);
return 1; return 1;
@ -142,118 +135,79 @@ __nscd_getgr_r (const char *key, request_type type, struct group *resultbuf,
if (gr_resp.found == 1) if (gr_resp.found == 1)
{ {
struct iovec *vec;
size_t *len; size_t *len;
char *p = buffer; char *p = buffer;
int nblocks;
size_t total_len; size_t total_len;
uintptr_t align; 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)
{
__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));
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;
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;
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;
}
/* Get this data. */
if (__readv (sock, vec, nblocks) != 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;
/* Now allocate the buffer the array for the group members. We must /* Now allocate the buffer the array for the group members. We must
align the pointer. */ align the pointer. */
align = ((__alignof__ (char *) - (p - ((char *) 0))) align = ((__alignof__ (char *) - (p - ((char *) 0)))
& (__alignof__ (char *) - 1)); & (__alignof__ (char *) - 1));
if (align + (1 + gr_resp.gr_mem_len) * sizeof (char *) > buflen) 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); __set_errno (ERANGE);
__close (sock); __close (sock);
return -1; return -1;
} }
p += align; p += align;
resultbuf->gr_mem = (char **) p; resultbuf->gr_mem = (char **) p;
p += (1 + gr_resp.gr_mem_len) * sizeof (char *); p += (1 + gr_resp.gr_mem_cnt) * sizeof (char *);
buflen -= align + (1 + gr_resp.gr_mem_len) * sizeof (char *); buflen -= align + (1 + gr_resp.gr_mem_cnt) * sizeof (char *);
resultbuf->gr_mem[gr_resp.gr_mem_len] = NULL; /* Set pointers for strings. */
resultbuf->gr_name = p;
p += gr_resp.gr_name_len;
resultbuf->gr_passwd = p;
p += gr_resp.gr_passwd_len;
if (gr_resp.gr_mem_len > 0) /* Fill in what we know now. */
resultbuf->gr_gid = gr_resp.gr_gid;
/* 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, 2) != total_len)
{ {
__close (sock);
return 1;
}
/* Clear the terminating entry. */
resultbuf->gr_mem[gr_resp.gr_mem_cnt] = NULL;
/* Prepare reading the group members. */ /* Prepare reading the group members. */
size_t i;
total_len = 0; total_len = 0;
for (i = 0; i < gr_resp.gr_mem_len; ++i) for (cnt = 0; cnt < gr_resp.gr_mem_cnt; ++cnt)
{ {
if (len[i] >= buflen) resultbuf->gr_mem[cnt] = p;
{ total_len += len[cnt];
__set_errno (ERANGE); p += len[cnt];
__close (sock);
return -1;
} }
vec[i].iov_base = resultbuf->gr_mem[i] = p; if (total_len > buflen)
vec[i].iov_len = len[i]; goto no_room;
total_len += len[i];
buflen -= len[i];
p += len[i];
*p++ = '\0';
}
#ifdef UIO_MAXIOV if (__read (sock, resultbuf->gr_mem[0], total_len) != total_len)
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); __close (sock);
return -1; return -1;
} }
}
__close (sock); __close (sock);
return 0; return 0;
} }

302
nscd/nscd_gethst_r.c Normal file
View File

@ -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;
}
}

View File

@ -31,7 +31,7 @@
int __nss_not_use_nscd_passwd; int __nss_not_use_nscd_passwd;
static int __nscd_getpw_r (const char *key, request_type type, static int nscd_getpw_r (const char *key, request_type type,
struct passwd *resultbuf, char *buffer, struct passwd *resultbuf, char *buffer,
size_t buflen); size_t buflen);
@ -42,7 +42,7 @@ __nscd_getpwnam_r (const char *name, struct passwd *resultbuf, char *buffer,
if (name == NULL) if (name == NULL)
return 1; return 1;
return __nscd_getpw_r (name, GETPWBYNAME, resultbuf, buffer, buflen); return nscd_getpw_r (name, GETPWBYNAME, resultbuf, buffer, buflen);
} }
int int
@ -60,12 +60,12 @@ __nscd_getpwuid_r (uid_t uid, struct passwd *resultbuf, char *buffer,
} }
p = buffer + plen + 1; 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. */ /* Create a socket connected to a name. */
static int static int
nscd_open_socket (void) open_socket (void)
{ {
struct sockaddr_un addr; struct sockaddr_un addr;
int sock; int sock;
@ -91,10 +91,10 @@ nscd_open_socket (void)
} }
static int static int
__nscd_getpw_r (const char *key, request_type type, struct passwd *resultbuf, nscd_getpw_r (const char *key, request_type type, struct passwd *resultbuf,
char *buffer, size_t buflen) char *buffer, size_t buflen)
{ {
int sock = nscd_open_socket (); int sock = open_socket ();
request_header req; request_header req;
pw_response_header pw_resp; pw_response_header pw_resp;
ssize_t nbytes; ssize_t nbytes;
@ -107,7 +107,7 @@ __nscd_getpw_r (const char *key, request_type type, struct passwd *resultbuf,
req.version = NSCD_VERSION; req.version = NSCD_VERSION;
req.type = type; req.type = type;
req.key_len = strlen (key); req.key_len = strlen (key) + 1;
nbytes = __write (sock, &req, sizeof (request_header)); nbytes = __write (sock, &req, sizeof (request_header));
if (nbytes != 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) if (pw_resp.found == 1)
{ {
struct iovec vec[5];
char *p = buffer; 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 if (buflen < total)
+ pw_resp.pw_gecos_len + 1 + pw_resp.pw_dir_len + 1
+ pw_resp.pw_shell_len + 1))
{ {
__set_errno (ERANGE); __set_errno (ERANGE);
__close (sock); __close (sock);
return -1; return -1;
} }
/* get pw_name */ /* Set the information we already have. */
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);
/* 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);
/* 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);
/* 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);
/* 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_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_uid = pw_resp.pw_uid;
resultbuf->pw_gid = pw_resp.pw_gid; resultbuf->pw_gid = pw_resp.pw_gid;
resultbuf->pw_gecos = vec[2].iov_base;
resultbuf->pw_gecos[pw_resp.pw_gecos_len] = '\0'; /* get pw_name */
resultbuf->pw_dir = vec[3].iov_base; resultbuf->pw_name = p;
resultbuf->pw_dir[pw_resp.pw_dir_len] = '\0'; p += pw_resp.pw_name_len;
resultbuf->pw_shell = vec[4].iov_base; /* get pw_passwd */
resultbuf->pw_shell[pw_resp.pw_shell_len] = '\0'; resultbuf->pw_passwd = p;
p += pw_resp.pw_passwd_len;
/* get pw_gecos */
resultbuf->pw_gecos = p;
p += pw_resp.pw_gecos_len;
/* get pw_dir */
resultbuf->pw_dir = p;
p += pw_resp.pw_dir_len;
/* get pw_pshell */
resultbuf->pw_shell = p;
nbytes = __read (sock, buffer, total);
__close (sock); __close (sock);
return 0;
return nbytes == total ? 0 : 1;
} }
else else
{ {

View File

@ -21,11 +21,13 @@
#define _NSCD_PROTO_H 1 #define _NSCD_PROTO_H 1
#include <grp.h> #include <grp.h>
#include <netdb.h>
#include <pwd.h> #include <pwd.h>
/* Variables for communication between NSCD handler functions and NSS. */ /* Variables for communication between NSCD handler functions and NSS. */
extern int __nss_not_use_nscd_passwd; extern int __nss_not_use_nscd_passwd;
extern int __nss_not_use_nscd_group; 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, extern int __nscd_getpwnam_r __P ((const char *name, struct passwd *resultbuf,
char *buffer, size_t buflen)); 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)); char *buffer, size_t buflen));
extern int __nscd_getgrgid_r __P ((uid_t uid, struct group *resultbuf, extern int __nscd_getgrgid_r __P ((uid_t uid, struct group *resultbuf,
char *buffer, size_t buflen)); 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 */ #endif /* _NSCD_PROTO_H */

View File

@ -17,71 +17,160 @@
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 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 <error.h>
#include <langinfo.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <unistd.h> #include <unistd.h>
#include <sys/types.h>
#include "nscd.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 void
print_stat (void) send_stats (int fd, struct database dbs[lastdb])
{ {
int sock = __nscd_open_socket (); struct statdata data;
request_header req; int cnt;
stat_response_header resp;
ssize_t nbytes;
if (sock == -1) memcpy (data.version, compilation, sizeof (compilation));
data.debug_level = debug_level;
data.ndbs = lastdb;
for (cnt = 0; cnt < lastdb; ++cnt)
{ {
fputs (_("nscd not running!\n"), stdout); data.dbs[cnt].enabled = dbs[cnt].enabled;
exit (EXIT_FAILURE); 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 (TEMP_FAILURE_RETRY (write (fd, &data, sizeof (data))) != sizeof (data))
{
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.version = NSCD_VERSION;
req.type = GETSTAT; req.type = GETSTAT;
req.key_len = 0; 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)) if (nbytes != sizeof (request_header))
{ {
perror (_("write incomplete")); int err = errno;
close (sock); close (fd);
exit (EXIT_FAILURE); error (EXIT_FAILURE, err, _("write incomplete"));
} }
nbytes = read (sock, &resp, sizeof (stat_response_header)); /* Read as much data as we expect. */
if (nbytes != sizeof (stat_response_header)) 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")); /* Not the right version. */
close (sock); int err = errno;
exit (EXIT_FAILURE); close (fd);
error (EXIT_FAILURE, err, _("cannot read statistics data"));
} }
close (sock); printf (_("nscd configuration:\n\n%15d server debug level\n"),
data.debug_level);
printf (_("nscd configuration:\n\n")); for (i = 0; i < lastdb; ++i)
printf (_("%12d server debug level\n\n"), resp.debug_level); {
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);
printf (_("passwd cache:\n\n")); if (enabled[0] == '\0')
printf (_("%12s cache is enabled\n"), resp.pw_enabled ? _("Yes") : _("No")); /* The locale does not provide this information so we have to
printf (_("%12ld cache hits on positive entries\n"), resp.pw_poshit); translate it ourself. Since we should avoid short translation
printf (_("%12ld cache hits on negative entries\n"), resp.pw_neghit); terms we artifically increase the length. */
printf (_("%12ld cache misses on positive entries\n"), resp.pw_posmiss); enabled = data.dbs[i].enabled ? _(" yes") : _(" no");
printf (_("%12ld cache misses on negative entries\n"), resp.pw_negmiss); if (check_file[0] == '\0')
printf (_("%12ld suggested size\n"), resp.pw_size); check_file = data.dbs[i].check_file ? _(" yes") : _(" no");
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")); if (all == 0)
printf (_("%12s cache is enabled\n"), resp.gr_enabled ? _("Yes") : _("No")); /* If nothing happened so far report a 0% hit rate. */
printf (_("%12ld cache hits on positive entries\n"), resp.gr_poshit); all = 1;
printf (_("%12ld cache hits on negative entries\n"), resp.gr_neghit);
printf (_("%12ld cache misses on positive entries\n"), resp.gr_posmiss); printf (_("\n%s cache:\n\n"
printf (_("%12ld cache misses on negative entries\n"), resp.gr_negmiss); "%15s cache is enabled\n"
printf (_("%12ld suggested size\n"), resp.gr_size); "%15Zd suggested size\n"
printf (_("%12ld seconds time to live for positive entries\n"), "%15ld seconds time to live for positive entries\n"
resp.gr_posttl); "%15ld seconds time to live for negative entries\n"
printf (_("%12ld seconds time to live for negative entries\n"), "%15ld cache hits on positive entries\n"
resp.gr_negttl); "%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);
} }

View File

@ -1,6 +1,7 @@
/* Copyright (c) 1998 Free Software Foundation, Inc. /* Cache handling for passwd lookup.
Copyright (C) 1998 Free Software Foundation, Inc.
This file is part of the GNU C Library. 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 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 modify it under the terms of the GNU Library General Public License as
@ -18,594 +19,228 @@
Boston, MA 02111-1307, USA. */ Boston, MA 02111-1307, USA. */
#include <errno.h> #include <errno.h>
#include <malloc.h> #include <error.h>
#include <pthread.h>
#include <pwd.h> #include <pwd.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include <time.h>
#include <unistd.h> #include <unistd.h>
#include <rpcsvc/nis.h>
#include <sys/types.h>
#include "dbg_log.h"
#include "nscd.h" #include "nscd.h"
#include "dbg_log.h"
static unsigned long int modulo = 211; /* This is the standard reply in case the service is disabled. */
static unsigned long int postimeout = 600; static const pw_response_header disabled =
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; version: NSCD_VERSION,
struct pwdhash *next; found: -1,
struct passwd *pwd; 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 pwdhash pwdhash;
struct uidhash /* This is the struct describing how to write this record. */
const struct iovec pwd_iov_disabled =
{ {
struct uidhash *next; iov_base: (void *) &disabled,
struct passwd *pwptr; iov_len: sizeof (disabled)
}; };
typedef struct uidhash uidhash;
struct neghash
/* This is the standard reply in case we haven't found the dataset. */
static const pw_response_header notfound =
{ {
time_t create; version: NSCD_VERSION,
struct neghash *next; found: 0,
char *key; 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 neghash neghash;
static pwdhash *pwdtbl; /* This is the struct describing how to write this record. */
static uidhash *uidtbl; static const struct iovec iov_notfound =
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)
{ {
stat->pw_poshit = poshit; iov_base: (void *) &notfound,
stat->pw_posmiss = posmiss; iov_len: sizeof (notfound)
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) struct passwddata
{ {
modulo = mod; pw_response_header resp;
} char strdata[0];
};
void
set_pos_pwd_ttl (unsigned long int ttl)
{
postimeout = ttl;
}
void
set_neg_pwd_ttl (unsigned long int ttl)
{
negtimeout = ttl;
}
int
cache_pwdinit ()
{
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);
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 static void
free_pwd (struct passwd *src) cache_addpw (struct database *db, int fd, request_header *req, void *key,
struct passwd *pwd)
{ {
free (src); ssize_t total;
ssize_t written;
time_t t = time (NULL);
if (pwd == 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 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);
}
if (written != total)
{
char buf[256];
dbg_log (_("short write in %s: %s"), __FUNCTION__,
strerror_r (errno, buf, sizeof (buf)));
}
} }
static int
add_cache (struct passwd *pwd) void
addpwbyname (struct database *db, int fd, request_header *req, void *key)
{ {
pwdhash *work; /* Search for the entry matching the key. Please note that we don't
uidhash *uidwork; look again in the table whether the dataset is now available. We
unsigned long int hash = __nis_hash (pwd->pw_name, simply insert it. It does not matter if it is in there twice. The
strlen (pwd->pw_name)) % modulo; pruning function only will look at the timestamp. */
int buflen = 256;
if (debug_flag) char *buffer = alloca (buflen);
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];
while (work->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;
}
static struct passwd *
cache_search_uid (uid_t uid)
{
uidhash *work;
work = &uidtbl[uid % modulo];
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;
}
static int
add_negcache (char *key)
{
neghash *work;
unsigned long int hash = __nis_hash (key, strlen (key)) % modulo;
if (debug_flag)
dbg_log (_("pwd_add_netgache (%s|%ld)"), key, hash);
work = &negtbl[hash];
if (negtbl[hash].key == NULL)
{
negtbl[hash].key = strdup (key);
negtbl[hash].next = NULL;
}
else
{
while (work->next != NULL)
work = work->next;
work->next = calloc (1, sizeof (neghash));
work->next->key = strdup (key);
work = work->next;
}
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 (strcmp (work->key, key) == 0)
return 1;
if (work->next != NULL)
work = work->next;
else
return 0;
}
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);
++poshit;
pw_send_answer (param->conn, pwd);
close_socket (param->conn);
pthread_rwlock_unlock (&pwdlock);
}
else
{
int status;
int buflen = 1024;
char *buffer = calloc (1, buflen);
struct passwd resultbuf; struct passwd resultbuf;
struct passwd *pwd;
if (debug_flag) if (debug_level > 0)
dbg_log (_("Doesn't found \"%s\" in cache !"), param->key); dbg_log (_("Haven't found \"%s\" in password cache!"), key);
pthread_rwlock_unlock (&pwdlock); while (getpwnam_r (key, &resultbuf, buffer, buflen, &pwd) != 0
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 == ERANGE)
{ {
errno = 0; errno = 0;
buflen += 1024; buflen += 256;
buffer = realloc (buffer, buflen); buffer = alloca (buflen);
} }
if (buffer != NULL && pwd != NULL) cache_addpw (db, fd, req, key, pwd);
{
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);
}
free (param->key);
free (param);
return NULL;
} }
void *
cache_pw_disabled (void *v_param) void
addpwbyuid (struct database *db, int fd, request_header *req, void *key)
{ {
param_t *param = (param_t *)v_param; /* 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);
if (debug_flag) if (debug_level > 0)
dbg_log (_("\tpasswd cache is disabled\n")); dbg_log (_("Haven't found \"%d\" in password cache!"), uid);
pw_send_disabled (param->conn); while (getpwuid_r (uid, &resultbuf, buffer, buflen, &pwd) != 0
return NULL;
}
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 == ERANGE)
{ {
errno = 0; errno = 0;
buflen += 1024; buflen += 256;
buffer = realloc (buffer, buflen); buffer = alloca (buflen);
} }
if (buffer != NULL && pwd != NULL) cache_addpw (db, fd, req, key, pwd);
{
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;
sleep (20);
while (!do_shutdown)
{
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);
}
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;
} }

View File

@ -1,7 +1,7 @@
libc { libc {
GLIBC_2.0 { GLIBC_2.0 {
# functions used in other libraries # functions used in other libraries
__nss_passwd_lookup; __nss_group_lookup; __nss_next; __nss_passwd_lookup; __nss_group_lookup; __nss_hosts_lookup; __nss_next;
_nss_files_parse_grent; _nss_files_parse_pwent; _nss_files_parse_spent; _nss_files_parse_grent; _nss_files_parse_pwent; _nss_files_parse_spent;
__nss_database_lookup; __nss_configure_lookup; __nss_database_lookup; __nss_configure_lookup;
} }

View File

@ -1,4 +1,4 @@
/* Copyright (C) 1996, 1997 Free Software Foundation, Inc. /* Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc.
This file is part of the GNU C Library. This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or The GNU C Library is free software; you can redistribute it and/or
@ -117,7 +117,6 @@ FUNCTION_NAME (ADD_PARAMS)
process gets a chance for a normal termination. */ process gets a chance for a normal termination. */
save = errno; save = errno;
free (buffer); free (buffer);
buffer = NULL;
__set_errno (save); __set_errno (save);
} }
buffer = new_buf; buffer = new_buf;

View File

@ -19,7 +19,9 @@
#include <errno.h> #include <errno.h>
#include "nsswitch.h" #include "nsswitch.h"
#include <nscd/nscd_proto.h> #ifdef USE_NSCD
# include <nscd/nscd_proto.h>
#endif
/*******************************************************************\ /*******************************************************************\
|* Here we assume several symbols to be defined: *| |* Here we assume several symbols to be defined: *|

View File

@ -28,7 +28,6 @@ routines := fgetpwent getpw putpwent \
include ../Rules include ../Rules
# We can later add the names of other thread packages here.
ifeq ($(have-thread-library),yes) ifeq ($(have-thread-library),yes)
CFLAGS-getpwuid_r.c = -DUSE_NSCD=1 CFLAGS-getpwuid_r.c = -DUSE_NSCD=1

View File

@ -120,11 +120,12 @@ typedef union querybuf
static enum nss_status getanswer_r (const querybuf *answer, int anslen, static enum nss_status getanswer_r (const querybuf *answer, int anslen,
const char *qname, int qtype, const char *qname, int qtype,
struct hostent *result, char *buffer, struct hostent *result, char *buffer,
size_t buflen, int *h_errnop); size_t buflen, int *errnop, int *h_errnop);
enum nss_status enum nss_status
_nss_dns_gethostbyname2_r (const char *name, int af, struct hostent *result, _nss_dns_gethostbyname2_r (const char *name, int af, struct hostent *result,
char *buffer, size_t buflen, int *h_errnop) char *buffer, size_t buflen, int *errnop,
int *h_errnop)
{ {
querybuf host_buffer; querybuf host_buffer;
int size, type, n; int size, type, n;
@ -141,7 +142,7 @@ _nss_dns_gethostbyname2_r (const char *name, int af, struct hostent *result,
break; break;
default: default:
*h_errnop = NETDB_INTERNAL; *h_errnop = NETDB_INTERNAL;
__set_errno (EAFNOSUPPORT); *errnop = EAFNOSUPPORT;
return NSS_STATUS_UNAVAIL; return NSS_STATUS_UNAVAIL;
} }
@ -160,26 +161,28 @@ _nss_dns_gethostbyname2_r (const char *name, int af, struct hostent *result,
if (n < 0) if (n < 0)
{ {
*h_errnop = h_errno; *h_errnop = h_errno;
*errnop = errno;
return errno == ECONNREFUSED ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND; return errno == ECONNREFUSED ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND;
} }
return getanswer_r (&host_buffer, n, name, type, result, buffer, buflen, return getanswer_r (&host_buffer, n, name, type, result, buffer, buflen,
h_errnop); errnop, h_errnop);
} }
enum nss_status enum nss_status
_nss_dns_gethostbyname_r (const char *name, struct hostent *result, _nss_dns_gethostbyname_r (const char *name, struct hostent *result,
char *buffer, size_t buflen, int *h_errnop) char *buffer, size_t buflen, int *errnop,
int *h_errnop)
{ {
enum nss_status status = NSS_STATUS_NOTFOUND; enum nss_status status = NSS_STATUS_NOTFOUND;
if (_res.options & RES_USE_INET6) if (_res.options & RES_USE_INET6)
status = _nss_dns_gethostbyname2_r (name, AF_INET6, result, buffer, status = _nss_dns_gethostbyname2_r (name, AF_INET6, result, buffer,
buflen, h_errnop); buflen, errnop, h_errnop);
if (status == NSS_STATUS_NOTFOUND) if (status == NSS_STATUS_NOTFOUND)
status = _nss_dns_gethostbyname2_r (name, AF_INET, result, buffer, status = _nss_dns_gethostbyname2_r (name, AF_INET, result, buffer,
buflen, h_errnop); buflen, errnop, h_errnop);
return status; return status;
} }
@ -188,7 +191,7 @@ _nss_dns_gethostbyname_r (const char *name, struct hostent *result,
enum nss_status enum nss_status
_nss_dns_gethostbyaddr_r (const char *addr, int len, int af, _nss_dns_gethostbyaddr_r (const char *addr, int len, int af,
struct hostent *result, char *buffer, size_t buflen, struct hostent *result, char *buffer, size_t buflen,
int *h_errnop) int *errnop, int *h_errnop)
{ {
static const u_char mapped[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0xff,0xff }; static const u_char mapped[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0xff,0xff };
static const u_char tunnelled[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0,0 }; static const u_char tunnelled[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0,0 };
@ -224,13 +227,13 @@ _nss_dns_gethostbyaddr_r (const char *addr, int len, int af,
size = IN6ADDRSZ; size = IN6ADDRSZ;
break; break;
default: default:
__set_errno (EAFNOSUPPORT); *errnop = EAFNOSUPPORT;
*h_errnop = NETDB_INTERNAL; *h_errnop = NETDB_INTERNAL;
return NSS_STATUS_UNAVAIL; return NSS_STATUS_UNAVAIL;
} }
if (size != len) if (size != len)
{ {
__set_errno (EAFNOSUPPORT); *errnop = EAFNOSUPPORT;
*h_errnop = NETDB_INTERNAL; *h_errnop = NETDB_INTERNAL;
return NSS_STATUS_UNAVAIL; return NSS_STATUS_UNAVAIL;
} }
@ -256,14 +259,16 @@ _nss_dns_gethostbyaddr_r (const char *addr, int len, int af,
if (n < 0) if (n < 0)
{ {
*h_errnop = h_errno; *h_errnop = h_errno;
*errnop = errno;
return errno == ECONNREFUSED ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND; return errno == ECONNREFUSED ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND;
} }
status = getanswer_r (&host_buffer, n, qbuf, T_PTR, result, buffer, buflen, status = getanswer_r (&host_buffer, n, qbuf, T_PTR, result, buffer, buflen,
h_errnop); errnop, h_errnop);
if (status != NSS_STATUS_SUCCESS) if (status != NSS_STATUS_SUCCESS)
{ {
*h_errnop = h_errno; *h_errnop = h_errno;
*errnop = errno;
return status; return status;
} }
@ -292,7 +297,7 @@ _nss_dns_gethostbyaddr_r (const char *addr, int len, int af,
static enum nss_status static enum nss_status
getanswer_r (const querybuf *answer, int anslen, const char *qname, int qtype, getanswer_r (const querybuf *answer, int anslen, const char *qname, int qtype,
struct hostent *result, char *buffer, size_t buflen, struct hostent *result, char *buffer, size_t buflen,
int *h_errnop) int *errnop, int *h_errnop)
{ {
struct host_data struct host_data
{ {
@ -344,7 +349,17 @@ getanswer_r (const querybuf *answer, int anslen, const char *qname, int qtype,
n = dn_expand (answer->buf, end_of_message, cp, bp, linebuflen); n = dn_expand (answer->buf, end_of_message, cp, bp, linebuflen);
if (n < 0 || (*name_ok) (bp) == 0) if (n < 0 || (*name_ok) (bp) == 0)
{ {
if (errno == EMSGSIZE)
{
/* There is not enough room in the input buffer. */
*errnop = ERANGE;
*h_errnop = NETDB_INTERNAL;
}
else
{
*errnop = errno;
*h_errnop = NO_RECOVERY; *h_errnop = NO_RECOVERY;
}
return NSS_STATUS_UNAVAIL; return NSS_STATUS_UNAVAIL;
} }
cp += n + QFIXEDSZ; cp += n + QFIXEDSZ;
@ -358,7 +373,7 @@ getanswer_r (const querybuf *answer, int anslen, const char *qname, int qtype,
n = strlen (bp) + 1; /* for the \0 */ n = strlen (bp) + 1; /* for the \0 */
if (n >= MAXHOSTNAMELEN) if (n >= MAXHOSTNAMELEN)
{ {
__set_h_errno (NO_RECOVERY); *h_errnop = NO_RECOVERY;
return NSS_STATUS_TRYAGAIN; return NSS_STATUS_TRYAGAIN;
} }
result->h_name = bp; result->h_name = bp;

View File

@ -1,4 +1,4 @@
/* Copyright (C) 1996, 1997 Free Software Foundation, Inc. /* Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc.
This file is part of the GNU C Library. This file is part of the GNU C Library.
Extended from original form by Ulrich Drepper <drepper@cygnus.com>, 1996. Extended from original form by Ulrich Drepper <drepper@cygnus.com>, 1996.
@ -104,7 +104,7 @@ static enum nss_status getanswer_r (const querybuf *answer, int anslen,
enum nss_status enum nss_status
_nss_dns_getnetbyname_r (const char *name, struct netent *result, _nss_dns_getnetbyname_r (const char *name, struct netent *result,
char *buffer, size_t buflen) char *buffer, size_t buflen, int *errnop)
{ {
/* Return entry for network with NAME. */ /* Return entry for network with NAME. */
querybuf net_buffer; querybuf net_buffer;
@ -115,11 +115,14 @@ _nss_dns_getnetbyname_r (const char *name, struct netent *result,
anslen = res_search (qbuf, C_IN, T_PTR, (u_char *) &net_buffer, anslen = res_search (qbuf, C_IN, T_PTR, (u_char *) &net_buffer,
sizeof (querybuf)); sizeof (querybuf));
if (anslen < 0) if (anslen < 0)
{
/* Nothing found. */ /* Nothing found. */
*errnop = errno;
return (errno == ECONNREFUSED return (errno == ECONNREFUSED
|| errno == EPFNOSUPPORT || errno == EPFNOSUPPORT
|| errno == EAFNOSUPPORT) || errno == EAFNOSUPPORT)
? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND; ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND;
}
return getanswer_r (&net_buffer, anslen, result, buffer, buflen, BYNAME); return getanswer_r (&net_buffer, anslen, result, buffer, buflen, BYNAME);
} }
@ -127,7 +130,7 @@ _nss_dns_getnetbyname_r (const char *name, struct netent *result,
enum nss_status enum nss_status
_nss_dns_getnetbyaddr_r (long net, int type, struct netent *result, _nss_dns_getnetbyaddr_r (long net, int type, struct netent *result,
char *buffer, size_t buflen) char *buffer, size_t buflen, int *errnop)
{ {
/* Return entry for network with NAME. */ /* Return entry for network with NAME. */
enum nss_status status; enum nss_status status;
@ -170,11 +173,14 @@ _nss_dns_getnetbyaddr_r (long net, int type, struct netent *result,
anslen = res_query (qbuf, C_IN, T_PTR, (u_char *) &net_buffer, anslen = res_query (qbuf, C_IN, T_PTR, (u_char *) &net_buffer,
sizeof (querybuf)); sizeof (querybuf));
if (anslen < 0) if (anslen < 0)
{
/* Nothing found. */ /* Nothing found. */
*errnop = errno;
return (errno == ECONNREFUSED return (errno == ECONNREFUSED
|| errno == EPFNOSUPPORT || errno == EPFNOSUPPORT
|| errno == EAFNOSUPPORT) || errno == EAFNOSUPPORT)
? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND; ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND;
}
status = getanswer_r (&net_buffer, anslen, result, buffer, buflen, BYADDR); status = getanswer_r (&net_buffer, anslen, result, buffer, buflen, BYADDR);
if (status == NSS_STATUS_SUCCESS) if (status == NSS_STATUS_SUCCESS)

View File

@ -0,0 +1 @@
#include <sysdeps/unix/sysv/linux/i386/setegid.c>

View File

@ -0,0 +1 @@
#include <sysdeps/unix/sysv/linux/i386/seteuid.c>

View File

@ -0,0 +1 @@
#include <sysdeps/unix/sysv/linux/i386/setfsgid.c>

View File

@ -0,0 +1 @@
s

View File

@ -0,0 +1 @@
#include <sysdeps/unix/sysv/linux/i386/setgid.c>

View File

@ -0,0 +1 @@
#include <sysdeps/unix/sysv/linux/i386/setresgid.c>

View File

@ -0,0 +1 @@
#include <sysdeps/unix/sysv/linux/i386/setresuid.c>

View File

@ -0,0 +1 @@
#include <sysdeps/unix/sysv/linux/i386/setuid.c>