diff options
author | Jakub Jelinek <jakub@redhat.com> | 2004-10-04 08:56:18 +0000 |
---|---|---|
committer | Jakub Jelinek <jakub@redhat.com> | 2004-10-04 08:56:18 +0000 |
commit | 85148842d401edf64f9edee7e5819a947c289ed2 (patch) | |
tree | 42f228e998070f60c3bdb2018c9921b221a6851b | |
parent | 6d96590587deec027c04fe576f11cff0f445eb32 (diff) | |
download | glibc-85148842d401edf64f9edee7e5819a947c289ed2.tar.gz glibc-85148842d401edf64f9edee7e5819a947c289ed2.tar.xz glibc-85148842d401edf64f9edee7e5819a947c289ed2.zip |
Updated to fedora-glibc-20041004T0747 cvs/fedora-glibc-2_3_3-64
-rw-r--r-- | ChangeLog | 84 | ||||
-rw-r--r-- | assert/assert.h | 23 | ||||
-rw-r--r-- | elf/rtld.c | 2 | ||||
-rw-r--r-- | fedora/branch.mk | 4 | ||||
-rw-r--r-- | fedora/glibc.spec.in | 15 | ||||
-rw-r--r-- | fedora/glibc_post_upgrade.c | 157 | ||||
-rw-r--r-- | malloc/arena.c | 62 | ||||
-rw-r--r-- | malloc/malloc.c | 20 | ||||
-rw-r--r-- | nscd/Makefile | 50 | ||||
-rw-r--r-- | nscd/connections.c | 702 | ||||
-rw-r--r-- | nscd/dbg_log.c | 9 | ||||
-rw-r--r-- | nscd/nscd.c | 17 | ||||
-rw-r--r-- | nscd/nscd.conf | 8 | ||||
-rw-r--r-- | nscd/nscd.h | 21 | ||||
-rw-r--r-- | nscd/nscd_conf.c | 44 | ||||
-rw-r--r-- | nscd/nscd_helper.c | 3 | ||||
-rw-r--r-- | nscd/nscd_initgroups.c | 15 | ||||
-rw-r--r-- | nscd/nscd_stat.c | 29 | ||||
-rw-r--r-- | nscd/selinux.c | 4 | ||||
-rw-r--r-- | sysdeps/generic/memmem.c | 6 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/Makefile | 4 |
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 |