1
0
mirror of https://sourceware.org/git/glibc.git synced 2025-05-30 04:04:54 +03:00
* nscd/connections.c: Implement r/o sharing of nscd's cache with client
	processes via shared memory.
	* nscd/nscd-client.h: Likewise.
	* nscd/nscd.h: Likewise.
	* nscd/nscd_conf.c: Likewise.
	* nscd/nscd_getgr_r.c: Likewise.
	* nscd/nscd_getpw_r.c: Likewise.
	* nscd/nscd_gethst_r.c: Likewise.
	* nscd/nscd.conf: Add new config parameters.
	* nscd/Makefile (aux): Add nscd_helper.
	* nscd/nscd_helper.c: New file.
	* nscd/mem.c (gc): Indicate beginning and end of the gc cycle.

	* nscd/hstcache.c: Simplify a lot.  We cache only the request itself,
	no derived information.
	* connections.c (nscd_init): Fix bug in testing size of the persistent.

	* nis/Makefile (aux): Add nis_hash.
	* nis/nis_hash.c: New file.  Split out from nis_util.c.
	* nis/nis_util.c: Move __nis_hash code in separate file.

	* csu/tst-atomic.c: Improve atomic_increment_val test which would
	not have found a ppc bug.
This commit is contained in:
Ulrich Drepper 2004-09-08 15:46:42 +00:00
parent 0a3ad40da9
commit c207f23b0b
17 changed files with 1106 additions and 493 deletions

View File

@ -1,5 +1,29 @@
2004-09-08 Ulrich Drepper <drepper@redhat.com> 2004-09-08 Ulrich Drepper <drepper@redhat.com>
* nscd/connections.c: Implement r/o sharing of nscd's cache with client
processes via shared memory.
* nscd/nscd-client.h: Likewise.
* nscd/nscd.h: Likewise.
* nscd/nscd_conf.c: Likewise.
* nscd/nscd_getgr_r.c: Likewise.
* nscd/nscd_getpw_r.c: Likewise.
* nscd/nscd_gethst_r.c: Likewise.
* nscd/nscd.conf: Add new config parameters.
* nscd/Makefile (aux): Add nscd_helper.
* nscd/nscd_helper.c: New file.
* nscd/mem.c (gc): Indicate beginning and end of the gc cycle.
* nscd/hstcache.c: Simplify a lot. We cache only the request itself,
no derived information.
* connections.c (nscd_init): Fix bug in testing size of the persistent.
* nis/Makefile (aux): Add nis_hash.
* nis/nis_hash.c: New file. Split out from nis_util.c.
* nis/nis_util.c: Move __nis_hash code in separate file.
* csu/tst-atomic.c: Improve atomic_increment_val test which would
not have found a ppc bug.
* sysdeps/s390/fpu/bits/mathinline.h: Remove unnecessary includes. * sysdeps/s390/fpu/bits/mathinline.h: Remove unnecessary includes.
* malloc/arena.c: Remove __MALLOC_P uses. * malloc/arena.c: Remove __MALLOC_P uses.

View File

@ -130,7 +130,8 @@ do_test (void)
ret = 1; ret = 1;
} }
if (atomic_increment_val (&mem) != 1) mem = 2;
if (atomic_increment_val (&mem) != 3)
{ {
puts ("atomic_increment_val test failed"); puts ("atomic_increment_val test failed");
ret = 1; ret = 1;

View File

@ -21,6 +21,8 @@
# #
subdir := nis subdir := nis
aux := nis_hash
headers := $(wildcard rpcsvc/*.[hx]) headers := $(wildcard rpcsvc/*.[hx])
distribute := nss-nis.h nss-nisplus.h nis_intern.h Banner \ distribute := nss-nis.h nss-nisplus.h nis_intern.h Banner \
nisplus-parser.h nis_xdr.h nss nisplus-parser.h nis_xdr.h nss

76
nis/nis_hash.c Normal file
View File

@ -0,0 +1,76 @@
/* Copyright (c) 1997, 1998, 1999, 2004 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Thorsten Kukuk <kukuk@suse.de>, 1997.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */
#include <rpcsvc/nis.h>
/* This is from libc/db/hash/hash_func.c, hash3 is static there */
/*
* This is INCREDIBLY ugly, but fast. We break the string up into 8 byte
* units. On the first time through the loop we get the "leftover bytes"
* (strlen % 8). On every other iteration, we perform 8 HASHC's so we handle
* all 8 bytes. Essentially, this saves us 7 cmp & branch instructions. If
* this routine is heavily used enough, it's worth the ugly coding.
*
* OZ's original sdbm hash
*/
uint32_t
__nis_hash (const void *keyarg, register size_t len)
{
register const u_char *key;
register size_t loop;
register uint32_t h;
#define HASHC h = *key++ + 65599 * h
h = 0;
key = keyarg;
if (len > 0)
{
loop = (len + 8 - 1) >> 3;
switch (len & (8 - 1))
{
case 0:
do {
HASHC;
/* FALLTHROUGH */
case 7:
HASHC;
/* FALLTHROUGH */
case 6:
HASHC;
/* FALLTHROUGH */
case 5:
HASHC;
/* FALLTHROUGH */
case 4:
HASHC;
/* FALLTHROUGH */
case 3:
HASHC;
/* FALLTHROUGH */
case 2:
HASHC;
/* FALLTHROUGH */
case 1:
HASHC;
} while (--loop);
}
}
return h;
}

View File

@ -1,4 +1,4 @@
/* Copyright (c) 1997, 1998, 1999 Free Software Foundation, Inc. /* Copyright (c) 1997, 1998, 1999, 2004 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@suse.de>, 1997. Contributed by Thorsten Kukuk <kukuk@suse.de>, 1997.
@ -47,58 +47,6 @@ __nis_finddirectory (directory_obj *dir, const_nis_name name)
return fd_res; return fd_res;
} }
/* This is from libc/db/hash/hash_func.c, hash3 is static there */
/*
* This is INCREDIBLY ugly, but fast. We break the string up into 8 byte
* units. On the first time through the loop we get the "leftover bytes"
* (strlen % 8). On every other iteration, we perform 8 HASHC's so we handle
* all 8 bytes. Essentially, this saves us 7 cmp & branch instructions. If
* this routine is heavily used enough, it's worth the ugly coding.
*
* OZ's original sdbm hash
*/
uint32_t
__nis_hash (const void *keyarg, register size_t len)
{
register const u_char *key;
register size_t loop;
register uint32_t h;
#define HASHC h = *key++ + 65599 * h /* The hash implementation is in a separate file. */
#include "nis_hash.c"
h = 0;
key = keyarg;
if (len > 0)
{
loop = (len + 8 - 1) >> 3;
switch (len & (8 - 1))
{
case 0:
do {
HASHC;
/* FALLTHROUGH */
case 7:
HASHC;
/* FALLTHROUGH */
case 6:
HASHC;
/* FALLTHROUGH */
case 5:
HASHC;
/* FALLTHROUGH */
case 4:
HASHC;
/* FALLTHROUGH */
case 3:
HASHC;
/* FALLTHROUGH */
case 2:
HASHC;
/* FALLTHROUGH */
case 1:
HASHC;
} while (--loop);
}
}
return (h);
}

View File

@ -22,6 +22,7 @@
subdir := nscd subdir := nscd
routines := nscd_getpw_r nscd_getgr_r nscd_gethst_r routines := nscd_getpw_r nscd_getgr_r nscd_gethst_r
aux := nscd_helper
include ../Makeconfig include ../Makeconfig

View File

@ -80,7 +80,10 @@ const char *serv2str[LASTREQ] =
[GETHOSTBYADDRv6] = "GETHOSTBYADDRv6", [GETHOSTBYADDRv6] = "GETHOSTBYADDRv6",
[SHUTDOWN] = "SHUTDOWN", [SHUTDOWN] = "SHUTDOWN",
[GETSTAT] = "GETSTAT", [GETSTAT] = "GETSTAT",
[INVALIDATE] = "INVALIDATE" [INVALIDATE] = "INVALIDATE",
[GETFDPW] = "GETFDPW",
[GETFDGR] = "GETFDGR",
[GETFDHST] = "GETFDHST"
}; };
/* The control data structures for the services. */ /* The control data structures for the services. */
@ -91,6 +94,7 @@ struct database_dyn dbs[lastdb] =
.enabled = 0, .enabled = 0,
.check_file = 1, .check_file = 1,
.persistent = 0, .persistent = 0,
.shared = 0,
.filename = "/etc/passwd", .filename = "/etc/passwd",
.db_filename = _PATH_NSCD_PASSWD_DB, .db_filename = _PATH_NSCD_PASSWD_DB,
.disabled_iov = &pwd_iov_disabled, .disabled_iov = &pwd_iov_disabled,
@ -105,6 +109,7 @@ struct database_dyn dbs[lastdb] =
.enabled = 0, .enabled = 0,
.check_file = 1, .check_file = 1,
.persistent = 0, .persistent = 0,
.shared = 0,
.filename = "/etc/group", .filename = "/etc/group",
.db_filename = _PATH_NSCD_GROUP_DB, .db_filename = _PATH_NSCD_GROUP_DB,
.disabled_iov = &grp_iov_disabled, .disabled_iov = &grp_iov_disabled,
@ -119,6 +124,7 @@ struct database_dyn dbs[lastdb] =
.enabled = 0, .enabled = 0,
.check_file = 1, .check_file = 1,
.persistent = 0, .persistent = 0,
.shared = 0,
.filename = "/etc/hosts", .filename = "/etc/hosts",
.db_filename = _PATH_NSCD_HOSTS_DB, .db_filename = _PATH_NSCD_HOSTS_DB,
.disabled_iov = &hst_iov_disabled, .disabled_iov = &hst_iov_disabled,
@ -132,7 +138,7 @@ struct database_dyn dbs[lastdb] =
/* Mapping of request type to database. */ /* Mapping of request type to database. */
static struct database_dyn *const serv2db[LASTDBREQ + 1] = static struct database_dyn *const serv2db[LASTREQ] =
{ {
[GETPWBYNAME] = &dbs[pwddb], [GETPWBYNAME] = &dbs[pwddb],
[GETPWBYUID] = &dbs[pwddb], [GETPWBYUID] = &dbs[pwddb],
@ -141,7 +147,10 @@ static struct database_dyn *const serv2db[LASTDBREQ + 1] =
[GETHOSTBYNAME] = &dbs[hstdb], [GETHOSTBYNAME] = &dbs[hstdb],
[GETHOSTBYNAMEv6] = &dbs[hstdb], [GETHOSTBYNAMEv6] = &dbs[hstdb],
[GETHOSTBYADDR] = &dbs[hstdb], [GETHOSTBYADDR] = &dbs[hstdb],
[GETHOSTBYADDRv6] = &dbs[hstdb] [GETHOSTBYADDRv6] = &dbs[hstdb],
[GETFDPW] = &dbs[pwddb],
[GETFDGR] = &dbs[grpdb],
[GETFDHST] = &dbs[hstdb],
}; };
@ -158,9 +167,6 @@ static int sock;
/* Number of times clients had to wait. */ /* Number of times clients had to wait. */
unsigned long int client_queued; unsigned long int client_queued;
/* Alignment requirement of the beginning of the data region. */
#define ALIGN 16
/* Initialize database information structures. */ /* Initialize database information structures. */
void void
@ -224,11 +230,10 @@ nscd_init (void)
dbs[cnt].persistent = 0; dbs[cnt].persistent = 0;
} }
else if ((total = (sizeof (head) else if ((total = (sizeof (head)
+ roundup (head.module + roundup (head.module * sizeof (ref_t),
* sizeof (struct hashentry),
ALIGN) ALIGN)
+ head.data_size)) + head.data_size))
< st.st_size) > st.st_size)
{ {
dbg_log (_("invalid persistent database file \"%s\": %s"), dbg_log (_("invalid persistent database file \"%s\": %s"),
dbs[cnt].db_filename, dbs[cnt].db_filename,
@ -253,6 +258,7 @@ nscd_init (void)
dbnames[cnt]); dbnames[cnt]);
dbs[cnt].wr_fd = fd; dbs[cnt].wr_fd = fd;
dbs[cnt].ro_fd = open (dbs[cnt].db_filename, O_RDONLY);
fd = -1; fd = -1;
/* We also need a read-only descriptor. */ /* We also need a read-only descriptor. */
dbs[cnt].ro_fd = open (dbs[cnt].db_filename, O_RDONLY); dbs[cnt].ro_fd = open (dbs[cnt].db_filename, O_RDONLY);
@ -439,6 +445,9 @@ cannot create read-only descriptor for \"%s\"; no mmap"),
* dbs[cnt].head->module); * dbs[cnt].head->module);
dbs[cnt].data = xmalloc (dbs[cnt].head->data_size); dbs[cnt].data = xmalloc (dbs[cnt].head->data_size);
dbs[cnt].head->first_free = 0; dbs[cnt].head->first_free = 0;
dbs[cnt].shared = 0;
assert (dbs[cnt].ro_fd == -1);
} }
if (dbs[cnt].check_file) if (dbs[cnt].check_file)
@ -529,6 +538,43 @@ invalidate_cache (char *key)
} }
#ifdef SCM_RIGHTS
static void
send_ro_fd (struct database_dyn *db, char *key, int fd)
{
/* If we do not have an read-only file descriptor do nothing. */
if (db->ro_fd == -1)
return;
/* We need to send some data along with the descriptor. */
struct iovec iov[1];
iov[0].iov_base = key;
iov[0].iov_len = strlen (key) + 1;
/* Prepare the control message to transfer the descriptor. */
char buf[CMSG_SPACE (sizeof (int))];
struct msghdr msg = { .msg_iov = iov, .msg_iovlen = 1,
.msg_control = buf, .msg_controllen = sizeof (buf) };
struct cmsghdr *cmsg = CMSG_FIRSTHDR (&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN (sizeof (int));
*(int *) CMSG_DATA (cmsg) = db->ro_fd;
msg.msg_controllen = cmsg->cmsg_len;
/* Send the control message. We repeat when we are interrupted but
everything else is ignored. */
(void) TEMP_FAILURE_RETRY (sendmsg (fd, &msg, 0));
if (__builtin_expect (debug_level > 0, 0))
dbg_log (_("provide access to FD %d, for %s"), db->ro_fd, key);
}
#endif /* SCM_RIGHTS */
/* Handle new request. */ /* Handle new request. */
static void static void
handle_request (int fd, request_header *req, void *key, uid_t uid) handle_request (int fd, request_header *req, void *key, uid_t uid)
@ -614,7 +660,7 @@ cannot handle old request version %d; current version is %d"),
else if (__builtin_expect (debug_level, 0) > 0) else if (__builtin_expect (debug_level, 0) > 0)
{ {
if (req->type == INVALIDATE) if (req->type == INVALIDATE)
dbg_log ("\t%s (%s)", serv2str[req->type], (char *)key); dbg_log ("\t%s (%s)", serv2str[req->type], (char *) key);
else else
dbg_log ("\t%s", serv2str[req->type]); dbg_log ("\t%s", serv2str[req->type]);
} }
@ -697,6 +743,14 @@ cannot handle old request version %d; current version is %d"),
} }
break; break;
case GETFDPW:
case GETFDGR:
case GETFDHST:
#ifdef SCM_RIGHTS
send_ro_fd (serv2db[req->type], key, fd);
#endif
break;
default: default:
/* Ignore the command, it's nothing we know. */ /* Ignore the command, it's nothing we know. */
break; break;
@ -733,7 +787,9 @@ nscd_run (void *p)
int timeout = -1; int timeout = -1;
if (run_prune) if (run_prune)
{ {
now = time (NULL); /* NB: we do not flush the timestamp update using msync since
this value doesnot matter on disk. */
dbs[my_number].head->timestamp = now = time (NULL);
timeout = now < next_prune ? 1000 * (next_prune - now) : 0; timeout = now < next_prune ? 1000 * (next_prune - now) : 0;
} }

View File

@ -77,7 +77,7 @@ static const hst_response_header notfound =
static void static void
cache_addhst (struct database_dyn *db, int fd, request_header *req, cache_addhst (struct database_dyn *db, int fd, request_header *req,
const void *key, struct hostent *hst, uid_t owner, int add_addr, const void *key, struct hostent *hst, uid_t owner,
struct hashentry *he, struct datahead *dh, int errval) struct hashentry *he, struct datahead *dh, int errval)
{ {
ssize_t total; ssize_t total;
@ -208,7 +208,7 @@ cache_addhst (struct database_dyn *db, int fd, request_header *req,
the current cache handling cannot handle and it is more than the current cache handling cannot handle and it is more than
questionable whether it is worthwhile complicating the cache questionable whether it is worthwhile complicating the cache
handling just for handling such a special case. */ handling just for handling such a special case. */
if (he == NULL && (add_addr || hst->h_addr_list[1] == NULL)) if (he == NULL && hst->h_addr_list[1] == NULL)
{ {
dataset = (struct dataset *) mempool_alloc (db, dataset = (struct dataset *) mempool_alloc (db,
total + req->key_len); total + req->key_len);
@ -269,10 +269,7 @@ cache_addhst (struct database_dyn *db, int fd, request_header *req,
itself. This is the case if the resolver is used and the name itself. This is the case if the resolver is used and the name
is extended by the domainnames from /etc/resolv.conf. Therefore is extended by the domainnames from /etc/resolv.conf. Therefore
we explicitly add the name here. */ we explicitly add the name here. */
if (req->type == GETHOSTBYNAME || req->type == GETHOSTBYNAMEv6) key_copy = memcpy (cp, key, req->key_len);
key_copy = memcpy (cp, key, req->key_len);
else
memset (cp, '\0', req->key_len);
/* Now we can determine whether on refill we have to create a new /* Now we can determine whether on refill we have to create a new
record or not. */ record or not. */
@ -349,141 +346,21 @@ cache_addhst (struct database_dyn *db, int fd, request_header *req,
problem is that we always must add the hash table entry problem is that we always must add the hash table entry
with the FIRST flag set first. Otherwise we get dangling with the FIRST flag set first. Otherwise we get dangling
pointers in case memory allocation fails. */ pointers in case memory allocation fails. */
assert (add_addr || hst->h_addr_list[1] == NULL); assert (hst->h_addr_list[1] == NULL);
/* Add the normal addresses. */
if (add_addr)
{
for (cnt = 0; cnt < h_addr_list_cnt; ++cnt)
{
if (cache_add (addr_list_type, addresses, hst->h_length,
&dataset->head, cnt == 0, db, owner) < 0)
{
/* Ensure the data can be recovered. */
if (cnt == 0)
dataset->head.usable = false;
goto out;
}
addresses += hst->h_length;
}
/* If necessary the IPv6 addresses. */
if (addr_list_type == GETHOSTBYADDR)
for (cnt = 0; cnt < h_addr_list_cnt; ++cnt)
{
if (cache_add (GETHOSTBYADDRv6, addresses, IN6ADDRSZ,
&dataset->head, false, db, owner) < 0)
goto out;
addresses += IN6ADDRSZ;
}
}
/* Avoid adding names if more than one address is available. See /* Avoid adding names if more than one address is available. See
above for more info. */ above for more info. */
else assert (req->type == GETHOSTBYNAME
{ || req->type == GETHOSTBYNAMEv6
assert (req->type == GETHOSTBYNAME || req->type == GETHOSTBYADDR
|| req->type == GETHOSTBYNAMEv6 || req->type == GETHOSTBYADDRv6);
|| req->type == GETHOSTBYADDR
|| req->type == GETHOSTBYADDRv6);
/* If necessary add the key for this request. */ if (cache_add (req->type, key_copy, req->key_len,
if (req->type == GETHOSTBYNAME) &dataset->head, true, db, owner) < 0)
{ /* Could not allocate memory. Make sure the
bool first = true; data gets discarded. */
if (addr_list_type == GETHOSTBYADDR) dataset->head.usable = false;
{
if (cache_add (GETHOSTBYNAME, key_copy, req->key_len,
&dataset->head, true, db, owner) < 0)
{
/* Could not allocate memory. Make sure the
data gets discarded. */
dataset->head.usable = false;
goto out;
}
first = false;
}
if (cache_add (GETHOSTBYNAMEv6, key_copy, req->key_len,
&dataset->head, first, db, owner) < 0)
{
/* Could not allocate memory. Make sure the
data gets discarded. */
if (first)
dataset->head.usable = false;
goto out;
}
}
else if (req->type == GETHOSTBYNAMEv6)
{
if (cache_add (GETHOSTBYNAMEv6, key_copy, req->key_len,
&dataset->head, true, db, owner) < 0)
{
/* Could not allocate memory. Make sure the
data gets discarded. */
dataset->head.usable = false;
goto out;
}
if (addr_list_type == GETHOSTBYADDR
&& cache_add (GETHOSTBYNAME, key_copy, req->key_len,
&dataset->head, false, db, owner) < 0)
goto out;
}
/* And finally the name. We mark this as the last entry. */
if (addr_list_type == GETHOSTBYADDR
&& req->type == GETHOSTBYADDR
&& cache_add (GETHOSTBYNAME, dataset->strdata, h_name_len,
&dataset->head, true, db, owner) < 0)
{
/* Could not allocate memory. Make sure the
data gets discarded. */
dataset->head.usable = false;
goto out;
}
if (cache_add (GETHOSTBYNAMEv6, dataset->strdata,
h_name_len, &dataset->head,
((req->type == GETHOSTBYADDR
&& addr_list_type != GETHOSTBYADDR)
|| req->type == GETHOSTBYADDRv6), db,
owner) < 0)
{
/* Could not allocate memory. Make sure the
data gets discarded. */
if ((req->type == GETHOSTBYADDR
&& addr_list_type != GETHOSTBYADDR)
|| req->type == GETHOSTBYADDRv6)
dataset->head.usable = false;
goto out;
}
if (addr_list_type == GETHOSTBYADDR
&& req->type != GETHOSTBYADDR
&& cache_add (GETHOSTBYNAME, dataset->strdata, h_name_len,
&dataset->head, false, db, owner) < 0)
goto out;
/* First add all the aliases. */
for (cnt = 0; cnt < h_aliases_cnt; ++cnt)
{
if (addr_list_type == GETHOSTBYADDR)
if (cache_add (GETHOSTBYNAME, aliases,
h_aliases_len[cnt], &dataset->head,
false, db, owner) < 0)
break;
if (cache_add (GETHOSTBYNAMEv6, aliases,
h_aliases_len[cnt], &dataset->head,
false, db, owner) < 0)
break;
aliases += h_aliases_len[cnt];
}
}
out:
pthread_rwlock_unlock (&db->lock); pthread_rwlock_unlock (&db->lock);
} }
} }
@ -534,10 +411,18 @@ addhstbyX (struct database_dyn *db, int fd, request_header *req,
if (__builtin_expect (debug_level > 0, 0)) if (__builtin_expect (debug_level > 0, 0))
{ {
if (he == NULL) const char *str;
dbg_log (_("Haven't found \"%s\" in hosts cache!"), (char *) key); char buf[INET6_ADDRSTRLEN + 1];
if (req->type == GETHOSTBYNAME || req->type == GETHOSTBYNAMEv6)
str = key;
else else
dbg_log (_("Reloading \"%s\" in hosts cache!"), (char *) key); str = inet_ntop (req->type == GETHOSTBYADDR ? AF_INET : AF_INET6,
key, buf, sizeof (buf));
if (he == NULL)
dbg_log (_("Haven't found \"%s\" in hosts cache!"), (char *) str);
else
dbg_log (_("Reloading \"%s\" in hosts cache!"), (char *) str);
} }
if (db->secure) if (db->secure)
@ -583,7 +468,7 @@ addhstbyX (struct database_dyn *db, int fd, request_header *req,
if (db->secure) if (db->secure)
seteuid (oldeuid); seteuid (oldeuid);
cache_addhst (db, fd, req, key, hst, uid, 0, he, dh, cache_addhst (db, fd, req, key, hst, uid, he, dh,
h_errno == TRY_AGAIN ? errval : 0); h_errno == TRY_AGAIN ? errval : 0);
if (use_malloc) if (use_malloc)

View File

@ -230,6 +230,12 @@ gc (struct database_dyn *db)
++next_data; ++next_data;
/* Now we start modifying the data. Make sure all readers of the
data are aware of this and temporarily don't use the data. */
++db->head->gc_cycle;
assert ((db->head->gc_cycle & 1) == 1);
/* We do not perform the move operations right away since the /* We do not perform the move operations right away since the
he_data array is not sorted by the address of the data. */ he_data array is not sorted by the address of the data. */
struct moveinfo struct moveinfo
@ -445,6 +451,11 @@ gc (struct database_dyn *db)
msync (db->head, db->data + db->head->first_free - (char *) db->head, msync (db->head, db->data + db->head->first_free - (char *) db->head,
MS_ASYNC); MS_ASYNC);
/* Now we are done modifying the data. */
++db->head->gc_cycle;
assert ((db->head->gc_cycle & 1) == 0);
/* We are done. */ /* We are done. */
out: out:
pthread_mutex_unlock (&db->memlock); pthread_mutex_unlock (&db->memlock);

View File

@ -23,6 +23,7 @@
#ifndef _NSCD_CLIENT_H #ifndef _NSCD_CLIENT_H
#define _NSCD_CLIENT_H 1 #define _NSCD_CLIENT_H 1
#include <atomic.h>
#include <nscd-types.h> #include <nscd-types.h>
/* Version number of the daemon interface */ /* Version number of the daemon interface */
@ -53,6 +54,9 @@ typedef enum
SHUTDOWN, /* Shut the server down. */ SHUTDOWN, /* Shut the server down. */
GETSTAT, /* Get the server statistic. */ GETSTAT, /* Get the server statistic. */
INVALIDATE, /* Invalidate one special cache. */ INVALIDATE, /* Invalidate one special cache. */
GETFDPW,
GETFDGR,
GETFDHST,
LASTREQ LASTREQ
} request_type; } request_type;
@ -110,9 +114,151 @@ typedef struct
} hst_response_header; } hst_response_header;
/* Type for offsets in data part of database. */
typedef uint32_t ref_t;
/* Value for invalid/no reference. */
#define ENDREF UINT32_MAX
/* Alignment requirement of the beginning of the data region. */
#define ALIGN 16
/* Head of record in data part of database. */
struct datahead
{
size_t allocsize; /* Allocated Bytes. */
size_t recsize; /* Size of the record. */
time_t timeout; /* Time when this entry becomes invalid. */
bool notfound; /* Nonzero if data for key has not been found. */
uint8_t nreloads; /* Reloads without use. */
bool usable; /* False if the entry must be ignored. */
/* We need to have the following element aligned for the response
header data types and their use in the 'struct dataset' types
defined in the XXXcache.c files. */
union
{
pw_response_header pwdata;
gr_response_header grdata;
hst_response_header hstdata;
ssize_t align1;
time_t align2;
} data[0];
};
/* Structure for one hash table entry. */
struct hashentry
{
request_type type:8; /* Which type of dataset. */
bool first; /* True if this was the original key. */
size_t len; /* Length of key. */
ref_t key; /* Pointer to key. */
uid_t owner; /* If secure table, this is the owner. */
ref_t next; /* Next entry in this hash bucket list. */
ref_t packet; /* Records for the result. */
union
{
struct hashentry *dellist; /* Next record to be deleted. This can be a
pointer since only nscd uses this field. */
ref_t *prevp; /* Pointer to field containing forward
reference. */
};
};
/* Current persistent database version. */
#define DB_VERSION 1
/* Maximum time allowed between updates of the timestamp. */
#define MAPPING_TIMEOUT (5 * 60)
/* Header of persistent database file. */
struct database_pers_head
{
int version;
int header_size;
int gc_cycle;
volatile time_t timestamp;
size_t module;
size_t data_size;
size_t first_free; /* Offset of first free byte in data area. */
size_t nentries;
size_t maxnentries;
size_t maxnsearched;
uintmax_t poshit;
uintmax_t neghit;
uintmax_t posmiss;
uintmax_t negmiss;
uintmax_t rdlockdelayed;
uintmax_t wrlockdelayed;
uintmax_t addfailed;
ref_t array[0];
};
/* Mapped database record. */
struct mapped_database
{
const struct database_pers_head *head;
const char *data;
size_t mapsize;
int counter; /* > 0 indicates it isusable. */
};
#define NO_MAPPING ((struct mapped_database *) -1l)
struct locked_map_ptr
{
int lock;
struct mapped_database *mapped;
};
#define libc_locked_map_ptr(name) static struct locked_map_ptr name
/* Open socket connection to nscd server. */ /* Open socket connection to nscd server. */
extern int __nscd_open_socket (const char *key, size_t keylen, extern int __nscd_open_socket (const char *key, size_t keylen,
request_type type, void *response, request_type type, void *response,
size_t responselen) attribute_hidden; size_t responselen) attribute_hidden;
/* Get reference of mapping. */
extern struct mapped_database *__nscd_get_map_ref (request_type type,
const char *name,
struct locked_map_ptr *mapptr,
int *gc_cyclep);
/* Unmap database. */
extern void __nscd_unmap (struct mapped_database *mapped);
/* Drop reference of mapping. */
static inline int __nscd_drop_map_ref (struct mapped_database *map,
int gc_cycle)
{
if (map != NO_MAPPING)
{
if (__builtin_expect (map->head->gc_cycle != gc_cycle, 0))
/* We might have read inconsistent data. */
return -1;
if (atomic_decrement_val (&map->counter) == 0)
__nscd_unmap (map);
}
return 0;
}
/* Search the mapped database. */
extern const struct datahead * __nscd_cache_search (request_type type,
const char *key,
size_t keylen,
const struct mapped_database *mapped);
#endif /* nscd.h */ #endif /* nscd.h */

View File

@ -19,6 +19,7 @@
# suggested-size <service> <prime number> # suggested-size <service> <prime number>
# check-files <service> <yes|no> # check-files <service> <yes|no>
# persistent <service> <yes|no> # persistent <service> <yes|no>
# shared <service> <yes|no>
# #
# Currently supported cache names (services): passwd, group, hosts # Currently supported cache names (services): passwd, group, hosts
# #
@ -37,6 +38,7 @@
suggested-size passwd 211 suggested-size passwd 211
check-files passwd yes check-files passwd yes
persistent passwd yes persistent passwd yes
shared passwd yes
enable-cache group yes enable-cache group yes
positive-time-to-live group 3600 positive-time-to-live group 3600
@ -44,6 +46,7 @@
suggested-size group 211 suggested-size group 211
check-files group yes check-files group yes
persistent group yes persistent group yes
shared group yes
enable-cache hosts yes enable-cache hosts yes
positive-time-to-live hosts 3600 positive-time-to-live hosts 3600
@ -51,3 +54,4 @@
suggested-size hosts 211 suggested-size hosts 211
check-files hosts yes check-files hosts yes
persistent hosts yes persistent hosts yes
shared hosts yes

View File

@ -42,30 +42,6 @@ typedef enum
} dbtype; } dbtype;
/* Head of record in data part of database. */
struct datahead
{
size_t allocsize; /* Allocated Bytes. */
size_t recsize; /* Size of the record. */
time_t timeout; /* Time when this entry becomes invalid. */
bool notfound; /* Nonzero if data for key has not been found. */
uint8_t nreloads; /* Reloads without use. */
bool usable; /* False if the entry must be ignored. */
/* We need to have the following element aligned for the response
header data types and their use in the 'struct dataset' types
defined in the XXXcache.c files. */
union
{
pw_response_header pwdata;
gr_response_header grdata;
hst_response_header hstdata;
ssize_t align1;
time_t align2;
} data[0];
};
/* Default limit on the number of times a value gets reloaded without /* Default limit on the number of times a value gets reloaded without
being used in the meantime. NSCD does not throw a value out as being used in the meantime. NSCD does not throw a value out as
soon as it times out. It tries to reload the value from the soon as it times out. It tries to reload the value from the
@ -74,63 +50,6 @@ struct datahead
#define DEFAULT_RELOAD_LIMIT 5 #define DEFAULT_RELOAD_LIMIT 5
/* Type for offsets in data part of database. */
typedef uint32_t ref_t;
/* Value for invalid/no reference. */
#define ENDREF UINT32_MAX
/* Structure for one hash table entry. */
struct hashentry
{
request_type type:8; /* Which type of dataset. */
bool first; /* True if this was the original key. */
size_t len; /* Length of key. */
ref_t key; /* Pointer to key. */
uid_t owner; /* If secure table, this is the owner. */
ref_t next; /* Next entry in this hash bucket list. */
ref_t packet; /* Records for the result. */
union
{
struct hashentry *dellist; /* Next record to be deleted. This can be a
pointer since only nscd uses this field. */
ref_t *prevp; /* Pointer to field containing forward
reference. */
};
};
/* Current persistent database version. */
#define DB_VERSION 1
/* Header of persistent database file. */
struct database_pers_head
{
int version;
int header_size;
size_t module;
size_t data_size;
size_t first_free; /* Offset of first free byte in data area. */
size_t nentries;
size_t maxnentries;
size_t maxnsearched;
uintmax_t poshit;
uintmax_t neghit;
uintmax_t posmiss;
uintmax_t negmiss;
uintmax_t rdlockdelayed;
uintmax_t wrlockdelayed;
uintmax_t addfailed;
ref_t array[0];
};
/* Structure describing dynamic part of one database. */ /* Structure describing dynamic part of one database. */
struct database_dyn struct database_dyn
{ {
@ -139,6 +58,7 @@ struct database_dyn
int enabled; int enabled;
int check_file; int check_file;
int persistent; int persistent;
int shared;
const char *filename; const char *filename;
const char *db_filename; const char *db_filename;
time_t file_mtime; time_t file_mtime;

View File

@ -216,6 +216,20 @@ nscd_parse_file (const char *fname, struct database_dyn dbs[lastdb])
if (cnt == lastdb) if (cnt == lastdb)
dbg_log ("database %s is not supported\n", arg1); dbg_log ("database %s is not supported\n", arg1);
} }
else if (strcmp (entry, "shared") == 0)
{
for (cnt = 0; cnt < lastdb; ++cnt)
if (strcmp (arg1, dbnames[cnt]) == 0)
{
if (strcmp (arg2, "no") == 0)
dbs[cnt].shared = 0;
else if (strcmp (arg2, "yes") == 0)
dbs[cnt].shared = 1;
break;
}
if (cnt == lastdb)
dbg_log ("database %s is not supported\n", arg1);
}
else if (strcmp (entry, "reload-count") == 0) else if (strcmp (entry, "reload-count") == 0)
{ {
if (strcasecmp (arg1, "unlimited") == 0) if (strcasecmp (arg1, "unlimited") == 0)

View File

@ -18,6 +18,7 @@
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */ 02111-1307 USA. */
#include <assert.h>
#include <errno.h> #include <errno.h>
#include <grp.h> #include <grp.h>
#include <stdint.h> #include <stdint.h>
@ -25,6 +26,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <sys/mman.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/uio.h> #include <sys/uio.h>
#include <sys/un.h> #include <sys/un.h>
@ -64,36 +66,82 @@ __nscd_getgrgid_r (gid_t gid, struct group *resultbuf, char *buffer,
} }
libc_locked_map_ptr (map_handle);
/* Note that we only free the structure if necessary. The memory
mapping is not removed since it is not visible to the malloc
handling. */
libc_freeres_fn (gr_map_free)
{
if (map_handle.mapped != NO_MAPPING)
free (map_handle.mapped);
}
static int static int
internal_function internal_function
nscd_getgr_r (const char *key, size_t keylen, request_type type, nscd_getgr_r (const char *key, size_t keylen, request_type type,
struct group *resultbuf, char *buffer, size_t buflen, struct group *resultbuf, char *buffer, size_t buflen,
struct group **result) struct group **result)
{ {
gr_response_header gr_resp; const gr_response_header *gr_resp = NULL;
int sock = __nscd_open_socket (key, keylen, type, &gr_resp, const uint32_t *len = NULL;
sizeof (gr_resp)); const char *gr_name = NULL;
if (sock == -1) size_t gr_name_len = 0;
int retval = -1;
int gc_cycle;
const char *recend = (const char *) ~UINTMAX_C (0);
/* If the mapping is available, try to search there instead of
communicating with the nscd. */
struct mapped_database *mapped = __nscd_get_map_ref (GETFDGR, "group",
&map_handle, &gc_cycle);
retry:
if (mapped != NO_MAPPING)
{ {
__nss_not_use_nscd_group = 1; const struct datahead *found = __nscd_cache_search (type, key, keylen,
return -1; mapped);
if (found != NULL)
{
gr_resp = &found->data[0].grdata;
len = (const uint32_t *) (gr_resp + 1);
/* The alignment is always sufficient. */
assert (((uintptr_t) len & (__alignof__ (*len) - 1)) == 0);
gr_name = ((const char *) len
+ gr_resp->gr_mem_cnt * sizeof (uint32_t));
gr_name_len = gr_resp->gr_name_len + gr_resp->gr_passwd_len;
recend = (const char *) found->data + found->recsize;
}
}
gr_response_header gr_resp_mem;
int sock = -1;
if (gr_resp == NULL)
{
sock = __nscd_open_socket (key, keylen, type, &gr_resp_mem,
sizeof (gr_resp_mem));
if (sock == -1)
{
__nss_not_use_nscd_group = 1;
goto out;
}
gr_resp = &gr_resp_mem;
} }
/* No value found so far. */ /* No value found so far. */
int retval = -1;
*result = NULL; *result = NULL;
if (gr_resp.found == -1) if (__builtin_expect (gr_resp->found == -1, 0))
{ {
/* The daemon does not cache this database. */ /* The daemon does not cache this database. */
__nss_not_use_nscd_group = 1; __nss_not_use_nscd_group = 1;
goto out; goto out_close;
} }
if (gr_resp.found == 1) if (gr_resp->found == 1)
{ {
struct iovec vec[2]; struct iovec vec[2];
uint32_t *len;
char *p = buffer; char *p = buffer;
size_t total_len; size_t total_len;
uintptr_t align; uintptr_t align;
@ -103,71 +151,90 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type,
align the pointer. */ align the pointer. */
align = ((__alignof__ (char *) - (p - ((char *) 0))) align = ((__alignof__ (char *) - (p - ((char *) 0)))
& (__alignof__ (char *) - 1)); & (__alignof__ (char *) - 1));
total_len = align + (1 + gr_resp.gr_mem_cnt) * sizeof (char *) total_len = (align + (1 + gr_resp->gr_mem_cnt) * sizeof (char *)
+ gr_resp.gr_name_len + gr_resp.gr_passwd_len; + gr_resp->gr_name_len + gr_resp->gr_passwd_len);
if (__builtin_expect (buflen < total_len, 0)) if (__builtin_expect (buflen < total_len, 0))
{ {
no_room: no_room:
__set_errno (ERANGE); __set_errno (ERANGE);
retval = ERANGE; retval = ERANGE;
goto out; goto out_close;
} }
buflen -= total_len; buflen -= total_len;
p += align; p += align;
resultbuf->gr_mem = (char **) p; resultbuf->gr_mem = (char **) p;
p += (1 + gr_resp.gr_mem_cnt) * sizeof (char *); p += (1 + gr_resp->gr_mem_cnt) * sizeof (char *);
/* Set pointers for strings. */ /* Set pointers for strings. */
resultbuf->gr_name = p; resultbuf->gr_name = p;
p += gr_resp.gr_name_len; p += gr_resp->gr_name_len;
resultbuf->gr_passwd = p; resultbuf->gr_passwd = p;
p += gr_resp.gr_passwd_len; p += gr_resp->gr_passwd_len;
/* Fill in what we know now. */ /* Fill in what we know now. */
resultbuf->gr_gid = gr_resp.gr_gid; resultbuf->gr_gid = gr_resp->gr_gid;
/* Allocate array to store lengths. */ /* Read the length information, group name, and password. */
len = (uint32_t *) alloca (gr_resp.gr_mem_cnt * sizeof (uint32_t)); if (len == NULL)
{
/* Allocate array to store lengths. */
len = (uint32_t *) alloca (gr_resp->gr_mem_cnt * sizeof (uint32_t));
total_len = gr_resp.gr_mem_cnt * sizeof (uint32_t); vec[0].iov_base = (void *) len;
vec[0].iov_base = len; vec[0].iov_len = gr_resp->gr_mem_cnt * sizeof (uint32_t);
vec[0].iov_len = total_len; vec[1].iov_base = resultbuf->gr_name;
vec[1].iov_base = resultbuf->gr_name; vec[1].iov_len = gr_resp->gr_name_len + gr_resp->gr_passwd_len;
vec[1].iov_len = gr_resp.gr_name_len + gr_resp.gr_passwd_len; total_len = vec[0].iov_len + vec[1].iov_len;
total_len += gr_resp.gr_name_len + gr_resp.gr_passwd_len;
/* Get this data. */ /* Get this data. */
size_t n = TEMP_FAILURE_RETRY (__readv (sock, vec, 2)); size_t n = TEMP_FAILURE_RETRY (__readv (sock, vec, 2));
if (__builtin_expect (n != total_len, 0)) if (__builtin_expect (n != total_len, 0))
goto out; goto out_close;
}
else
/* We already have the data. Just copy the group name and
password. */
memcpy (resultbuf->gr_name, gr_name, gr_name_len);
/* Clear the terminating entry. */ /* Clear the terminating entry. */
resultbuf->gr_mem[gr_resp.gr_mem_cnt] = NULL; resultbuf->gr_mem[gr_resp->gr_mem_cnt] = NULL;
/* Prepare reading the group members. */ /* Prepare reading the group members. */
total_len = 0; total_len = 0;
for (cnt = 0; cnt < gr_resp.gr_mem_cnt; ++cnt) for (cnt = 0; cnt < gr_resp->gr_mem_cnt; ++cnt)
{ {
resultbuf->gr_mem[cnt] = p; resultbuf->gr_mem[cnt] = p;
total_len += len[cnt]; total_len += len[cnt];
p += len[cnt]; p += len[cnt];
} }
if (__builtin_expect (gr_name + gr_name_len + total_len > recend, 0))
goto out_close;
if (__builtin_expect (total_len > buflen, 0)) if (__builtin_expect (total_len > buflen, 0))
goto no_room; goto no_room;
retval = 0; retval = 0;
n = TEMP_FAILURE_RETRY (__read (sock, resultbuf->gr_mem[0], if (gr_name == NULL)
total_len));
if (__builtin_expect (n != total_len, 0))
{ {
/* The `errno' to some value != ERANGE. */ size_t n = TEMP_FAILURE_RETRY (__read (sock, resultbuf->gr_mem[0],
__set_errno (ENOENT); total_len));
retval = ENOENT; if (__builtin_expect (n != total_len, 0))
{
/* The `errno' to some value != ERANGE. */
__set_errno (ENOENT);
retval = ENOENT;
}
else
*result = resultbuf;
} }
else else
*result = resultbuf; {
/* Copy the group member names. */
memcpy (resultbuf->gr_mem[0], gr_name + gr_name_len, total_len);
*result = resultbuf;
}
} }
else else
{ {
@ -177,8 +244,15 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type,
retval = 0; retval = 0;
} }
out_close:
if (sock != -1)
close_not_cancel_no_status (sock);
out: out:
close_not_cancel_no_status (sock); if (__nscd_drop_map_ref (mapped, gc_cycle) != 0)
/* When we come here this means there has been a GC cycle while we
were looking for the data. This means the data might have been
inconsistent. Retry. */
goto retry;
return retval; return retval;
} }

View File

@ -17,6 +17,7 @@
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */ 02111-1307 USA. */
#include <assert.h>
#include <errno.h> #include <errno.h>
#include <netdb.h> #include <netdb.h>
#include <resolv.h> #include <resolv.h>
@ -26,9 +27,7 @@
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <arpa/nameser.h> #include <arpa/nameser.h>
#include <sys/socket.h> #include <sys/mman.h>
#include <sys/uio.h>
#include <sys/un.h>
#include <not-cancel.h> #include <not-cancel.h>
#include "nscd-client.h" #include "nscd-client.h"
@ -89,56 +88,15 @@ __nscd_gethostbyaddr_r (const void *addr, socklen_t len, int type,
} }
/* Create a socket connected to a name. */ libc_locked_map_ptr (map_handle);
int /* Note that we only free the structure if necessary. The memory
__nscd_open_socket (const char *key, size_t keylen, request_type type, mapping is not removed since it is not visible to the malloc
void *response, size_t responselen) handling. */
libc_freeres_fn (gr_map_free)
{ {
struct sockaddr_un addr;
int sock;
int saved_errno = errno;
sock = __socket (PF_UNIX, SOCK_STREAM, 0); if (map_handle.mapped != NO_MAPPING)
if (sock < 0) free (map_handle.mapped);
{
__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)
{
__set_errno (saved_errno);
goto out;
}
request_header req;
req.version = NSCD_VERSION;
req.type = type;
req.key_len = keylen;
struct iovec vec[2];
vec[0].iov_base = &req;
vec[0].iov_len = sizeof (request_header);
vec[1].iov_base = (void *) key;
vec[1].iov_len = keylen;
ssize_t nbytes = TEMP_FAILURE_RETRY (__writev (sock, vec, 2));
if (nbytes != (ssize_t) (sizeof (request_header) + keylen))
{
out:
close_not_cancel_no_status (sock);
sock = -1;
}
else
{
nbytes = TEMP_FAILURE_RETRY (__read (sock, response, responselen));
if (nbytes != (ssize_t) responselen)
goto out;
}
return sock;
} }
@ -148,30 +106,89 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
struct hostent *resultbuf, char *buffer, size_t buflen, struct hostent *resultbuf, char *buffer, size_t buflen,
struct hostent **result, int *h_errnop) struct hostent **result, int *h_errnop)
{ {
hst_response_header hst_resp; const hst_response_header *hst_resp = NULL;
int sock = __nscd_open_socket (key, keylen, type, &hst_resp, const char *h_name = NULL;
sizeof (hst_resp)); const uint32_t *aliases_len = NULL;
if (sock == -1) const char *addr_list = NULL;
size_t addr_list_len = 0;
int retval = -1;
int gc_cycle;
const char *recend = (const char *) ~UINTMAX_C (0);
int sock = -1;
/* If the mapping is available, try to search there instead of
communicating with the nscd. */
struct mapped_database *mapped = __nscd_get_map_ref (GETFDHST, "hosts",
&map_handle, &gc_cycle);
retry:
if (mapped != MAP_FAILED)
{ {
__nss_not_use_nscd_hosts = 1; const struct datahead *found = __nscd_cache_search (type, key, keylen,
return -1; mapped);
if (found != NULL)
{
hst_resp = &found->data[0].hstdata;
h_name = (char *) (hst_resp + 1);
aliases_len = (uint32_t *) (h_name + hst_resp->h_name_len);
addr_list = ((char *) aliases_len
+ hst_resp->h_aliases_cnt * sizeof (uint32_t));
addr_list_len = hst_resp->h_addr_list_cnt * INADDRSZ;
#ifndef _STRING_ARCH_unaligned
/* The aliases_len array in the mapped database might very
well be unaligned. We will access it word-wise so on
platforms which do not tolerate unaligned accesses we
need to make an aligned copy. */
if (((uintptr_t) aliases_len & (__alignof__ (*aliases_len) - 1))
!= 0)
{
uint32_t *tmp = alloca (hst_resp->h_aliases_cnt
* sizeof (uint32_t));
aliases_len = memcpy (tmp, aliases_len,
hst_resp->h_aliases_cnt
* sizeof (uint32_t));
}
#endif
if (type != GETHOSTBYADDR && type != GETHOSTBYNAME)
{
if (hst_resp->h_length == INADDRSZ)
addr_list += addr_list_len;
addr_list_len = hst_resp->h_addr_list_cnt * IN6ADDRSZ;
}
recend = (const char *) found->data + found->recsize;
if (__builtin_expect ((const char *) addr_list + addr_list_len
> recend, 0))
goto out_close;
}
}
hst_response_header hst_resp_mem;
if (hst_resp == NULL)
{
sock = __nscd_open_socket (key, keylen, type, &hst_resp_mem,
sizeof (hst_resp_mem));
if (sock == -1)
{
__nss_not_use_nscd_hosts = 1;
goto out;;
}
hst_resp = &hst_resp_mem;
} }
/* No value found so far. */ /* No value found so far. */
int retval = -1;
*result = NULL; *result = NULL;
if (hst_resp.found == -1) if (__builtin_expect (hst_resp->found == -1, 0))
{ {
/* The daemon does not cache this database. */ /* The daemon does not cache this database. */
__nss_not_use_nscd_hosts = 1; __nss_not_use_nscd_hosts = 1;
goto out; goto out_close;
} }
if (hst_resp.found == 1) if (hst_resp->found == 1)
{ {
struct iovec vec[4]; struct iovec vec[4];
uint32_t *aliases_len;
char *cp = buffer; char *cp = buffer;
uintptr_t align1; uintptr_t align1;
uintptr_t align2; uintptr_t align2;
@ -185,96 +202,110 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
align the pointer and the base of the h_addr_list pointers. */ align the pointer and the base of the h_addr_list pointers. */
align1 = ((__alignof__ (char *) - (cp - ((char *) 0))) align1 = ((__alignof__ (char *) - (cp - ((char *) 0)))
& (__alignof__ (char *) - 1)); & (__alignof__ (char *) - 1));
align2 = ((__alignof__ (char *) - ((cp + align1 + hst_resp.h_name_len) align2 = ((__alignof__ (char *) - ((cp + align1 + hst_resp->h_name_len)
- ((char *) 0))) - ((char *) 0)))
& (__alignof__ (char *) - 1)); & (__alignof__ (char *) - 1));
if (buflen < (align1 + hst_resp.h_name_len + align2 if (buflen < (align1 + hst_resp->h_name_len + align2
+ ((hst_resp.h_aliases_cnt + hst_resp.h_addr_list_cnt + 2) + ((hst_resp->h_aliases_cnt + hst_resp->h_addr_list_cnt
+ 2)
* sizeof (char *)) * sizeof (char *))
+ hst_resp.h_addr_list_cnt * (type == AF_INET + hst_resp->h_addr_list_cnt * (type == AF_INET
? INADDRSZ : IN6ADDRSZ))) ? INADDRSZ : IN6ADDRSZ)))
{ {
no_room: no_room:
__set_errno (ERANGE); __set_errno (ERANGE);
retval = ERANGE; retval = ERANGE;
goto out; goto out_close;
} }
cp += align1; cp += align1;
/* Prepare the result as far as we can. */ /* Prepare the result as far as we can. */
resultbuf->h_aliases = (char **) cp; resultbuf->h_aliases = (char **) cp;
cp += (hst_resp.h_aliases_cnt + 1) * sizeof (char *); cp += (hst_resp->h_aliases_cnt + 1) * sizeof (char *);
resultbuf->h_addr_list = (char **) cp; resultbuf->h_addr_list = (char **) cp;
cp += (hst_resp.h_addr_list_cnt + 1) * sizeof (char *); cp += (hst_resp->h_addr_list_cnt + 1) * sizeof (char *);
resultbuf->h_name = cp; resultbuf->h_name = cp;
cp += hst_resp.h_name_len + align2; cp += hst_resp->h_name_len + align2;
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 (uint32_t));
vec[1].iov_base = aliases_len;
vec[1].iov_len = hst_resp.h_aliases_cnt * sizeof (uint32_t);
total_len = (hst_resp.h_name_len
+ hst_resp.h_aliases_cnt * sizeof (uint32_t));
n = 2;
if (type == GETHOSTBYADDR || type == GETHOSTBYNAME) if (type == GETHOSTBYADDR || type == GETHOSTBYNAME)
{ {
vec[2].iov_base = cp;
vec[2].iov_len = hst_resp.h_addr_list_cnt * INADDRSZ;
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_addrtype = AF_INET;
resultbuf->h_length = INADDRSZ; resultbuf->h_length = INADDRSZ;
total_len += hst_resp.h_addr_list_cnt * INADDRSZ;
n = 3;
} }
else 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_addrtype = AF_INET6;
resultbuf->h_length = IN6ADDRSZ; resultbuf->h_length = IN6ADDRSZ;
}
total_len += hst_resp.h_addr_list_cnt * IN6ADDRSZ; for (cnt = 0; cnt < hst_resp->h_addr_list_cnt; ++cnt)
{
++n; resultbuf->h_addr_list[cnt] = cp;
cp += resultbuf->h_length;
} }
resultbuf->h_addr_list[cnt] = NULL; resultbuf->h_addr_list[cnt] = NULL;
if ((size_t) TEMP_FAILURE_RETRY (__readv (sock, vec, n)) != total_len) if (h_name == NULL)
goto out; {
vec[0].iov_base = resultbuf->h_name;
vec[0].iov_len = hst_resp->h_name_len;
total_len = hst_resp->h_name_len;
n = 1;
if (hst_resp->h_aliases_cnt > 0)
{
aliases_len = alloca (hst_resp->h_aliases_cnt
* sizeof (uint32_t));
vec[n].iov_base = (void *) aliases_len;
vec[n].iov_len = hst_resp->h_aliases_cnt * sizeof (uint32_t);
total_len += hst_resp->h_aliases_cnt * sizeof (uint32_t);
++n;
}
if (type == GETHOSTBYADDR || type == GETHOSTBYNAME)
{
vec[n].iov_base = resultbuf->h_addr_list[0];
vec[n].iov_len = hst_resp->h_addr_list_cnt * INADDRSZ;
total_len += hst_resp->h_addr_list_cnt * INADDRSZ;
++n;
}
else
{
if (hst_resp->h_length == INADDRSZ)
{
ignore = alloca (hst_resp->h_addr_list_cnt * INADDRSZ);
vec[n].iov_base = ignore;
vec[n].iov_len = hst_resp->h_addr_list_cnt * INADDRSZ;
total_len += hst_resp->h_addr_list_cnt * INADDRSZ;
++n;
}
vec[n].iov_base = resultbuf->h_addr_list[0];
vec[n].iov_len = hst_resp->h_addr_list_cnt * IN6ADDRSZ;
total_len += hst_resp->h_addr_list_cnt * IN6ADDRSZ;
++n;
}
if ((size_t) TEMP_FAILURE_RETRY (__readv (sock, vec, n))
!= total_len)
goto out_close;
}
else
{
memcpy (resultbuf->h_name, h_name, hst_resp->h_name_len);
memcpy (resultbuf->h_addr_list[0], addr_list, addr_list_len);
}
/* Now we also can read the aliases. */ /* Now we also can read the aliases. */
total_len = 0; total_len = 0;
for (cnt = 0; cnt < hst_resp.h_aliases_cnt; ++cnt) for (cnt = 0; cnt < hst_resp->h_aliases_cnt; ++cnt)
{ {
resultbuf->h_aliases[cnt] = cp; resultbuf->h_aliases[cnt] = cp;
cp += aliases_len[cnt]; cp += aliases_len[cnt];
@ -282,14 +313,29 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
} }
resultbuf->h_aliases[cnt] = NULL; resultbuf->h_aliases[cnt] = NULL;
if (__builtin_expect ((const char *) addr_list + addr_list_len
+ total_len > recend, 0))
goto out_close;
/* See whether this would exceed the buffer capacity. */ /* See whether this would exceed the buffer capacity. */
if (cp > buffer + buflen) if (__builtin_expect (cp > buffer + buflen, 0))
goto no_room; goto no_room;
/* And finally read the aliases. */ /* And finally read the aliases. */
if ((size_t) TEMP_FAILURE_RETRY (__read (sock, resultbuf->h_aliases[0], if (addr_list == NULL)
total_len)) == total_len)
{ {
if ((size_t) TEMP_FAILURE_RETRY (__read (sock,
resultbuf->h_aliases[0],
total_len)) == total_len)
{
retval = 0;
*result = resultbuf;
}
}
else
{
memcpy (resultbuf->h_aliases[0],
(const char *) addr_list + addr_list_len, total_len);
retval = 0; retval = 0;
*result = resultbuf; *result = resultbuf;
} }
@ -297,7 +343,7 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
else else
{ {
/* Store the error number. */ /* Store the error number. */
*h_errnop = hst_resp.error; *h_errnop = hst_resp->error;
/* The `errno' to some value != ERANGE. */ /* The `errno' to some value != ERANGE. */
__set_errno (ENOENT); __set_errno (ENOENT);
@ -305,8 +351,15 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
retval = 0; retval = 0;
} }
out_close:
if (sock != -1)
close_not_cancel_no_status (sock);
out: out:
__close (sock); if (__nscd_drop_map_ref (mapped, gc_cycle) != 0)
/* When we come here this means there has been a GC cycle while we
were looking for the data. This means the data might have been
inconsistent. Retry. */
goto retry;
return retval; return retval;
} }

View File

@ -17,6 +17,7 @@
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */ 02111-1307 USA. */
#include <assert.h>
#include <errno.h> #include <errno.h>
#include <pwd.h> #include <pwd.h>
#include <stdint.h> #include <stdint.h>
@ -24,6 +25,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <sys/mman.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/uio.h> #include <sys/uio.h>
#include <sys/un.h> #include <sys/un.h>
@ -64,70 +66,124 @@ __nscd_getpwuid_r (uid_t uid, struct passwd *resultbuf, char *buffer,
} }
libc_locked_map_ptr (map_handle);
/* Note that we only free the structure if necessary. The memory
mapping is not removed since it is not visible to the malloc
handling. */
libc_freeres_fn (gr_map_free)
{
if (map_handle.mapped != NO_MAPPING)
free (map_handle.mapped);
}
static int static int
internal_function internal_function
nscd_getpw_r (const char *key, size_t keylen, request_type type, nscd_getpw_r (const char *key, size_t keylen, request_type type,
struct passwd *resultbuf, char *buffer, size_t buflen, struct passwd *resultbuf, char *buffer, size_t buflen,
struct passwd **result) struct passwd **result)
{ {
pw_response_header pw_resp; const pw_response_header *pw_resp = NULL;
int sock = __nscd_open_socket (key, keylen, type, &pw_resp, const char *pw_name = NULL;
sizeof (pw_resp)); int retval = -1;
if (sock == -1) int gc_cycle;
const char *recend = (const char *) ~UINTMAX_C (0);
/* If the mapping is available, try to search there instead of
communicating with the nscd. */
struct mapped_database *mapped = __nscd_get_map_ref (GETFDPW, "passwd",
&map_handle, &gc_cycle);
retry:
if (mapped != NO_MAPPING)
{ {
__nss_not_use_nscd_passwd = 1; const struct datahead *found = __nscd_cache_search (type, key, keylen,
return -1; mapped);
if (found != NULL)
{
pw_resp = &found->data[0].pwdata;
pw_name = (const char *) (pw_resp + 1);
recend = (const char *) found->data + found->recsize;
}
}
pw_response_header pw_resp_mem;
int sock = -1;
if (pw_resp == NULL)
{
sock = __nscd_open_socket (key, keylen, type, &pw_resp_mem,
sizeof (pw_resp_mem));
if (sock == -1)
{
__nss_not_use_nscd_passwd = 1;
goto out;
}
pw_resp = &pw_resp_mem;
} }
/* No value found so far. */ /* No value found so far. */
int retval = -1;
*result = NULL; *result = NULL;
if (__builtin_expect (pw_resp.found == -1, 0)) if (__builtin_expect (pw_resp->found == -1, 0))
{ {
/* The daemon does not cache this database. */ /* The daemon does not cache this database. */
__nss_not_use_nscd_passwd = 1; __nss_not_use_nscd_passwd = 1;
goto out; goto out_close;
} }
if (pw_resp.found == 1) if (pw_resp->found == 1)
{ {
char *p = buffer; /* Set the information we already have. */
size_t total = (pw_resp.pw_name_len + pw_resp.pw_passwd_len resultbuf->pw_uid = pw_resp->pw_uid;
+ pw_resp.pw_gecos_len + pw_resp.pw_dir_len resultbuf->pw_gid = pw_resp->pw_gid;
+ pw_resp.pw_shell_len);
char *p = buffer;
/* get pw_name */
resultbuf->pw_name = p;
p += pw_resp->pw_name_len;
/* get pw_passwd */
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;
p += pw_resp->pw_shell_len;
ssize_t total = p - buffer;
if (__builtin_expect (pw_name + total > recend, 0))
goto out_close;
if (__builtin_expect (buflen < total, 0)) if (__builtin_expect (buflen < total, 0))
{ {
__set_errno (ERANGE); __set_errno (ERANGE);
retval = ERANGE; retval = ERANGE;
goto out; goto out_close;
} }
/* Set the information we already have. */ retval = 0;
resultbuf->pw_uid = pw_resp.pw_uid; if (pw_name == NULL)
resultbuf->pw_gid = pw_resp.pw_gid;
/* get pw_name */
resultbuf->pw_name = p;
p += pw_resp.pw_name_len;
/* get pw_passwd */
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;
ssize_t nbytes = TEMP_FAILURE_RETRY (__read (sock, buffer, total));
if (nbytes == (ssize_t) total)
{ {
retval = 0; ssize_t nbytes = TEMP_FAILURE_RETRY (__read (sock, buffer, total));
if (__builtin_expect (nbytes != total, 0))
{
/* The `errno' to some value != ERANGE. */
__set_errno (ENOENT);
retval = ENOENT;
}
else
*result = resultbuf;
}
else
{
/* Copy the various strings. */
memcpy (resultbuf->pw_name, pw_name, total);
*result = resultbuf; *result = resultbuf;
} }
} }
@ -139,8 +195,15 @@ nscd_getpw_r (const char *key, size_t keylen, request_type type,
retval = 0; retval = 0;
} }
out_close:
if (sock != -1)
close_not_cancel_no_status (sock);
out: out:
close_not_cancel_no_status (sock); if (__nscd_drop_map_ref (mapped, gc_cycle) != 0)
/* When we come here this means there has been a GC cycle while we
were looking for the data. This means the data might have been
inconsistent. Retry. */
goto retry;
return retval; return retval;
} }

335
nscd/nscd_helper.c Normal file
View File

@ -0,0 +1,335 @@
/* Copyright (C) 1998-2002, 2003, 2004 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 Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; 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 <fcntl.h>
#include <stdbool.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/uio.h>
#include <sys/un.h>
#include <not-cancel.h>
#include <nis/rpcsvc/nis.h>
#include "nscd-client.h"
static int
open_socket (void)
{
int sock = __socket (PF_UNIX, SOCK_STREAM, 0);
if (sock < 0)
return -1;
/* Make socket non-blocking. */
int fl = __fcntl (sock, F_GETFL);
if (fl != -1)
__fcntl (sock, F_SETFL, fl | O_NONBLOCK);
struct sockaddr_un sun;
sun.sun_family = AF_UNIX;
strcpy (sun.sun_path, _PATH_NSCDSOCKET);
if (__connect (sock, (struct sockaddr *) &sun, sizeof (sun)) < 0
&& errno != EINPROGRESS)
goto out;
struct pollfd fds[1];
fds[0].fd = sock;
fds[0].events = POLLOUT | POLLERR | POLLHUP;
if (__poll (fds, 1, 5 * 1000) > 0)
/* Success. We do not check for success of the connect call here.
If it failed, the following operations will fail. */
return sock;
out:
close_not_cancel_no_status (sock);
return -1;
}
void
__nscd_unmap (struct mapped_database *mapped)
{
assert (mapped->counter == 0);
munmap ((void *) mapped->head, mapped->mapsize);
free (mapped);
}
/* Try to get a file descriptor for the shared meory segment
containing the database. */
static struct mapped_database *
get_mapping (request_type type, const char *key,
struct mapped_database **mappedp)
{
struct mapped_database *result = NO_MAPPING;
#ifdef SCM_RIGHTS
const size_t keylen = strlen (key) + 1;
char resdata[keylen];
int saved_errno = errno;
int mapfd = -1;
/* Send the request. */
struct iovec iov[2];
request_header req;
int sock = open_socket ();
if (sock < 0)
goto out;
req.version = NSCD_VERSION;
req.type = type;
req.key_len = keylen;
iov[0].iov_base = &req;
iov[0].iov_len = sizeof (req);
iov[1].iov_base = (void *) key;
iov[1].iov_len = keylen;
if (TEMP_FAILURE_RETRY (__writev (sock, iov, 2))
!= iov[0].iov_len + iov[1].iov_len)
/* We cannot even write the request. */
goto out_close2;
/* Room for the data sent along with the file descriptor. We expect
the key name back. */
iov[0].iov_base = resdata;
iov[0].iov_len = keylen;
char buf[CMSG_SPACE (sizeof (int))];
struct msghdr msg = { .msg_iov = iov, .msg_iovlen = 1,
.msg_control = buf, .msg_controllen = sizeof (buf) };
struct cmsghdr *cmsg = CMSG_FIRSTHDR (&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN (sizeof (int));
*(int *) CMSG_DATA (cmsg) = -1;
msg.msg_controllen = cmsg->cmsg_len;
struct pollfd fds[1];
fds[0].fd = sock;
fds[0].events = POLLIN | POLLERR | POLLHUP;
if (__poll (fds, 1, 5 * 1000) <= 0)
/* Failure or timeout. */
goto out_close2;
if (TEMP_FAILURE_RETRY (__recvmsg (sock, &msg, 0)) != keylen
|| msg.msg_controllen != CMSG_LEN (sizeof (int)))
goto out_close2;
mapfd = *(int *) CMSG_DATA (cmsg);
struct stat64 st;
if (strcmp (resdata, key) != 0
|| fstat64 (mapfd, &st) != 0
|| st.st_size < sizeof (struct database_pers_head))
goto out_close;
struct database_pers_head head;
if (TEMP_FAILURE_RETRY (__pread (mapfd, &head, sizeof (head), 0))
!= sizeof (head))
goto out_close;
if (head.version != DB_VERSION || head.header_size != sizeof (head)
/* This really should not happen but who knows, maybe the update
thread got stuck. */
|| head.timestamp + MAPPING_TIMEOUT < time (NULL))
goto out_close;
size_t size = (sizeof (head) + roundup (head.module * sizeof (ref_t), ALIGN)
+ head.data_size);
if (st.st_size < size)
goto out_close;
/* The file is large enough, map it now. */
void *mapping = __mmap (NULL, size, PROT_READ, MAP_SHARED, mapfd, 0);
if (mapping != MAP_FAILED)
{
/* Allocate a record for the mapping. */
struct mapped_database *newp;
newp = malloc (sizeof (*newp));
if (newp == NULL)
{
/* Ugh, after all we went through the memory allocation failed. */
munmap (result, size);
goto out_close;
}
newp->head = mapping;
newp->data = ((char *) mapping + head.header_size
+ roundup (head.module * sizeof (ref_t), ALIGN));
newp->mapsize = size;
/* Set counter to 1 to show it is usable. */
newp->counter = 1;
result = newp;
}
out_close:
__close (mapfd);
out_close2:
__close (sock);
out:
__set_errno (saved_errno);
#endif /* SCM_RIGHTS */
struct mapped_database *oldval = *mappedp;
*mappedp = result;
if (oldval != NULL && atomic_decrement_val (&oldval->counter) == 0)
__nscd_unmap (oldval);
return result;
}
struct mapped_database *
__nscd_get_map_ref (request_type type, const char *name,
struct locked_map_ptr *mapptr, int *gc_cyclep)
{
struct mapped_database *cur = mapptr->mapped;
if (cur == NO_MAPPING)
return cur;
int cnt = 0;
while (atomic_compare_and_exchange_val_acq (&mapptr->lock, 1, 0) != 0)
{
// XXX Best number of rounds?
if (++cnt > 5)
return NO_MAPPING;
atomic_delay ();
}
cur = mapptr->mapped;
if (__builtin_expect (cur != NO_MAPPING, 1))
{
/* If not mapped or timestamp not updated, request new map. */
if (cur == NULL
// XXX The following syscalls increases the cost of the entire
// XXX lookup by a factor of 5 but unfortunately there is not
// XXX much we can do except hoping we get a userlevel
// XXX implementation soon.
|| cur->head->timestamp + MAPPING_TIMEOUT < time (NULL))
cur = get_mapping (type, name, &mapptr->mapped);
if (__builtin_expect (cur != NO_MAPPING, 1))
{
if (__builtin_expect (((*gc_cyclep = cur->head->gc_cycle) & 1) != 0,
0))
cur = NO_MAPPING;
else
atomic_increment (&cur->counter);
}
}
mapptr->lock = 0;
return cur;
}
const struct datahead *
__nscd_cache_search (request_type type, const char *key, size_t keylen,
const struct mapped_database *mapped)
{
unsigned long int hash = __nis_hash (key, keylen) % mapped->head->module;
ref_t work = mapped->head->array[hash];
while (work != ENDREF)
{
struct hashentry *here = (struct hashentry *) (mapped->data + work);
if (type == here->type && keylen == here->len
&& memcmp (key, mapped->data + here->key, keylen) == 0)
{
/* We found the entry. Increment the appropriate counter. */
const struct datahead *dh
= (struct datahead *) (mapped->data + here->packet);
/* See whether we must ignore the entry or whether something
is wrong because garbage collection is in progress. */
if (dh->usable && ((char *) dh + dh->allocsize
<= (char *) mapped->head + mapped->mapsize))
return dh;
}
work = here->next;
}
return NULL;
}
/* Create a socket connected to a name. */
int
__nscd_open_socket (const char *key, size_t keylen, request_type type,
void *response, size_t responselen)
{
int saved_errno = errno;
int sock = open_socket ();
if (sock >= 0)
{
request_header req;
req.version = NSCD_VERSION;
req.type = type;
req.key_len = keylen;
struct iovec vec[2];
vec[0].iov_base = &req;
vec[0].iov_len = sizeof (request_header);
vec[1].iov_base = (void *) key;
vec[1].iov_len = keylen;
ssize_t nbytes = TEMP_FAILURE_RETRY (__writev (sock, vec, 2));
if (nbytes == (ssize_t) (sizeof (request_header) + keylen))
{
/* Wait for data. */
struct pollfd fds[1];
fds[0].fd = sock;
fds[0].events = POLLIN | POLLERR | POLLHUP;
if (__poll (fds, 1, 5 * 1000) > 0)
{
nbytes = TEMP_FAILURE_RETRY (__read (sock, response,
responselen));
if (nbytes == (ssize_t) responselen)
return sock;
}
}
close_not_cancel_no_status (sock);
}
__set_errno (saved_errno);
return -1;
}