mirror of
https://sourceware.org/git/glibc.git
synced 2024-12-17 08:21:07 +00:00
Update.
1998-10-18 Ulrich Drepper <drepper@cygnus.com> * resolv/nss_dns/dns-host.c: Add missing errnop parameter to the NSS functions. * resolv/nss_dns/dns-network.c: Likewise. * grp/Makefile: Don't search for linuxhtreads in add-ons, use have-thread-library to determine whether threads are available. * pwd/Makefile: Remove wrong comment. * inet/Makefile: Define CFLAGS-gethstbyad_r.c, CFLAGS-gethstbynm_r.c, and CFLAGS-gethstbynm2_r.c to -DUSE_NSCD=1. * locale/C-messages.c: Define default strings for YESTR and NOSTR. * nss/Versions: Add __nss_hosts_lookup. * nss/getXXbyYY.c: Remove unneeded assignment. * nss/getXXbyYY_r.c: Include nscd/nscd_proto.h only if needed. Almost complete rewrite of the NSCD to make it smaller, faster, add more functionnality and make it easier to extend. * nscd/Makfile (routines): Add nscd_gethst_r. (nscd-modules): Add hstcache, gethstbyad_r, gethstbynm2_r, and cache. * nscd/cache.c: New file. * nscd/gethstbyad_r.c: New file. * nscd/gethstbynm2_r.c: New file. * nscd/hstcache.c: New file. * nscd/nscd_gethst_r.c: New file. * nscd/connections.c: Rewritten. Don't start new thread for every new connection. Use a fixed set of threads which handle all connections and also the cache cleanup. * nscd/grpcache.c: Rewritten to use generic cache handling functions in cache.c. * nscd/nscd.c: Recognize new parameter nthreads. Adjust initialization for rewrite. Remove handle_requests function. * nscd/nscd.h (NSCD_VERSION): Bump to 2. Define new data structure for the new unified cache and the host database entries. * nscd/nscd_conf.c: Rewrite parsing partly to allow adding of more databases easily. Recognize check-files and threads definitions. * nscd/nscd.conf: Add definition of enable-cache and check-files to passwd and group definitions. Add new set of definitions for hosts. * nscd/nscd_getgr_r.c: Rewrite for new protocol. * nscd/nscd_getpw_r.c: Likewise. * nscd/nscd_proto.h: Add prototype for host database functions. * nscd/nscd_stat.c: Rewrite to simplify printing of information for many databases. * nscd/dbg_log.c: Remove unnecessary variable initializations. Global variable debug_flag is renamed to dbg_level. * nscd/dbg_log.h: Declare set_logfile.
This commit is contained in:
parent
6cde0c6047
commit
67479a700e
53
ChangeLog
53
ChangeLog
@ -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>
|
||||
|
||||
* sysdeps/unix/sysv/linux/bits/fcntl.h: Add dummy definition of
|
||||
|
10
PROJECTS
10
PROJECTS
@ -149,3 +149,13 @@ contact <bug-glibc@gnu.org>.
|
||||
{
|
||||
... 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.
|
||||
|
@ -30,8 +30,7 @@ tests := testgrp
|
||||
|
||||
include ../Rules
|
||||
|
||||
# We can later add the names of other thread packages here.
|
||||
ifneq (,$(findstring linuxthreads,$(add-ons)))
|
||||
ifeq ($(have-thread-library),yes)
|
||||
|
||||
CFLAGS-getgrgid_r.c = -DUSE_NSCD=1
|
||||
CFLAGS-getgrnam_r.c = -DUSE_NSCD=1
|
||||
|
@ -54,3 +54,11 @@ CFLAGS-rexec.c = -w
|
||||
CFLAGS-ruserpass.c = -w
|
||||
|
||||
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
|
||||
|
@ -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.
|
||||
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: "^[nN]" },
|
||||
{ string: "" },
|
||||
{ string: "" }
|
||||
{ string: "yes" },
|
||||
{ string: "no" }
|
||||
}
|
||||
};
|
||||
|
@ -21,12 +21,13 @@
|
||||
#
|
||||
subdir := nscd
|
||||
|
||||
routines := nscd_getpw_r nscd_getgr_r
|
||||
routines := nscd_getpw_r nscd_getgr_r nscd_gethst_r
|
||||
|
||||
include ../Makeconfig
|
||||
|
||||
nscd-modules := nscd connections pwdcache getpwnam_r getpwuid_r grpcache \
|
||||
getgrnam_r getgrgid_r dbg_log nscd_conf nscd_stat
|
||||
getgrnam_r getgrgid_r hstcache gethstbyad_r gethstbynm2_r \
|
||||
dbg_log nscd_conf nscd_stat cache
|
||||
|
||||
ifeq ($(have-thread-library),yes)
|
||||
|
||||
|
247
nscd/cache.c
Normal file
247
nscd/cache.c
Normal 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);
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
/* Copyright (c) 1998 Free Software Foundation, Inc.
|
||||
/* Inner loops of cache daemon.
|
||||
Copyright (C) 1998 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1998.
|
||||
Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public License as
|
||||
@ -17,549 +18,405 @@
|
||||
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <error.h>
|
||||
#include <fcntl.h>
|
||||
#include <libintl.h>
|
||||
#include <locale.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <pwd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#include "nscd.h"
|
||||
#include "dbg_log.h"
|
||||
|
||||
/* Socket 0 in the array is named and exported into the file namespace
|
||||
as a connection point for clients. There's a one to one
|
||||
correspondence between sock[i] and read_polls[i]. */
|
||||
static int sock[MAX_NUM_CONNECTIONS];
|
||||
static int socks_active;
|
||||
static struct pollfd read_polls[MAX_NUM_CONNECTIONS];
|
||||
static pthread_mutex_t sock_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
/* Mapping of request type to database. */
|
||||
static const dbtype serv2db[LASTDBREQ + 1] =
|
||||
{
|
||||
[GETPWBYNAME] = pwddb,
|
||||
[GETPWBYUID] = pwddb,
|
||||
[GETGRBYNAME] = grpdb,
|
||||
[GETGRBYGID] = grpdb,
|
||||
[GETHOSTBYNAME] = hstdb,
|
||||
[GETHOSTBYNAMEv6] = hstdb,
|
||||
[GETHOSTBYADDR] = hstdb,
|
||||
[GETHOSTBYADDRv6] = hstdb,
|
||||
};
|
||||
|
||||
/* Map request type to a string. */
|
||||
const char *serv2str[LASTREQ] =
|
||||
{
|
||||
[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
|
||||
close_sockets (void)
|
||||
{
|
||||
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)
|
||||
nscd_init (const char *conffile)
|
||||
{
|
||||
struct sockaddr_un sock_addr;
|
||||
int i;
|
||||
size_t cnt;
|
||||
|
||||
/* Initialize the connections db. */
|
||||
socks_active = 0;
|
||||
/* Read the configuration file. */
|
||||
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 (i = 0; i < MAX_NUM_CONNECTIONS; i++)
|
||||
read_polls[i].fd = -1;
|
||||
for (cnt = 0; cnt < lastdb; ++cnt)
|
||||
if (dbs[cnt].enabled)
|
||||
{
|
||||
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. */
|
||||
sock[0] = socket (AF_UNIX, SOCK_STREAM, 0);
|
||||
if (sock[0] < 0)
|
||||
sock = socket (AF_UNIX, SOCK_STREAM, 0);
|
||||
if (sock < 0)
|
||||
{
|
||||
perror (_("cannot create socket"));
|
||||
dbg_log (_("cannot open socket: %s"), strerror (errno));
|
||||
exit (1);
|
||||
}
|
||||
/* Bind a name to the socket. */
|
||||
sock_addr.sun_family = AF_UNIX;
|
||||
strcpy (sock_addr.sun_path, _PATH_NSCDSOCKET);
|
||||
if (bind (sock[0], (struct sockaddr *) &sock_addr, sizeof (sock_addr)) < 0)
|
||||
if (bind (sock, (struct sockaddr *) &sock_addr, sizeof (sock_addr)) < 0)
|
||||
{
|
||||
dbg_log ("%s: %s", _PATH_NSCDSOCKET, strerror (errno));
|
||||
exit (1);
|
||||
}
|
||||
|
||||
/* Set permissions for the socket. */
|
||||
chmod (_PATH_NSCDSOCKET, 0666);
|
||||
|
||||
/* Set the socket up to accept connections. */
|
||||
if (listen (sock[0], MAX_NUM_CONNECTIONS) < 0)
|
||||
if (listen (sock, SOMAXCONN) < 0)
|
||||
{
|
||||
perror (_("cannot enable socket to accept connections"));
|
||||
dbg_log (_("cannot enable socket to accept connections: %s"),
|
||||
strerror (errno));
|
||||
exit (1);
|
||||
}
|
||||
|
||||
/* Add the socket to the server's set of active sockets. */
|
||||
read_polls[0].fd = sock[0];
|
||||
read_polls[0].events = POLLRDNORM;
|
||||
++socks_active;
|
||||
}
|
||||
|
||||
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;
|
||||
if (pwd != NULL)
|
||||
/* Close the connections. */
|
||||
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;
|
||||
resp.pw_name_len = strlen (pwd->pw_name);
|
||||
resp.pw_passwd_len = strlen (pwd->pw_passwd);
|
||||
resp.pw_uid = pwd->pw_uid;
|
||||
resp.pw_gid = pwd->pw_gid;
|
||||
resp.pw_gecos_len = strlen (pwd->pw_gecos);
|
||||
resp.pw_dir_len = strlen (pwd->pw_dir);
|
||||
resp.pw_shell_len = strlen (pwd->pw_shell);
|
||||
dbg_log (_("\
|
||||
cannot handle old request version %d; current version is %d"),
|
||||
req->version, NSCD_VERSION);
|
||||
return;
|
||||
}
|
||||
|
||||
if (req->type >= GETPWBYNAME && req->type <= LASTDBREQ)
|
||||
{
|
||||
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
|
||||
{
|
||||
resp.found = 0;
|
||||
resp.pw_name_len = 0;
|
||||
resp.pw_passwd_len = 0;
|
||||
resp.pw_uid = -1;
|
||||
resp.pw_gid = -1;
|
||||
resp.pw_gecos_len = 0;
|
||||
resp.pw_dir_len = 0;
|
||||
resp.pw_shell_len = 0;
|
||||
}
|
||||
if (sock[conn] == 0)
|
||||
/* Get the key. */
|
||||
char keybuf[req.key_len];
|
||||
|
||||
if (TEMP_FAILURE_RETRY (read (fd, keybuf, req.key_len))
|
||||
!= req.key_len)
|
||||
{
|
||||
dbg_log (_("bad connection id on send response [%d|%d]"),
|
||||
conn, sock[conn]);
|
||||
return;
|
||||
dbg_log (_("short read while reading request key: %s"),
|
||||
strerror_r (errno, buf, sizeof (buf)));
|
||||
close (fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Add response header. */
|
||||
vec[0].iov_base = &resp;
|
||||
vec[0].iov_len = sizeof (pw_response_header);
|
||||
total_len = sizeof (pw_response_header);
|
||||
nblocks = 1;
|
||||
/* Phew, we got all the data, now process it. */
|
||||
handle_request (fd, &req, keybuf);
|
||||
|
||||
if (resp.found)
|
||||
/* We are done. */
|
||||
close (fd);
|
||||
}
|
||||
}
|
||||
|
||||
if (run_prune)
|
||||
{
|
||||
/* Add pw_name. */
|
||||
vec[1].iov_base = pwd->pw_name;
|
||||
vec[1].iov_len = resp.pw_name_len;
|
||||
total_len += resp.pw_name_len;
|
||||
/* Add pw_passwd. */
|
||||
vec[2].iov_base = pwd->pw_passwd;
|
||||
vec[2].iov_len = resp.pw_passwd_len;
|
||||
total_len += resp.pw_passwd_len;
|
||||
/* Add pw_gecos. */
|
||||
vec[3].iov_base = pwd->pw_gecos;
|
||||
vec[3].iov_len = resp.pw_gecos_len;
|
||||
total_len += resp.pw_gecos_len;
|
||||
/* Add pw_dir. */
|
||||
vec[4].iov_base = pwd->pw_dir;
|
||||
vec[4].iov_len = resp.pw_dir_len;
|
||||
total_len += resp.pw_dir_len;
|
||||
/* Add pw_shell. */
|
||||
vec[5].iov_base = pwd->pw_shell;
|
||||
vec[5].iov_len = resp.pw_shell_len;
|
||||
total_len += resp.pw_shell_len;
|
||||
|
||||
nblocks = 6;
|
||||
now = time (NULL);
|
||||
timeout = now < next_prune ? 1000 * (next_prune - now) : 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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
|
||||
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;
|
||||
resp.found = -1;
|
||||
resp.pw_name_len = 0;
|
||||
resp.pw_passwd_len = 0;
|
||||
resp.pw_uid = -1;
|
||||
resp.pw_gid = -1;
|
||||
resp.pw_gecos_len = 0;
|
||||
resp.pw_dir_len = 0;
|
||||
resp.pw_shell_len = 0;
|
||||
pthread_attr_init (&attr);
|
||||
pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
|
||||
|
||||
if (sock[conn] == 0)
|
||||
{
|
||||
dbg_log (_("bad connection id on send response [%d|%d]"),
|
||||
conn, sock[conn]);
|
||||
return;
|
||||
}
|
||||
/* We allow less than LASTDB threads only for debugging. */
|
||||
if (debug_level == 0)
|
||||
nthreads = MAX (nthreads, lastdb);
|
||||
|
||||
/* Send response header. */
|
||||
if (write (sock[conn], &resp, sizeof (pw_response_header))
|
||||
!= sizeof (pw_response_header))
|
||||
dbg_log (_("write incomplete on send response: %s"), strerror (errno));
|
||||
}
|
||||
|
||||
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));
|
||||
for (i = 1; i < nthreads; ++i)
|
||||
pthread_create (&th, &attr, nscd_run, (void *) i);
|
||||
|
||||
pthread_attr_destroy (&attr);
|
||||
|
||||
nscd_run ((void *) 0);
|
||||
}
|
||||
|
@ -28,8 +28,8 @@
|
||||
if in debug mode and no debug file, we write the messages to stderr,
|
||||
else to syslog. */
|
||||
|
||||
FILE *dbgout = NULL;
|
||||
int debug_flag = 0;
|
||||
FILE *dbgout;
|
||||
int debug_level;
|
||||
|
||||
int
|
||||
set_logfile (const char *logfile)
|
||||
@ -47,7 +47,7 @@ dbg_log (const char *fmt,...)
|
||||
va_start (ap, fmt);
|
||||
vsnprintf (msg2, sizeof (msg), fmt, ap);
|
||||
|
||||
if (debug_flag)
|
||||
if (debug_level > 0)
|
||||
{
|
||||
snprintf (msg, sizeof (msg), "%d: %s\n", getpid (), msg2);
|
||||
if (dbgout)
|
||||
|
@ -20,8 +20,10 @@
|
||||
#ifndef _DBG_LOG_H
|
||||
#define _DBG_LOG_H 1
|
||||
|
||||
extern int debug_flag;
|
||||
extern int debug_level;
|
||||
|
||||
extern void dbg_log (const char *, ...);
|
||||
extern void dbg_log (const char *str, ...);
|
||||
|
||||
extern int set_logfile (const char *logfile);
|
||||
|
||||
#endif
|
||||
|
31
nscd/gethstbyad_r.c
Normal file
31
nscd/gethstbyad_r.c
Normal 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
39
nscd/gethstbynm2_r.c
Normal 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"
|
720
nscd/grpcache.c
720
nscd/grpcache.c
@ -1,6 +1,7 @@
|
||||
/* Copyright (c) 1998 Free Software Foundation, Inc.
|
||||
/* Cache handling for group lookup.
|
||||
Copyright (C) 1998 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1998.
|
||||
Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public License as
|
||||
@ -17,603 +18,232 @@
|
||||
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 <grp.h>
|
||||
#include <pthread.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <rpcsvc/nis.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "dbg_log.h"
|
||||
#include "nscd.h"
|
||||
#include "dbg_log.h"
|
||||
|
||||
static unsigned long modulo = 211;
|
||||
static unsigned long postimeout = 3600;
|
||||
static unsigned long negtimeout = 60;
|
||||
|
||||
static unsigned long poshit = 0;
|
||||
static unsigned long posmiss = 0;
|
||||
static unsigned long neghit = 0;
|
||||
static unsigned long negmiss = 0;
|
||||
|
||||
struct grphash
|
||||
/* This is the standard reply in case the service is disabled. */
|
||||
static const gr_response_header disabled =
|
||||
{
|
||||
time_t create;
|
||||
struct grphash *next;
|
||||
struct group *grp;
|
||||
version: NSCD_VERSION,
|
||||
found: -1,
|
||||
gr_name_len: 0,
|
||||
gr_passwd_len: 0,
|
||||
gr_gid: -1,
|
||||
gr_mem_cnt: 0,
|
||||
};
|
||||
typedef struct grphash grphash;
|
||||
|
||||
struct gidhash
|
||||
/* This is the struct describing how to write this record. */
|
||||
const struct iovec grp_iov_disabled =
|
||||
{
|
||||
struct gidhash *next;
|
||||
struct group *grptr;
|
||||
iov_base: (void *) &disabled,
|
||||
iov_len: sizeof (disabled)
|
||||
};
|
||||
typedef struct gidhash gidhash;
|
||||
|
||||
struct neghash
|
||||
|
||||
/* This is the standard reply in case we haven't found the dataset. */
|
||||
static const gr_response_header notfound =
|
||||
{
|
||||
time_t create;
|
||||
struct neghash *next;
|
||||
char *key;
|
||||
version: NSCD_VERSION,
|
||||
found: 0,
|
||||
gr_name_len: 0,
|
||||
gr_passwd_len: 0,
|
||||
gr_gid: -1,
|
||||
gr_mem_cnt: 0,
|
||||
};
|
||||
typedef struct neghash neghash;
|
||||
|
||||
static grphash *grptbl;
|
||||
static gidhash *gidtbl;
|
||||
static neghash *negtbl;
|
||||
|
||||
static pthread_rwlock_t grplock = PTHREAD_RWLOCK_INITIALIZER;
|
||||
static pthread_rwlock_t neglock = PTHREAD_RWLOCK_INITIALIZER;
|
||||
|
||||
static void *grptable_update (void *);
|
||||
static void *negtable_update (void *);
|
||||
|
||||
void
|
||||
get_gr_stat (stat_response_header *stat)
|
||||
/* This is the struct describing how to write this record. */
|
||||
static const struct iovec iov_notfound =
|
||||
{
|
||||
stat->gr_poshit = poshit;
|
||||
stat->gr_posmiss = posmiss;
|
||||
stat->gr_neghit = neghit;
|
||||
stat->gr_negmiss = negmiss;
|
||||
stat->gr_size = modulo;
|
||||
stat->gr_posttl = postimeout;
|
||||
stat->gr_negttl = negtimeout;
|
||||
}
|
||||
iov_base: (void *) ¬found,
|
||||
iov_len: sizeof (notfound)
|
||||
};
|
||||
|
||||
void
|
||||
set_grp_modulo (unsigned long mod)
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
if (grp == NULL)
|
||||
{
|
||||
while (work->next != NULL)
|
||||
work = work->next;
|
||||
/* We have no data. This means we send the standard reply for this
|
||||
case. */
|
||||
void *copy;
|
||||
|
||||
work->next = calloc (1, sizeof (grphash));
|
||||
work->next->grp = save_grp (grp);
|
||||
work = work->next;
|
||||
}
|
||||
total = sizeof (notfound);
|
||||
|
||||
time (&work->create);
|
||||
gidwork = &gidtbl[grp->gr_gid % modulo];
|
||||
if (gidwork->grptr == NULL)
|
||||
gidwork->grptr = work->grp;
|
||||
else
|
||||
{
|
||||
while (gidwork->next != NULL)
|
||||
gidwork = gidwork->next;
|
||||
written = writev (fd, &iov_notfound, 1);
|
||||
|
||||
gidwork->next = calloc (1, sizeof (gidhash));
|
||||
gidwork->next->grptr = work->grp;
|
||||
}
|
||||
copy = malloc (req->key_len);
|
||||
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 *
|
||||
cache_search_name (const char *name)
|
||||
{
|
||||
grphash *work;
|
||||
unsigned long int hash = __nis_hash (name, strlen(name)) % modulo;
|
||||
/* Now get the lock to safely insert the records. */
|
||||
pthread_rwlock_rdlock (&db->lock);
|
||||
|
||||
work = &grptbl[hash];
|
||||
cache_add (req->type, copy, req->key_len, &iov_notfound,
|
||||
sizeof (notfound), (void *) -1, 0, t, db);
|
||||
|
||||
while (work->grp != NULL)
|
||||
{
|
||||
if (strcmp (work->grp->gr_name, name) == 0)
|
||||
return work->grp;
|
||||
if (work->next != NULL)
|
||||
work = work->next;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct group *
|
||||
cache_search_gid (gid_t gid)
|
||||
{
|
||||
gidhash *work;
|
||||
|
||||
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;
|
||||
pthread_rwlock_unlock (&db->lock);
|
||||
}
|
||||
else
|
||||
{
|
||||
while (work->next != NULL)
|
||||
work = work->next;
|
||||
/* Determine the I/O structure. */
|
||||
struct groupdata *data;
|
||||
size_t gr_name_len = strlen (grp->gr_name) + 1;
|
||||
size_t gr_passwd_len = strlen (grp->gr_passwd) + 1;
|
||||
size_t gr_mem_cnt = 0;
|
||||
size_t *gr_mem_len;
|
||||
size_t gr_mem_len_total = 0;
|
||||
char *gr_name;
|
||||
char *cp;
|
||||
char buf[12];
|
||||
ssize_t n;
|
||||
size_t cnt;
|
||||
|
||||
work->next = calloc (1, sizeof (neghash));
|
||||
work->next->key = strdup (key);
|
||||
work = work->next;
|
||||
}
|
||||
/* We need this to insert the `bygid' entry. */
|
||||
n = snprintf (buf, sizeof (buf), "%d", grp->gr_gid) + 1;
|
||||
|
||||
time (&work->create);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
cache_search_neg (const char *key)
|
||||
{
|
||||
neghash *work;
|
||||
unsigned long int hash = __nis_hash (key, strlen (key)) % modulo;
|
||||
|
||||
if (debug_flag)
|
||||
dbg_log (_("grp_cache_search_neg (%s|%ld)"), key, hash);
|
||||
|
||||
work = &negtbl[hash];
|
||||
|
||||
while (work->key != NULL)
|
||||
/* Determine the length of all members. */
|
||||
while (grp->gr_mem[gr_mem_cnt])
|
||||
++gr_mem_cnt;
|
||||
gr_mem_len = (size_t *) alloca (gr_mem_cnt * sizeof (size_t));
|
||||
for (gr_mem_cnt = 0; grp->gr_mem[gr_mem_cnt]; ++gr_mem_cnt)
|
||||
{
|
||||
if (strcmp (work->key, key) == 0)
|
||||
return 1;
|
||||
if (work->next != NULL)
|
||||
work = work->next;
|
||||
else
|
||||
return 0;
|
||||
gr_mem_len[gr_mem_cnt] = strlen (grp->gr_mem[gr_mem_cnt]) + 1;
|
||||
gr_mem_len_total += gr_mem_len[gr_mem_cnt];
|
||||
}
|
||||
|
||||
/* We allocate all data in one memory block: the iov vector,
|
||||
the response header and the dataset itself. */
|
||||
total = (sizeof (struct groupdata)
|
||||
+ gr_mem_cnt * sizeof (size_t)
|
||||
+ gr_name_len + gr_passwd_len + gr_mem_len_total);
|
||||
data = (struct groupdata *) malloc (total + n);
|
||||
if (data == NULL)
|
||||
/* There is no reason to go on. */
|
||||
error (EXIT_FAILURE, errno, _("while allocating cache entry"));
|
||||
|
||||
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;
|
||||
|
||||
pthread_rwlock_rdlock (&grplock);
|
||||
grp = cache_search_name (param->key);
|
||||
if (debug_level > 0)
|
||||
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
|
||||
necessary to avoid to much malloc/free/strcpy. */
|
||||
|
||||
if (grp != NULL)
|
||||
while (getgrnam_r (key, &resultbuf, buffer, buflen, &grp) != 0
|
||||
&& errno == ERANGE)
|
||||
{
|
||||
if (debug_flag)
|
||||
dbg_log (_("Found \"%s\" in cache !"), param->key);
|
||||
|
||||
++poshit;
|
||||
gr_send_answer (param->conn, grp);
|
||||
close_socket (param->conn);
|
||||
|
||||
pthread_rwlock_unlock (&grplock);
|
||||
errno = 0;
|
||||
buflen += 256;
|
||||
buffer = alloca (buflen);
|
||||
}
|
||||
else
|
||||
{
|
||||
int status;
|
||||
int buflen = 1024;
|
||||
char *buffer = calloc (1, buflen);
|
||||
|
||||
cache_addgr (db, fd, req, key, grp);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
addgrbygid (struct database *db, int fd, request_header *req, void *key)
|
||||
{
|
||||
/* Search for the entry matching the key. Please note that we don't
|
||||
look again in the table whether the dataset is now available. We
|
||||
simply insert it. It does not matter if it is in there twice. The
|
||||
pruning function only will look at the timestamp. */
|
||||
int buflen = 256;
|
||||
char *buffer = alloca (buflen);
|
||||
struct group resultbuf;
|
||||
struct group *grp;
|
||||
gid_t gid = atol (key);
|
||||
|
||||
if (debug_flag)
|
||||
dbg_log (_("Doesn't found \"%s\" in cache !"), param->key);
|
||||
if (debug_level > 0)
|
||||
dbg_log (_("Haven't found \"%d\" in group 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
|
||||
&& (getgrnam_r (param->key, &resultbuf, buffer, buflen, &grp)
|
||||
!= 0)
|
||||
while (getgrgid_r (gid, &resultbuf, buffer, buflen, &grp) != 0
|
||||
&& errno == ERANGE)
|
||||
{
|
||||
errno = 0;
|
||||
buflen += 1024;
|
||||
buffer = realloc (buffer, buflen);
|
||||
buflen += 256;
|
||||
buffer = alloca (buflen);
|
||||
}
|
||||
|
||||
if (buffer != NULL && grp != NULL)
|
||||
{
|
||||
struct group *tmp;
|
||||
|
||||
++poshit;
|
||||
pthread_rwlock_wrlock (&grplock);
|
||||
/* While we are waiting on the lock, somebody else could
|
||||
add this entry. */
|
||||
tmp = cache_search_name (param->key);
|
||||
if (tmp == NULL)
|
||||
add_cache (grp);
|
||||
pthread_rwlock_unlock (&grplock);
|
||||
}
|
||||
else
|
||||
{
|
||||
pthread_rwlock_wrlock (&neglock);
|
||||
add_negcache (param->key);
|
||||
++negmiss;
|
||||
pthread_rwlock_unlock (&neglock);
|
||||
}
|
||||
}
|
||||
else
|
||||
++neghit;
|
||||
|
||||
gr_send_answer (param->conn, grp);
|
||||
close_socket (param->conn);
|
||||
if (buffer != NULL)
|
||||
free (buffer);
|
||||
}
|
||||
free (param->key);
|
||||
free (param);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *
|
||||
cache_gr_disabled (void *v_param)
|
||||
{
|
||||
param_t *param = (param_t *)v_param;
|
||||
|
||||
if (debug_flag)
|
||||
dbg_log (_("\tgroup cache is disabled\n"));
|
||||
|
||||
gr_send_disabled (param->conn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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;
|
||||
cache_addgr (db, fd, req, key, grp);
|
||||
}
|
||||
|
405
nscd/hstcache.c
Normal file
405
nscd/hstcache.c
Normal 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 *) ¬found,
|
||||
iov_len: sizeof (notfound)
|
||||
};
|
||||
|
||||
|
||||
struct hostdata
|
||||
{
|
||||
hst_response_header resp;
|
||||
char strdata[0];
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
cache_addhst (struct database *db, int fd, request_header *req, void *key,
|
||||
struct hostent *hst)
|
||||
{
|
||||
ssize_t total;
|
||||
ssize_t written;
|
||||
time_t t = time (NULL);
|
||||
|
||||
if (hst == NULL)
|
||||
{
|
||||
/* We have no data. This means we send the standard reply for this
|
||||
case. */
|
||||
void *copy;
|
||||
|
||||
total = sizeof (notfound);
|
||||
|
||||
written = writev (fd, &iov_notfound, 1);
|
||||
|
||||
copy = malloc (req->key_len);
|
||||
if (copy == NULL)
|
||||
error (EXIT_FAILURE, errno, _("while allocating key copy"));
|
||||
memcpy (copy, key, req->key_len);
|
||||
|
||||
/* Compute the timeout time. */
|
||||
t += db->negtimeout;
|
||||
|
||||
/* Now get the lock to safely insert the records. */
|
||||
pthread_rwlock_rdlock (&db->lock);
|
||||
|
||||
cache_add (req->type, copy, req->key_len, &iov_notfound,
|
||||
sizeof (notfound), (void *) -1, 0, t, db);
|
||||
|
||||
pthread_rwlock_unlock (&db->lock);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Determine the I/O structure. */
|
||||
struct hostdata *data;
|
||||
size_t h_name_len = strlen (hst->h_name) + 1;
|
||||
size_t h_aliases_cnt;
|
||||
size_t *h_aliases_len;
|
||||
size_t h_addr_list_cnt;
|
||||
int addr_list_type;
|
||||
char *addresses;
|
||||
char *aliases;
|
||||
char *key_copy = NULL;
|
||||
char *cp;
|
||||
size_t cnt;
|
||||
|
||||
/* Determine the number of aliases. */
|
||||
h_aliases_cnt = 0;
|
||||
for (cnt = 0; hst->h_aliases[cnt] != NULL; ++cnt)
|
||||
++h_aliases_cnt;
|
||||
/* Determine the length of all aliases. */
|
||||
h_aliases_len = alloca (h_aliases_cnt * sizeof (size_t));
|
||||
total = 0;
|
||||
for (cnt = 0; cnt < h_aliases_cnt; ++cnt)
|
||||
{
|
||||
h_aliases_len[cnt] = strlen (hst->h_aliases[cnt]) + 1;
|
||||
total += h_aliases_len[cnt];
|
||||
}
|
||||
|
||||
/* Determine the number of addresses. */
|
||||
h_addr_list_cnt = 0;
|
||||
for (cnt = 0; hst->h_addr_list[cnt]; ++cnt)
|
||||
++h_addr_list_cnt;
|
||||
|
||||
/* We allocate all data in one memory block: the iov vector,
|
||||
the response header and the dataset itself. */
|
||||
total += (sizeof (struct hostdata)
|
||||
+ h_name_len
|
||||
+ h_aliases_cnt * sizeof (size_t)
|
||||
+ h_addr_list_cnt * (hst->h_length
|
||||
+ (hst->h_length == INADDRSZ
|
||||
? IN6ADDRSZ : 0)));
|
||||
|
||||
data = (struct hostdata *) malloc (total + req->key_len);
|
||||
if (data == NULL)
|
||||
/* There is no reason to go on. */
|
||||
error (EXIT_FAILURE, errno, _("while allocating cache entry"));
|
||||
|
||||
data->resp.found = 1;
|
||||
data->resp.h_name_len = h_name_len;
|
||||
data->resp.h_aliases_cnt = h_aliases_cnt;
|
||||
data->resp.h_addrtype = hst->h_addrtype;
|
||||
data->resp.h_length = hst->h_length;
|
||||
data->resp.h_addr_list_cnt = h_addr_list_cnt;
|
||||
data->resp.error = NETDB_SUCCESS;
|
||||
|
||||
cp = data->strdata;
|
||||
|
||||
cp = mempcpy (cp, hst->h_name, h_name_len);
|
||||
cp = mempcpy (cp, h_aliases_len, h_aliases_cnt * sizeof (size_t));
|
||||
|
||||
/* The normal addresses first. */
|
||||
addresses = cp;
|
||||
for (cnt = 0; cnt < h_addr_list_cnt; ++cnt)
|
||||
cp = mempcpy (cp, hst->h_addr_list[cnt], hst->h_length);
|
||||
|
||||
/* And the generated IPv6 addresses if necessary. */
|
||||
if (hst->h_length == INADDRSZ)
|
||||
{
|
||||
/* Generate the IPv6 addresses. */
|
||||
for (cnt = 0; cnt < h_addr_list_cnt; cp += IN6ADDRSZ, ++cnt)
|
||||
map_v4v6_address (hst->h_addr_list[cnt], cp);
|
||||
}
|
||||
|
||||
/* Then the aliases. */
|
||||
aliases = cp;
|
||||
for (cnt = 0; cnt < h_aliases_cnt; ++cnt)
|
||||
cp = mempcpy (cp, hst->h_aliases[cnt], h_aliases_len[cnt]);
|
||||
|
||||
assert (cp == data->strdata + total - sizeof (hst_response_header));
|
||||
|
||||
/* If we are adding a GETHOSTBYNAME{,v6} entry we must be prepared
|
||||
that the answer we get from the NSS does not contain the key
|
||||
itself. This is the case if the resolver is used and the name
|
||||
is extended by the domainnames from /etc/resolv.conf. Therefore
|
||||
we explicitly add the name here. */
|
||||
if (req->type == GETHOSTBYNAME || req->type == GETHOSTBYNAMEv6)
|
||||
key_copy = memcpy (cp, key, req->key_len);
|
||||
|
||||
/* We write the dataset before inserting it to the database
|
||||
since while inserting this thread might block and so would
|
||||
unnecessarily let the receiver wait. */
|
||||
written = write (fd, data, total);
|
||||
|
||||
addr_list_type = (hst->h_length == INADDRSZ
|
||||
? GETHOSTBYADDR : GETHOSTBYADDRv6);
|
||||
|
||||
/* Compute the timeout time. */
|
||||
t += db->postimeout;
|
||||
|
||||
/* Now get the lock to safely insert the records. */
|
||||
pthread_rwlock_rdlock (&db->lock);
|
||||
|
||||
/* First add all the aliases. */
|
||||
for (cnt = 0; cnt < h_aliases_cnt; ++cnt)
|
||||
{
|
||||
if (addr_list_type == GETHOSTBYADDR)
|
||||
cache_add (GETHOSTBYNAME, aliases, h_aliases_len[cnt], data, total,
|
||||
data, 0, t, db);
|
||||
|
||||
cache_add (GETHOSTBYNAMEv6, aliases, h_aliases_len[cnt], data, total,
|
||||
data, 0, t, db);
|
||||
|
||||
aliases += h_aliases_len[cnt];
|
||||
}
|
||||
|
||||
/* Next the normal addresses. */
|
||||
for (cnt = 0; cnt < h_addr_list_cnt; ++cnt)
|
||||
{
|
||||
cache_add (addr_list_type, addresses, hst->h_length, data, total,
|
||||
data, 0, t, db);
|
||||
addresses += hst->h_length;
|
||||
}
|
||||
|
||||
/* If necessary the IPv6 addresses. */
|
||||
if (addr_list_type == GETHOSTBYADDR)
|
||||
for (cnt = 0; cnt < h_addr_list_cnt; ++cnt)
|
||||
{
|
||||
cache_add (GETHOSTBYADDRv6, addresses, IN6ADDRSZ, data, total,
|
||||
data, 0, t, db);
|
||||
addresses += IN6ADDRSZ;
|
||||
}
|
||||
|
||||
/* If necessary add the key for this request. */
|
||||
if (req->type == GETHOSTBYNAME || req->type == GETHOSTBYNAMEv6)
|
||||
{
|
||||
if (addr_list_type == GETHOSTBYADDR)
|
||||
cache_add (GETHOSTBYNAME, key_copy, req->key_len, data, total,
|
||||
data, 0, t, db);
|
||||
cache_add (GETHOSTBYNAMEv6, key_copy, req->key_len, data,
|
||||
total, data, 0, t, db);
|
||||
}
|
||||
|
||||
/* And finally the name. We mark this as the last entry. */
|
||||
if (addr_list_type == GETHOSTBYADDR)
|
||||
cache_add (GETHOSTBYNAME, data->strdata, h_name_len, data, total, data,
|
||||
0, t, db);
|
||||
cache_add (GETHOSTBYNAMEv6, data->strdata, h_name_len, data,
|
||||
total, data, 1, t, db);
|
||||
|
||||
pthread_rwlock_unlock (&db->lock);
|
||||
}
|
||||
|
||||
if (written != total)
|
||||
{
|
||||
char buf[256];
|
||||
dbg_log (_("short write in %s: %s"), __FUNCTION__,
|
||||
strerror_r (errno, buf, sizeof (buf)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
addhstbyname (struct database *db, int fd, request_header *req, void *key)
|
||||
{
|
||||
/* Search for the entry matching the key. Please note that we don't
|
||||
look again in the table whether the dataset is now available. We
|
||||
simply insert it. It does not matter if it is in there twice. The
|
||||
pruning function only will look at the timestamp. */
|
||||
int buflen = 512;
|
||||
char *buffer = alloca (buflen);
|
||||
struct hostent resultbuf;
|
||||
struct hostent *hst;
|
||||
|
||||
if (debug_level > 0)
|
||||
dbg_log (_("Haven't found \"%s\" in hosts cache!"), key);
|
||||
|
||||
while (gethostbyname2_r (key, AF_INET, &resultbuf, buffer, buflen, &hst,
|
||||
&h_errno) != 0
|
||||
&& h_errno == NETDB_INTERNAL
|
||||
&& errno == ERANGE)
|
||||
{
|
||||
errno = 0;
|
||||
buflen += 256;
|
||||
buffer = alloca (buflen);
|
||||
}
|
||||
|
||||
cache_addhst (db, fd, req, key, hst);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
addhstbyaddr (struct database *db, int fd, request_header *req, void *key)
|
||||
{
|
||||
/* Search for the entry matching the key. Please note that we don't
|
||||
look again in the table whether the dataset is now available. We
|
||||
simply insert it. It does not matter if it is in there twice. The
|
||||
pruning function only will look at the timestamp. */
|
||||
int buflen = 512;
|
||||
char *buffer = alloca (buflen);
|
||||
struct hostent resultbuf;
|
||||
struct hostent *hst;
|
||||
|
||||
if (debug_level > 0)
|
||||
{
|
||||
char buf[64];
|
||||
dbg_log (_("Haven't found \"%s\" in hosts cache!"),
|
||||
inet_ntop (AF_INET, key, buf, sizeof (buf)));
|
||||
}
|
||||
|
||||
while (gethostbyaddr_r (key, INADDRSZ, AF_INET, &resultbuf, buffer, buflen,
|
||||
&hst, &h_errno) != 0
|
||||
&& h_errno == NETDB_INTERNAL
|
||||
&& errno == ERANGE)
|
||||
{
|
||||
errno = 0;
|
||||
buflen += 256;
|
||||
buffer = alloca (buflen);
|
||||
}
|
||||
|
||||
cache_addhst (db, fd, req, key, hst);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
addhstbynamev6 (struct database *db, int fd, request_header *req, void *key)
|
||||
{
|
||||
/* Search for the entry matching the key. Please note that we don't
|
||||
look again in the table whether the dataset is now available. We
|
||||
simply insert it. It does not matter if it is in there twice. The
|
||||
pruning function only will look at the timestamp. */
|
||||
int buflen = 512;
|
||||
char *buffer = alloca (buflen);
|
||||
struct hostent resultbuf;
|
||||
struct hostent *hst;
|
||||
|
||||
if (debug_level > 0)
|
||||
dbg_log (_("Haven't found \"%s\" in hosts cache!"), key);
|
||||
|
||||
while (gethostbyname2_r (key, AF_INET6, &resultbuf, buffer, buflen, &hst,
|
||||
&h_errno) != 0
|
||||
&& h_errno == NETDB_INTERNAL
|
||||
&& errno == ERANGE)
|
||||
{
|
||||
errno = 0;
|
||||
buflen += 256;
|
||||
buffer = alloca (buflen);
|
||||
}
|
||||
|
||||
cache_addhst (db, fd, req, key, hst);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
addhstbyaddrv6 (struct database *db, int fd, request_header *req, void *key)
|
||||
{
|
||||
/* Search for the entry matching the key. Please note that we don't
|
||||
look again in the table whether the dataset is now available. We
|
||||
simply insert it. It does not matter if it is in there twice. The
|
||||
pruning function only will look at the timestamp. */
|
||||
int buflen = 512;
|
||||
char *buffer = alloca (buflen);
|
||||
struct hostent resultbuf;
|
||||
struct hostent *hst;
|
||||
|
||||
if (debug_level > 0)
|
||||
{
|
||||
char buf[64];
|
||||
dbg_log (_("Haven't found \"%s\" in hosts cache!"),
|
||||
inet_ntop (AF_INET6, key, buf, sizeof (buf)));
|
||||
}
|
||||
|
||||
while (gethostbyaddr_r (key, IN6ADDRSZ, AF_INET6, &resultbuf, buffer, buflen,
|
||||
&hst, &h_errno) != 0
|
||||
&& h_errno == NETDB_INTERNAL
|
||||
&& errno == ERANGE)
|
||||
{
|
||||
errno = 0;
|
||||
buflen += 256;
|
||||
buffer = alloca (buflen);
|
||||
}
|
||||
|
||||
cache_addhst (db, fd, req, key, hst);
|
||||
}
|
243
nscd/nscd.c
243
nscd/nscd.c
@ -17,9 +17,10 @@
|
||||
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* nscd - Name Service Cache Daemon. Caches passwd and group. */
|
||||
/* nscd - Name Service Cache Daemon. Caches passwd, group, and hosts. */
|
||||
|
||||
#include <argp.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <error.h>
|
||||
#include <libintl.h>
|
||||
@ -61,12 +62,10 @@ int do_shutdown = 0;
|
||||
int disabled_passwd = 0;
|
||||
int disabled_group = 0;
|
||||
int go_background = 1;
|
||||
const char *conffile = _PATH_NSCDCONF;
|
||||
static const char *conffile = _PATH_NSCDCONF;
|
||||
|
||||
static void termination_handler (int signum);
|
||||
static int check_pid (const char *file);
|
||||
static int write_pid (const char *file);
|
||||
static void handle_requests (void);
|
||||
|
||||
/* Name and version of program. */
|
||||
static void print_version (FILE *stream, struct argp_state *state);
|
||||
@ -79,6 +78,7 @@ static const struct argp_option options[] =
|
||||
N_("Read configuration data from NAME") },
|
||||
{ "debug", 'd', NULL, 0,
|
||||
N_("Do not fork and display messages on the current tty") },
|
||||
{ "nthreads", 't', N_("NUMBER"), 0, N_("Start NUMBER threads") },
|
||||
{ "shutdown", 'K', NULL, 0, N_("Shut the server down") },
|
||||
{ "statistic", 'g', NULL, 0, N_("Print current configuration statistic") },
|
||||
{ NULL, 0, NULL, 0, NULL }
|
||||
@ -118,10 +118,7 @@ main (int argc, char **argv)
|
||||
|
||||
/* Check if we are already running. */
|
||||
if (check_pid (_PATH_NSCDPID))
|
||||
{
|
||||
fputs (_("already running"), stderr);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
error (EXIT_FAILURE, 0, _("already running"));
|
||||
|
||||
/* Behave like a daemon. */
|
||||
if (go_background)
|
||||
@ -144,7 +141,7 @@ main (int argc, char **argv)
|
||||
if (write_pid (_PATH_NSCDPID) < 0)
|
||||
dbg_log ("%s: %s", _PATH_NSCDPID, strerror (errno));
|
||||
|
||||
/* Ignore job control signals */
|
||||
/* Ignore job control signals. */
|
||||
signal (SIGTTOU, SIG_IGN);
|
||||
signal (SIGTTIN, SIG_IGN);
|
||||
signal (SIGTSTP, SIG_IGN);
|
||||
@ -155,21 +152,14 @@ main (int argc, char **argv)
|
||||
signal (SIGTERM, termination_handler);
|
||||
signal (SIGPIPE, SIG_IGN);
|
||||
|
||||
/* Cleanup files created by a previous `bind' */
|
||||
/* Cleanup files created by a previous `bind'. */
|
||||
unlink (_PATH_NSCDSOCKET);
|
||||
|
||||
nscd_parse_file (conffile);
|
||||
/* Init databases. */
|
||||
nscd_init (conffile);
|
||||
|
||||
/* Create first sockets */
|
||||
init_sockets ();
|
||||
/* Init databases */
|
||||
if ((cache_pwdinit () < 0) || (cache_grpinit () < 0))
|
||||
{
|
||||
fputs (_("Not enough memory\n"), stderr);
|
||||
return 1;
|
||||
}
|
||||
/* Handle incoming requests */
|
||||
handle_requests ();
|
||||
start_threads ();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -182,20 +172,19 @@ parse_opt (int key, char *arg, struct argp_state *state)
|
||||
switch (key)
|
||||
{
|
||||
case 'd':
|
||||
debug_flag = 1;
|
||||
++debug_level;
|
||||
go_background = 0;
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
conffile = arg;
|
||||
break;
|
||||
|
||||
case 'K':
|
||||
if (getuid () != 0)
|
||||
error (EXIT_FAILURE, 0, _("Only root is allowed to use this option!"));
|
||||
{
|
||||
printf (_("Only root is allowed to use this option!\n\n"));
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
{
|
||||
int sock = __nscd_open_socket ();
|
||||
int sock = nscd_open_socket ();
|
||||
request_header req;
|
||||
ssize_t nbytes;
|
||||
|
||||
@ -205,19 +194,24 @@ parse_opt (int key, char *arg, struct argp_state *state)
|
||||
req.version = NSCD_VERSION;
|
||||
req.type = SHUTDOWN;
|
||||
req.key_len = 0;
|
||||
nbytes = write (sock, &req, sizeof (request_header));
|
||||
nbytes = TEMP_FAILURE_RETRY (write (sock, &req,
|
||||
sizeof (request_header)));
|
||||
close (sock);
|
||||
if (nbytes != req.key_len)
|
||||
exit (EXIT_FAILURE);
|
||||
else
|
||||
exit (EXIT_SUCCESS);
|
||||
exit (nbytes != sizeof (request_header) ? EXIT_FAILURE : EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
case 'g':
|
||||
print_stat ();
|
||||
exit (EXIT_SUCCESS);
|
||||
receive_print_stats ();
|
||||
/* Does not return. */
|
||||
|
||||
case 't':
|
||||
nthreads = atol (arg);
|
||||
break;
|
||||
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -231,13 +225,14 @@ Copyright (C) %s Free Software Foundation, Inc.\n\
|
||||
This is free software; see the source for copying conditions. There is NO\n\
|
||||
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
|
||||
"), "1998");
|
||||
fprintf (stream, gettext ("Written by %s.\n"), "Thorsten Kukuk");
|
||||
fprintf (stream, gettext ("Written by %s.\n"),
|
||||
"Thorsten Kukuk and Ulrich Drepper");
|
||||
}
|
||||
|
||||
|
||||
/* Create a socket connected to a name. */
|
||||
int
|
||||
__nscd_open_socket (void)
|
||||
nscd_open_socket (void)
|
||||
{
|
||||
struct sockaddr_un addr;
|
||||
int sock;
|
||||
@ -247,6 +242,7 @@ __nscd_open_socket (void)
|
||||
return -1;
|
||||
|
||||
addr.sun_family = AF_UNIX;
|
||||
assert (sizeof (addr.sun_path) >= sizeof (_PATH_NSCDSOCKET));
|
||||
strcpy (addr.sun_path, _PATH_NSCDSOCKET);
|
||||
if (connect (sock, (struct sockaddr *) &addr, sizeof (addr)) < 0)
|
||||
{
|
||||
@ -258,12 +254,12 @@ __nscd_open_socket (void)
|
||||
}
|
||||
|
||||
/* Cleanup. */
|
||||
static void
|
||||
void
|
||||
termination_handler (int signum)
|
||||
{
|
||||
close_sockets ();
|
||||
|
||||
/* Clean up the files created by `bind'. */
|
||||
/* Clean up the file created by `bind'. */
|
||||
unlink (_PATH_NSCDSOCKET);
|
||||
|
||||
/* Clean up pid file. */
|
||||
@ -282,11 +278,12 @@ check_pid (const char *file)
|
||||
if (fp)
|
||||
{
|
||||
pid_t pid;
|
||||
int n;
|
||||
|
||||
fscanf (fp, "%d", &pid);
|
||||
n = fscanf (fp, "%d", &pid);
|
||||
fclose (fp);
|
||||
|
||||
if (kill (pid, 0) == 0)
|
||||
if (n != 1 || kill (pid, 0) == 0)
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -305,176 +302,10 @@ write_pid (const char *file)
|
||||
return -1;
|
||||
|
||||
fprintf (fp, "%d\n", getpid ());
|
||||
if (ferror (fp))
|
||||
if (fflush (fp) || ferror (fp))
|
||||
return -1;
|
||||
|
||||
fclose (fp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Type of the lookup function for netname2user. */
|
||||
typedef int (*pwbyname_function) (const char *name, struct passwd *pw,
|
||||
char *buffer, size_t buflen);
|
||||
|
||||
/* Handle incoming requests. */
|
||||
static
|
||||
void handle_requests (void)
|
||||
{
|
||||
request_header req;
|
||||
int conn; /* Handle on which connection (client) the request came from. */
|
||||
int done = 0;
|
||||
char *key;
|
||||
pthread_attr_t th_attr;
|
||||
|
||||
/* We will create all threads detached. Therefore prepare an attribute
|
||||
now. */
|
||||
pthread_attr_init (&th_attr);
|
||||
pthread_attr_setdetachstate (&th_attr, PTHREAD_CREATE_DETACHED);
|
||||
|
||||
while (!done)
|
||||
{
|
||||
key = NULL;
|
||||
get_request (&conn, &req, &key);
|
||||
if (debug_flag)
|
||||
dbg_log (_("handle_requests: request received (Version = %d)"),
|
||||
req.version);
|
||||
switch (req.type)
|
||||
{
|
||||
case GETPWBYNAME:
|
||||
{
|
||||
param_t *param = malloc (sizeof (param_t));
|
||||
pthread_t thread;
|
||||
int status;
|
||||
|
||||
if (debug_flag)
|
||||
dbg_log ("\tGETPWBYNAME (%s)", key);
|
||||
param->key = key;
|
||||
param->conn = conn;
|
||||
if (disabled_passwd)
|
||||
status = pthread_create (&thread, &th_attr, cache_pw_disabled,
|
||||
(void *)param);
|
||||
else
|
||||
status = pthread_create (&thread, &th_attr, cache_getpwnam,
|
||||
(void *)param);
|
||||
if (status != 0)
|
||||
{
|
||||
dbg_log (_("Creation of thread failed: %s"), strerror (errno));
|
||||
close_socket (conn);
|
||||
}
|
||||
pthread_detach (thread);
|
||||
}
|
||||
break;
|
||||
case GETPWBYUID:
|
||||
{
|
||||
param_t *param = malloc (sizeof (param_t));
|
||||
pthread_t thread;
|
||||
int status;
|
||||
|
||||
if (debug_flag)
|
||||
dbg_log ("\tGETPWBYUID (%s)", key);
|
||||
param->key = key;
|
||||
param->conn = conn;
|
||||
if (disabled_passwd)
|
||||
status = pthread_create (&thread, &th_attr, cache_pw_disabled,
|
||||
(void *)param);
|
||||
else
|
||||
status = pthread_create (&thread, &th_attr, cache_getpwuid,
|
||||
(void *)param);
|
||||
if (status != 0)
|
||||
{
|
||||
dbg_log (_("Creation of thread failed: %s"), strerror (errno));
|
||||
close_socket (conn);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GETGRBYNAME:
|
||||
{
|
||||
param_t *param = malloc (sizeof (param_t));
|
||||
pthread_t thread;
|
||||
int status;
|
||||
|
||||
if (debug_flag)
|
||||
dbg_log ("\tGETGRBYNAME (%s)", key);
|
||||
param->key = key;
|
||||
param->conn = conn;
|
||||
if (disabled_group)
|
||||
status = pthread_create (&thread, &th_attr, cache_gr_disabled,
|
||||
(void *)param);
|
||||
else
|
||||
status = pthread_create (&thread, &th_attr, cache_getgrnam,
|
||||
(void *)param);
|
||||
if (status != 0)
|
||||
{
|
||||
dbg_log (_("Creation of thread failed: %s"), strerror (errno));
|
||||
close_socket (conn);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GETGRBYGID:
|
||||
{
|
||||
param_t *param = malloc (sizeof (param_t));
|
||||
pthread_t thread;
|
||||
int status;
|
||||
|
||||
if (debug_flag)
|
||||
dbg_log ("\tGETGRBYGID (%s)", key);
|
||||
param->key = key;
|
||||
param->conn = conn;
|
||||
if (disabled_group)
|
||||
status = pthread_create (&thread, &th_attr, cache_gr_disabled,
|
||||
(void *)param);
|
||||
else
|
||||
status = pthread_create (&thread, &th_attr, cache_getgrgid,
|
||||
(void *)param);
|
||||
if (status != 0)
|
||||
{
|
||||
dbg_log (_("Creation of thread failed: %s"), strerror (errno));
|
||||
close_socket (conn);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GETHOSTBYNAME:
|
||||
/* Not yetimplemented. */
|
||||
close_socket (conn);
|
||||
break;
|
||||
case GETHOSTBYADDR:
|
||||
/* Not yet implemented. */
|
||||
close_socket (conn);
|
||||
break;
|
||||
case SHUTDOWN:
|
||||
do_shutdown = 1;
|
||||
close_socket (0);
|
||||
close_socket (conn);
|
||||
/* Clean up the files created by `bind'. */
|
||||
unlink (_PATH_NSCDSOCKET);
|
||||
/* Clean up pid file. */
|
||||
unlink (_PATH_NSCDPID);
|
||||
done = 1;
|
||||
break;
|
||||
case GETSTAT:
|
||||
{
|
||||
stat_response_header resp;
|
||||
|
||||
if (debug_flag)
|
||||
dbg_log ("\tGETSTAT");
|
||||
|
||||
get_pw_stat (&resp);
|
||||
get_gr_stat (&resp);
|
||||
resp.debug_level = debug_flag;
|
||||
resp.pw_enabled = !disabled_passwd;
|
||||
resp.gr_enabled = !disabled_group;
|
||||
|
||||
stat_send (conn, &resp);
|
||||
|
||||
close_socket (conn);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
dbg_log (_("Unknown request (%d)"), req.type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pthread_attr_destroy (&th_attr);
|
||||
}
|
||||
|
@ -6,25 +6,36 @@
|
||||
# Legal entries are:
|
||||
#
|
||||
# logfile <file>
|
||||
# enable-cache <service> <yes|no>
|
||||
# debug-level <level>
|
||||
#
|
||||
# enable-cache <service> <yes|no>
|
||||
# positive-time-to-live <service> <time in seconds>
|
||||
# negative-time-to-live <service> <time in seconds>
|
||||
# suggested-size <service> <prime number>
|
||||
# check-files <service> <yes|no>
|
||||
#
|
||||
# Currently supported cache names (services): passwd, group
|
||||
#
|
||||
|
||||
|
||||
# logfile /var/adm/nscd.log
|
||||
# enable-cache hosts no
|
||||
|
||||
debug-level 0
|
||||
|
||||
enable-cache passwd yes
|
||||
positive-time-to-live passwd 600
|
||||
negative-time-to-live passwd 20
|
||||
suggested-size passwd 211
|
||||
check-files passwd yes
|
||||
|
||||
enable-cache group yes
|
||||
positive-time-to-live group 3600
|
||||
negative-time-to-live group 60
|
||||
suggested-size group 211
|
||||
check-files group yes
|
||||
|
||||
enable-cache hosts yes
|
||||
positive-time-to-live hosts 3600
|
||||
negative-time-to-live hosts 20
|
||||
suggested-size hosts 211
|
||||
check-files hosts yes
|
||||
|
220
nscd/nscd.h
220
nscd/nscd.h
@ -20,16 +20,35 @@
|
||||
#ifndef _NSCD_H
|
||||
#define _NSCD_H 1
|
||||
|
||||
#include <grp.h>
|
||||
#include <pwd.h>
|
||||
#include <pthread.h>
|
||||
#include <time.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
|
||||
/* Version number of the daemon interface */
|
||||
#define NSCD_VERSION 1
|
||||
#define NSCD_VERSION 2
|
||||
|
||||
/* How many threads do we spawn maximal ? */
|
||||
#define MAX_NUM_CONNECTIONS 16
|
||||
/* Path of the file where the PID of the running system is stored. */
|
||||
#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
|
||||
{
|
||||
GETPWBYNAME,
|
||||
@ -37,22 +56,67 @@ typedef enum
|
||||
GETGRBYNAME,
|
||||
GETGRBYGID,
|
||||
GETHOSTBYNAME,
|
||||
GETHOSTBYNAMEv6,
|
||||
GETHOSTBYADDR,
|
||||
SHUTDOWN, /* Shut the server down */
|
||||
GETSTAT /* Get the server statistic */
|
||||
GETHOSTBYADDRv6,
|
||||
LASTDBREQ = GETHOSTBYADDRv6,
|
||||
SHUTDOWN, /* Shut the server down. */
|
||||
GETSTAT, /* Get the server statistic. */
|
||||
LASTREQ,
|
||||
} request_type;
|
||||
|
||||
|
||||
/* Structure for one hash table entry. */
|
||||
struct hashentry
|
||||
{
|
||||
request_type type; /* Which type of dataset. */
|
||||
size_t len; /* Length of key. */
|
||||
void *key; /* Pointer to key. */
|
||||
struct hashentry *next; /* Next entry in this hash bucket list. */
|
||||
time_t timeout; /* Time when this entry becomes invalid. */
|
||||
ssize_t total; /* Number of bytes in PACKET. */
|
||||
const void *packet; /* Records for the result. */
|
||||
void *data; /* The malloc()ed respond record. */
|
||||
int last; /* Nonzero if DATA should be free()d. */
|
||||
struct hashentry *dellist; /* Next record to be deleted. */
|
||||
};
|
||||
|
||||
/* Structure describing one database. */
|
||||
struct database
|
||||
{
|
||||
pthread_rwlock_t lock;
|
||||
|
||||
int enabled;
|
||||
int check_file;
|
||||
const char *filename;
|
||||
time_t file_mtime;
|
||||
size_t module;
|
||||
|
||||
const struct iovec *disabled_iov;
|
||||
|
||||
unsigned long int postimeout;
|
||||
unsigned long int negtimeout;
|
||||
|
||||
unsigned long int poshit;
|
||||
unsigned long int neghit;
|
||||
unsigned long int posmiss;
|
||||
unsigned long int negmiss;
|
||||
|
||||
struct hashentry **array;
|
||||
};
|
||||
|
||||
|
||||
/* Header common to all requests */
|
||||
typedef struct
|
||||
{
|
||||
/* Version number of the daemon interface */
|
||||
int version;
|
||||
/* Service requested */
|
||||
request_type type;
|
||||
/* key len */
|
||||
ssize_t key_len;
|
||||
int version; /* Version number of the daemon interface. */
|
||||
request_type type; /* Service requested. */
|
||||
ssize_t key_len; /* Key length. */
|
||||
} request_header;
|
||||
|
||||
|
||||
/* Structure sent in reply to password query. Note that this struct is
|
||||
sent also if the service is disabled or there is no record found. */
|
||||
typedef struct
|
||||
{
|
||||
int version;
|
||||
@ -66,6 +130,9 @@ typedef struct
|
||||
ssize_t pw_shell_len;
|
||||
} pw_response_header;
|
||||
|
||||
|
||||
/* Structure sent in reply to group query. Note that this struct is
|
||||
sent also if the service is disabled or there is no record found. */
|
||||
typedef struct
|
||||
{
|
||||
int version;
|
||||
@ -73,77 +140,82 @@ typedef struct
|
||||
ssize_t gr_name_len;
|
||||
ssize_t gr_passwd_len;
|
||||
gid_t gr_gid;
|
||||
ssize_t gr_mem_len;
|
||||
ssize_t gr_mem_cnt;
|
||||
} gr_response_header;
|
||||
|
||||
|
||||
/* 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
|
||||
{
|
||||
int debug_level;
|
||||
int pw_enabled;
|
||||
unsigned long pw_poshit;
|
||||
unsigned long pw_posmiss;
|
||||
unsigned long pw_neghit;
|
||||
unsigned long pw_negmiss;
|
||||
unsigned long pw_size;
|
||||
unsigned long pw_posttl;
|
||||
unsigned long pw_negttl;
|
||||
int gr_enabled;
|
||||
unsigned long gr_poshit;
|
||||
unsigned long gr_posmiss;
|
||||
unsigned long gr_neghit;
|
||||
unsigned long gr_negmiss;
|
||||
unsigned long gr_size;
|
||||
unsigned long gr_posttl;
|
||||
unsigned long gr_negttl;
|
||||
} stat_response_header;
|
||||
int version;
|
||||
int found;
|
||||
ssize_t h_name_len;
|
||||
ssize_t h_aliases_cnt;
|
||||
int h_addrtype;
|
||||
int h_length;
|
||||
ssize_t h_addr_list_cnt;
|
||||
int error;
|
||||
} hst_response_header;
|
||||
|
||||
#define _PATH_NSCDPID "/var/run/nscd.pid"
|
||||
#define _PATH_NSCDSOCKET "/var/run/.nscd_socket"
|
||||
#define _PATH_NSCDCONF "/etc/nscd.conf"
|
||||
/* Global variables. */
|
||||
extern const char *dbnames[lastdb];
|
||||
extern const char *serv2str[LASTREQ];
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *key;
|
||||
int conn;
|
||||
} param_t;
|
||||
extern const struct iovec pwd_iov_disabled;
|
||||
extern const struct iovec grp_iov_disabled;
|
||||
extern const struct iovec hst_iov_disabled;
|
||||
|
||||
extern int do_shutdown; /* 1 if we should quit the programm. */
|
||||
extern int disabled_passwd;
|
||||
extern int disabled_group;
|
||||
/* Number of threads to run. */
|
||||
extern int nthreads;
|
||||
|
||||
extern int nscd_parse_file __P ((const char *fname));
|
||||
extern int set_logfile __P ((const char *logfile));
|
||||
extern void set_pos_pwd_ttl __P ((unsigned long));
|
||||
extern void set_neg_pwd_ttl __P ((unsigned long));
|
||||
extern void set_pos_grp_ttl __P ((unsigned long));
|
||||
extern void set_neg_grp_ttl __P ((unsigned long));
|
||||
extern void set_pwd_modulo __P ((unsigned long));
|
||||
extern void set_grp_modulo __P ((unsigned long));
|
||||
|
||||
extern void init_sockets __P ((void));
|
||||
extern void close_socket __P ((int conn));
|
||||
extern void close_sockets __P ((void));
|
||||
extern void get_request __P ((int *conn, request_header *req, char **key));
|
||||
extern void pw_send_answer __P ((int conn, struct passwd *pwd));
|
||||
extern void pw_send_disabled __P ((int conn));
|
||||
extern void gr_send_answer __P ((int conn, struct group *grp));
|
||||
extern void gr_send_disabled __P ((int conn));
|
||||
/* Prototypes for global functions. */
|
||||
|
||||
extern int cache_pwdinit __P ((void));
|
||||
extern void *cache_getpwnam __P ((void *param));
|
||||
extern void *cache_getpwuid __P ((void *param));
|
||||
extern void *cache_pw_disabled __P ((void *param));
|
||||
/* nscd.c */
|
||||
extern void termination_handler (int signum);
|
||||
extern int nscd_open_socket (void);
|
||||
|
||||
extern int cache_grpinit __P ((void));
|
||||
extern void *cache_getgrnam __P ((void *param));
|
||||
extern void *cache_getgrgid __P ((void *param));
|
||||
extern void *cache_gr_disabled __P ((void *param));
|
||||
/* connections.c */
|
||||
extern void nscd_init (const char *conffile);
|
||||
extern void close_sockets (void);
|
||||
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));
|
||||
extern void get_gr_stat __P ((stat_response_header *resp));
|
||||
extern void print_stat __P ((void));
|
||||
extern void stat_send __P ((int conn, stat_response_header *resp));
|
||||
/* nscd_stat.c */
|
||||
extern void send_stats (int fd, struct database dbs[lastdb]);
|
||||
extern int receive_print_stats (void);
|
||||
|
||||
#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 */
|
||||
|
@ -22,17 +22,27 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "dbg_log.h"
|
||||
#include "nscd.h"
|
||||
|
||||
/* Names of the databases. */
|
||||
const char *dbnames[lastdb] =
|
||||
{
|
||||
[pwddb] = "passwd",
|
||||
[grpdb] = "group",
|
||||
[hstdb] = "hosts"
|
||||
};
|
||||
|
||||
int
|
||||
nscd_parse_file (const char *fname)
|
||||
nscd_parse_file (const char *fname, struct database dbs[lastdb])
|
||||
{
|
||||
FILE *fp;
|
||||
char *line, *cp, *entry, *arg1, *arg2;
|
||||
size_t len;
|
||||
int cnt;
|
||||
|
||||
/* Open the configuration file. */
|
||||
fp = fopen (fname, "r");
|
||||
@ -92,41 +102,64 @@ nscd_parse_file (const char *fname)
|
||||
|
||||
if (strcmp (entry, "positive-time-to-live") == 0)
|
||||
{
|
||||
if (strcmp (arg1, "passwd") == 0)
|
||||
set_pos_pwd_ttl (atol (arg2));
|
||||
else if (strcmp (arg1, "group") == 0)
|
||||
set_pos_grp_ttl (atol (arg2));
|
||||
else
|
||||
for (cnt = 0; cnt < lastdb; ++cnt)
|
||||
if (strcmp (arg1, dbnames[cnt]) == 0)
|
||||
{
|
||||
dbs[cnt].postimeout = atol (arg2);
|
||||
break;
|
||||
}
|
||||
if (cnt == lastdb)
|
||||
dbg_log ("server %s is not supported\n", arg1);
|
||||
}
|
||||
else if (strcmp (entry, "negative-time-to-live") == 0)
|
||||
{
|
||||
if (strcmp (arg1, "passwd") == 0)
|
||||
set_neg_pwd_ttl (atol (arg2));
|
||||
else if (strcmp (arg1, "group") == 0)
|
||||
set_neg_grp_ttl (atol (arg2));
|
||||
else
|
||||
dbg_log (_("service %s is not supported"), arg1);
|
||||
for (cnt = 0; cnt < lastdb; ++cnt)
|
||||
if (strcmp (arg1, dbnames[cnt]) == 0)
|
||||
{
|
||||
dbs[cnt].negtimeout = atol (arg2);
|
||||
break;
|
||||
}
|
||||
if (cnt == lastdb)
|
||||
dbg_log ("server %s is not supported\n", arg1);
|
||||
}
|
||||
else if (strcmp (entry, "suggested-size") == 0)
|
||||
{
|
||||
if (strcmp (arg1, "passwd") == 0)
|
||||
set_pwd_modulo (atol (arg2));
|
||||
else if (strcmp (arg1, "group") == 0)
|
||||
set_grp_modulo (atol (arg2));
|
||||
else
|
||||
dbg_log (_("service %s is not supported"), arg1);
|
||||
}
|
||||
else if (strcmp (entry, "enable-cache") ==0)
|
||||
for (cnt = 0; cnt < lastdb; ++cnt)
|
||||
if (strcmp (arg1, dbnames[cnt]) == 0)
|
||||
{
|
||||
if (strcmp (arg1, "passwd") == 0
|
||||
&& strcmp (arg2, "no") == 0)
|
||||
disabled_passwd = 1;
|
||||
else if (strcmp (arg1, "group") == 0
|
||||
&& strcmp (arg2, "no") == 0)
|
||||
disabled_group = 1;
|
||||
else
|
||||
dbg_log (_("service %s is not supported"), arg1);
|
||||
dbs[cnt].module = atol (arg2);
|
||||
break;
|
||||
}
|
||||
if (cnt == lastdb)
|
||||
dbg_log ("server %s is not supported\n", arg1);
|
||||
}
|
||||
else if (strcmp (entry, "enable-cache") == 0)
|
||||
{
|
||||
for (cnt = 0; cnt < lastdb; ++cnt)
|
||||
if (strcmp (arg1, dbnames[cnt]) == 0)
|
||||
{
|
||||
if (strcmp (arg2, "no") == 0)
|
||||
dbs[cnt].enabled = 0;
|
||||
else if (strcmp (arg2, "yes") == 0)
|
||||
dbs[cnt].enabled = 1;
|
||||
break;
|
||||
}
|
||||
if (cnt == lastdb)
|
||||
dbg_log ("server %s is not supported\n", arg1);
|
||||
}
|
||||
else if (strcmp (entry, "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)
|
||||
{
|
||||
@ -137,7 +170,12 @@ nscd_parse_file (const char *fname)
|
||||
{
|
||||
int level = atoi (arg1);
|
||||
if (level > 0)
|
||||
debug_flag = level;
|
||||
debug_level = level;
|
||||
}
|
||||
else if (strcmp (entry, "threads") == 0)
|
||||
{
|
||||
if (nthreads == -1)
|
||||
nthreads = MAX (atol (arg1), lastdb);
|
||||
}
|
||||
else
|
||||
dbg_log (_("Unknown option: %s %s %s"), entry, arg1, arg2);
|
||||
|
@ -32,41 +32,36 @@
|
||||
|
||||
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,
|
||||
size_t buflen);
|
||||
|
||||
|
||||
int
|
||||
__nscd_getgrnam_r (const char *name, struct group *resultbuf, char *buffer,
|
||||
size_t buflen)
|
||||
{
|
||||
if (name == NULL)
|
||||
return 1;
|
||||
|
||||
return __nscd_getgr_r (name, GETGRBYNAME, resultbuf, buffer, buflen);
|
||||
return nscd_getgr_r (name, strlen (name) + 1, GETGRBYNAME, resultbuf,
|
||||
buffer, buflen);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
__nscd_getgrgid_r (gid_t gid, struct group *resultbuf, char *buffer,
|
||||
size_t buflen)
|
||||
{
|
||||
char *p = buffer;
|
||||
int plen;
|
||||
char buf[12];
|
||||
size_t n;
|
||||
|
||||
plen = __snprintf (buffer, buflen, "%d", gid);
|
||||
if (plen == -1)
|
||||
{
|
||||
__set_errno (ERANGE);
|
||||
return -1;
|
||||
}
|
||||
p = buffer + plen + 1;
|
||||
n = __snprintf (buf, sizeof (buf), "%d", gid) + 1;
|
||||
|
||||
return __nscd_getgr_r (buffer, GETGRBYGID, resultbuf, p, buflen - plen -1);
|
||||
return nscd_getgr_r (buf, n, GETGRBYGID, resultbuf, buffer, buflen);
|
||||
}
|
||||
|
||||
|
||||
/* Create a socket connected to a name. */
|
||||
static int
|
||||
nscd_open_socket (void)
|
||||
open_socket (void)
|
||||
{
|
||||
struct sockaddr_un addr;
|
||||
int sock;
|
||||
@ -91,16 +86,16 @@ nscd_open_socket (void)
|
||||
return sock;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
__nscd_getgr_r (const char *key, request_type type, struct group *resultbuf,
|
||||
char *buffer, size_t buflen)
|
||||
nscd_getgr_r (const char *key, size_t keylen, request_type type,
|
||||
struct group *resultbuf, char *buffer, size_t buflen)
|
||||
{
|
||||
int sock = nscd_open_socket ();
|
||||
int sock = open_socket ();
|
||||
request_header req;
|
||||
gr_response_header gr_resp;
|
||||
ssize_t nbytes;
|
||||
size_t maxiov;
|
||||
size_t sum;
|
||||
struct iovec vec[2];
|
||||
|
||||
if (sock == -1)
|
||||
{
|
||||
@ -110,16 +105,14 @@ __nscd_getgr_r (const char *key, request_type type, struct group *resultbuf,
|
||||
|
||||
req.version = NSCD_VERSION;
|
||||
req.type = type;
|
||||
req.key_len = strlen (key);
|
||||
nbytes = __write (sock, &req, sizeof (request_header));
|
||||
if (nbytes != sizeof (request_header))
|
||||
{
|
||||
__close (sock);
|
||||
return 1;
|
||||
}
|
||||
req.key_len = keylen;
|
||||
|
||||
nbytes = __write (sock, key, req.key_len);
|
||||
if (nbytes != req.key_len)
|
||||
vec[0].iov_base = &req;
|
||||
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);
|
||||
return 1;
|
||||
@ -142,118 +135,79 @@ __nscd_getgr_r (const char *key, request_type type, struct group *resultbuf,
|
||||
|
||||
if (gr_resp.found == 1)
|
||||
{
|
||||
struct iovec *vec;
|
||||
size_t *len;
|
||||
char *p = buffer;
|
||||
int nblocks;
|
||||
size_t total_len;
|
||||
uintptr_t align;
|
||||
|
||||
/* 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;
|
||||
size_t cnt;
|
||||
|
||||
/* Now allocate the buffer the array for the group members. We must
|
||||
align the pointer. */
|
||||
align = ((__alignof__ (char *) - (p - ((char *) 0)))
|
||||
& (__alignof__ (char *) - 1));
|
||||
if (align + (1 + gr_resp.gr_mem_len) * sizeof (char *) > buflen)
|
||||
if (buflen < (align + (1 + gr_resp.gr_mem_cnt) * sizeof (char *)
|
||||
+ gr_resp.gr_name_len + gr_resp.gr_passwd_len))
|
||||
{
|
||||
no_room:
|
||||
__set_errno (ERANGE);
|
||||
__close (sock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
p += align;
|
||||
resultbuf->gr_mem = (char **) p;
|
||||
p += (1 + gr_resp.gr_mem_len) * sizeof (char *);
|
||||
buflen -= align + (1 + gr_resp.gr_mem_len) * sizeof (char *);
|
||||
p += (1 + gr_resp.gr_mem_cnt) * 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. */
|
||||
size_t i;
|
||||
|
||||
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)
|
||||
{
|
||||
__set_errno (ERANGE);
|
||||
__close (sock);
|
||||
return -1;
|
||||
resultbuf->gr_mem[cnt] = p;
|
||||
total_len += len[cnt];
|
||||
p += len[cnt];
|
||||
}
|
||||
|
||||
vec[i].iov_base = resultbuf->gr_mem[i] = p;
|
||||
vec[i].iov_len = len[i];
|
||||
total_len += len[i];
|
||||
buflen -= len[i];
|
||||
p += len[i];
|
||||
*p++ = '\0';
|
||||
}
|
||||
if (total_len > buflen)
|
||||
goto no_room;
|
||||
|
||||
#ifdef UIO_MAXIOV
|
||||
maxiov = UIO_MAXIOV;
|
||||
#else
|
||||
maxiov = sysconf (_SC_UIO_MAXIOV);
|
||||
#endif
|
||||
|
||||
sum = 0;
|
||||
while (i > maxiov)
|
||||
{
|
||||
sum += __readv (sock, vec, maxiov);
|
||||
vec += maxiov;
|
||||
i -= maxiov;
|
||||
}
|
||||
|
||||
if (sum + __readv (sock, vec, i) != total_len)
|
||||
if (__read (sock, resultbuf->gr_mem[0], total_len) != total_len)
|
||||
{
|
||||
__close (sock);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
__close (sock);
|
||||
return 0;
|
||||
}
|
||||
|
302
nscd/nscd_gethst_r.c
Normal file
302
nscd/nscd_gethst_r.c
Normal 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;
|
||||
}
|
||||
}
|
@ -31,7 +31,7 @@
|
||||
|
||||
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,
|
||||
size_t buflen);
|
||||
|
||||
@ -42,7 +42,7 @@ __nscd_getpwnam_r (const char *name, struct passwd *resultbuf, char *buffer,
|
||||
if (name == NULL)
|
||||
return 1;
|
||||
|
||||
return __nscd_getpw_r (name, GETPWBYNAME, resultbuf, buffer, buflen);
|
||||
return nscd_getpw_r (name, GETPWBYNAME, resultbuf, buffer, buflen);
|
||||
}
|
||||
|
||||
int
|
||||
@ -60,12 +60,12 @@ __nscd_getpwuid_r (uid_t uid, struct passwd *resultbuf, char *buffer,
|
||||
}
|
||||
p = buffer + plen + 1;
|
||||
|
||||
return __nscd_getpw_r (buffer, GETPWBYUID, resultbuf, p, buflen - plen -1);
|
||||
return nscd_getpw_r (buffer, GETPWBYUID, resultbuf, p, buflen - plen - 1);
|
||||
}
|
||||
|
||||
/* Create a socket connected to a name. */
|
||||
static int
|
||||
nscd_open_socket (void)
|
||||
open_socket (void)
|
||||
{
|
||||
struct sockaddr_un addr;
|
||||
int sock;
|
||||
@ -91,10 +91,10 @@ nscd_open_socket (void)
|
||||
}
|
||||
|
||||
static int
|
||||
__nscd_getpw_r (const char *key, request_type type, struct passwd *resultbuf,
|
||||
nscd_getpw_r (const char *key, request_type type, struct passwd *resultbuf,
|
||||
char *buffer, size_t buflen)
|
||||
{
|
||||
int sock = nscd_open_socket ();
|
||||
int sock = open_socket ();
|
||||
request_header req;
|
||||
pw_response_header pw_resp;
|
||||
ssize_t nbytes;
|
||||
@ -107,7 +107,7 @@ __nscd_getpw_r (const char *key, request_type type, struct passwd *resultbuf,
|
||||
|
||||
req.version = NSCD_VERSION;
|
||||
req.type = type;
|
||||
req.key_len = strlen (key);
|
||||
req.key_len = strlen (key) + 1;
|
||||
nbytes = __write (sock, &req, sizeof (request_header));
|
||||
if (nbytes != sizeof (request_header))
|
||||
{
|
||||
@ -139,68 +139,42 @@ __nscd_getpw_r (const char *key, request_type type, struct passwd *resultbuf,
|
||||
|
||||
if (pw_resp.found == 1)
|
||||
{
|
||||
struct iovec vec[5];
|
||||
char *p = buffer;
|
||||
size_t total = (pw_resp.pw_name_len + pw_resp.pw_passwd_len
|
||||
+ pw_resp.pw_gecos_len + pw_resp.pw_dir_len
|
||||
+ pw_resp.pw_shell_len);
|
||||
|
||||
if (buflen < (pw_resp.pw_name_len + 1 + pw_resp.pw_passwd_len + 1
|
||||
+ pw_resp.pw_gecos_len + 1 + pw_resp.pw_dir_len + 1
|
||||
+ pw_resp.pw_shell_len + 1))
|
||||
if (buflen < total)
|
||||
{
|
||||
__set_errno (ERANGE);
|
||||
__close (sock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* get pw_name */
|
||||
vec[0].iov_base = p;
|
||||
vec[0].iov_len = pw_resp.pw_name_len;
|
||||
p += pw_resp.pw_name_len + 1;
|
||||
buflen -= (pw_resp.pw_name_len + 1);
|
||||
/* 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';
|
||||
/* Set the information we already have. */
|
||||
resultbuf->pw_uid = pw_resp.pw_uid;
|
||||
resultbuf->pw_gid = pw_resp.pw_gid;
|
||||
resultbuf->pw_gecos = vec[2].iov_base;
|
||||
resultbuf->pw_gecos[pw_resp.pw_gecos_len] = '\0';
|
||||
resultbuf->pw_dir = vec[3].iov_base;
|
||||
resultbuf->pw_dir[pw_resp.pw_dir_len] = '\0';
|
||||
resultbuf->pw_shell = vec[4].iov_base;
|
||||
resultbuf->pw_shell[pw_resp.pw_shell_len] = '\0';
|
||||
|
||||
/* 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;
|
||||
|
||||
nbytes = __read (sock, buffer, total);
|
||||
|
||||
__close (sock);
|
||||
return 0;
|
||||
|
||||
return nbytes == total ? 0 : 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -21,11 +21,13 @@
|
||||
#define _NSCD_PROTO_H 1
|
||||
|
||||
#include <grp.h>
|
||||
#include <netdb.h>
|
||||
#include <pwd.h>
|
||||
|
||||
/* Variables for communication between NSCD handler functions and NSS. */
|
||||
extern int __nss_not_use_nscd_passwd;
|
||||
extern int __nss_not_use_nscd_group;
|
||||
extern int __nss_not_use_nscd_hosts;
|
||||
|
||||
extern int __nscd_getpwnam_r __P ((const char *name, struct passwd *resultbuf,
|
||||
char *buffer, size_t buflen));
|
||||
@ -35,5 +37,17 @@ extern int __nscd_getgrnam_r __P ((const char *name, struct group *resultbuf,
|
||||
char *buffer, size_t buflen));
|
||||
extern int __nscd_getgrgid_r __P ((uid_t uid, struct group *resultbuf,
|
||||
char *buffer, size_t buflen));
|
||||
extern int __nscd_gethostbyname_r __P ((const char *name,
|
||||
struct hostent *resultbuf,
|
||||
char *buffer, size_t buflen,
|
||||
int *h_errnop));
|
||||
extern int __nscd_gethostbyname2_r __P ((const char *name, int af,
|
||||
struct hostent *resultbuf,
|
||||
char *buffer, size_t buflen,
|
||||
int *h_errnop));
|
||||
extern int __nscd_gethostbyaddr_r __P ((const char *addr, int len, int type,
|
||||
struct hostent *resultbuf,
|
||||
char *buffer, size_t buflen,
|
||||
int *h_errnop));
|
||||
|
||||
#endif /* _NSCD_PROTO_H */
|
||||
|
175
nscd/nscd_stat.c
175
nscd/nscd_stat.c
@ -17,71 +17,160 @@
|
||||
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
#include <errno.h>
|
||||
#include <error.h>
|
||||
#include <langinfo.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "nscd.h"
|
||||
#include "dbg_log.h"
|
||||
|
||||
/* We use this to make sure the receiver is the same. */
|
||||
static const char compilation[21] = __DATE__ " " __TIME__;
|
||||
|
||||
/* Statistic data for one database. */
|
||||
struct dbstat
|
||||
{
|
||||
int enabled;
|
||||
int check_file;
|
||||
size_t module;
|
||||
|
||||
unsigned long int postimeout;
|
||||
unsigned long int negtimeout;
|
||||
|
||||
unsigned long int poshit;
|
||||
unsigned long int neghit;
|
||||
unsigned long int posmiss;
|
||||
unsigned long int negmiss;
|
||||
};
|
||||
|
||||
/* Record for transmitting statistics. */
|
||||
struct statdata
|
||||
{
|
||||
char version[sizeof (compilation)];
|
||||
int debug_level;
|
||||
int ndbs;
|
||||
struct dbstat dbs[lastdb];
|
||||
};
|
||||
|
||||
|
||||
void
|
||||
print_stat (void)
|
||||
send_stats (int fd, struct database dbs[lastdb])
|
||||
{
|
||||
int sock = __nscd_open_socket ();
|
||||
request_header req;
|
||||
stat_response_header resp;
|
||||
ssize_t nbytes;
|
||||
struct statdata data;
|
||||
int cnt;
|
||||
|
||||
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);
|
||||
exit (EXIT_FAILURE);
|
||||
data.dbs[cnt].enabled = dbs[cnt].enabled;
|
||||
data.dbs[cnt].check_file = dbs[cnt].check_file;
|
||||
data.dbs[cnt].module = dbs[cnt].module;
|
||||
data.dbs[cnt].postimeout = dbs[cnt].postimeout;
|
||||
data.dbs[cnt].negtimeout = dbs[cnt].negtimeout;
|
||||
data.dbs[cnt].poshit = dbs[cnt].poshit;
|
||||
data.dbs[cnt].neghit = dbs[cnt].neghit;
|
||||
data.dbs[cnt].posmiss = dbs[cnt].posmiss;
|
||||
data.dbs[cnt].negmiss = dbs[cnt].negmiss;
|
||||
}
|
||||
|
||||
if (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.type = GETSTAT;
|
||||
req.key_len = 0;
|
||||
nbytes = write (sock, &req, sizeof (request_header));
|
||||
nbytes = TEMP_FAILURE_RETRY (write (fd, &req, sizeof (request_header)));
|
||||
if (nbytes != sizeof (request_header))
|
||||
{
|
||||
perror (_("write incomplete"));
|
||||
close (sock);
|
||||
exit (EXIT_FAILURE);
|
||||
int err = errno;
|
||||
close (fd);
|
||||
error (EXIT_FAILURE, err, _("write incomplete"));
|
||||
}
|
||||
|
||||
nbytes = read (sock, &resp, sizeof (stat_response_header));
|
||||
if (nbytes != sizeof (stat_response_header))
|
||||
/* Read as much data as we expect. */
|
||||
if (TEMP_FAILURE_RETRY (read (fd, &data, sizeof (data))) != sizeof (data)
|
||||
|| (memcmp (data.version, compilation, sizeof (compilation)) != 0
|
||||
/* Yes, this is an assignment! */
|
||||
&& errno == EINVAL))
|
||||
{
|
||||
perror (_("read incomplete"));
|
||||
close (sock);
|
||||
exit (EXIT_FAILURE);
|
||||
/* Not the right version. */
|
||||
int err = errno;
|
||||
close (fd);
|
||||
error (EXIT_FAILURE, err, _("cannot read statistics data"));
|
||||
}
|
||||
|
||||
close (sock);
|
||||
printf (_("nscd configuration:\n\n%15d server debug level\n"),
|
||||
data.debug_level);
|
||||
|
||||
printf (_("nscd configuration:\n\n"));
|
||||
printf (_("%12d server debug level\n\n"), resp.debug_level);
|
||||
for (i = 0; i < lastdb; ++i)
|
||||
{
|
||||
unsigned long int hit = data.dbs[i].poshit + data.dbs[i].neghit;
|
||||
unsigned long int all = hit + data.dbs[i].posmiss + data.dbs[i].negmiss;
|
||||
const char *enabled = nl_langinfo (data.dbs[i].enabled ? YESSTR : NOSTR);
|
||||
const char *check_file = nl_langinfo (data.dbs[i].check_file
|
||||
? YESSTR : NOSTR);
|
||||
|
||||
printf (_("passwd cache:\n\n"));
|
||||
printf (_("%12s cache is enabled\n"), resp.pw_enabled ? _("Yes") : _("No"));
|
||||
printf (_("%12ld cache hits on positive entries\n"), resp.pw_poshit);
|
||||
printf (_("%12ld cache hits on negative entries\n"), resp.pw_neghit);
|
||||
printf (_("%12ld cache misses on positive entries\n"), resp.pw_posmiss);
|
||||
printf (_("%12ld cache misses on negative entries\n"), resp.pw_negmiss);
|
||||
printf (_("%12ld suggested size\n"), resp.pw_size);
|
||||
printf (_("%12ld seconds time to live for positive entries\n"),
|
||||
resp.pw_posttl);
|
||||
printf (_("%12ld seconds time to live for negative entries\n\n"),
|
||||
resp.pw_negttl);
|
||||
if (enabled[0] == '\0')
|
||||
/* The locale does not provide this information so we have to
|
||||
translate it ourself. Since we should avoid short translation
|
||||
terms we artifically increase the length. */
|
||||
enabled = data.dbs[i].enabled ? _(" yes") : _(" no");
|
||||
if (check_file[0] == '\0')
|
||||
check_file = data.dbs[i].check_file ? _(" yes") : _(" no");
|
||||
|
||||
printf (_("group cache:\n\n"));
|
||||
printf (_("%12s cache is enabled\n"), resp.gr_enabled ? _("Yes") : _("No"));
|
||||
printf (_("%12ld cache hits on positive entries\n"), resp.gr_poshit);
|
||||
printf (_("%12ld cache hits on negative entries\n"), resp.gr_neghit);
|
||||
printf (_("%12ld cache misses on positive entries\n"), resp.gr_posmiss);
|
||||
printf (_("%12ld cache misses on negative entries\n"), resp.gr_negmiss);
|
||||
printf (_("%12ld suggested size\n"), resp.gr_size);
|
||||
printf (_("%12ld seconds time to live for positive entries\n"),
|
||||
resp.gr_posttl);
|
||||
printf (_("%12ld seconds time to live for negative entries\n"),
|
||||
resp.gr_negttl);
|
||||
if (all == 0)
|
||||
/* If nothing happened so far report a 0% hit rate. */
|
||||
all = 1;
|
||||
|
||||
printf (_("\n%s cache:\n\n"
|
||||
"%15s cache is enabled\n"
|
||||
"%15Zd suggested size\n"
|
||||
"%15ld seconds time to live for positive entries\n"
|
||||
"%15ld seconds time to live for negative entries\n"
|
||||
"%15ld cache hits on positive entries\n"
|
||||
"%15ld cache hits on negative entries\n"
|
||||
"%15ld cache misses on positive entries\n"
|
||||
"%15ld cache misses on negative entries\n"
|
||||
"%15ld%% cache hit rate\n"
|
||||
"%15s check /etc/%s for changes\n"),
|
||||
dbnames[i], enabled,
|
||||
data.dbs[i].module,
|
||||
data.dbs[i].postimeout, data.dbs[i].negtimeout,
|
||||
data.dbs[i].poshit, data.dbs[i].neghit,
|
||||
data.dbs[i].posmiss, data.dbs[i].negmiss,
|
||||
(100 * hit) / all,
|
||||
check_file, dbnames[i]);
|
||||
}
|
||||
|
||||
close (fd);
|
||||
|
||||
exit (0);
|
||||
}
|
||||
|
733
nscd/pwdcache.c
733
nscd/pwdcache.c
@ -1,6 +1,7 @@
|
||||
/* Copyright (c) 1998 Free Software Foundation, Inc.
|
||||
/* Cache handling for passwd lookup.
|
||||
Copyright (C) 1998 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1998.
|
||||
Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public License as
|
||||
@ -18,594 +19,228 @@
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
#include <errno.h>
|
||||
#include <malloc.h>
|
||||
#include <pthread.h>
|
||||
#include <error.h>
|
||||
#include <pwd.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <rpcsvc/nis.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "dbg_log.h"
|
||||
#include "nscd.h"
|
||||
#include "dbg_log.h"
|
||||
|
||||
static unsigned long int modulo = 211;
|
||||
static unsigned long int postimeout = 600;
|
||||
static unsigned long int negtimeout = 20;
|
||||
|
||||
static unsigned long int poshit = 0;
|
||||
static unsigned long int posmiss = 0;
|
||||
static unsigned long int neghit = 0;
|
||||
static unsigned long int negmiss = 0;
|
||||
|
||||
struct pwdhash
|
||||
/* This is the standard reply in case the service is disabled. */
|
||||
static const pw_response_header disabled =
|
||||
{
|
||||
time_t create;
|
||||
struct pwdhash *next;
|
||||
struct passwd *pwd;
|
||||
version: NSCD_VERSION,
|
||||
found: -1,
|
||||
pw_name_len: 0,
|
||||
pw_passwd_len: 0,
|
||||
pw_uid: -1,
|
||||
pw_gid: -1,
|
||||
pw_gecos_len: 0,
|
||||
pw_dir_len: 0,
|
||||
pw_shell_len: 0
|
||||
};
|
||||
typedef struct pwdhash pwdhash;
|
||||
|
||||
struct uidhash
|
||||
/* This is the struct describing how to write this record. */
|
||||
const struct iovec pwd_iov_disabled =
|
||||
{
|
||||
struct uidhash *next;
|
||||
struct passwd *pwptr;
|
||||
iov_base: (void *) &disabled,
|
||||
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;
|
||||
struct neghash *next;
|
||||
char *key;
|
||||
version: NSCD_VERSION,
|
||||
found: 0,
|
||||
pw_name_len: 0,
|
||||
pw_passwd_len: 0,
|
||||
pw_uid: -1,
|
||||
pw_gid: -1,
|
||||
pw_gecos_len: 0,
|
||||
pw_dir_len: 0,
|
||||
pw_shell_len: 0
|
||||
};
|
||||
typedef struct neghash neghash;
|
||||
|
||||
static pwdhash *pwdtbl;
|
||||
static uidhash *uidtbl;
|
||||
static neghash *negtbl;
|
||||
|
||||
static pthread_rwlock_t pwdlock = PTHREAD_RWLOCK_INITIALIZER;
|
||||
static pthread_rwlock_t neglock = PTHREAD_RWLOCK_INITIALIZER;
|
||||
|
||||
static void *pwdtable_update (void *);
|
||||
static void *negtable_update (void *);
|
||||
|
||||
void
|
||||
get_pw_stat (stat_response_header *stat)
|
||||
/* This is the struct describing how to write this record. */
|
||||
static const struct iovec iov_notfound =
|
||||
{
|
||||
stat->pw_poshit = poshit;
|
||||
stat->pw_posmiss = posmiss;
|
||||
stat->pw_neghit = neghit;
|
||||
stat->pw_negmiss = negmiss;
|
||||
stat->pw_size = modulo;
|
||||
stat->pw_posttl = postimeout;
|
||||
stat->pw_negttl = negtimeout;
|
||||
}
|
||||
iov_base: (void *) ¬found,
|
||||
iov_len: sizeof (notfound)
|
||||
};
|
||||
|
||||
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
|
||||
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;
|
||||
uidhash *uidwork;
|
||||
unsigned long int hash = __nis_hash (pwd->pw_name,
|
||||
strlen (pwd->pw_name)) % modulo;
|
||||
|
||||
if (debug_flag)
|
||||
dbg_log (_("pwd_add_cache (%s)"), pwd->pw_name);
|
||||
|
||||
work = &pwdtbl[hash];
|
||||
|
||||
if (pwdtbl[hash].pwd == NULL)
|
||||
pwdtbl[hash].pwd = save_pwd (pwd);
|
||||
else
|
||||
{
|
||||
while (work->next != NULL)
|
||||
work = work->next;
|
||||
|
||||
work->next = calloc (1, sizeof (pwdhash));
|
||||
work->next->pwd = save_pwd (pwd);
|
||||
work = work->next;
|
||||
}
|
||||
/* Set a pointer from the pwuid hash table to the pwname hash table */
|
||||
time (&work->create);
|
||||
uidwork = &uidtbl[pwd->pw_uid % modulo];
|
||||
if (uidwork->pwptr == NULL)
|
||||
uidwork->pwptr = work->pwd;
|
||||
else
|
||||
{
|
||||
while (uidwork->next != NULL)
|
||||
uidwork = uidwork->next;
|
||||
|
||||
uidwork->next = calloc (1, sizeof (uidhash));
|
||||
uidwork->next->pwptr = work->pwd;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct passwd *
|
||||
cache_search_name (const char *name)
|
||||
{
|
||||
pwdhash *work;
|
||||
unsigned long int hash = __nis_hash (name, strlen (name)) % modulo;
|
||||
|
||||
work = &pwdtbl[hash];
|
||||
|
||||
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);
|
||||
/* 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;
|
||||
|
||||
if (debug_flag)
|
||||
dbg_log (_("Doesn't found \"%s\" in cache !"), param->key);
|
||||
if (debug_level > 0)
|
||||
dbg_log (_("Haven't found \"%s\" in password cache!"), key);
|
||||
|
||||
pthread_rwlock_unlock (&pwdlock);
|
||||
|
||||
pthread_rwlock_rdlock (&neglock);
|
||||
status = cache_search_neg (param->key);
|
||||
pthread_rwlock_unlock (&neglock);
|
||||
|
||||
if (status == 0)
|
||||
{
|
||||
while (buffer != NULL
|
||||
&& (getpwnam_r (param->key, &resultbuf, buffer, buflen, &pwd)
|
||||
!= 0)
|
||||
while (getpwnam_r (key, &resultbuf, buffer, buflen, &pwd) != 0
|
||||
&& errno == ERANGE)
|
||||
{
|
||||
errno = 0;
|
||||
buflen += 1024;
|
||||
buffer = realloc (buffer, buflen);
|
||||
buflen += 256;
|
||||
buffer = alloca (buflen);
|
||||
}
|
||||
|
||||
if (buffer != NULL && pwd != NULL)
|
||||
{
|
||||
struct passwd *tmp;
|
||||
|
||||
++posmiss;
|
||||
pthread_rwlock_wrlock (&pwdlock);
|
||||
/* While we are waiting on the lock, somebody else could
|
||||
add this entry. */
|
||||
tmp = cache_search_name (param->key);
|
||||
if (tmp == NULL)
|
||||
add_cache (pwd);
|
||||
pthread_rwlock_unlock (&pwdlock);
|
||||
}
|
||||
else
|
||||
{
|
||||
++negmiss;
|
||||
pthread_rwlock_wrlock (&neglock);
|
||||
add_negcache (param->key);
|
||||
pthread_rwlock_unlock (&neglock);
|
||||
}
|
||||
}
|
||||
else
|
||||
++neghit;
|
||||
pw_send_answer (param->conn, pwd);
|
||||
close_socket (param->conn);
|
||||
if (buffer != NULL)
|
||||
free (buffer);
|
||||
}
|
||||
free (param->key);
|
||||
free (param);
|
||||
return NULL;
|
||||
cache_addpw (db, fd, req, key, pwd);
|
||||
}
|
||||
|
||||
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)
|
||||
dbg_log (_("\tpasswd cache is disabled\n"));
|
||||
if (debug_level > 0)
|
||||
dbg_log (_("Haven't found \"%d\" in password cache!"), uid);
|
||||
|
||||
pw_send_disabled (param->conn);
|
||||
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)
|
||||
while (getpwuid_r (uid, &resultbuf, buffer, buflen, &pwd) != 0
|
||||
&& errno == ERANGE)
|
||||
{
|
||||
errno = 0;
|
||||
buflen += 1024;
|
||||
buffer = realloc (buffer, buflen);
|
||||
buflen += 256;
|
||||
buffer = alloca (buflen);
|
||||
}
|
||||
|
||||
if (buffer != NULL && pwd != NULL)
|
||||
{
|
||||
struct passwd *tmp;
|
||||
|
||||
++posmiss;
|
||||
pthread_rwlock_wrlock (&pwdlock);
|
||||
/* While we are waiting on the lock, somebody else could
|
||||
add this entry. */
|
||||
tmp = cache_search_uid (uid);
|
||||
if (tmp == NULL)
|
||||
add_cache (pwd);
|
||||
pthread_rwlock_unlock (&pwdlock);
|
||||
}
|
||||
else
|
||||
{
|
||||
++negmiss;
|
||||
pthread_rwlock_wrlock (&neglock);
|
||||
add_negcache (param->key);
|
||||
pthread_rwlock_unlock (&neglock);
|
||||
}
|
||||
}
|
||||
else
|
||||
++neghit;
|
||||
|
||||
pw_send_answer (param->conn, pwd);
|
||||
close_socket (param->conn);
|
||||
if (buffer != NULL)
|
||||
free (buffer);
|
||||
}
|
||||
free (param->key);
|
||||
free (param);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *
|
||||
pwdtable_update (void *v)
|
||||
{
|
||||
time_t now;
|
||||
int i;
|
||||
|
||||
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;
|
||||
cache_addpw (db, fd, req, key, pwd);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
libc {
|
||||
GLIBC_2.0 {
|
||||
# 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_database_lookup; __nss_configure_lookup;
|
||||
}
|
||||
|
@ -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.
|
||||
|
||||
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. */
|
||||
save = errno;
|
||||
free (buffer);
|
||||
buffer = NULL;
|
||||
__set_errno (save);
|
||||
}
|
||||
buffer = new_buf;
|
||||
|
@ -19,7 +19,9 @@
|
||||
|
||||
#include <errno.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: *|
|
||||
|
@ -28,7 +28,6 @@ routines := fgetpwent getpw putpwent \
|
||||
|
||||
include ../Rules
|
||||
|
||||
# We can later add the names of other thread packages here.
|
||||
ifeq ($(have-thread-library),yes)
|
||||
|
||||
CFLAGS-getpwuid_r.c = -DUSE_NSCD=1
|
||||
|
@ -120,11 +120,12 @@ typedef union querybuf
|
||||
static enum nss_status getanswer_r (const querybuf *answer, int anslen,
|
||||
const char *qname, int qtype,
|
||||
struct hostent *result, char *buffer,
|
||||
size_t buflen, int *h_errnop);
|
||||
size_t buflen, int *errnop, int *h_errnop);
|
||||
|
||||
enum nss_status
|
||||
_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;
|
||||
int size, type, n;
|
||||
@ -141,7 +142,7 @@ _nss_dns_gethostbyname2_r (const char *name, int af, struct hostent *result,
|
||||
break;
|
||||
default:
|
||||
*h_errnop = NETDB_INTERNAL;
|
||||
__set_errno (EAFNOSUPPORT);
|
||||
*errnop = EAFNOSUPPORT;
|
||||
return NSS_STATUS_UNAVAIL;
|
||||
}
|
||||
|
||||
@ -160,26 +161,28 @@ _nss_dns_gethostbyname2_r (const char *name, int af, struct hostent *result,
|
||||
if (n < 0)
|
||||
{
|
||||
*h_errnop = h_errno;
|
||||
*errnop = errno;
|
||||
return errno == ECONNREFUSED ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND;
|
||||
}
|
||||
|
||||
return getanswer_r (&host_buffer, n, name, type, result, buffer, buflen,
|
||||
h_errnop);
|
||||
errnop, h_errnop);
|
||||
}
|
||||
|
||||
|
||||
enum nss_status
|
||||
_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;
|
||||
|
||||
if (_res.options & RES_USE_INET6)
|
||||
status = _nss_dns_gethostbyname2_r (name, AF_INET6, result, buffer,
|
||||
buflen, h_errnop);
|
||||
buflen, errnop, h_errnop);
|
||||
if (status == NSS_STATUS_NOTFOUND)
|
||||
status = _nss_dns_gethostbyname2_r (name, AF_INET, result, buffer,
|
||||
buflen, h_errnop);
|
||||
buflen, errnop, h_errnop);
|
||||
|
||||
return status;
|
||||
}
|
||||
@ -188,7 +191,7 @@ _nss_dns_gethostbyname_r (const char *name, struct hostent *result,
|
||||
enum nss_status
|
||||
_nss_dns_gethostbyaddr_r (const char *addr, int len, int af,
|
||||
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 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;
|
||||
break;
|
||||
default:
|
||||
__set_errno (EAFNOSUPPORT);
|
||||
*errnop = EAFNOSUPPORT;
|
||||
*h_errnop = NETDB_INTERNAL;
|
||||
return NSS_STATUS_UNAVAIL;
|
||||
}
|
||||
if (size != len)
|
||||
{
|
||||
__set_errno (EAFNOSUPPORT);
|
||||
*errnop = EAFNOSUPPORT;
|
||||
*h_errnop = NETDB_INTERNAL;
|
||||
return NSS_STATUS_UNAVAIL;
|
||||
}
|
||||
@ -256,14 +259,16 @@ _nss_dns_gethostbyaddr_r (const char *addr, int len, int af,
|
||||
if (n < 0)
|
||||
{
|
||||
*h_errnop = h_errno;
|
||||
*errnop = errno;
|
||||
return errno == ECONNREFUSED ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND;
|
||||
}
|
||||
|
||||
status = getanswer_r (&host_buffer, n, qbuf, T_PTR, result, buffer, buflen,
|
||||
h_errnop);
|
||||
errnop, h_errnop);
|
||||
if (status != NSS_STATUS_SUCCESS)
|
||||
{
|
||||
*h_errnop = h_errno;
|
||||
*errnop = errno;
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -292,7 +297,7 @@ _nss_dns_gethostbyaddr_r (const char *addr, int len, int af,
|
||||
static enum nss_status
|
||||
getanswer_r (const querybuf *answer, int anslen, const char *qname, int qtype,
|
||||
struct hostent *result, char *buffer, size_t buflen,
|
||||
int *h_errnop)
|
||||
int *errnop, int *h_errnop)
|
||||
{
|
||||
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);
|
||||
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;
|
||||
}
|
||||
return NSS_STATUS_UNAVAIL;
|
||||
}
|
||||
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 */
|
||||
if (n >= MAXHOSTNAMELEN)
|
||||
{
|
||||
__set_h_errno (NO_RECOVERY);
|
||||
*h_errnop = NO_RECOVERY;
|
||||
return NSS_STATUS_TRYAGAIN;
|
||||
}
|
||||
result->h_name = bp;
|
||||
|
@ -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.
|
||||
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
|
||||
_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. */
|
||||
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,
|
||||
sizeof (querybuf));
|
||||
if (anslen < 0)
|
||||
{
|
||||
/* Nothing found. */
|
||||
*errnop = errno;
|
||||
return (errno == ECONNREFUSED
|
||||
|| errno == EPFNOSUPPORT
|
||||
|| errno == EAFNOSUPPORT)
|
||||
? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND;
|
||||
}
|
||||
|
||||
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
|
||||
_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. */
|
||||
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,
|
||||
sizeof (querybuf));
|
||||
if (anslen < 0)
|
||||
{
|
||||
/* Nothing found. */
|
||||
*errnop = errno;
|
||||
return (errno == ECONNREFUSED
|
||||
|| errno == EPFNOSUPPORT
|
||||
|| errno == EAFNOSUPPORT)
|
||||
? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND;
|
||||
}
|
||||
|
||||
status = getanswer_r (&net_buffer, anslen, result, buffer, buflen, BYADDR);
|
||||
if (status == NSS_STATUS_SUCCESS)
|
||||
|
1
sysdeps/unix/sysv/linux/m68k/setegid.c
Normal file
1
sysdeps/unix/sysv/linux/m68k/setegid.c
Normal file
@ -0,0 +1 @@
|
||||
#include <sysdeps/unix/sysv/linux/i386/setegid.c>
|
1
sysdeps/unix/sysv/linux/m68k/seteuid.c
Normal file
1
sysdeps/unix/sysv/linux/m68k/seteuid.c
Normal file
@ -0,0 +1 @@
|
||||
#include <sysdeps/unix/sysv/linux/i386/seteuid.c>
|
1
sysdeps/unix/sysv/linux/m68k/setfsgid.c
Normal file
1
sysdeps/unix/sysv/linux/m68k/setfsgid.c
Normal file
@ -0,0 +1 @@
|
||||
#include <sysdeps/unix/sysv/linux/i386/setfsgid.c>
|
1
sysdeps/unix/sysv/linux/m68k/setfsuid.c
Normal file
1
sysdeps/unix/sysv/linux/m68k/setfsuid.c
Normal file
@ -0,0 +1 @@
|
||||
s
|
1
sysdeps/unix/sysv/linux/m68k/setgid.c
Normal file
1
sysdeps/unix/sysv/linux/m68k/setgid.c
Normal file
@ -0,0 +1 @@
|
||||
#include <sysdeps/unix/sysv/linux/i386/setgid.c>
|
1
sysdeps/unix/sysv/linux/m68k/setresgid.c
Normal file
1
sysdeps/unix/sysv/linux/m68k/setresgid.c
Normal file
@ -0,0 +1 @@
|
||||
#include <sysdeps/unix/sysv/linux/i386/setresgid.c>
|
1
sysdeps/unix/sysv/linux/m68k/setresuid.c
Normal file
1
sysdeps/unix/sysv/linux/m68k/setresuid.c
Normal file
@ -0,0 +1 @@
|
||||
#include <sysdeps/unix/sysv/linux/i386/setresuid.c>
|
1
sysdeps/unix/sysv/linux/m68k/setuid.c
Normal file
1
sysdeps/unix/sysv/linux/m68k/setuid.c
Normal file
@ -0,0 +1 @@
|
||||
#include <sysdeps/unix/sysv/linux/i386/setuid.c>
|
Loading…
Reference in New Issue
Block a user