about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJakub Jelinek <jakub@redhat.com>2004-10-04 08:56:18 +0000
committerJakub Jelinek <jakub@redhat.com>2004-10-04 08:56:18 +0000
commit85148842d401edf64f9edee7e5819a947c289ed2 (patch)
tree42f228e998070f60c3bdb2018c9921b221a6851b
parent6d96590587deec027c04fe576f11cff0f445eb32 (diff)
downloadglibc-cvs/fedora-glibc-2_3_3-64.tar.gz
glibc-cvs/fedora-glibc-2_3_3-64.tar.xz
glibc-cvs/fedora-glibc-2_3_3-64.zip
Updated to fedora-glibc-20041004T0747 cvs/fedora-glibc-2_3_3-64
-rw-r--r--ChangeLog84
-rw-r--r--assert/assert.h23
-rw-r--r--elf/rtld.c2
-rw-r--r--fedora/branch.mk4
-rw-r--r--fedora/glibc.spec.in15
-rw-r--r--fedora/glibc_post_upgrade.c157
-rw-r--r--malloc/arena.c62
-rw-r--r--malloc/malloc.c20
-rw-r--r--nscd/Makefile50
-rw-r--r--nscd/connections.c702
-rw-r--r--nscd/dbg_log.c9
-rw-r--r--nscd/nscd.c17
-rw-r--r--nscd/nscd.conf8
-rw-r--r--nscd/nscd.h21
-rw-r--r--nscd/nscd_conf.c44
-rw-r--r--nscd/nscd_helper.c3
-rw-r--r--nscd/nscd_initgroups.c15
-rw-r--r--nscd/nscd_stat.c29
-rw-r--r--nscd/selinux.c4
-rw-r--r--sysdeps/generic/memmem.c6
-rw-r--r--sysdeps/unix/sysv/linux/Makefile4
21 files changed, 1047 insertions, 232 deletions
diff --git a/ChangeLog b/ChangeLog
index 0f691c3dbd..fcce2d4263 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,89 @@
+2004-10-04  Ulrich Drepper  <drepper@redhat.com>
+
+	* assert/assert.h: Give up on using __builtin_expect.
+
+	* elf/rtld.c (dl_main): Only skip => output in ldd mode if both
+	strings are identical.
+
+2004-03-18  Jakub Jelinek  <jakub@redhat.com>
+
+	* malloc/arena.c (aligned_heap_area): New variable.
+	(new_heap): If aligned_heap_area != NULL, attempt to use that
+	first.  If HEAP_MAX_SIZE << 1 area is already HEAP_MAX_SIZE bytes
+	aligned, remember the second half in aligned_heap_area.
+	(delete_heap): Clear aligned_heap_area if deleting the area right
+	before aligned_heap_area.
+
+2004-10-03  Juerg Billeter  <j@bitron.ch>
+
+	* nscd/nscd_initgroups.c (__nscd_getgrouplist): Return -1 if nscd
+	can't be used.
+
+2004-10-03  Ulrich Drepper  <drepper@redhat.com>
+
+	Dynamically create new threads if necessary.
+	* nscd/connections.c (fd_ready): If no thread available for processing
+	the request, create a new one unless the limit is reached.
+	(start_threads): Check errors from pthread_create.
+	* nscd/nscd.h: Declare max_nthreads.
+	* nscd/nscd_conf.c: Parse max-nthreads entry.
+	* nscd/nscd.conf: Add max-threads entry.
+	* nscd/nscd_stat.c: Print current and maximum number of threads.
+
+	Implement paranoia mode.
+	* nscd/connections.c (nscd_init): Mark database and socket descriptors
+	as close on exec.
+	(restart): New function.
+	(restart_p): New function.
+	(nscd_run): Add missing descrement of nready in case readylist is
+	empty.
+	(main_loop_poll): Call restart_p and restart.
+	(main_loop_epoll): Likewise.
+	(begin_drop_privileges): Save original UID and GID.
+	* nscd/nscd.c: Define new variables paranoia, restart_time,
+	restart_interval, oldcwd, old_gid, old_uid.
+	(main): Disable paranoia mode if we are not forking.
+	(check_pid): When re-execing, the PID file contains the same PID as
+	the current process.  Do not fail in this case.
+	* nscd/nscd.conf: Add paranoia and restart-interval entries.
+	* nscd/nscd.h: Define RESTART_INTERVAL.  Declare new variables.
+	* nscd/nscd_conf.c: Parse paranoia and restart-internal configurations.
+	* nscd/nscd_stat.c: Print paranoia and restart-internal values.
+
+	* nscd/connections.c: Implement alternative loop for main thread
+	which uses epoll.
+	* sysdeps/unix/sysv/linux/Makefile [subdir=nscd]
+	(CFLAGS-connections.c): Add -DHAVE_EPOLL.
+
+2004-10-02  Ulrich Drepper  <drepper@redhat.com>
+
+	* nscd/Makefile (CFLAGS-initgrcache.c): Add to CFLAGS-* variables,
+	don't replace old content.
+
+	* nscd/connections.c: Rewrite handling of incoming connections.  All
+	are handled by one thread which then hands of the descriptors for the
+	real work to the worker threads.
+	* nscd/Makefile: Link nscd with librt.
+
+	* nscd/selinux.c: Pretty printing.
+
+	* nscd/dbg_log.c (dbg_log): Don't add unnecessary newline to
+	output.  Let syslog do the formatting if debug_level == 0.
+
+	* nscd/nscd_helper.c (get_mapping): No need to check timestamp if
+	nscd_certainly_running is nonzero.
+
+2004-10-02  Simon Josefsson  <jas@extundo.com>
+
+	[BZ #420]
+	* sysdeps/generic/memmem.c [!_LIBC]: Define __builtin_expect, to
+	make the file usable inside gnulib.
+
 2004-10-01  Ulrich Drepper  <drepper@redhat.com>
 
+	* malloc/malloc.c (public_vALLOc): Add missing use of hooks.
+	(public_pVALLOc): Likewise.
+
 	* nscd/nscd_initgroups.c (__nscd_getgrouplist): Always add the
 	group the caller provided unless there is a real problem.
 
diff --git a/assert/assert.h b/assert/assert.h
index 7458e15de2..2f83146274 100644
--- a/assert/assert.h
+++ b/assert/assert.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 1991,1992,1994-2001,2003 Free Software Foundation, Inc.
+/* Copyright (C) 1991,1992,1994-2001,2003,2004 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
@@ -84,33 +84,16 @@ extern void __assert (const char *__assertion, const char *__file, int __line)
 
 __END_DECLS
 
-/* For the macro definition we use gcc's __builtin_expect if possible
-   to generate good code for the non-error case.  gcc 3.0 is a good
-   enough estimate for when the feature became available.  */
-# if __GNUC_PREREQ (3, 0)
-#  define assert(expr) \
-  (__ASSERT_VOID_CAST (__builtin_expect (!!(expr), 1) ? 0 :		      \
-		       (__assert_fail (__STRING(expr), __FILE__, __LINE__,    \
-				       __ASSERT_FUNCTION), 0)))
-# else
-#  define assert(expr) \
+# define assert(expr) \
   (__ASSERT_VOID_CAST ((expr) ? 0 :					      \
 		       (__assert_fail (__STRING(expr), __FILE__, __LINE__,    \
 				       __ASSERT_FUNCTION), 0)))
-# endif
 
 # ifdef	__USE_GNU
-#  if __GNUC_PREREQ (3, 0)
-#   define assert_perror(errnum) \
-  (__ASSERT_VOID_CAST (__builtin_expect (!(errnum), 1) ? 0 :		      \
-		       (__assert_perror_fail ((errnum), __FILE__, __LINE__,   \
-					      __ASSERT_FUNCTION), 0)))
-#  else
-#   define assert_perror(errnum) \
+#  define assert_perror(errnum) \
   (__ASSERT_VOID_CAST (!(errnum) ? 0 :					      \
 		       (__assert_perror_fail ((errnum), __FILE__, __LINE__,   \
 					      __ASSERT_FUNCTION), 0)))
-#  endif
 # endif
 
 /* Version 2.4 and later of GCC define a magical variable `__PRETTY_FUNCTION__'
diff --git a/elf/rtld.c b/elf/rtld.c
index 28a33f26f3..a1b9136045 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -1673,7 +1673,7 @@ cannot allocate TLS data structures for initial thread");
 	    if (l->l_faked)
 	      /* The library was not found.  */
 	      _dl_printf ("\t%s => not found\n", l->l_libname->name);
-	    else if (l->l_libname->name[0] == '/')
+	    else if (strcmp (l->l_libname->name, l->l_name) == 0)
 	      _dl_printf ("\t%s (0x%0*Zx)\n", l->l_libname->name,
 			  (int) sizeof l->l_map_start * 2,
 			  (size_t) l->l_map_start);
diff --git a/fedora/branch.mk b/fedora/branch.mk
index 748fba0809..5e0abb6fc6 100644
--- a/fedora/branch.mk
+++ b/fedora/branch.mk
@@ -1,5 +1,5 @@
 # This file is updated automatically by Makefile.
 glibc-branch := fedora
 glibc-base := HEAD
-fedora-sync-date := 2004-10-01 21:34 UTC
-fedora-sync-tag := fedora-glibc-20041001T2134
+fedora-sync-date := 2004-10-04 07:47 UTC
+fedora-sync-tag := fedora-glibc-20041004T0747
diff --git a/fedora/glibc.spec.in b/fedora/glibc.spec.in
index 7830e73fa2..090a7e4375 100644
--- a/fedora/glibc.spec.in
+++ b/fedora/glibc.spec.in
@@ -1,4 +1,4 @@
-%define glibcrelease 63
+%define glibcrelease 64
 %define auxarches i586 i686 athlon sparcv9 alphaev6
 %define prelinkarches noarch
 %define nptlarches i386 i686 athlon x86_64 ia64 s390 s390x sparcv9 ppc ppc64
@@ -1239,7 +1239,18 @@ rm -f *.filelist*
 %endif
 
 %changelog
-* Fri Oct  1 2004 Jakub Jelinek <jakub@redhat.com> 2.3.3-62
+* Mon Oct  4 2004 Jakub Jelinek <jakub@redhat.com> 2.3.3-64
+- update from CVS
+  - stop using __builtin_expect in assert and assert_perror
+    (#127606)
+  - try to avoid too much VA fragmentation with malloc
+    on flexmap layout (#118574)
+  - nscd robustification
+  - change valloc to use debugging hooks (#134385)
+- make glibc_post_upgrade more verbose on errors (Fergal Daly,
+  #125700)
+
+* Fri Oct  1 2004 Jakub Jelinek <jakub@redhat.com> 2.3.3-63
 - update from CVS
   - fix __nscd_getgrouplist
   - fix a typo in x86_64 pthread_mutex_timedwait fix
diff --git a/fedora/glibc_post_upgrade.c b/fedora/glibc_post_upgrade.c
index 095b1ebb32..df151e3cd3 100644
--- a/fedora/glibc_post_upgrade.c
+++ b/fedora/glibc_post_upgrade.c
@@ -13,10 +13,22 @@ register void *__thread_self __asm ("g7");
 #include <string.h>
 #include <sys/stat.h>
 
-int main(void)
+#define verbose_exec(failcode, path...) \
+  do							\
+    {							\
+      char *const arr[] = { path, NULL };		\
+      vexec (failcode, arr);				\
+    } while (0)
+
+__attribute__((noinline)) void vexec (int failcode, char *const path[]);
+__attribute__((noinline)) void says (const char *str);
+__attribute__((noinline)) void sayn (long num);
+__attribute__((noinline)) void message (char *const path[]);
+
+int
+main (void)
 {
-  pid_t pid;
-  int status, rerun_ldconfig = 0, rerun_cnt = 0;
+  int rerun_ldconfig = 0, rerun_cnt = 0;
   char initpath[256];
 
 #ifdef __i386__
@@ -102,18 +114,7 @@ int main(void)
       char linkbuf[64], *linkp;
       int linklen;
 
-      pid = vfork ();
-      if (pid == 0)
-	{
-	  execl ("/sbin/ldconfig", "/sbin/ldconfig", NULL);
-	  _exit (110);
-	}
-      else if (pid < 0)
-	_exit (111);
-      if (waitpid (0, &status, 0) != pid || !WIFEXITED (status))
-	_exit (112);
-      if (WEXITSTATUS (status))
-	_exit (WEXITSTATUS (status));
+      verbose_exec (110, "/sbin/ldconfig", "/sbin/ldconfig");
 
       rerun_ldconfig = 0;
 #ifdef LIBTLS
@@ -150,18 +151,7 @@ int main(void)
 
   if (! utimes (GCONV_MODULES_CACHE, NULL))
     {
-      pid = vfork ();
-      if (pid == 0)
-	{
-	  execl ("/usr/sbin/iconvconfig", "/usr/sbin/iconvconfig", NULL);
-	  _exit (113);
-	}
-      else if (pid < 0)
-	_exit (114);
-      if (waitpid (0, &status, 0) != pid || !WIFEXITED (status))
-	_exit (115);
-      if (WEXITSTATUS (status))
-	_exit (WEXITSTATUS (status));
+      verbose_exec (113, "/usr/sbin/iconvconfig", "/usr/sbin/iconvconfig");
     }
 
   /* Check if telinit is available and the init fifo as well.  */
@@ -173,32 +163,14 @@ int main(void)
       readlink ("/proc/1/root", initpath, 256) <= 0)
     _exit (0);
 
-  pid = vfork ();
-  if (pid == 0)
-    {
-      execl ("/sbin/telinit", "/sbin/telinit", "u", NULL);
-      _exit (116);
-    }
-  else if (pid < 0)
-    _exit (117);
-  if (waitpid (0, &status, 0) != pid || !WIFEXITED (status))
-    _exit (118);
+  verbose_exec (116, "/sbin/telinit", "/sbin/telinit", "u");
 
   /* Check if we can safely condrestart sshd.  */
   if (access ("/sbin/service", X_OK) == 0
       && access ("/usr/sbin/sshd", X_OK) == 0
       && access ("/bin/bash", X_OK) == 0)
     {
-      pid = vfork ();
-      if (pid == 0)
-	{
-	  execl ("/sbin/service", "/sbin/service", "sshd", "condrestart", NULL);
-	  _exit (119);
-	}
-      else if (pid < 0)
-	_exit (120);
-      if (waitpid (0, &status, 0) != pid || !WIFEXITED (status))
-	_exit (121);
+	 verbose_exec (121, "/sbin/service", "/sbin/service", "sshd", "condrestart");
     }
 
   _exit(0);
@@ -213,9 +185,10 @@ pid_t __fork (void) { return -1; }
 char thr_buf[65536];
 
 #ifndef __powerpc__
-int __libc_start_main (int (*main) (void), int argc, char **argv,
-		       void (*init) (void), void (*fini) (void),
-		       void (*rtld_fini) (void), void * stack_end)
+int
+__libc_start_main (int (*main) (void), int argc, char **argv,
+		   void (*init) (void), void (*fini) (void),
+		   void (*rtld_fini) (void), void * stack_end)
 #else
 struct startup_info
 {
@@ -225,11 +198,11 @@ struct startup_info
   void (*fini) (void);
 };
 
-int __libc_start_main (int argc, char **ubp_av,
-		       char **ubp_ev,
-		       void *auxvec, void (*rtld_fini) (void),
-		       struct startup_info *stinfo,
-		       char **stack_on_entry)
+int
+__libc_start_main (int argc, char **ubp_av, char **ubp_ev,
+                   void *auxvec, void (*rtld_fini) (void),
+		   struct startup_info *stinfo,
+		   char **stack_on_entry)
 #endif
 {
 #if defined __ia64__ || defined __powerpc64__
@@ -254,3 +227,77 @@ int __libc_start_main (int argc, char **ubp_av,
   main();
   return 0;
 }
+
+void
+vexec (int failcode, char *const path[])
+{
+  pid_t pid;
+  int status, save_errno;
+
+  pid = vfork ();
+  if (pid == 0)
+    {	
+      execv (path[0], path + 1);
+      save_errno = errno;
+      message (path);
+      says (" exec failed with errno ");
+      sayn (save_errno);
+      says ("\n");
+      _exit (failcode);
+    }
+  else if (pid < 0)
+    {
+      save_errno = errno;
+      message (path);
+      says (" fork failed with errno ");
+      sayn (save_errno);
+      says ("\n");
+      _exit (failcode + 1);
+    }
+  if (waitpid (0, &status, 0) != pid || !WIFEXITED (status))
+    {
+      message (path);
+      says (" child terminated abnormally\n");
+      _exit (failcode + 2);
+    }
+  if (WEXITSTATUS (status))
+    {
+      message (path);
+      says (" child exited with exit code ");
+      sayn (WEXITSTATUS (status));
+      says ("\n");
+      _exit (WEXITSTATUS (status));
+    }
+}
+
+void
+says (const char *str)
+{
+  write (1, str, strlen (str));
+}
+
+void
+sayn (long num)
+{
+  char string[sizeof (long) * 3 + 1];
+  char *p = string + sizeof (string) - 1;
+
+  *p = '\0';
+  if (num == 0)
+    *--p = '0';
+  else
+    while (num)
+      {
+        *--p = '0' + num % 10;
+        num = num / 10;
+      }
+
+  says (p);
+}
+
+void
+message (char *const path[])
+{
+  says ("/usr/sbin/glibc_post_upgrade: While trying to execute ");
+  says (path[0]);
+}
diff --git a/malloc/arena.c b/malloc/arena.c
index 3adfbc45f8..00f40971f3 100644
--- a/malloc/arena.c
+++ b/malloc/arena.c
@@ -550,6 +550,16 @@ dump_heap(heap) heap_info *heap;
 
 #endif /* MALLOC_DEBUG > 1 */
 
+/* If consecutive mmap (0, HEAP_MAX_SIZE << 1, ...) calls return decreasing
+   addresses as opposed to increasing, new_heap would badly fragment the
+   address space.  In that case remember the second HEAP_MAX_SIZE part
+   aligned to HEAP_MAX_SIZE from last mmap (0, HEAP_MAX_SIZE << 1, ...)
+   call (if it is already aligned) and try to reuse it next time.  We need
+   no locking for it, as kernel ensures the atomicity for us - worst case
+   we'll call mmap (addr, HEAP_MAX_SIZE, ...) for some value of addr in
+   multiple threads, but only one will succeed.  */
+static char *aligned_heap_area;
+
 /* Create a new heap.  size is automatically rounded up to a multiple
    of the page size. */
 
@@ -580,21 +590,38 @@ new_heap(size, top_pad) size_t size, top_pad;
      No swap space needs to be reserved for the following large
      mapping (on Linux, this is the case for all non-writable mappings
      anyway). */
-  p1 = (char *)MMAP(0, HEAP_MAX_SIZE<<1, PROT_NONE, MAP_PRIVATE|MAP_NORESERVE);
-  if(p1 != MAP_FAILED) {
-    p2 = (char *)(((unsigned long)p1 + (HEAP_MAX_SIZE-1)) & ~(HEAP_MAX_SIZE-1));
-    ul = p2 - p1;
-    munmap(p1, ul);
-    munmap(p2 + HEAP_MAX_SIZE, HEAP_MAX_SIZE - ul);
-  } else {
-    /* Try to take the chance that an allocation of only HEAP_MAX_SIZE
-       is already aligned. */
-    p2 = (char *)MMAP(0, HEAP_MAX_SIZE, PROT_NONE, MAP_PRIVATE|MAP_NORESERVE);
-    if(p2 == MAP_FAILED)
-      return 0;
-    if((unsigned long)p2 & (HEAP_MAX_SIZE-1)) {
+  p2 = MAP_FAILED;
+  if(aligned_heap_area) {
+    p2 = (char *)MMAP(aligned_heap_area, HEAP_MAX_SIZE, PROT_NONE,
+		      MAP_PRIVATE|MAP_NORESERVE);
+    aligned_heap_area = NULL;
+    if (p2 != MAP_FAILED && ((unsigned long)p2 & (HEAP_MAX_SIZE-1))) {
       munmap(p2, HEAP_MAX_SIZE);
-      return 0;
+      p2 = MAP_FAILED;
+    }
+  }
+  if(p2 == MAP_FAILED) {
+    p1 = (char *)MMAP(0, HEAP_MAX_SIZE<<1, PROT_NONE,
+		      MAP_PRIVATE|MAP_NORESERVE);
+    if(p1 != MAP_FAILED) {
+      p2 = (char *)(((unsigned long)p1 + (HEAP_MAX_SIZE-1))
+		    & ~(HEAP_MAX_SIZE-1));
+      ul = p2 - p1;
+      if (ul)
+	munmap(p1, ul);
+      else
+	aligned_heap_area = p2 + HEAP_MAX_SIZE;
+      munmap(p2 + HEAP_MAX_SIZE, HEAP_MAX_SIZE - ul);
+    } else {
+      /* Try to take the chance that an allocation of only HEAP_MAX_SIZE
+	 is already aligned. */
+      p2 = (char *)MMAP(0, HEAP_MAX_SIZE, PROT_NONE, MAP_PRIVATE|MAP_NORESERVE);
+      if(p2 == MAP_FAILED)
+	return 0;
+      if((unsigned long)p2 & (HEAP_MAX_SIZE-1)) {
+	munmap(p2, HEAP_MAX_SIZE);
+	return 0;
+      }
     }
   }
   if(mprotect(p2, size, PROT_READ|PROT_WRITE) != 0) {
@@ -644,7 +671,12 @@ grow_heap(h, diff) heap_info *h; long diff;
 
 /* Delete a heap. */
 
-#define delete_heap(heap) munmap((char*)(heap), HEAP_MAX_SIZE)
+#define delete_heap(heap) \
+  do {								\
+    if ((char *)(heap) + HEAP_MAX_SIZE == aligned_heap_area)	\
+      aligned_heap_area = NULL;					\
+    munmap((char*)(heap), HEAP_MAX_SIZE);			\
+  } while (0)
 
 static int
 internal_function
diff --git a/malloc/malloc.c b/malloc/malloc.c
index dcc54c46f1..a41171490a 100644
--- a/malloc/malloc.c
+++ b/malloc/malloc.c
@@ -1709,7 +1709,7 @@ struct malloc_chunk {
     chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
             |             Size of previous chunk, if allocated            | |
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-            |             Size of chunk, in bytes                         |P|
+            |             Size of chunk, in bytes                       |M|P|
       mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
             |             User data starts here...                          .
             .                                                               .
@@ -1771,7 +1771,7 @@ nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
         MINSIZE bytes long, it is replenished.
 
      2. Chunks allocated via mmap, which have the second-lowest-order
-        bit (IS_MMAPPED) set in their size fields.  Because they are
+        bit M (IS_MMAPPED) set in their size fields.  Because they are
         allocated one-by-one, each must contain its own trailing size field.
 
 */
@@ -3530,6 +3530,13 @@ public_vALLOc(size_t bytes)
 
   if(__malloc_initialized < 0)
     ptmalloc_init ();
+
+  __malloc_ptr_t (*hook) __MALLOC_PMT ((size_t, size_t,
+					__const __malloc_ptr_t)) =
+    __memalign_hook;
+  if (hook != NULL)
+    return (*hook)(mp_.pagesize, bytes, RETURN_ADDRESS (0));
+
   arena_get(ar_ptr, bytes + mp_.pagesize + MINSIZE);
   if(!ar_ptr)
     return 0;
@@ -3546,6 +3553,15 @@ public_pVALLOc(size_t bytes)
 
   if(__malloc_initialized < 0)
     ptmalloc_init ();
+
+  __malloc_ptr_t (*hook) __MALLOC_PMT ((size_t, size_t,
+					__const __malloc_ptr_t)) =
+    __memalign_hook;
+  if (hook != NULL)
+    return (*hook)(mp_.pagesize,
+		   (bytes + mp_.pagesize - 1) & ~(mp_.pagesize - 1),
+		   RETURN_ADDRESS (0));
+
   arena_get(ar_ptr, bytes + 2*mp_.pagesize + MINSIZE);
   p = _int_pvalloc(ar_ptr, bytes);
   (void)mutex_unlock(&ar_ptr->mutex);
diff --git a/nscd/Makefile b/nscd/Makefile
index d597cf77cb..b0ef3cd5f2 100644
--- a/nscd/Makefile
+++ b/nscd/Makefile
@@ -74,28 +74,28 @@ ifeq (yesyes,$(have-fpie)$(build-shared))
 nscd-cflags += -fpie
 endif
 
-CFLAGS-nscd.c = $(nscd-cflags)
-CFLAGS-connections.c = $(nscd-cflags)
-CFLAGS-pwdcache.c = $(nscd-cflags)
-CFLAGS-getpwnam_r.c = $(nscd-cflags)
-CFLAGS-getpwuid_r.c = $(nscd-cflags)
-CFLAGS-grpcache.c = $(nscd-cflags)
-CFLAGS-getgrnam_r.c = $(nscd-cflags)
-CFLAGS-getgrgid_r.c = $(nscd-cflags)
-CFLAGS-hstcache.c = $(nscd-cflags)
-CFLAGS-gethstbyad_r.c = $(nscd-cflags)
-CFLAGS-gethstbynm2_r.c = $(nscd-cflags)
-CFLAGS-dbg_log.c = $(nscd-cflags)
-CFLAGS-nscd_conf.c = $(nscd-cflags)
-CFLAGS-nscd_stat.c = $(nscd-cflags)
-CFLAGS-cache.c = $(nscd-cflags)
-CFLAGS-xmalloc.c = $(nscd-cflags)
-CFLAGS-xstrdup.c = $(nscd-cflags)
-CFLAGS-mem.c = $(nscd-cflags)
-CFLAGS-nscd_setup_thread.c = $(nscd-cflags)
-CFLAGS-aicache.c = $(nscd-cflags)
-CFLAGS-selinux.c = $(nscd-cflags)
-CFLAGS-initgrcache.c = $(nscd-cflags)
+CFLAGS-nscd.c += $(nscd-cflags)
+CFLAGS-connections.c += $(nscd-cflags)
+CFLAGS-pwdcache.c += $(nscd-cflags)
+CFLAGS-getpwnam_r.c += $(nscd-cflags)
+CFLAGS-getpwuid_r.c += $(nscd-cflags)
+CFLAGS-grpcache.c += $(nscd-cflags)
+CFLAGS-getgrnam_r.c += $(nscd-cflags)
+CFLAGS-getgrgid_r.c += $(nscd-cflags)
+CFLAGS-hstcache.c += $(nscd-cflags)
+CFLAGS-gethstbyad_r.c += $(nscd-cflags)
+CFLAGS-gethstbynm2_r.c += $(nscd-cflags)
+CFLAGS-dbg_log.c += $(nscd-cflags)
+CFLAGS-nscd_conf.c += $(nscd-cflags)
+CFLAGS-nscd_stat.c += $(nscd-cflags)
+CFLAGS-cache.c += $(nscd-cflags)
+CFLAGS-xmalloc.c += $(nscd-cflags)
+CFLAGS-xstrdup.c += $(nscd-cflags)
+CFLAGS-mem.c += $(nscd-cflags)
+CFLAGS-nscd_setup_thread.c += $(nscd-cflags)
+CFLAGS-aicache.c += $(nscd-cflags)
+CFLAGS-selinux.c += $(nscd-cflags)
+CFLAGS-initgrcache.c += $(nscd-cflags)
 
 ifeq (yesyes,$(have-fpie)$(build-shared))
 $(objpfx)nscd: $(addprefix $(objpfx),$(nscd-modules:=.o))
@@ -117,9 +117,11 @@ $(objpfx)nscd: $(nscd-modules:%=$(objpfx)%.o)
 $(objpfx)nscd_nischeck: $(objpfx)nscd_nischeck.o
 
 ifeq ($(build-shared),yes)
-$(objpfx)nscd: $(shared-thread-library) $(common-objpfx)nis/libnsl.so
+$(objpfx)nscd: $(common-objpfx)rt/librt.so $(shared-thread-library) \
+	       $(common-objpfx)nis/libnsl.so
 $(objpfx)nscd_nischeck: $(common-objpfx)nis/libnsl.so
 else
-$(objpfx)nscd: $(static-thread-library) $(common-objpfx)nis/libnsl.a
+$(objpfx)nscd: $(common-objpfx)rt/librt.a $(static-thread-library) \
+	       $(common-objpfx)nis/libnsl.a
 $(objpfx)nscd_nischeck: $(common-objpfx)nis/libnsl.a
 endif
diff --git a/nscd/connections.c b/nscd/connections.c
index 53795bb3b9..2bd3bec5b0 100644
--- a/nscd/connections.c
+++ b/nscd/connections.c
@@ -18,6 +18,7 @@
    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
    02111-1307 USA.  */
 
+#include <alloca.h>
 #include <assert.h>
 #include <atomic.h>
 #include <error.h>
@@ -32,6 +33,9 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include <arpa/inet.h>
+#ifdef HAVE_EPOLL
+# include <sys/epoll.h>
+#endif
 #include <sys/mman.h>
 #include <sys/param.h>
 #include <sys/poll.h>
@@ -65,6 +69,8 @@ static gid_t *server_groups;
 #endif
 static int server_ngroups;
 
+static pthread_attr_t attr;
+
 static void begin_drop_privileges (void);
 static void finish_drop_privileges (void);
 
@@ -163,8 +169,10 @@ static struct database_dyn *const serv2db[LASTREQ] =
 #define CACHE_PRUNE_INTERVAL	15
 
 
-/* Number of threads to use.  */
+/* Initial number of threads to use.  */
 int nthreads = -1;
+/* Maximum number of threads to use.  */
+int max_nthreads = 32;
 
 /* Socket for incoming connections.  */
 static int sock;
@@ -434,6 +442,18 @@ cannot create read-only descriptor for \"%s\"; no mmap"),
 	      }
 	  }
 
+	if (paranoia
+	    && ((dbs[cnt].wr_fd != -1
+		 && fcntl (dbs[cnt].wr_fd, F_SETFD, FD_CLOEXEC) == -1)
+		|| (dbs[cnt].ro_fd != -1
+		    && fcntl (dbs[cnt].ro_fd, F_SETFD, FD_CLOEXEC) == -1)))
+	  {
+	    dbg_log (_("\
+cannot set socket to close on exec: %s; disabling paranoia mode"),
+		     strerror (errno));
+	    paranoia = 0;
+	  }
+
 	if (dbs[cnt].head == NULL)
 	  {
 	    /* We do not use the persistent database.  Just
@@ -490,11 +510,22 @@ cannot create read-only descriptor for \"%s\"; no mmap"),
       exit (1);
     }
 
-  /* We don't wait for data otherwise races between threads can get
-     them stuck on accept.  */
+  /* We don't want to get stuck on accept.  */
   int fl = fcntl (sock, F_GETFL);
-  if (fl != -1)
-    fcntl (sock, F_SETFL, fl | O_NONBLOCK);
+  if (fl == -1 || fcntl (sock, F_SETFL, fl | O_NONBLOCK) == -1)
+    {
+      dbg_log (_("cannot change socket to nonblocking mode: %s"),
+	       strerror (errno));
+      exit (1);
+    }
+
+  /* The descriptor needs to be closed on exec.  */
+  if (paranoia && fcntl (sock, F_SETFD, FD_CLOEXEC) == -1)
+    {
+      dbg_log (_("cannot set socket to close on exec: %s"),
+	       strerror (errno));
+      exit (1);
+    }
 
   /* Set permissions for the socket.  */
   chmod (_PATH_NSCDSOCKET, DEFFILEMODE);
@@ -785,91 +816,253 @@ cannot handle old request version %d; current version is %d"),
 }
 
 
+/* Restart the process.  */
+static void
+restart (void)
+{
+  /* First determine the parameters.  We do not use the parameters
+     passed to main() since in case nscd is started by running the
+     dynamic linker this will not work.  Yes, this is not the usual
+     case but nscd is part of glibc and we occasionally do this.  */
+  size_t buflen = 1024;
+  char *buf = alloca (buflen);
+  size_t readlen = 0;
+  int fd = open ("/proc/self/cmdline", O_RDONLY);
+  if (fd == -1)
+    {
+      dbg_log (_("\
+cannot open /proc/self/cmdline: %s; disabling paranoia mode"),
+	       strerror (errno));
+
+      paranoia = 0;
+      return;
+    }
+
+  while (1)
+    {
+      ssize_t n = TEMP_FAILURE_RETRY (read (fd, buf + readlen,
+					    buflen - readlen));
+      if (n == -1)
+	{
+	  dbg_log (_("\
+cannot open /proc/self/cmdline: %s; disabling paranoia mode"),
+		   strerror (errno));
+
+	  close (fd);
+	  paranoia = 0;
+	  return;
+	}
+
+      readlen += n;
+
+      if (readlen < buflen)
+	break;
+
+      /* We might have to extend the buffer.  */
+      size_t old_buflen = buflen;
+      char *newp = extend_alloca (buf, buflen, 2 * buflen);
+      buf = memmove (newp, buf, old_buflen);
+    }
+
+  close (fd);
+
+  /* Parse the command line.  Worst case scenario: every two
+     characters form one parameter (one character plus NUL).  */
+  char **argv = alloca ((readlen / 2 + 1) * sizeof (argv[0]));
+  int argc = 0;
+
+  char *cp = buf;
+  while (cp < buf + readlen)
+    {
+      argv[argc++] = cp;
+      cp = (char *) rawmemchr (cp, '\0') + 1;
+    }
+  argv[argc] = NULL;
+
+  /* Second, change back to the old user if we changed it.  */
+  if (server_user != NULL)
+    {
+      if (setuid (old_uid) != 0)
+	{
+	  dbg_log (_("\
+cannot change to old UID: %s; disabling paranoia mode"),
+		   strerror (errno));
+
+	  paranoia = 0;
+	  return;
+	}
+
+      if (setgid (old_gid) != 0)
+	{
+	  dbg_log (_("\
+cannot change to old GID: %s; disabling paranoia mode"),
+		   strerror (errno));
+
+	  setuid (server_uid);
+	  paranoia = 0;
+	  return;
+	}
+    }
+
+  /* Next change back to the old working directory.  */
+  if (chdir (oldcwd) == -1)
+    {
+      dbg_log (_("\
+cannot change to old working directory: %s; disabling paranoia mode"),
+	       strerror (errno));
+
+      if (server_user != NULL)
+	{
+	  setuid (server_uid);
+	  setgid (server_gid);
+	}
+      paranoia = 0;
+      return;
+    }
+
+  /* Synchronize memory.  */
+  for (int cnt = 0; cnt < lastdb; ++cnt)
+    {
+      /* Make sure nobody keeps using the database.  */
+      dbs[cnt].head->timestamp = 0;
+
+      if (dbs[cnt].persistent)
+	// XXX async OK?
+	msync (dbs[cnt].head, dbs[cnt].memsize, MS_ASYNC);
+    }
+
+  /* The preparations are done.  */
+  execv ("/proc/self/exe", argv);
+
+  /* If we come here, we will never be able to re-exec.  */
+  dbg_log (_("re-exec failed: %s; disabling paranoia mode"),
+	   strerror (errno));
+
+  if (server_user != NULL)
+    {
+      setuid (server_uid);
+      setgid (server_gid);
+    }
+  chdir ("/");
+  paranoia = 0;
+}
+
+
+/* List of file descriptors.  */
+struct fdlist
+{
+  int fd;
+  struct fdlist *next;
+};
+/* Memory allocated for the list.  */
+static struct fdlist *fdlist;
+/* List of currently ready-to-read file descriptors.  */
+static struct fdlist *readylist;
+
+/* Conditional variable and mutex to signal availability of entries in
+   READYLIST.  The condvar is initialized dynamically since we might
+   use a different clock depending on availability.  */
+static pthread_cond_t readylist_cond;
+static pthread_mutex_t readylist_lock = PTHREAD_MUTEX_INITIALIZER;
+
+/* The clock to use with the condvar.  */
+static clockid_t timeout_clock = CLOCK_REALTIME;
+
+/* Number of threads ready to handle the READYLIST.  */
+static unsigned long int nready;
+
+
 /* 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)
 {
-  long int my_number = (long int) p;
-  struct pollfd conn;
-  int run_prune = my_number < lastdb && dbs[my_number].enabled;
-  time_t next_prune = run_prune ? time (NULL) + CACHE_PRUNE_INTERVAL : 0;
-  static unsigned long int nready;
+  const long int my_number = (long int) p;
+  const int run_prune = my_number < lastdb && dbs[my_number].enabled;
+  struct timespec prune_ts;
+  int to = 0;
+  char buf[256];
 
   if (run_prune)
-    setup_thread (&dbs[my_number]);
+    {
+      setup_thread (&dbs[my_number]);
 
-  conn.fd = sock;
-  conn.events = POLLRDNORM;
+      /* We are running.  */
+      dbs[my_number].head->timestamp = time (NULL);
 
-  while (1)
-    {
-      int nr;
-      time_t now = 0;
+      if (clock_gettime (timeout_clock, &prune_ts) == -1)
+	/* Should never happen.  */
+	abort ();
 
-      /* One more thread available.  */
-      atomic_increment (&nready);
+      /* Compute timeout time.  */
+      prune_ts.tv_sec += CACHE_PRUNE_INTERVAL;
+    }
+
+  /* Initial locking.  */
+  pthread_mutex_lock (&readylist_lock);
 
-    no_conn:
-      do
+  /* One more thread available.  */
+  ++nready;
+
+  while (1)
+    {
+      while (readylist == NULL)
 	{
-	  int timeout = -1;
 	  if (run_prune)
 	    {
-	      /* NB: we do not flush the timestamp update using msync since
-		 this value doesnot matter on disk.  */
-	      dbs[my_number].head->timestamp = now = time (NULL);
-	      timeout = now < next_prune ? 1000 * (next_prune - now) : 0;
+	      /* Wait, but not forever.  */
+	      to = pthread_cond_timedwait (&readylist_cond, &readylist_lock,
+					   &prune_ts);
+
+	      /* If we were woken and there is no work to be done,
+		 just start pruning.  */
+	      if (readylist == NULL && to == ETIMEDOUT)
+		{
+		  --nready;
+		  pthread_mutex_unlock (&readylist_lock);
+		  goto only_prune;
+		}
 	    }
+	  else
+	    /* No need to timeout.  */
+	    pthread_cond_wait (&readylist_cond, &readylist_lock);
+	}
 
-	  nr = poll (&conn, 1, timeout);
+      struct fdlist *it = readylist->next;
+      if (readylist->next == readylist)
+	/* Just one entry on the list.  */
+	readylist = NULL;
+      else
+	readylist->next = it->next;
 
-	  if (nr == 0)
-	    {
-	      /* The `poll' call timed out.  It's time to clean up the
-		 cache.  */
-	      atomic_decrement (&nready);
-	      assert (my_number < lastdb);
-	      prune_cache (&dbs[my_number], time(NULL));
-	      now = time (NULL);
-	      next_prune = now + CACHE_PRUNE_INTERVAL;
-
-	      goto try_get;
-	    }
-	}
-      while ((conn.revents & POLLRDNORM) == 0);
+      /* Extract the information and mark the record ready to be used
+	 again.  */
+      int fd = it->fd;
+      it->next = NULL;
 
-    got_data:;
-      /* We have a new incoming connection.  Accept the connection.  */
-      int fd = TEMP_FAILURE_RETRY (accept (conn.fd, NULL, NULL));
-      request_header req;
-      char buf[256];
-      uid_t uid = -1;
-#ifdef SO_PEERCRED
-      pid_t pid = 0;
-#endif
+      /* One more thread available.  */
+      --nready;
 
-      if (__builtin_expect (fd, 0) < 0)
-	{
-	  if (errno != EAGAIN && errno != EWOULDBLOCK)
-	    dbg_log (_("while accepting connection: %s"),
-		     strerror_r (errno, buf, sizeof (buf)));
-	  goto no_conn;
-	}
+      /* We are done with the list.  */
+      pthread_mutex_unlock (&readylist_lock);
 
-      /* This thread is busy.  */
-      atomic_decrement (&nready);
+      /* We do not want to block on a short read or so.  */
+      int fl = fcntl (fd, F_GETFL);
+      if (fl == -1 || fcntl (fd, F_SETFL, fl | O_NONBLOCK) == -1)
+	goto close_and_out;
 
       /* Now read the request.  */
+      request_header req;
       if (__builtin_expect (TEMP_FAILURE_RETRY (read (fd, &req, sizeof (req)))
 			    != sizeof (req), 0))
 	{
+	  /* We failed to read data.  Note that this also might mean we
+	     failed because we would have blocked.  */
 	  if (debug_level > 0)
 	    dbg_log (_("short read while reading request: %s"),
 		     strerror_r (errno, buf, sizeof (buf)));
-	  close (fd);
-	  continue;
+	  goto close_and_out;
 	}
 
       /* Check whether this is a valid request type.  */
@@ -878,7 +1071,10 @@ nscd_run (void *p)
 
       /* Some systems have no SO_PEERCRED implementation.  They don't
 	 care about security so we don't as well.  */
+      uid_t uid = -1;
 #ifdef SO_PEERCRED
+      pid_t pid = 0;
+
       if (secure_in_use)
 	{
 	  struct ucred caller;
@@ -909,8 +1105,9 @@ nscd_run (void *p)
 
       /* 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.  */
+#define MAXKEYLEN 1024
       if (__builtin_expect (req.key_len, 1) < 0
-	  || __builtin_expect (req.key_len, 1) > 1024)
+	  || __builtin_expect (req.key_len, 1) > MAXKEYLEN)
 	{
 	  if (debug_level > 0)
 	    dbg_log (_("key length in request too long: %d"), req.key_len);
@@ -918,17 +1115,17 @@ nscd_run (void *p)
       else
 	{
 	  /* Get the key.  */
-	  char keybuf[req.key_len];
+	  char keybuf[MAXKEYLEN];
 
 	  if (__builtin_expect (TEMP_FAILURE_RETRY (read (fd, keybuf,
 							  req.key_len))
 				!= req.key_len, 0))
 	    {
+	      /* Again, this can also mean we would have blocked.  */
 	      if (debug_level > 0)
 		dbg_log (_("short read while reading request key: %s"),
 			 strerror_r (errno, buf, sizeof (buf)));
-	      close (fd);
-	      continue;
+	      goto close_and_out;
 	    }
 
 	  if (__builtin_expect (debug_level, 0) > 0)
@@ -952,44 +1149,380 @@ handle_request: request received (Version = %d)"), req.version);
       /* We are done.  */
       close (fd);
 
-      /* Just determine whether any data is present.  We do this to
-	 measure whether clients are queued up.  */
-    try_get:
-      nr = poll (&conn, 1, 0);
-      if (nr != 0)
+      /* Check whether we should be pruning the cache. */
+      assert (run_prune || to == 0);
+      if (to == ETIMEDOUT)
 	{
-	  if (nready == 0)
-	    ++client_queued;
+	only_prune:
+	  /* The pthread_cond_timedwait() call timed out.  It is time
+		 to clean up the cache.  */
+	  assert (my_number < lastdb);
+	  prune_cache (&dbs[my_number],
+		       prune_ts.tv_sec + (prune_ts.tv_nsec >= 500000000));
+
+	  if (clock_gettime (timeout_clock, &prune_ts) == -1)
+	    /* Should never happen.  */
+	    abort ();
+
+	  /* Compute next timeout time.  */
+	  prune_ts.tv_sec += CACHE_PRUNE_INTERVAL;
+
+	  /* In case the list is emtpy we do not want to run the prune
+	     code right away again.  */
+	  to = 0;
+	}
+
+      /* Re-locking.  */
+      pthread_mutex_lock (&readylist_lock);
+
+      /* One more thread available.  */
+      ++nready;
+    }
+}
+
 
-	  atomic_increment (&nready);
+static unsigned int nconns;
+
+static void
+fd_ready (int fd)
+{
+  pthread_mutex_lock (&readylist_lock);
+
+  /* Find an empty entry in FDLIST.  */
+  size_t inner;
+  for (inner = 0; inner < nconns; ++inner)
+    if (fdlist[inner].next == NULL)
+      break;
+  assert (inner < nconns);
 
-	  goto got_data;
+  fdlist[inner].fd = fd;
+
+  if (readylist == NULL)
+    readylist = fdlist[inner].next = &fdlist[inner];
+  else
+    {
+      fdlist[inner].next = readylist->next;
+      readylist = readylist->next = &fdlist[inner];
+    }
+
+  bool do_signal = true;
+  if (__builtin_expect (nready == 0, 0))
+    {
+      ++client_queued;
+      do_signal = false;
+
+      /* Try to start another thread to help out.  */
+      pthread_t th;
+      if (nthreads < max_nthreads
+	  && pthread_create (&th, &attr, nscd_run,
+			     (void *) (long int) nthreads) == 0)
+	{
+	  /* We got another thread.  */
+	  ++nthreads;
+	  /* The new thread might new a kick.  */
+	  do_signal = true;
 	}
+
     }
+
+  pthread_mutex_unlock (&readylist_lock);
+
+  /* Tell one of the worker threads there is work to do.  */
+  if (do_signal)
+    pthread_cond_signal (&readylist_cond);
+}
+
+
+/* Check whether restarting should happen.  */
+static inline int
+restart_p (time_t now)
+{
+  return (paranoia && readylist == NULL && nready == nthreads
+	  && now >= restart_time);
 }
 
 
+/* Array for times a connection was accepted.  */
+static time_t *starttime;
+
+
+static void
+__attribute__ ((__noreturn__))
+main_loop_poll (void)
+{
+  struct pollfd *conns = (struct pollfd *) xmalloc (nconns
+						    * sizeof (conns[0]));
+
+  conns[0].fd = sock;
+  conns[0].events = POLLRDNORM;
+  size_t nused = 1;
+  size_t firstfree = 1;
+
+  while (1)
+    {
+      /* Wait for any event.  We wait at most a couple of seconds so
+	 that we can check whether we should close any of the accepted
+	 connections since we have not received a request.  */
+#define MAX_ACCEPT_TIMEOUT 30
+#define MIN_ACCEPT_TIMEOUT 5
+#define MAIN_THREAD_TIMEOUT \
+  (MAX_ACCEPT_TIMEOUT * 1000						      \
+   - ((MAX_ACCEPT_TIMEOUT - MIN_ACCEPT_TIMEOUT) * 1000 * nused) / (2 * nconns))
+
+      int n = poll (conns, nused, MAIN_THREAD_TIMEOUT);
+
+      time_t now = time (NULL);
+
+      /* If there is a descriptor ready for reading or there is a new
+	 connection, process this now.  */
+      if (n > 0)
+	{
+	  if (conns[0].revents != 0)
+	    {
+	      /* We have a new incoming connection.  Accept the connection.  */
+	      int fd = TEMP_FAILURE_RETRY (accept (sock, NULL, NULL));
+
+	      /* use the descriptor if we have not reached the limit.  */
+	      if (fd >= 0 && firstfree < nconns)
+		{
+		  conns[firstfree].fd = fd;
+		  conns[firstfree].events = POLLRDNORM;
+		  starttime[firstfree] = now;
+		  if (firstfree >= nused)
+		    nused = firstfree + 1;
+
+		  do
+		    ++firstfree;
+		  while (firstfree < nused && conns[firstfree].fd != -1);
+		}
+
+	      --n;
+	    }
+
+	  for (size_t cnt = 1; cnt < nused && n > 0; ++cnt)
+	    if (conns[cnt].revents != 0)
+	      {
+		fd_ready (conns[cnt].fd);
+
+		/* Clean up the CONNS array.  */
+		conns[cnt].fd = -1;
+		if (cnt < firstfree)
+		  firstfree = cnt;
+		if (cnt == nused - 1)
+		  do
+		    --nused;
+		  while (conns[nused - 1].fd == -1);
+
+		--n;
+	      }
+	}
+
+      /* Now find entries which have timed out.  */
+      assert (nused > 0);
+
+      /* We make the timeout length depend on the number of file
+	 descriptors currently used.  */
+#define ACCEPT_TIMEOUT \
+  (MAX_ACCEPT_TIMEOUT							      \
+   - ((MAX_ACCEPT_TIMEOUT - MIN_ACCEPT_TIMEOUT) * nused) / nconns)
+      time_t laststart = now - ACCEPT_TIMEOUT;
+
+      for (size_t cnt = nused - 1; cnt > 0; --cnt)
+	{
+	  if (conns[cnt].fd != -1 && starttime[cnt] < laststart)
+	    {
+	      /* Remove the entry, it timed out.  */
+	      (void) close (conns[cnt].fd);
+	      conns[cnt].fd = -1;
+
+	      if (cnt < firstfree)
+		firstfree = cnt;
+	      if (cnt == nused - 1)
+		do
+		  --nused;
+		while (conns[nused - 1].fd == -1);
+	    }
+	}
+
+      if (restart_p (now))
+	restart ();
+    }
+}
+
+
+#ifdef HAVE_EPOLL
+static void
+main_loop_epoll (int efd)
+{
+  struct epoll_event ev = { 0, };
+  int nused = 1;
+  size_t highest = 0;
+
+  /* Add the socket.  */
+  ev.events = EPOLLRDNORM;
+  ev.data.fd = sock;
+  if (epoll_ctl (efd, EPOLL_CTL_ADD, sock, &ev) == -1)
+    /* We cannot use epoll.  */
+    return;
+
+  while (1)
+    {
+      struct epoll_event revs[100];
+# define nrevs (sizeof (revs) / sizeof (revs[0]))
+
+      int n = epoll_wait (efd, revs, nrevs, MAIN_THREAD_TIMEOUT);
+
+      time_t now = time (NULL);
+
+      for (int cnt = 0; cnt < n; ++cnt)
+	if (revs[cnt].data.fd == sock)
+	  {
+	    /* A new connection.  */
+	    int fd = TEMP_FAILURE_RETRY (accept (sock, NULL, NULL));
+
+	    if (fd >= 0)
+	      {
+		/* Try to add the  new descriptor.  */
+		ev.data.fd = fd;
+		if (fd >= nconns
+		    || epoll_ctl (efd, EPOLL_CTL_ADD, fd, &ev) == -1)
+		  /* The descriptor is too large or something went
+		     wrong.  Close the descriptor.  */
+		  close (fd);
+		else
+		  {
+		    /* Remember when we accepted the connection.  */
+		    starttime[fd] = now;
+
+		    if (fd > highest)
+		      highest = fd;
+
+		    ++nused;
+		  }
+	      }
+	  }
+	else
+	  {
+	    /* Remove the descriptor from the epoll descriptor.  */
+	    struct epoll_event ev = { 0, };
+	    (void) epoll_ctl (efd, EPOLL_CTL_DEL, revs[cnt].data.fd, &ev);
+
+	    /* Get a worked to handle the request.  */
+	    fd_ready (revs[cnt].data.fd);
+
+	    /* Reset the time.  */
+	    starttime[revs[cnt].data.fd] = 0;
+	    if (revs[cnt].data.fd == highest)
+	      do
+		--highest;
+	      while (highest > 0 && starttime[highest] == 0);
+
+	    --nused;
+	  }
+
+      /*  Now look for descriptors for accepted connections which have
+	  no reply in too long of a time.  */
+      time_t laststart = now - ACCEPT_TIMEOUT;
+      for (int cnt = highest; cnt > STDERR_FILENO; --cnt)
+	if (cnt != sock && starttime[cnt] != 0 && starttime[cnt] < laststart)
+	  {
+	    /* We are waiting for this one for too long.  Close it.  */
+	    struct epoll_event ev = {0, };
+	    (void) epoll_ctl (efd, EPOLL_CTL_DEL, cnt, &ev);
+
+	    (void) close (cnt);
+
+	    starttime[cnt] = 0;
+	    if (cnt == highest)
+	      --highest;
+	  }
+	else if (cnt != sock && starttime[cnt] == 0 && cnt == highest)
+	  --highest;
+
+      if (restart_p (now))
+	restart ();
+    }
+}
+#endif
+
+
 /* Start all the threads we want.  The initial process is thread no. 1.  */
 void
 start_threads (void)
 {
-  long int i;
-  pthread_attr_t attr;
-  pthread_t th;
+  /* Initialize the conditional variable we will use.  The only
+     non-standard attribute we might use is the clock selection.  */
+  pthread_condattr_t condattr;
+  pthread_condattr_init (&condattr);
+
+#if _POSIX_CLOCK_SELECTION >= 0 && _POSIX_MONOTONIC_CLOCK >= 0
+  /* Determine whether the monotonous clock is available.  */
+  struct timespec dummy;
+  if (clock_getres (CLOCK_MONOTONIC, &dummy) == 0
+      && pthread_condattr_setclock (&condattr, CLOCK_MONOTONIC) == 0)
+    timeout_clock = CLOCK_MONOTONIC;
+#endif
+
+  pthread_cond_init (&readylist_cond, &condattr);
+  pthread_condattr_destroy (&condattr);
 
+
+  /* Create the attribute for the threads.  They are all created
+     detached.  */
   pthread_attr_init (&attr);
   pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
+  /* Use 1MB stacks, twice as much for 64-bit architectures.  */
+  pthread_attr_setstacksize (&attr, 1024 * 1024 * (sizeof (void *) / 4));
 
   /* We allow less than LASTDB threads only for debugging.  */
   if (debug_level == 0)
     nthreads = MAX (nthreads, lastdb);
 
-  for (i = 1; i < nthreads; ++i)
-    pthread_create (&th, &attr, nscd_run, (void *) i);
+  int nfailed = 0;
+  for (long int i = 0; i < nthreads; ++i)
+    {
+      pthread_t th;
+      if (pthread_create (&th, &attr, nscd_run, (void *) (i - nfailed)) != 0)
+	++nfailed;
+    }
+  if (nthreads - nfailed < lastdb)
+    {
+      /* We could not start enough threads.  */
+      dbg_log (_("could only start %d threads; terminating"),
+	       nthreads - nfailed);
+      exit (1);
+    }
 
-  pthread_attr_destroy (&attr);
+  /* Determine how much room for descriptors we should initially
+     allocate.  This might need to change later if we cap the number
+     with MAXCONN.  */
+  const long int nfds = sysconf (_SC_OPEN_MAX);
+#define MINCONN 32
+#define MAXCONN 16384
+  if (nfds == -1 || nfds > MAXCONN)
+    nconns = MAXCONN;
+  else if (nfds < MINCONN)
+    nconns = MINCONN;
+  else
+    nconns = nfds;
+
+  /* We need memory to pass descriptors on to the worker threads.  */
+  fdlist = (struct fdlist *) xcalloc (nconns, sizeof (fdlist[0]));
+  /* Array to keep track when connection was accepted.  */
+  starttime = (time_t *) xcalloc (nconns, sizeof (starttime[0]));
+
+  /* In the main thread we execute the loop which handles incoming
+     connections.  */
+#ifdef HAVE_EPOLL
+  int efd = epoll_create (100);
+  if (efd != -1)
+    {
+      main_loop_epoll (efd);
+      close (efd);
+    }
+#endif
 
-  nscd_run ((void *) 0);
+  main_loop_poll ();
 }
 
 /* Look up the uid, gid, and supplementary groups to run nscd as. When
@@ -1010,6 +1543,13 @@ begin_drop_privileges (void)
   server_uid = pwd->pw_uid;
   server_gid = pwd->pw_gid;
 
+  /* Save the old UID/GID if we have to change back.  */
+  if (paranoia)
+    {
+      old_uid = getuid ();
+      old_gid = getgid ();
+    }
+
   if (getgrouplist (server_user, server_gid, NULL, &server_ngroups) == 0)
     {
       /* This really must never happen.  */
diff --git a/nscd/dbg_log.c b/nscd/dbg_log.c
index bcd9426020..afa06dcbe9 100644
--- a/nscd/dbg_log.c
+++ b/nscd/dbg_log.c
@@ -61,7 +61,8 @@ dbg_log (const char *fmt,...)
 
   if (debug_level > 0)
     {
-      snprintf (msg, sizeof (msg), "%d: %s\n", getpid (), msg2);
+      snprintf (msg, sizeof (msg), "%d: %s%s", getpid (), msg2,
+		msg2[strlen (msg2) - 1] == '\n' ? "" : "\n");
       if (dbgout)
 	{
 	  fputs (msg, dbgout);
@@ -71,9 +72,7 @@ dbg_log (const char *fmt,...)
 	fputs (msg, stderr);
     }
   else
-    {
-      snprintf (msg, sizeof (msg), "%d: %s", getpid (), msg2);
-      syslog (LOG_NOTICE, "%s", msg);
-    }
+    syslog (LOG_NOTICE, "%d %s", getpid (), msg2);
+
   va_end (ap);
 }
diff --git a/nscd/nscd.c b/nscd/nscd.c
index 2a4cb2291d..15a7ea2530 100644
--- a/nscd/nscd.c
+++ b/nscd/nscd.c
@@ -79,6 +79,13 @@ time_t start_time;
 
 uintptr_t pagesize_m1;
 
+int paranoia;
+time_t restart_time;
+time_t restart_interval = RESTART_INTERVAL;
+const char *oldcwd;
+uid_t old_uid;
+gid_t old_gid;
+
 static int check_pid (const char *file);
 static int write_pid (const char *file);
 
@@ -255,6 +262,9 @@ main (int argc, char **argv)
       signal (SIGTTIN, SIG_IGN);
       signal (SIGTSTP, SIG_IGN);
     }
+  else
+    /* In foreground mode we are not paranoid.  */
+    paranoia = 0;
 
   /* Start the SELinux AVC.  */
   if (selinux_enabled)
@@ -422,6 +432,7 @@ nscd_open_socket (void)
   return sock;
 }
 
+
 /* Cleanup.  */
 void
 termination_handler (int signum)
@@ -469,7 +480,11 @@ check_pid (const char *file)
       n = fscanf (fp, "%d", &pid);
       fclose (fp);
 
-      if (n != 1 || kill (pid, 0) == 0)
+      /* If we cannot parse the file default to assuming nscd runs.
+	 If the PID is alive, assume it is running.  That all unless
+	 the PID is the same as the current process' since tha latter
+	 can mean we re-exec.  */
+      if ((n != 1 || kill (pid, 0) == 0) && pid != getpid ())
         return 1;
     }
 
diff --git a/nscd/nscd.conf b/nscd/nscd.conf
index 0560beba0d..9cb9fa292d 100644
--- a/nscd/nscd.conf
+++ b/nscd/nscd.conf
@@ -7,11 +7,14 @@
 #
 #	logfile			<file>
 #	debug-level		<level>
-#	threads			<#threads to use>
+#	threads			<initial #threads to use>
+#	max-threads		<maximum #threads to use>
 #	server-user             <user to run server as instead of root>
 #		server-user is ignored if nscd is started with -S parameters
 #       stat-user               <user who is allowed to request statistics>
 #	reload-count		unlimited|<number>
+#	paranoia		<yes|no>
+#	restart-interval	<time in seconds>
 #
 #       enable-cache		<service> <yes|no>
 #	positive-time-to-live	<service> <time in seconds>
@@ -27,10 +30,13 @@
 
 #	logfile			/var/log/nscd.log
 #	threads			6
+#	max-threads		128
 	server-user		nscd
 #	stat-user		nocpulse
 	debug-level		0
 #	reload-count		5
+	paranoia		no
+#	restart-interval	3600
 
 	enable-cache		passwd		yes
 	positive-time-to-live	passwd		600
diff --git a/nscd/nscd.h b/nscd/nscd.h
index 3a9660d3ec..4e00f69fb9 100644
--- a/nscd/nscd.h
+++ b/nscd/nscd.h
@@ -50,6 +50,10 @@ typedef enum
 #define DEFAULT_RELOAD_LIMIT 5
 
 
+/* Time before restarting the process in paranoia mode.  */
+#define RESTART_INTERVAL (60 * 60)
+
+
 /* Structure describing dynamic part of one database.  */
 struct database_dyn
 {
@@ -98,8 +102,10 @@ extern const struct iovec grp_iov_disabled;
 extern const struct iovec hst_iov_disabled;
 
 
-/* Number of threads to run.  */
+/* Initial number of threads to run.  */
 extern int nthreads;
+/* Maximum number of threads to use.  */
+extern int max_nthreads;
 
 /* Tables for which we cache data with uid.  */
 extern int secure_in_use; /* Is one of the above 1?  */
@@ -127,6 +133,19 @@ extern unsigned int reload_count;
 /* Pagesize minus one.  */
 extern uintptr_t pagesize_m1;
 
+/* Nonzero if paranoia mode is enabled.  */
+extern int paranoia;
+/* Time after which the process restarts.  */
+extern time_t restart_time;
+/* How much time between restarts.  */
+extern time_t restart_interval;
+/* Old current working directory.  */
+extern const char *oldcwd;
+/* Old user and group ID.  */
+extern uid_t old_uid;
+extern gid_t old_gid;
+
+
 /* Prototypes for global functions.  */
 
 /* nscd.c */
diff --git a/nscd/nscd_conf.c b/nscd/nscd_conf.c
index 8a312ff459..2bca368de6 100644
--- a/nscd/nscd_conf.c
+++ b/nscd/nscd_conf.c
@@ -18,13 +18,15 @@
    02111-1307 USA.  */
 
 #include <ctype.h>
+#include <errno.h>
+#include <libintl.h>
 #include <malloc.h>
 #include <pwd.h>
 #include <stdio.h>
 #include <stdio_ext.h>
 #include <stdlib.h>
 #include <string.h>
-#include <libintl.h>
+#include <unistd.h>
 #include <sys/param.h>
 #include <sys/types.h>
 
@@ -182,6 +184,10 @@ nscd_parse_file (const char *fname, struct database_dyn dbs[lastdb])
 	  if (nthreads == -1)
 	    nthreads = MAX (atol (arg1), lastdb);
 	}
+      else if (strcmp (entry, "max-threads") == 0)
+	{
+	  max_nthreads = MAX (atol (arg1), lastdb);
+	}
       else if (strcmp (entry, "server-user") == 0)
         {
           if (!arg1)
@@ -191,7 +197,7 @@ nscd_parse_file (const char *fname, struct database_dyn dbs[lastdb])
         }
       else if (strcmp (entry, "stat-user") == 0)
         {
-          if (!arg1)
+          if (arg1 == NULL)
             dbg_log (_("Must specify user name for stat-user option"));
           else
 	    {
@@ -248,11 +254,45 @@ nscd_parse_file (const char *fname, struct database_dyn dbs[lastdb])
 		dbg_log (_("invalid value for 'reload-count': %u"), count);
 	    }
 	}
+      else if (strcmp (entry, "paranoia") == 0)
+	{
+	  if (strcmp (arg1, "no") == 0)
+	    paranoia = 0;
+	  else if (strcmp (arg1, "yes") == 0)
+	    paranoia = 1;
+	}
+      else if (strcmp (entry, "restart-interval") == 0)
+	{
+	  if (arg1 != NULL)
+	    restart_interval = atol (arg1);
+	  else
+            dbg_log (_("Must specify value for restart-interval option"));
+	}
       else
 	dbg_log (_("Unknown option: %s %s %s"), entry, arg1, arg2);
     }
   while (!feof_unlocked (fp));
 
+  if (paranoia)
+    {
+      restart_time = time (NULL) + restart_interval;
+
+      /* Save the old current workding directory if we are in paranoia
+	 mode.  We have to change back to it.  */
+      oldcwd = get_current_dir_name ();
+      if (oldcwd == NULL)
+	{
+	  dbg_log (_("\
+cannot get current working directory: %s; disabling paranoia mode"),
+		   strerror (errno));
+	  paranoia = 0;
+	}
+    }
+
+  /* Enforce sanity.  */
+  if (max_nthreads < nthreads)
+    max_nthreads = nthreads;
+
   /* Free the buffer.  */
   free (line);
   /* Close configuration file.  */
diff --git a/nscd/nscd_helper.c b/nscd/nscd_helper.c
index 3c8693a93d..0e16cb8aeb 100644
--- a/nscd/nscd_helper.c
+++ b/nscd/nscd_helper.c
@@ -160,7 +160,8 @@ get_mapping (request_type type, const char *key,
   if (head.version != DB_VERSION || head.header_size != sizeof (head)
       /* This really should not happen but who knows, maybe the update
 	 thread got stuck.  */
-      || head.timestamp + MAPPING_TIMEOUT < time (NULL))
+      || (! head.nscd_certainly_running
+	  && head.timestamp + MAPPING_TIMEOUT < time (NULL)))
     goto out_close;
 
   size_t size = (sizeof (head) + roundup (head.module * sizeof (ref_t), ALIGN)
diff --git a/nscd/nscd_initgroups.c b/nscd/nscd_initgroups.c
index d6cb00000e..ce44f654d7 100644
--- a/nscd/nscd_initgroups.c
+++ b/nscd/nscd_initgroups.c
@@ -81,8 +81,11 @@ __nscd_getgrouplist (const char *user, gid_t group, long int *size,
       sock = __nscd_open_socket (user, userlen, INITGROUPS, &initgr_resp_mem,
 				 sizeof (initgr_resp_mem));
       if (sock == -1)
-	/* nscd not running or wrong version or hosts caching disabled.  */
-	__nss_not_use_nscd_group = 1;
+	{
+	  /* nscd not running or wrong version or hosts caching disabled.  */
+	  __nss_not_use_nscd_group = 1;
+	  goto out;
+	}
 
       initgr_resp = &initgr_resp_mem;
     }
@@ -128,8 +131,12 @@ __nscd_getgrouplist (const char *user, gid_t group, long int *size,
 	}
     }
   else
-    /* No group found yet.   */
-    retval = 0;
+    {
+      /* No group found yet.   */
+      retval = 0;
+
+      assert (*size >= 1);
+    }
 
   /* Check whether GROUP is part of the mix.  If not, add it.  */
   if (retval >= 0)
diff --git a/nscd/nscd_stat.c b/nscd/nscd_stat.c
index 3e3be5bc8c..9231642278 100644
--- a/nscd/nscd_stat.c
+++ b/nscd/nscd_stat.c
@@ -143,6 +143,8 @@ receive_print_stats (void)
   int fd;
   int i;
   uid_t uid = getuid ();
+  const char *yesstr = nl_langinfo (YESSTR);
+  const char *nostr = nl_langinfo (NOSTR);
 
   /* Find out whether there is another user but root allowed to
      request statistics.  */
@@ -223,31 +225,34 @@ receive_print_stats (void)
   else
     printf (_("            %2lus  server runtime\n"), diff);
 
-  printf (_("%15lu  number of times clients had to wait\n"),
-	  data.client_queued);
+  printf (_("%15d  current number of threads\n"
+	    "%15d  maximum number of threads\n"
+	    "%15lu  number of times clients had to wait\n"
+	    "%15s  paranoia mode enabled\n"
+	    "%15lu  restart internal\n"),
+	  nthreads, max_nthreads, data.client_queued,
+	  paranoia ? yesstr : nostr, (unsigned long int) restart_interval);
 
   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);
-      const char *shared = nl_langinfo (data.dbs[i].shared ? YESSTR : NOSTR);
-      const char *persistent = nl_langinfo (data.dbs[i].persistent
-					    ? YESSTR : NOSTR);
+      const char *enabled = data.dbs[i].enabled ? yesstr : nostr;
+      const char *check_file = data.dbs[i].check_file ? yesstr : nostr;
+      const char *shared = data.dbs[i].shared ? yesstr : nostr;
+      const char *persistent = data.dbs[i].persistent ? yesstr : nostr;
 
       if (enabled[0] == '\0')
 	/* The locale does not provide this information so we have to
 	   translate it ourself.  Since we should avoid short translation
 	   terms we artifically increase the length.  */
-	enabled = data.dbs[i].enabled ? _("     yes") : _("      no");
+	enabled = data.dbs[i].enabled ? yesstr : nostr;
       if (check_file[0] == '\0')
-	check_file = data.dbs[i].check_file ? _("     yes") : _("      no");
+	check_file = data.dbs[i].check_file ? yesstr : nostr;
       if (shared[0] == '\0')
-	shared = data.dbs[i].shared ? _("     yes") : _("      no");
+	shared = data.dbs[i].shared ? yesstr : nostr;
       if (persistent[0] == '\0')
-	persistent = data.dbs[i].persistent ? _("     yes") : _("      no");
+	persistent = data.dbs[i].persistent ? yesstr : nostr;
 
       if (all == 0)
 	/* If nothing happened so far report a 0% hit rate.  */
diff --git a/nscd/selinux.c b/nscd/selinux.c
index 77651e05c3..f57f0920ae 100644
--- a/nscd/selinux.c
+++ b/nscd/selinux.c
@@ -207,8 +207,8 @@ nscd_request_avc_has_perm (int fd, request_type req)
       dbg_log (_("Error getting context of nscd"));
       goto out;
     }
-  if (avc_context_to_sid (scon, &ssid) < 0 ||
-      avc_context_to_sid (tcon, &tsid) < 0)
+  if (avc_context_to_sid (scon, &ssid) < 0
+      || avc_context_to_sid (tcon, &tsid) < 0)
     {
       dbg_log (_("Error getting sid from context"));
       goto out;
diff --git a/sysdeps/generic/memmem.c b/sysdeps/generic/memmem.c
index 9e27548dd5..c40462104a 100644
--- a/sysdeps/generic/memmem.c
+++ b/sysdeps/generic/memmem.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 1991,92,93,94,96,97,98,2000 Free Software Foundation, Inc.
+/* Copyright (C) 1991,92,93,94,96,97,98,2000,2004 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
@@ -19,6 +19,10 @@
 #include <stddef.h>
 #include <string.h>
 
+#ifndef _LIBC
+# define __builtin_expect(expr, val)   (expr)
+#endif
+
 #undef memmem
 
 /* Return the first occurrence of NEEDLE in HAYSTACK.  */
diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
index f83463738d..fd089dd236 100644
--- a/sysdeps/unix/sysv/linux/Makefile
+++ b/sysdeps/unix/sysv/linux/Makefile
@@ -150,3 +150,7 @@ ifeq ($(subdir),rt)
 CFLAGS-mq_send.c += -fexceptions
 CFLAGS-mq_receive.c += -fexceptions
 endif
+
+ifeq ($(subdir),nscd)
+CFLAGS-connections.c += -DHAVE_EPOLL
+endif