aboutsummaryrefslogtreecommitdiff
path: root/nscd
diff options
context:
space:
mode:
Diffstat (limited to 'nscd')
-rw-r--r--nscd/Makefile46
-rw-r--r--nscd/TODO7
-rw-r--r--nscd/connections.c529
-rw-r--r--nscd/dbg_log.c64
-rw-r--r--nscd/dbg_log.h27
-rw-r--r--nscd/getgrgid_r.c30
-rw-r--r--nscd/getgrnam_r.c29
-rw-r--r--nscd/getpwnam_r.c30
-rw-r--r--nscd/getpwuid_r.c30
-rw-r--r--nscd/grpcache.c589
-rw-r--r--nscd/nscd.c423
-rw-r--r--nscd/nscd.conf30
-rw-r--r--nscd/nscd.h149
-rw-r--r--nscd/nscd.init50
-rw-r--r--nscd/nscd_conf.c148
-rw-r--r--nscd/nscd_getgr_r.c211
-rw-r--r--nscd/nscd_getpw_r.c198
-rw-r--r--nscd/nscd_proto.h35
-rw-r--r--nscd/nscd_stat.c87
-rw-r--r--nscd/pwdcache.c581
20 files changed, 3293 insertions, 0 deletions
diff --git a/nscd/Makefile b/nscd/Makefile
new file mode 100644
index 0000000000..4510c4d452
--- /dev/null
+++ b/nscd/Makefile
@@ -0,0 +1,46 @@
+# Copyright (C) 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
+# 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.
+
+#
+# Sub-makefile for nscd portion of the library.
+#
+subdir := nscd
+
+routines := nscd_getpw_r nscd_getgr_r
+
+# We can later add the names of other thread packages here.
+ifeq (,$(findstring linuxthreads,$(add-ons)))
+
+others := nscd
+install-sbin := nscd
+
+endif
+
+nscd-routines := nscd connections pwdcache getpwnam_r getpwuid_r grpcache\
+ getgrnam_r getgrgid_r dbg_log nscd_conf nscd_stat
+extra-objs := $(nscd-routines:=.o)
+
+distribute := nscd.h dbg_log.h
+
+include ../Rules
+
+ifeq ($(build-shared),yes)
+$(objpfx)nscd: $(nscd-routines:%=$(objpfx)%.o) $(objpfx)../linuxthreads/libpthread.so$(libpthread.so-version) $(objpfx)../nis/libnsl.so$(libnsl.so-version)
+else
+$(objpfx)nscd: $(nscd-routines:%=$(objpfx)%.o) $(objpfx)../linuxthreads/libpthread.a $(objpfx)../nis/libnsl.a
+endif
diff --git a/nscd/TODO b/nscd/TODO
new file mode 100644
index 0000000000..16c2835468
--- /dev/null
+++ b/nscd/TODO
@@ -0,0 +1,7 @@
+
+* We should use readv/writev for group entries, too
+
+* If we have reached the max. # of process, close accept socket.
+ ! THIS COULD CAUSE THE KERNEL TO HANG ! BE CAREFUL !
+
+* Implement cache for hosts
diff --git a/nscd/connections.c b/nscd/connections.c
new file mode 100644
index 0000000000..abde747a8a
--- /dev/null
+++ b/nscd/connections.c
@@ -0,0 +1,529 @@
+/* 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.
+
+ 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 <error.h>
+#include <fcntl.h>
+#include <libintl.h>
+#include <locale.h>
+#include <pthread.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.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. */
+static int sock[MAX_NUM_CONNECTIONS];
+static int socks_active;
+static fd_set read_set;
+static pthread_mutex_t sock_lock = PTHREAD_MUTEX_INITIALIZER;
+
+
+/* Cleanup. */
+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"), strerror (errno));
+
+ sock[i] = 0;
+ --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;
+ --socks_active;
+
+ pthread_mutex_unlock (&sock_lock);
+}
+
+/* Local rountine, 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;
+ FD_SET (sock[i], &read_set);
+ 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 simultainious 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 a selectset. */
+static int
+handle_new_request (fd_set read_selects, int **connp, request_header **reqp,
+ char **key)
+{
+ ssize_t nbytes;
+ int i;
+
+ if (debug_flag)
+ dbg_log ("handle_new_request");
+
+ /* Find the descriptor. */
+ for (i = 1; i < MAX_NUM_CONNECTIONS; ++i)
+ if (FD_ISSET(sock[i], &read_selects))
+ break;
+
+ 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);
+ FD_CLR (sock[i], &read_set);
+ 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);
+ FD_CLR (sock[i], &read_set);
+ 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 i, nr, done = 0;
+ fd_set read_selects;
+
+ 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)
+ {
+ /* Set up the socket descriptor mask for the select.
+ copy read_set into the local copy. */
+
+ FD_ZERO (&read_selects);
+ pthread_mutex_lock (&sock_lock);
+ for (i = 0; i < MAX_NUM_CONNECTIONS; ++i)
+ {
+ if (FD_ISSET (sock[i], &read_set))
+ FD_SET (sock[i], &read_selects);
+ }
+ pthread_mutex_unlock (&sock_lock);
+ /* Poll active connections using select(). */
+ nr = select (FD_SETSIZE, &read_selects, NULL, NULL, NULL);
+ if (nr <= 0)
+ {
+ perror (_("Select new reads"));
+ exit (1);
+ }
+ if (FD_ISSET (sock[0], &read_selects))
+ /* 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 (read_selects, &conn, &req, key) == 0)
+ {
+ FD_CLR (sock[*conn], &read_set);
+ done = 1;
+ }
+ }
+ } /* While not_done. */
+}
+
+void
+init_sockets (void)
+{
+ struct sockaddr_un sock_addr;
+
+ /* Initialize the connections db. */
+ socks_active = 0;
+ FD_ZERO (&read_set);
+
+ /* Create the socket. */
+ sock[0] = socket (AF_UNIX, SOCK_STREAM, 0);
+ if (sock[0] < 0)
+ {
+ perror (_("cannot create socket"));
+ 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)
+ {
+ 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)
+ {
+ perror (_("cannot enable socket to accept connections"));
+ exit (1);
+ }
+
+ /* Add the socket to the server's set of active sockets. */
+ FD_SET (sock[0], &read_set);
+ ++socks_active;
+}
+
+void
+pw_send_answer (int conn, struct passwd *pwd)
+{
+ pw_response_header resp;
+
+ resp.version = NSCD_VERSION;
+ if (pwd != NULL)
+ {
+ resp.found = 1;
+ resp.pw_name_len = strlen (pwd->pw_name);
+ resp.pw_passwd_len = strlen (pwd->pw_passwd);
+ resp.pw_uid = pwd->pw_uid;
+ resp.pw_gid = pwd->pw_gid;
+ resp.pw_gecos_len = strlen (pwd->pw_gecos);
+ resp.pw_dir_len = strlen (pwd->pw_dir);
+ resp.pw_shell_len = strlen (pwd->pw_shell);
+ }
+ else
+ {
+ resp.found = 0;
+ resp.pw_name_len = 0;
+ resp.pw_passwd_len = 0;
+ resp.pw_uid = -1;
+ resp.pw_gid = -1;
+ resp.pw_gecos_len = 0;
+ resp.pw_dir_len = 0;
+ resp.pw_shell_len = 0;
+ }
+ if (sock[conn] == 0)
+ {
+ dbg_log (_("bad connection id on send response [%d|%d]"),
+ conn, sock[conn]);
+ return;
+ }
+
+ /* Send response header. */
+ if (write (sock[conn], &resp, sizeof (pw_response_header)) !=
+ sizeof (pw_response_header))
+ {
+ dbg_log (_("write incomplete on send response: %s"), strerror (errno));
+ return;
+ }
+
+ if (resp.found)
+ {
+ struct iovec vec[5];
+
+ /* Send pw_name. */
+ vec[0].iov_base = pwd->pw_name;
+ vec[0].iov_len = resp.pw_name_len;
+ /* Send pw_passwd. */
+ vec[1].iov_base = pwd->pw_passwd;
+ vec[1].iov_len = resp.pw_passwd_len;
+ /* Send pw_gecos. */
+ vec[2].iov_base = pwd->pw_gecos;
+ vec[2].iov_len = resp.pw_gecos_len;
+ /* Send pw_dir. */
+ vec[3].iov_base = pwd->pw_dir;
+ vec[3].iov_len = resp.pw_dir_len;
+ /* Send pw_shell. */
+ vec[4].iov_base = pwd->pw_shell;
+ vec[4].iov_len = resp.pw_shell_len;
+
+ if (writev (sock[conn], vec, 5) != (resp.pw_name_len + resp.pw_passwd_len
+ + resp.pw_gecos_len + resp.pw_dir_len
+ + resp.pw_shell_len))
+ dbg_log (_("write incomplete on send passwd answer: %s"),
+ strerror (errno));
+ }
+}
+
+void
+pw_send_disabled (int conn)
+{
+ pw_response_header resp;
+
+ resp.version = NSCD_VERSION;
+ resp.found = -1;
+ resp.pw_name_len = 0;
+ resp.pw_passwd_len = 0;
+ resp.pw_uid = -1;
+ resp.pw_gid = -1;
+ resp.pw_gecos_len = 0;
+ resp.pw_dir_len = 0;
+ resp.pw_shell_len = 0;
+
+ if (sock[conn] == 0)
+ {
+ dbg_log ("bad connection id on send response [%d|%d]",
+ conn, sock[conn]);
+ return;
+ }
+
+ /* 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)
+{
+ gr_response_header resp;
+
+ 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;
+ }
+
+ /* Send response header. */
+ if (write (sock[conn], &resp, sizeof (gr_response_header))
+ != sizeof (gr_response_header))
+ {
+ dbg_log (_("write incomplete on send response: %s"), strerror (errno));
+ return;
+ }
+
+ if (resp.found)
+ {
+ unsigned int l = 0;
+
+ /* Send gr_name. */
+ if (write (sock[conn], grp->gr_name, resp.gr_name_len)
+ != resp.gr_name_len)
+ {
+ dbg_log (_("write incomplete on send response: %s"),
+ strerror (errno));
+ return;
+ }
+ /* Send gr_passwd. */
+ if (write (sock[conn], grp->gr_passwd, resp.gr_passwd_len)
+ != resp.gr_passwd_len)
+ {
+ dbg_log (_("write incomplete on send response: %s"),
+ strerror (errno));
+ return;
+ }
+
+ while (grp->gr_mem[l])
+ {
+ size_t len = strlen (grp->gr_mem[l]);
+
+ if (write (sock[conn], &len, sizeof (len)) != sizeof (len))
+ {
+ dbg_log (_("write incomplete on send response: %s"),
+ strerror (errno));
+ return;
+ }
+ if (write (sock[conn], grp->gr_mem[l], len) != len)
+ {
+ dbg_log (_("write incomplete on send response: %s"),
+ strerror (errno));
+ return;
+ }
+ ++l;
+ }
+ }
+}
+
+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));
+}
diff --git a/nscd/dbg_log.c b/nscd/dbg_log.c
new file mode 100644
index 0000000000..37065e446e
--- /dev/null
+++ b/nscd/dbg_log.c
@@ -0,0 +1,64 @@
+/* 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.
+
+ 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 <stdarg.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <unistd.h>
+#include "dbg_log.h"
+#include "nscd.h"
+
+/* if in debug mode and we have a debug file, we write the messages to it,
+ if in debug mode and no debug file, we write the messages to stderr,
+ else to syslog. */
+
+FILE *dbgout = NULL;
+int debug_flag = 0;
+
+int
+set_logfile (const char *logfile)
+{
+ dbgout = fopen (logfile, "a");
+ return dbgout == NULL ? 0 : 1;
+}
+
+void
+dbg_log (const char *fmt,...)
+{
+ va_list ap;
+ char msg[512], msg2[512];
+
+ va_start (ap, fmt);
+ vsnprintf (msg2, sizeof (msg), fmt, ap);
+
+ if (debug_flag)
+ {
+ snprintf (msg, sizeof (msg), "%d: %s\n", getpid (), msg2);
+ if (dbgout)
+ fputs (msg, dbgout);
+ else
+ fputs (msg, stderr);
+ }
+ else
+ {
+ snprintf (msg, sizeof (msg), "%d: %s", getpid (), msg2);
+ syslog (LOG_NOTICE, msg);
+ }
+ va_end (ap);
+}
diff --git a/nscd/dbg_log.h b/nscd/dbg_log.h
new file mode 100644
index 0000000000..c3d1dc4559
--- /dev/null
+++ b/nscd/dbg_log.h
@@ -0,0 +1,27 @@
+/* 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.
+
+ 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. */
+
+#ifndef _DBG_LOG_H
+#define _DBG_LOG_H 1
+
+extern int debug_flag;
+
+extern void dbg_log (const char *, ...);
+
+#endif
diff --git a/nscd/getgrgid_r.c b/nscd/getgrgid_r.c
new file mode 100644
index 0000000000..3011602671
--- /dev/null
+++ b/nscd/getgrgid_r.c
@@ -0,0 +1,30 @@
+/* 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 <grp.h>
+
+
+#define LOOKUP_TYPE struct group
+#define FUNCTION_NAME getgrgid
+#define DATABASE_NAME group
+#define ADD_PARAMS gid_t gid
+#define ADD_VARIABLES gid
+#define BUFLEN NSS_BUFLEN_GROUP
+
+#include <nss/getXXbyYY_r.c>
diff --git a/nscd/getgrnam_r.c b/nscd/getgrnam_r.c
new file mode 100644
index 0000000000..3575e74b1f
--- /dev/null
+++ b/nscd/getgrnam_r.c
@@ -0,0 +1,29 @@
+/* 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 <grp.h>
+
+
+#define LOOKUP_TYPE struct group
+#define FUNCTION_NAME getgrnam
+#define DATABASE_NAME group
+#define ADD_PARAMS const char *name
+#define ADD_VARIABLES name
+
+#include <nss/getXXbyYY_r.c>
diff --git a/nscd/getpwnam_r.c b/nscd/getpwnam_r.c
new file mode 100644
index 0000000000..328c3055f8
--- /dev/null
+++ b/nscd/getpwnam_r.c
@@ -0,0 +1,30 @@
+/* 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 <pwd.h>
+
+
+#define LOOKUP_TYPE struct passwd
+#define FUNCTION_NAME getpwnam
+#define DATABASE_NAME passwd
+#define ADD_PARAMS const char *name
+#define ADD_VARIABLES name
+#define BUFLEN NSS_BUFLEN_PASSWD
+
+#include <nss/getXXbyYY_r.c>
diff --git a/nscd/getpwuid_r.c b/nscd/getpwuid_r.c
new file mode 100644
index 0000000000..91bd802d61
--- /dev/null
+++ b/nscd/getpwuid_r.c
@@ -0,0 +1,30 @@
+/* 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 <pwd.h>
+
+
+#define LOOKUP_TYPE struct passwd
+#define FUNCTION_NAME getpwuid
+#define DATABASE_NAME passwd
+#define ADD_PARAMS uid_t uid
+#define ADD_VARIABLES uid
+#define BUFLEN NSS_BUFLEN_PASSWD
+
+#include <nss/getXXbyYY_r.c>
diff --git a/nscd/grpcache.c b/nscd/grpcache.c
new file mode 100644
index 0000000000..9f6c767fd7
--- /dev/null
+++ b/nscd/grpcache.c
@@ -0,0 +1,589 @@
+/* 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.
+
+ 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 <grp.h>
+#include <malloc.h>
+#include <pthread.h>
+#include <string.h>
+#include <rpcsvc/nis.h>
+#include <sys/types.h>
+
+#include "dbg_log.h"
+#include "nscd.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
+{
+ time_t create;
+ struct grphash *next;
+ struct group *grp;
+};
+typedef struct grphash grphash;
+
+struct gidhash
+{
+ struct gidhash *next;
+ struct grphash *grptr;
+};
+typedef struct gidhash gidhash;
+
+struct neghash
+{
+ time_t create;
+ struct neghash *next;
+ char *key;
+};
+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)
+{
+ 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;
+}
+
+void
+set_grp_modulo (unsigned long mod)
+{
+ modulo = mod;
+}
+
+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 (1, modulo * sizeof (grphash));
+ if (grptbl == NULL)
+ return -1;
+ calloc (1, modulo * sizeof (grphash));
+ if (gidtbl == NULL)
+ return -1;
+ negtbl = calloc (1, 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;
+
+ dest = calloc (1, sizeof (struct group));
+ dest->gr_name = strdup (src->gr_name);
+ dest->gr_passwd = strdup (src->gr_passwd);
+ dest->gr_gid = src->gr_gid;
+
+ /* How many members does this group have? */
+ l = 0;
+ while (src->gr_mem[l])
+ ++l;
+
+ dest->gr_mem = calloc (1, sizeof (char *) * (l+1));
+ l = 0;
+ while (src->gr_mem[l])
+ {
+ dest->gr_mem[l] = strdup (src->gr_mem[l]);
+ ++l;
+ }
+
+ return dest;
+}
+
+static void
+free_grp (struct group *src)
+{
+ unsigned long int l;
+
+ free (src->gr_name);
+ free (src->gr_passwd);
+
+ l = 0;
+ while (src->gr_mem[l])
+ {
+ free (src->gr_mem[l]);
+ ++l;
+ }
+ free (src->gr_mem);
+ free (src);
+}
+
+static int
+add_cache (struct group *grp)
+{
+ grphash *work;
+ unsigned long int hash = __nis_hash (grp->gr_name,
+ strlen (grp->gr_name)) % modulo;
+
+ work = &grptbl[hash];
+
+ if (grptbl[hash].grp == NULL)
+ grptbl[hash].grp = save_grp (grp);
+ else
+ {
+ while (work->next != NULL)
+ work = work->next;
+
+ work->next = calloc (1, sizeof (grphash));
+ work->next->grp = save_grp (grp);
+ work = work->next;
+ }
+
+ time (&work->create);
+ gidtbl[grp->gr_gid % modulo].grptr = work;
+
+ return 0;
+}
+
+static struct group *
+cache_search_name (const char *name)
+{
+ grphash *work;
+ unsigned long int hash = __nis_hash (name, strlen(name)) % modulo;
+
+ work = &grptbl[hash];
+
+ 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->grp->gr_gid == gid)
+ return work->grptr->grp;
+ 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;
+
+ work = &negtbl[hash];
+
+ if (negtbl[hash].key == NULL)
+ negtbl[hash].key = strdup (key);
+ 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;
+
+ 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_getgrnam (void *v_param)
+{
+ param_t *param = (param_t *)v_param;
+ struct group *grp, resultbuf;
+
+ pthread_rwlock_rdlock (&grplock);
+ grp = 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 (grp)
+ {
+ 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);
+ }
+ else
+ {
+ int buflen = 1024;
+ char *buffer = calloc (1, buflen);
+ int status;
+
+ if (debug_flag)
+ dbg_log (_("Doesn't found \"%s\" in cache !"), param->key);
+
+ pthread_rwlock_unlock (&grplock);
+
+ pthread_rwlock_rdlock (&neglock);
+ status = cache_search_neg (param->key);
+ pthread_rwlock_unlock (&neglock);
+
+ if (status == 0)
+ {
+ while (buffer != NULL
+ && (getgrnam_r (param->key, &resultbuf, buffer, buflen, &grp)
+ != 0)
+ && errno == ERANGE)
+ {
+ errno = 0;
+ buflen += 1024;
+ buffer = realloc (buffer, buflen);
+ }
+
+ if (buffer != NULL && grp != NULL)
+ {
+ struct group *tmp;
+
+ ++poshit;
+ pthread_rwlock_wrlock (&grplock);
+ /* While we are waiting on the lock, somebody else could
+ add this entry. */
+ tmp = cache_search_name (param->key);
+ if (tmp == NULL)
+ add_cache (grp);
+ pthread_rwlock_unlock (&grplock);
+ }
+ else
+ {
+ pthread_rwlock_wrlock (&neglock);
+ add_negcache (param->key);
+ ++negmiss;
+ pthread_rwlock_unlock (&neglock);
+ }
+ }
+ else
+ ++neghit;
+
+ gr_send_answer (param->conn, grp);
+ close_socket (param->conn);
+ if (buffer != NULL)
+ free (buffer);
+ }
+ free (param->key);
+ free (param);
+ return NULL;
+}
+
+void *
+cache_gr_disabled (void *v_param)
+{
+ param_t *param = (param_t *)v_param;
+
+ 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 !\n"), 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 !\n"), 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;
+}
+
+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->grp->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 (_("(pwdtable_update) Release wait lock\n"));
+ pthread_rwlock_unlock (&grplock);
+ sleep (20);
+ }
+ return NULL;
+}
+
+void *
+negtable_update (void *v)
+{
+ time_t now;
+ int i;
+
+ sleep (30);
+
+ while (!do_shutdown)
+ {
+ if (debug_flag > 2)
+ dbg_log (_("(negtable_update) Wait for write lock!"));
+
+ pthread_rwlock_wrlock (&neglock);
+
+ if (debug_flag > 2)
+ dbg_log (_("(negtable_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 (_("(negtable_update) Release wait lock"));
+ pthread_rwlock_unlock (&neglock);
+ sleep (10);
+ }
+ return NULL;
+}
diff --git a/nscd/nscd.c b/nscd/nscd.c
new file mode 100644
index 0000000000..59d4a60e02
--- /dev/null
+++ b/nscd/nscd.c
@@ -0,0 +1,423 @@
+/* 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.
+
+ 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. */
+
+/* nscd - Name Service Cache Daemon. Caches passwd and group. */
+
+#include <errno.h>
+#include <getopt.h>
+#include <libintl.h>
+#include <locale.h>
+#include <pthread.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "dbg_log.h"
+#include "nscd.h"
+
+/* Get libc version number. */
+#include <version.h>
+
+#define PACKAGE _libc_intl_domainname
+
+/* Structure used by main() thread to keep track of the number of
+ active threads. Used to limit how many threads it will create
+ and under a shutdown condition to wait till all in-progress
+ requests have finished before "turning off the lights". */
+
+typedef struct
+{
+ int num_active;
+ pthread_cond_t thread_exit_cv;
+ pthread_mutex_t mutex;
+} thread_info_t;
+
+thread_info_t thread_info;
+
+int do_shutdown = 0;
+int disabled_passwd = 0;
+int disabled_group = 0;
+
+static void termination_handler (int signum);
+static int check_pid (const char *file);
+static int write_pid (const char *file);
+static void usage (int status) __attribute__ ((noreturn));
+static void handle_requests (void);
+
+int
+main (int argc, char **argv)
+{
+ int go_background = 1;
+ const char *conffile = _PATH_NSCDCONF;
+
+ /* Set locale via LC_ALL. */
+ setlocale (LC_ALL, "");
+ /* Set the text message domain. */
+ textdomain (PACKAGE);
+
+ while (1)
+ {
+ int c;
+ int option_index = 0;
+ static struct option long_options[] = {
+ { "debug", no_argument, NULL, 'd' },
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'V' },
+ { "shutdown", no_argument, NULL, 'K' },
+ {NULL, 0, NULL, '\0'}
+ };
+
+ c = getopt_long (argc, argv, "df:ghKV", long_options, &option_index);
+ if (c == (-1))
+ break;
+ switch (c)
+ {
+ case 'd':
+ debug_flag = 1;
+ go_background = 0;
+ break;
+ case 'f':
+ conffile = optarg;
+ break;
+ case 'h':
+ usage (EXIT_SUCCESS);
+ break;
+ case 'K':
+ if (getuid () != 0)
+ {
+ printf (_("Only root is allowed to use this option!\n\n"));
+ usage (EXIT_FAILURE);
+ }
+ {
+ int sock = __nscd_open_socket ();
+ request_header req;
+ ssize_t nbytes;
+
+ if (sock == -1)
+ exit (EXIT_FAILURE);
+
+ req.version = NSCD_VERSION;
+ req.type = SHUTDOWN;
+ req.key_len = 0;
+ nbytes = write (sock, &req, sizeof (request_header));
+ close (sock);
+ if (nbytes != req.key_len)
+ exit (EXIT_FAILURE);
+ else
+ exit (EXIT_SUCCESS);
+ }
+ case 'g':
+ print_stat ();
+ exit (EXIT_SUCCESS);
+ case 'V':
+ printf ("nscd (GNU %s) %s\n", PACKAGE, VERSION);
+ printf (_("\
+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");
+ printf (_("Written by %s.\n"), "Thorsten Kukuk");
+ exit (EXIT_SUCCESS);
+ default:
+ usage (EXIT_FAILURE);
+ }
+ }
+
+ signal (SIGINT, termination_handler);
+ signal (SIGQUIT, termination_handler);
+ signal (SIGTERM, termination_handler);
+
+ /* Check if we are already running. */
+ if (check_pid (_PATH_NSCDPID))
+ {
+ fputs (_("already running"), stderr);
+ exit (EXIT_FAILURE);
+ }
+
+ /* Behave like a daemon. */
+ if (go_background)
+ {
+ openlog ("nscd", LOG_CONS | LOG_ODELAY, LOG_DAEMON);
+
+ if (daemon (0, 0) < 0)
+ {
+ fprintf (stderr, _("connot auto-background: %s\n"),
+ strerror (errno));
+ exit (EXIT_FAILURE);
+ }
+ if (write_pid (_PATH_NSCDPID) < 0)
+ dbg_log ("%s: %s", _PATH_NSCDPID, strerror (errno));
+
+ /* Ignore job control signals */
+ signal (SIGTTOU, SIG_IGN);
+ signal (SIGTTIN, SIG_IGN);
+ signal (SIGTSTP, SIG_IGN);
+ }
+ /* Cleanup files created by a previous `bind' */
+ unlink (_PATH_NSCDSOCKET);
+
+ nscd_parse_file (conffile);
+
+ /* Create first sockets */
+ init_sockets ();
+ /* Init databases */
+ cache_pwdinit ();
+ cache_grpinit ();
+ /* Handle incoming requests */
+ handle_requests ();
+
+ return 0;
+}
+
+/* Create a socket connected to a name. */
+int
+__nscd_open_socket (void)
+{
+ struct sockaddr_un addr;
+ int sock;
+
+ sock = socket (PF_UNIX, SOCK_STREAM, 0);
+ if (sock < 0)
+ return -1;
+
+ addr.sun_family = AF_UNIX;
+ strcpy (addr.sun_path, _PATH_NSCDSOCKET);
+ if (connect (sock, (struct sockaddr *) &addr, sizeof (addr)) < 0)
+ {
+ close (sock);
+ return -1;
+ }
+
+ return sock;
+}
+
+/* Cleanup. */
+static void
+termination_handler (int signum)
+{
+ close_sockets ();
+
+ /* Clean up the files created by `bind'. */
+ unlink (_PATH_NSCDSOCKET);
+
+ /* Clean up pid file. */
+ unlink (_PATH_NSCDPID);
+
+ exit (EXIT_SUCCESS);
+}
+
+/* Display usage information and exit. */
+static void
+usage (int status)
+{
+ if (status != EXIT_SUCCESS)
+ fprintf (stderr, _("Try `%s --help' for more information.\n"),
+ program_invocation_name);
+ else
+ {
+ printf (_("\
+Usage: %s [OPTION]...\n\
+ -d, --debug do not fork and display messages on the current tty\n\
+ -h, --help display this help and exit\n\
+ -V, --version output version information and exit\n\
+ -f configuration-file read configuration data from the specified file.\n\
+ -K, --shutdown shut the server down.\n\
+ -g Prints configuration and statistics to stdout.\n"),
+ program_invocation_name);
+ fputs (_("\
+Report bugs using the `glibcbug' script to <bugs@gnu.org>.\n"),
+ stdout);
+ }
+ exit (status);
+}
+
+/* Returns 1 if the process in pid file FILE is running, 0 if not. */
+static int
+check_pid (const char *file)
+{
+ FILE *fp;
+
+ fp = fopen (file, "r");
+ if (fp)
+ {
+ pid_t pid;
+
+ fscanf (fp, "%d", &pid);
+ fclose (fp);
+
+ if (kill (pid, 0) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Write the current process id to the file FILE.
+ Returns 0 if successful, -1 if not. */
+static int
+write_pid (const char *file)
+{
+ FILE *fp;
+
+ fp = fopen (file, "w");
+ if (fp == NULL)
+ return -1;
+
+ fprintf (fp, "%d\n", getpid ());
+ if (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);
+
+/* Hanlde 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;
+
+ 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;
+
+ if (debug_flag)
+ dbg_log ("\tGETPWBYNAME (%s)", key);
+ param->key = key;
+ param->conn = conn;
+ if (disabled_passwd)
+ pthread_create (&thread, NULL, cache_pw_disabled, (void *)param);
+ else
+ pthread_create (&thread, NULL, cache_getpwnam, (void *)param);
+ pthread_detach (thread);
+ }
+ break;
+ case GETPWBYUID:
+ {
+ param_t *param = malloc (sizeof (param_t));
+ pthread_t thread;
+
+ if (debug_flag)
+ dbg_log ("\tGETPWBYUID (%s)", key);
+ param->key = key;
+ param->conn = conn;
+ if (disabled_passwd)
+ pthread_create (&thread, NULL, cache_pw_disabled, (void *)param);
+ else
+ pthread_create (&thread, NULL, cache_getpwuid, (void *)param);
+ pthread_detach (thread);
+ }
+ break;
+ case GETGRBYNAME:
+ {
+ param_t *param = malloc (sizeof (param_t));
+ pthread_t thread;
+
+ if (debug_flag)
+ dbg_log ("\tGETGRBYNAME (%s)", key);
+ param->key = key;
+ param->conn = conn;
+ if (disabled_group)
+ pthread_create (&thread, NULL, cache_gr_disabled, (void *)param);
+ else
+ pthread_create (&thread, NULL, cache_getgrnam, (void *)param);
+ pthread_detach (thread);
+ }
+ break;
+ case GETGRBYGID:
+ {
+ param_t *param = malloc (sizeof (param_t));
+ pthread_t thread;
+
+ if (debug_flag)
+ dbg_log ("\tGETGRBYGID (%s)", key);
+ param->key = key;
+ param->conn = conn;
+ if (disabled_group)
+ pthread_create (&thread, NULL, cache_gr_disabled, (void *)param);
+ else
+ pthread_create (&thread, NULL, cache_getgrgid, (void *)param);
+ pthread_detach (thread);
+ }
+ 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;
+ }
+ }
+}
diff --git a/nscd/nscd.conf b/nscd/nscd.conf
new file mode 100644
index 0000000000..5d8c7f31ac
--- /dev/null
+++ b/nscd/nscd.conf
@@ -0,0 +1,30 @@
+#
+# /etc/nscd.conf
+#
+# An example Name Service Cache config file. This file is needed by nscd.
+#
+# Legal entries are:
+#
+# logfile <file>
+# enable-cache <service> <yes|no>
+# debug-level <level>
+# positive-time-to-live <service> <time in seconds>
+# negative-time-to-live <service> <time in seconds>
+# suggested-size <service> <prime number>
+#
+# Currently supported cache names (services): passwd, group
+#
+
+
+# logfile /var/adm/nscd.log
+# enable-cache hosts no
+
+ debug-level 0
+
+ positive-time-to-live passwd 600
+ negative-time-to-live passwd 20
+ suggested-size passwd 211
+
+ positive-time-to-live group 3600
+ negative-time-to-live group 60
+ suggested-size group 211
diff --git a/nscd/nscd.h b/nscd/nscd.h
new file mode 100644
index 0000000000..4835542619
--- /dev/null
+++ b/nscd/nscd.h
@@ -0,0 +1,149 @@
+/* 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.
+
+ 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. */
+
+#ifndef _NSCD_H
+#define _NSCD_H 1
+
+#include <grp.h>
+#include <pwd.h>
+
+/* Version number of the daemon interface */
+#define NSCD_VERSION 1
+
+/* How many threads do we spawn maximal ? */
+#define MAX_NUM_CONNECTIONS 16
+
+/* Services provided */
+typedef enum
+{
+ GETPWBYNAME,
+ GETPWBYUID,
+ GETGRBYNAME,
+ GETGRBYGID,
+ GETHOSTBYNAME,
+ GETHOSTBYADDR,
+ SHUTDOWN, /* Shut the server down */
+ GETSTAT /* Get the server statistic */
+} request_type;
+
+/* 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;
+} request_header;
+
+typedef struct
+{
+ int version;
+ int found;
+ ssize_t pw_name_len;
+ ssize_t pw_passwd_len;
+ uid_t pw_uid;
+ gid_t pw_gid;
+ ssize_t pw_gecos_len;
+ ssize_t pw_dir_len;
+ ssize_t pw_shell_len;
+} pw_response_header;
+
+typedef struct
+{
+ int version;
+ int found;
+ ssize_t gr_name_len;
+ ssize_t gr_passwd_len;
+ gid_t gr_gid;
+ ssize_t gr_mem_len;
+} gr_response_header;
+
+typedef struct
+{
+ int debug_level;
+ int pw_enabled;
+ unsigned long pw_poshit;
+ unsigned long pw_posmiss;
+ unsigned long pw_neghit;
+ unsigned long pw_negmiss;
+ unsigned long pw_size;
+ unsigned long pw_posttl;
+ unsigned long pw_negttl;
+ int gr_enabled;
+ unsigned long gr_poshit;
+ unsigned long gr_posmiss;
+ unsigned long gr_neghit;
+ unsigned long gr_negmiss;
+ unsigned long gr_size;
+ unsigned long gr_posttl;
+ unsigned long gr_negttl;
+} stat_response_header;
+
+#define _PATH_NSCDPID "/var/run/nscd.pid"
+#define _PATH_NSCDSOCKET "/var/run/.nscd_socket"
+#define _PATH_NSCDCONF "/etc/nscd.conf"
+
+typedef struct
+{
+ char *key;
+ int conn;
+} param_t;
+
+extern int do_shutdown; /* 1 if we should quit the programm. */
+extern int disabled_passwd;
+extern int disabled_group;
+
+extern int nscd_parse_file __P ((const char *fname));
+extern int set_logfile __P ((const char *logfile));
+extern void set_pos_pwd_ttl __P ((unsigned long));
+extern void set_neg_pwd_ttl __P ((unsigned long));
+extern void set_pos_grp_ttl __P ((unsigned long));
+extern void set_neg_grp_ttl __P ((unsigned long));
+extern void set_pwd_modulo __P ((unsigned long));
+extern void set_grp_modulo __P ((unsigned long));
+
+extern void init_sockets __P ((void));
+extern void close_socket __P ((int conn));
+extern void close_sockets __P ((void));
+extern void get_request __P ((int *conn, request_header *req, char **key));
+extern void pw_send_answer __P ((int conn, struct passwd *pwd));
+extern void pw_send_disabled __P ((int conn));
+extern void gr_send_answer __P ((int conn, struct group *grp));
+extern void gr_send_disabled __P ((int conn));
+
+extern int cache_pwdinit __P ((void));
+extern void *cache_getpwnam __P ((void *param));
+extern void *cache_getpwuid __P ((void *param));
+extern void *cache_pw_disabled __P ((void *param));
+
+extern int cache_grpinit __P ((void));
+extern void *cache_getgrnam __P ((void *param));
+extern void *cache_getgrgid __P ((void *param));
+extern void *cache_gr_disabled __P ((void *param));
+
+extern int __nscd_open_socket __P ((void));
+
+extern void get_pw_stat __P ((stat_response_header *resp));
+extern void get_gr_stat __P ((stat_response_header *resp));
+extern void print_stat __P ((void));
+extern void stat_send __P ((int conn, stat_response_header *resp));
+
+#endif
diff --git a/nscd/nscd.init b/nscd/nscd.init
new file mode 100644
index 0000000000..097ce42c99
--- /dev/null
+++ b/nscd/nscd.init
@@ -0,0 +1,50 @@
+#!/bin/sh
+#
+# nscd: Starts the Name Switch Cache Daemon
+#
+# chkconfig: 345 52 25
+# description: This is a daemon which handles passwd and group lookups
+# for running programs and cache the results for the next
+# query. You should start this daemon only if you use
+# slow Services like NIS or NIS+
+
+# Source function library.
+. /etc/rc.d/init.d/functions
+
+# See how we were called.
+case "$1" in
+ start)
+ test -f /etc/nscd.conf -a -f /usr/sbin/nscd || exit 0
+ secure=""
+# for table in passwd group
+# do
+# if egrep '^'$table':.*nisplus' /etc/nsswitch.conf >/dev/null
+# then
+# /usr/lib/nscd_nischeck $table ||
+# secure="$secure -S $table,yes"
+# fi
+# done
+ echo -n "Starting Name Switch Cache Daemon: "
+ daemon nscd $secure
+ echo
+ touch /var/lock/subsys/nscd
+ ;;
+ stop)
+ test -f /usr/sbin/nscd || exit 0
+ echo -n "Stopping Name Switch Cache Daemon: "
+ /usr/sbin/nscd -K
+ rm -f /var/lock/subsys/nscd
+ echo nscd
+ ;;
+ status)
+ status nscd
+ ;;
+ restart)
+ $0 stop
+ $0 start
+ ;;
+ *)
+ echo "Usage: /etc/rc.d/init.d/nscd.init {start|stop|status|restart}"
+ ;;
+esac
+exit 0
diff --git a/nscd/nscd_conf.c b/nscd/nscd_conf.c
new file mode 100644
index 0000000000..59c225e566
--- /dev/null
+++ b/nscd/nscd_conf.c
@@ -0,0 +1,148 @@
+/* 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.
+
+ 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 <malloc.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "dbg_log.h"
+#include "nscd.h"
+
+int
+nscd_parse_file (const char *fname)
+{
+ FILE *fp;
+ char *line, *cp, *entry, *arg1, *arg2;
+ size_t len;
+
+ /* Open the configuration file. */
+ fp = fopen (fname, "r");
+ if (fp == NULL)
+ return -1;
+
+ line = NULL;
+ len = 0;
+
+ do
+ {
+ ssize_t n = getline (&line, &len, fp);
+ if (n < 0)
+ break;
+ if (line[n - 1] == '\n')
+ line[n - 1] = '\0';
+
+ /* Because the file format does not know any form of quoting we
+ can search forward for the next '#' character and if found
+ make it terminating the line. */
+ cp = strchr (line, '#');
+ if (cp != NULL)
+ *cp = '\0';
+
+ /* If the line is blank it is ignored. */
+ if (line[0] == '\0')
+ continue;
+
+ entry = line;
+ while (isspace (*entry) && *entry != '\0')
+ ++entry;
+ cp = entry;
+ while (!isspace (*cp) && *cp != '\0')
+ ++cp;
+ arg1 = cp;
+ ++arg1;
+ *cp = '\0';
+ if (*cp = '\0' || strlen (entry) == 0)
+ dbg_log (_("Parse error: %s"), line);
+ while (isspace (*arg1) && *arg1 != '\0')
+ ++arg1;
+ cp = arg1;
+ while (!isspace (*cp) && *cp != '\0')
+ ++cp;
+ arg2 = cp;
+ ++arg2;
+ *cp = '\0';
+ if (strlen (arg2) > 0)
+ {
+ while (isspace (*arg2) && *arg2 != '\0')
+ ++arg2;
+ cp = arg2;
+ while (!isspace (*cp) && *cp != '\0')
+ ++cp;
+ *cp = '\0';
+ }
+
+ 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
+ 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);
+ }
+ 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)
+ {
+ if (strcmp (arg1, "passwd") == 0
+ && strcmp (arg2, "no") == 0)
+ disabled_passwd = 1;
+ }
+ else if (strcmp (entry, "logfile") == 0)
+ {
+ if (!set_logfile (arg1))
+ dbg_log (_("Could not create log file \"%s\""), arg1);
+ }
+ else if (strcmp (entry, "debug-level") == 0)
+ {
+ int level = atoi (arg1);
+ if (level > 0)
+ debug_flag = level;
+ }
+ else
+ dbg_log (_("Unknown option: %s %s %s"), entry, arg1, arg2);
+ }
+ while (!feof (fp));
+
+ /* Free the buffer. */
+ free (line);
+ /* Close configuration file. */
+ fclose (fp);
+
+ return 0;
+}
diff --git a/nscd/nscd_getgr_r.c b/nscd/nscd_getgr_r.c
new file mode 100644
index 0000000000..6739657e48
--- /dev/null
+++ b/nscd/nscd_getgr_r.c
@@ -0,0 +1,211 @@
+/* Copyright (C) 1998 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Thorsten Kukuk <kukuk@uni-paderborn.de>, 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 <grp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "nscd.h"
+#include "nscd_proto.h"
+
+static int __nscd_getgr_r (const char *key, 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);
+}
+
+int
+__nscd_getgrgid_r (gid_t gid, struct group *resultbuf, char *buffer,
+ size_t buflen)
+{
+ char *p = buffer;
+ char plen;
+
+ plen = snprintf (buffer, buflen, "%d", gid);
+ if (plen == -1)
+ {
+ __set_errno (ERANGE);
+ return -1;
+ }
+ p = buffer + plen + 1;
+
+ return __nscd_getgr_r (buffer, GETGRBYGID, resultbuf, p, buflen - plen -1);
+}
+
+/* Create a socket connected to a name. */
+static int
+nscd_open_socket (void)
+{
+ struct sockaddr_un addr;
+ int sock;
+
+ sock = socket (PF_UNIX, SOCK_STREAM, 0);
+ if (sock < 0)
+ return -1;
+
+ addr.sun_family = AF_UNIX;
+ strcpy (addr.sun_path, _PATH_NSCDSOCKET);
+ if (connect (sock, (struct sockaddr *) &addr, sizeof (addr)) < 0)
+ {
+ close (sock);
+ return -1;
+ }
+
+ return sock;
+}
+
+static int
+__nscd_getgr_r (const char *key, request_type type, struct group *resultbuf,
+ char *buffer, size_t buflen)
+{
+ int sock = nscd_open_socket ();
+ request_header req;
+ gr_response_header gr_resp;
+ ssize_t nbytes;
+
+ if (sock == -1)
+ return 1;
+
+ 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;
+ }
+
+ nbytes = write (sock, key, req.key_len);
+ if (nbytes != req.key_len)
+ {
+ close (sock);
+ return 1;
+ }
+
+ nbytes = read (sock, &gr_resp, sizeof (gr_response_header));
+ if (nbytes != sizeof (gr_response_header))
+ {
+ close (sock);
+ return 1;
+ }
+
+ if (gr_resp.found == -1)
+ {
+ close (sock);
+ return 1;
+ }
+
+ if (gr_resp.found == 1)
+ {
+ size_t i;
+ char *p = buffer;
+
+ if (buflen < gr_resp.gr_name_len + 1)
+ {
+ __set_errno (ERANGE);
+ close (sock);
+ return -1;
+ }
+ resultbuf->gr_name = p;
+ p += gr_resp.gr_name_len + 1;
+ buflen -= (gr_resp.gr_name_len + 1);
+ nbytes = read (sock, resultbuf->gr_name, gr_resp.gr_name_len);
+ if (nbytes != gr_resp.gr_name_len)
+ {
+ close (sock);
+ return 1;
+ }
+ resultbuf->gr_name[gr_resp.gr_name_len] = '\0';
+
+ if (buflen < gr_resp.gr_passwd_len + 1)
+ {
+ __set_errno (ERANGE);
+ close (sock);
+ return -1;
+ }
+ resultbuf->gr_passwd = p;
+ p += gr_resp.gr_passwd_len + 1;
+ buflen -= (gr_resp.gr_passwd_len + 1);
+ nbytes = read (sock, resultbuf->gr_passwd, gr_resp.gr_passwd_len);
+ if (nbytes != gr_resp.gr_passwd_len)
+ {
+ close (sock);
+ return 1;
+ }
+ resultbuf->gr_passwd[gr_resp.gr_passwd_len] = '\0';
+
+ resultbuf->gr_gid = gr_resp.gr_gid;
+
+ if (buflen < ((gr_resp.gr_mem_len + 1) * sizeof (char *)))
+ {
+ __set_errno (ERANGE);
+ close (sock);
+ return -1;
+ }
+ resultbuf->gr_mem = (char **)p;
+ p += ((gr_resp.gr_mem_len + 1) * sizeof (char *));
+ buflen -= ((gr_resp.gr_mem_len + 1) * sizeof (char *));
+
+ resultbuf->gr_mem[gr_resp.gr_mem_len] = NULL;
+
+ for (i = 0; i < gr_resp.gr_mem_len; ++i)
+ {
+ size_t len;
+ nbytes = read (sock, &len, sizeof (len));
+ if (nbytes != sizeof (len))
+ {
+ close (sock);
+ return 1;
+ }
+
+ if (buflen < (len + 1))
+ {
+ __set_errno (ERANGE);
+ close (sock);
+ return -1;
+ }
+ resultbuf->gr_mem[i] = p;
+ p += len + 1;
+ buflen -= (len + 1);
+ nbytes = read (sock, resultbuf->gr_mem[i], len);
+ resultbuf->gr_mem[i][len] = '\0';
+ if (nbytes != len)
+ {
+ close (sock);
+ return 1;
+ }
+ }
+ }
+ close (sock);
+ return 0;
+}
diff --git a/nscd/nscd_getpw_r.c b/nscd/nscd_getpw_r.c
new file mode 100644
index 0000000000..c956abc3b4
--- /dev/null
+++ b/nscd/nscd_getpw_r.c
@@ -0,0 +1,198 @@
+/* Copyright (C) 1998 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Thorsten Kukuk <kukuk@uni-paderborn.de>, 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 <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+
+#include "nscd.h"
+
+static int __nscd_getpw_r (const char *key, request_type type,
+ struct passwd *resultbuf, char *buffer,
+ size_t buflen);
+
+int
+__nscd_getpwnam_r (const char *name, struct passwd *resultbuf, char *buffer,
+ size_t buflen)
+{
+ if (name == NULL)
+ return 1;
+
+ return __nscd_getpw_r (name, GETPWBYNAME, resultbuf, buffer, buflen);
+}
+
+int
+__nscd_getpwuid_r (uid_t uid, struct passwd *resultbuf, char *buffer,
+ size_t buflen)
+{
+ char *p = buffer;
+ char plen;
+
+ plen = snprintf (buffer, buflen, "%d", uid);
+ if (plen == -1)
+ {
+ __set_errno (ERANGE);
+ return -1;
+ }
+ p = buffer + 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)
+{
+ struct sockaddr_un addr;
+ int sock;
+
+ sock = socket (PF_UNIX, SOCK_STREAM, 0);
+ if (sock < 0)
+ return -1;
+
+ addr.sun_family = AF_UNIX;
+ strcpy (addr.sun_path, _PATH_NSCDSOCKET);
+ if (connect (sock, (struct sockaddr *) &addr, sizeof (addr)) < 0)
+ {
+ close (sock);
+ return -1;
+ }
+
+ return sock;
+}
+
+static int
+__nscd_getpw_r (const char *key, request_type type, struct passwd *resultbuf,
+ char *buffer, size_t buflen)
+{
+ int sock = nscd_open_socket ();
+ request_header req;
+ pw_response_header pw_resp;
+ ssize_t nbytes;
+
+ if (sock == -1)
+ return 1;
+
+ 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;
+ }
+
+ nbytes = write (sock, key, req.key_len);
+ if (nbytes != req.key_len)
+ {
+ close (sock);
+ return 1;
+ }
+
+ nbytes = read (sock, &pw_resp, sizeof (pw_response_header));
+ if (nbytes != sizeof (pw_response_header))
+ {
+ close (sock);
+ return 1;
+ }
+
+ if (pw_resp.found == -1)
+ {
+ close (sock);
+ return 1;
+ }
+
+ if (pw_resp.found == 1)
+ {
+ struct iovec vec[5];
+ char *p = buffer;
+
+ 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)
+ {
+ __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_dir_len;
+ p += pw_resp.pw_dir_len + 1;
+ buflen -= (pw_resp.pw_dir_len + 1);
+
+ nbytes = readv (sock, vec, 5);
+ if (nbytes != 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)
+ {
+ close (sock);
+ return 1;
+ }
+
+ resultbuf->pw_name = vec[0].iov_base;
+ resultbuf->pw_name[pw_resp.pw_name_len] = '\0';
+ resultbuf->pw_passwd = vec[1].iov_base;
+ resultbuf->pw_passwd[pw_resp.pw_passwd_len] = '\0';
+ resultbuf->pw_uid = pw_resp.pw_uid;
+ resultbuf->pw_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';
+
+ close (sock);
+ return 0;
+ }
+ else
+ {
+ close (sock);
+ return -1;
+ }
+}
diff --git a/nscd/nscd_proto.h b/nscd/nscd_proto.h
new file mode 100644
index 0000000000..6f7b30df88
--- /dev/null
+++ b/nscd/nscd_proto.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 1998 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Thorsten Kukuk <kukuk@uni-paderborn.de>, 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. */
+
+#ifndef _NSCD_PROTO_H
+#define _NSCD_PROTO_H 1
+
+#include <grp.h>
+#include <pwd.h>
+
+extern int __nscd_getpwnam_r __P ((const char *name, struct passwd *resultbuf,
+ char *buffer, size_t buflen));
+extern int __nscd_getpwuid_r __P ((uid_t uid, struct passwd *resultbuf,
+ char *buffer, size_t buflen));
+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));
+
+#endif /* _NSCD_PROTO_H */
diff --git a/nscd/nscd_stat.c b/nscd/nscd_stat.c
new file mode 100644
index 0000000000..d8182885ac
--- /dev/null
+++ b/nscd/nscd_stat.c
@@ -0,0 +1,87 @@
+/* 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.
+
+ 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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include "nscd.h"
+
+void
+print_stat (void)
+{
+ int sock = __nscd_open_socket ();
+ request_header req;
+ stat_response_header resp;
+ ssize_t nbytes;
+
+ if (sock == -1)
+ {
+ fputs (_("nscd not running!\n"), stdout);
+ exit (EXIT_FAILURE);
+ }
+
+ req.version = NSCD_VERSION;
+ req.type = GETSTAT;
+ req.key_len = 0;
+ nbytes = write (sock, &req, sizeof (request_header));
+ if (nbytes != sizeof (request_header))
+ {
+ perror (_("write incomplete"));
+ close (sock);
+ exit (EXIT_FAILURE);
+ }
+
+ nbytes = read (sock, &resp, sizeof (stat_response_header));
+ if (nbytes != sizeof (stat_response_header))
+ {
+ perror (_("read incomplete"));
+ close (sock);
+ exit (EXIT_FAILURE);
+ }
+
+ close (sock);
+
+ printf (_("nscd configuration:\n\n"));
+ printf (_("%12d server debug level\n\n"), resp.debug_level);
+
+ printf (_("passwd cache:\n\n"));
+ printf (_("%12s cache is enabled\n"), resp.pw_enabled ? _("Yes") : _("No"));
+ printf (_("%12ld cache hits on positive entries\n"), resp.pw_poshit);
+ printf (_("%12ld cache hits on negative entries\n"), resp.pw_neghit);
+ printf (_("%12ld cache misses on positive entries\n"), resp.pw_posmiss);
+ printf (_("%12ld cache misses on negative entries\n"), resp.pw_negmiss);
+ printf (_("%12ld suggested size\n"), resp.pw_size);
+ printf (_("%12ld seconds time to live for positive entries\n"),
+ resp.pw_posttl);
+ printf (_("%12ld seconds time to live for negative entries\n\n"),
+ resp.pw_negttl);
+
+ printf (_("group cache:\n\n"));
+ printf (_("%12s cache is enabled\n"), resp.gr_enabled ? _("Yes") : _("No"));
+ printf (_("%12ld cache hits on positive entries\n"), resp.gr_poshit);
+ printf (_("%12ld cache hits on negative entries\n"), resp.gr_neghit);
+ printf (_("%12ld cache misses on positive entries\n"), resp.gr_posmiss);
+ printf (_("%12ld cache misses on negative entries\n"), resp.gr_negmiss);
+ printf (_("%12ld suggested size\n"), resp.gr_size);
+ printf (_("%12ld seconds time to live for positive entries\n"),
+ resp.gr_posttl);
+ printf (_("%12ld seconds time to live for negative entries\n"),
+ resp.gr_negttl);
+}
diff --git a/nscd/pwdcache.c b/nscd/pwdcache.c
new file mode 100644
index 0000000000..a3676666da
--- /dev/null
+++ b/nscd/pwdcache.c
@@ -0,0 +1,581 @@
+/* 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.
+
+ 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 <malloc.h>
+#include <pthread.h>
+#include <pwd.h>
+#include <string.h>
+#include <rpcsvc/nis.h>
+#include <sys/types.h>
+
+#include "dbg_log.h"
+#include "nscd.h"
+
+static unsigned long int modulo = 211;
+static unsigned long int postimeout = 600;
+static unsigned long int negtimeout = 20;
+
+static unsigned long int poshit = 0;
+static unsigned long int posmiss = 0;
+static unsigned long int neghit = 0;
+static unsigned long int negmiss = 0;
+
+struct pwdhash
+{
+ time_t create;
+ struct pwdhash *next;
+ struct passwd *pwd;
+};
+typedef struct pwdhash pwdhash;
+
+struct uidhash
+{
+ struct uidhash *next;
+ struct pwdhash *pwptr;
+};
+typedef struct uidhash uidhash;
+
+struct neghash
+{
+ time_t create;
+ struct neghash *next;
+ char *key;
+};
+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)
+{
+ stat->pw_poshit = poshit;
+ stat->pw_posmiss = posmiss;
+ stat->pw_neghit = neghit;
+ stat->pw_negmiss = negmiss;
+ stat->pw_size = modulo;
+ stat->pw_posttl = postimeout;
+ stat->pw_negttl = negtimeout;
+}
+
+void
+set_pwd_modulo (unsigned long int mod)
+{
+ modulo = mod;
+}
+
+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_t thread;
+
+ pwdtbl = calloc (modulo, sizeof (pwdhash));
+ if (pwdtbl == NULL)
+ return -1;
+ uidtbl = calloc (modulo, sizeof (pwdhash));
+ if (uidtbl == NULL)
+ return -1;
+ negtbl = calloc (modulo, sizeof (neghash));
+ if (negtbl == NULL)
+ return -1;
+
+ pthread_create (&thread, NULL, pwdtable_update, (void *)NULL);
+ pthread_detach (thread);
+ pthread_create (&thread, NULL, negtable_update, (void *)NULL);
+ pthread_detach (thread);
+ return 0;
+}
+
+static struct passwd *
+save_pwd (struct passwd *src)
+{
+ struct passwd *dest;
+
+ dest = calloc (1, sizeof (struct passwd));
+ dest->pw_name = strdup (src->pw_name);
+ dest->pw_passwd = strdup (src->pw_passwd);
+ dest->pw_uid = src->pw_uid;
+ dest->pw_gid = src->pw_gid;
+ dest->pw_gecos = strdup (src->pw_gecos);
+ dest->pw_dir = strdup (src->pw_dir);
+ dest->pw_shell = strdup (src->pw_shell);
+
+ return dest;
+}
+
+static void
+free_pwd (struct passwd *src)
+{
+ free (src->pw_name);
+ free (src->pw_passwd);
+ free (src->pw_gecos);
+ free (src->pw_dir);
+ free (src->pw_shell);
+ free (src);
+}
+
+static int
+add_cache (struct passwd *pwd)
+{
+ pwdhash *work;
+ unsigned long int hash = __nis_hash (pwd->pw_name,
+ strlen (pwd->pw_name)) % modulo;
+
+ if (debug_flag)
+ dbg_log (_("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);
+ uidtbl[pwd->pw_uid % modulo].pwptr = work;
+
+ 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->pwd->pw_uid == uid)
+ return work->pwptr->pwd;
+ 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 (_("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;
+ }
+ /* Set a pointer from the pwuid hash table to the pwname hash table */
+ 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;
+
+ work = &negtbl[hash];
+
+ if (debug_flag)
+ dbg_log (_("cache_search_neg (%s|%ld)"), key, 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)
+{
+ param_t *param = (param_t *)v_param;
+ struct passwd *pwd, resultbuf;
+
+ 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);
+ pwd = &resultbuf;
+ }
+ else
+ {
+ int status;
+ int buflen = 1024;
+ char *buffer = malloc (buflen);
+
+ if (debug_flag)
+ dbg_log (_("Doesn't found \"%s\" in cache !"), param->key);
+
+ pthread_rwlock_unlock (&pwdlock);
+
+ pthread_rwlock_rdlock (&neglock);
+ status = cache_search_neg (param->key);
+ pthread_rwlock_unlock (&neglock);
+
+ if (status == 0)
+ {
+ while (buffer != NULL
+ && (getpwnam_r (param->key, &resultbuf, buffer, buflen, &pwd)
+ != 0)
+ && errno == ERANGE)
+ {
+ errno = 0;
+ buflen += 1024;
+ buffer = realloc (buffer, buflen);
+ }
+
+ if (buffer != NULL && pwd != NULL)
+ {
+ struct passwd *tmp;
+
+ ++posmiss;
+ pthread_rwlock_wrlock (&pwdlock);
+ /* While we are waiting on the lock, somebody else could
+ add this entry. */
+ tmp = cache_search_name (param->key);
+ if (tmp == NULL)
+ add_cache (pwd);
+ pthread_rwlock_unlock (&pwdlock);
+ }
+ else
+ {
+ ++negmiss;
+ pthread_rwlock_wrlock (&neglock);
+ add_negcache (param->key);
+ pthread_rwlock_unlock (&neglock);
+ }
+ }
+ else
+ ++neghit;
+ pw_send_answer (param->conn, pwd);
+ close_socket (param->conn);
+ if (buffer != NULL)
+ free (buffer);
+ }
+ free (param->key);
+ free (param);
+ return NULL;
+}
+
+void *
+cache_pw_disabled (void *v_param)
+{
+ param_t *param = (param_t *)v_param;
+
+ 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)
+ && errno == ERANGE)
+ {
+ errno = 0;
+ buflen += 1024;
+ buffer = realloc (buffer, buflen);
+ }
+
+ if (buffer != NULL && pwd != NULL)
+ {
+ struct passwd *tmp;
+
+ ++posmiss;
+ pthread_rwlock_wrlock (&pwdlock);
+ /* While we are waiting on the lock, somebody else could
+ add this entry. */
+ tmp = cache_search_uid (uid);
+ if (tmp == NULL)
+ add_cache (pwd);
+ pthread_rwlock_unlock (&pwdlock);
+ }
+ else
+ {
+ ++negmiss;
+ pthread_rwlock_wrlock (&neglock);
+ add_negcache (param->key);
+ pthread_rwlock_unlock (&neglock);
+ }
+ }
+ else
+ ++neghit;
+
+ pw_send_answer (param->conn, pwd);
+ close_socket (param->conn);
+ if (buffer != NULL)
+ free (buffer);
+ }
+ free (param->key);
+ free (param);
+ return NULL;
+}
+
+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->pwd->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;
+}
+
+void *
+negtable_update (void *v)
+{
+ time_t now;
+ int i;
+
+ sleep (30);
+
+ while (!do_shutdown)
+ {
+ if (debug_flag > 2)
+ dbg_log (_("(negtable_update) Wait for write lock!"));
+
+ pthread_rwlock_wrlock (&neglock);
+
+ if (debug_flag)
+ dbg_log (_("(negtable_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)
+ dbg_log (_("(negtable_update) Release wait lock"));
+
+ pthread_rwlock_unlock (&neglock);
+ sleep (10);
+ }
+ return NULL;
+}