1 /* This file is part of unscd, a complete nscd replacement.
2 * Copyright (C) 2007 Denys Vlasenko. Licensed under the GPL version 2. */
4 /* unscd is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 2 of the License.
8 * unscd is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You can download the GNU General Public License from the GNU website
14 * at http://www.gnu.org/ or write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
20 gcc -Wall -Wunused-parameter -Os -o nscd nscd.c
22 gcc -fomit-frame-pointer -Wl,--sort-section -Wl,alignment -Wl,--sort-common
27 nscd problems are not exactly unheard of. Over the years, there were
28 quite a bit of bugs in it. This leads people to invent babysitters
29 which restart crashed/hung nscd. This is ugly.
31 After looking at nscd source in glibc I arrived to the conclusion
32 that its design is contributing to this significantly. Even if nscd's
33 code is 100.00% perfect and bug-free, it can still suffer from bugs
34 in libraries it calls.
36 As designed, it's a multithreaded program which calls NSS libraries.
37 These libraries are not part of libc, they may be provided
38 by third-party projects (samba, ldap, you name it).
40 Thus nscd cannot be sure that libraries it calls do not have memory
41 or file descriptor leaks and other bugs.
43 Since nscd is multithreaded program with single shared cache,
44 any resource leak in any NSS library has cumulative effect.
45 Even if a NSS library leaks a file descriptor 0.01% of the time,
46 this will make nscd crash or hang after some time.
48 Of course bugs in NSS .so modules should be fixed, but meanwhile
49 I do want nscd which does not crash or lock up.
51 So I went ahead and wrote a replacement.
53 It is a single-threaded server process which offloads all NSS
54 lookups to worker children (not threads, but fully independent
55 processes). Cache hits are handled by parent. Only cache misses
56 start worker children. This design is immune against
57 resource leaks and hangs in NSS libraries.
59 It is also many times smaller.
61 Currently (v0.36) it emulates glibc nscd pretty closely
62 (handles same command line flags and config file), and is moderately tested.
64 Please note that as of 2008-08 it is not in wide use (yet?).
65 If you have trouble compiling it, see an incompatibility with
66 "standard" one or experience hangs/crashes, please report it to
67 vda.linux@googlemail.com
69 ***********************************************************************/
71 /* Make struct ucred appear in sys/socket.h */
73 /* For all good things */
90 #include <sys/socket.h>
92 #include <sys/types.h>
98 /* For inet_ntoa (for debug build only) */
99 #include <arpa/inet.h>
102 * 0.21 add SEGV reporting to worker
103 * 0.22 don't do freeaddrinfo() in GETAI worker, it's crashy
104 * 0.23 add parameter parsing
105 * 0.24 add conf file parsing, not using results yet
106 * 0.25 used some of conf file settings (not tested)
107 * 0.26 almost all conf file settings are wired up
108 * 0.27 a bit more of almost all conf file settings are wired up
109 * 0.28 optimized cache aging
110 * 0.29 implemented invalidate and shutdown options
111 * 0.30 fixed buglet (sizeof(ptr) != sizeof(array))
112 * 0.31 reduced client_info by one member
113 * 0.32 fix nttl/size defaults; simpler check for worker child in main()
114 * 0.33 tweak includes so that it builds on my new machine (64-bit userspace);
115 * do not die on unknown service name, just warn
116 * ("services" is a new service we don't support)
117 * 0.34 create /var/run/nscd/nscd.pid pidfile like glibc nscd 2.8 does;
118 * delay setuid'ing itself to server-user after log and pidfile are open
119 * 0.35 readlink /proc/self/exe and use result if execing /proc/self/exe fails
120 * 0.36 excercise extreme paranoia handling server-user option;
121 * a little bit more verbose logging:
122 * L_DEBUG2 log level added, use debug-level 7 to get it
123 * 0.37 users reported over-zealous "detected change in /etc/passwd",
124 * apparently stat() returns random garbage in unused padding
125 * on some systems. Made the check less paranoid.
126 * 0.38 log POLLHUP better
127 * 0.39 log answers to client better, log getpwnam in the worker,
128 * pass debug level value down to worker.
129 * 0.40 fix handling of shutdown and invalidate requests;
130 * fix bug with answer written in several pieces
131 * 0.40.1 set hints.ai_socktype = SOCK_STREAM in GETAI request
132 * 0.41 eliminate double caching of two near-simultaneous identical requests -
134 * 0.42 execute /proc/self/exe by link name first (better comm field)
135 * 0.43 fix off-by-one error in setgroups
136 * 0.44 make -d[ddd] bump up debug - easier to explain to users
137 * how to produce detailed log (no nscd.conf tweaking)
138 * 0.45 Fix out-of-bounds array access and log/pid file permissions -
139 * thanks to Sebastian Krahmer (krahmer AT suse.de)
140 * 0.46 fix a case when we forgot to remove a future entry on worker failure
141 * 0.47 fix nscd without -d to not bump debug level
143 #define PROGRAM_VERSION "0.47"
145 #define DEBUG_BUILD 1
152 #define ARRAY_SIZE(x) ((unsigned)(sizeof(x) / sizeof((x)[0])))
154 #define NORETURN __attribute__ ((__noreturn__))
157 #ifdef MY_CPU_HATES_CHARS
158 typedef int smallint;
160 typedef signed char smallint;
166 L_DEBUG = ((1 << 1) * DEBUG_BUILD),
167 L_DEBUG2 = ((1 << 2) * DEBUG_BUILD),
168 L_DUMP = ((1 << 3) * DEBUG_BUILD),
174 static smallint debug = D_DAEMON;
176 static void verror(const char *s, va_list p, const char *strerr)
179 int sz, rem, strerr_len;
183 if (debug & D_STAMP) {
184 gettimeofday(&tv, NULL);
185 sz = sprintf(msgbuf, "%02u:%02u:%02u.%05u ",
186 (unsigned)((tv.tv_sec / (60*60)) % 24),
187 (unsigned)((tv.tv_sec / 60) % 60),
188 (unsigned)(tv.tv_sec % 60),
189 (unsigned)(tv.tv_usec / 10));
191 rem = sizeof(msgbuf) - sz;
192 sz += vsnprintf(msgbuf + sz, rem, s, p);
193 rem = sizeof(msgbuf) - sz; /* can be negative after this! */
196 strerr_len = strlen(strerr);
197 if (rem >= strerr_len + 4) { /* ": STRERR\n\0" */
200 strcpy(msgbuf + sz, strerr);
209 fputs(msgbuf, stderr);
212 static void error(const char *msg, ...)
216 verror(msg, p, NULL);
220 static void error_and_die(const char *msg, ...) NORETURN;
221 static void error_and_die(const char *msg, ...)
225 verror(msg, p, NULL);
230 static void perror_and_die(const char *msg, ...) NORETURN;
231 static void perror_and_die(const char *msg, ...)
235 /* Guard against "<error message>: Success" */
236 verror(msg, p, errno ? strerror(errno) : NULL);
241 static void nscd_log(int mask, const char *msg, ...)
246 verror(msg, p, NULL);
251 #define log(lvl, ...) do { if (lvl) nscd_log(lvl, __VA_ARGS__); } while (0)
254 static void dump(const void *ptr, int len)
257 const unsigned char *buf;
260 if (!(debug & L_DUMP))
265 int chunk = ((len >= 16) ? 16 : len);
267 "%02x %02x %02x %02x %02x %02x %02x %02x "
268 "%02x %02x %02x %02x %02x %02x %02x %02x " + (16-chunk) * 5,
269 buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7],
270 buf[8], buf[9],buf[10],buf[11],buf[12],buf[13],buf[14],buf[15]
272 fprintf(stderr, "%*s", (16-chunk) * 3, "");
276 unsigned char c = *buf++;
277 *p++ = (c >= 32 && c < 127 ? c : '.');
285 void dump(const void *ptr, int len);
288 #define hex_dump(p,n) do { if (L_DUMP) dump(p,n); } while (0)
290 static int xopen3(const char *pathname, int flags, int mode)
292 int fd = open(pathname, flags, mode);
294 perror_and_die("open");
298 static void xpipe(int *fds)
301 perror_and_die("pipe");
304 static void xexecve(const char *filename, char **argv, char **envp) NORETURN;
305 static void xexecve(const char *filename, char **argv, char **envp)
307 execve(filename, argv, envp);
308 perror_and_die("cannot re-exec %s", filename);
311 static void ndelay_on(int fd)
313 int fl = fcntl(fd, F_GETFL);
315 perror_and_die("F_GETFL");
316 if (fcntl(fd, F_SETFL, fl | O_NONBLOCK) < 0)
317 perror_and_die("setting O_NONBLOCK");
320 static void close_on_exec(int fd)
322 if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0)
323 perror_and_die("setting FD_CLOEXEC");
326 static unsigned monotonic_ms(void)
329 if (syscall(__NR_clock_gettime, CLOCK_MONOTONIC, &ts))
330 perror_and_die("clock_gettime(MONOTONIC)");
331 return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
334 static unsigned strsize(const char *str)
336 return strlen(str) + 1;
339 static unsigned strsize_aligned4(const char *str)
341 return (strlen(str) + 1 + 3) & (~3);
344 static ssize_t safe_read(int fd, void *buf, size_t count)
348 n = read(fd, buf, count);
349 } while (n < 0 && errno == EINTR);
353 static ssize_t full_read(int fd, void *buf, size_t len)
359 cc = safe_read(fd, buf, len);
361 return cc; /* read() returns -1 on failure. */
364 buf = ((char *)buf) + cc;
372 static void xsafe_read(int fd, void *buf, size_t len)
374 if (len != safe_read(fd, buf, len))
375 perror_and_die("short read");
377 static void xfull_read(int fd, void *buf, size_t len)
379 if (len != full_read(fd, buf, len))
380 perror_and_die("short read");
384 static ssize_t safe_write(int fd, const void *buf, size_t count)
388 n = write(fd, buf, count);
389 } while (n < 0 && errno == EINTR);
393 static ssize_t full_write(int fd, const void *buf, size_t len)
400 cc = safe_write(fd, buf, len);
402 return cc; /* write() returns -1 on failure. */
404 buf = ((const char *)buf) + cc;
410 static void xsafe_write(int fd, const void *buf, size_t count)
412 if (count != safe_write(fd, buf, count))
413 perror_and_die("short write of %ld bytes", (long)count);
415 static void xfull_write(int fd, const void *buf, size_t count)
417 if (count != full_write(fd, buf, count))
418 perror_and_die("short write of %ld bytes", (long)count);
421 static void xmovefd(int from_fd, int to_fd)
423 if (from_fd != to_fd) {
424 if (dup2(from_fd, to_fd) < 0)
425 perror_and_die("dup2");
430 static unsigned getnum(const char *str)
432 if (str[0] >= '0' && str[0] <= '9') {
434 unsigned long l = strtoul(str, &p, 10);
435 /* must not overflow int even after x1000 */
436 if (!*p && l <= INT_MAX / 1000)
439 error_and_die("malformed or too big number '%s'", str);
442 static char *skip_whitespace(const char *s)
444 /* NB: isspace('\0') returns 0 */
445 while (isspace(*s)) ++s;
449 static char *skip_non_whitespace(const char *s)
451 while (*s && !isspace(*s)) ++s;
455 static void *xmalloc(unsigned sz)
457 void *p = malloc(sz);
459 error_and_die("out of memory");
463 static void *xzalloc(unsigned sz)
465 void *p = xmalloc(sz);
470 static void *xrealloc(void *p, unsigned size)
472 p = realloc(p, size);
474 error_and_die("out of memory");
478 static const char *xstrdup(const char *str)
480 const char *p = strdup(str);
482 error_and_die("out of memory");
497 static const char srv_name[3][7] = {
506 smallint srv_enable[3];
507 smallint check_files[3];
512 /* We try to closely mimic glibc nscd */
513 .logfile = NULL, /* default is to not have a log file */
515 .srv_enable = { 0, 0, 0 },
516 .check_files = { 1, 1, 1 },
517 .pttl = { 3600, 3600, 3600 },
518 .nttl = { 20, 60, 20 },
519 /* huh, what is the default cache size in glibc nscd? */
520 .size = { 256 * 8 / 3, 256 * 8 / 3, 256 * 8 / 3 },
523 static const char default_conffile[] = "/etc/nscd.conf";
524 static const char *self_exe_points_to = "/proc/self/exe";
528 ** Clients, workers machinery
531 /* Header common to all requests */
532 #define USER_REQ_STRUCT \
533 uint32_t version; /* Version number of the daemon interface */ \
534 uint32_t type; /* Service requested */ \
535 uint32_t key_len; /* Key length */
537 typedef struct user_req_header {
543 MAX_USER_REQ_SIZE = 1024,
544 USER_HDR_SIZE = sizeof(user_req_header),
545 /* DNS queries time out after 20 seconds,
546 * we will allow for a bit more */
547 WORKER_TIMEOUT_SEC = 30,
548 CLIENT_TIMEOUT_MS = 100,
549 SMALL_POLL_TIMEOUT_MS = 200,
552 typedef struct user_req {
554 struct { /* as came from client */
557 struct { /* when stored in cache, overlaps .version */
559 /* (timestamp24 * 256) == timestamp in ms */
560 unsigned timestamp24:24;
563 char reqbuf[MAX_USER_REQ_SIZE - USER_HDR_SIZE];
566 /* Compile-time check for correct size */
567 struct BUG_wrong_user_req_size {
568 char BUG_wrong_user_req_size[sizeof(user_req) == MAX_USER_REQ_SIZE ? 1 : -1];
580 SHUTDOWN, /* Shut the server down */
581 GETSTAT, /* Get the server statistic */
582 INVALIDATE, /* Invalidate one special cache */
594 static const char *const typestr[] = {
595 "GETPWBYNAME", /* done */
596 "GETPWBYUID", /* done */
597 "GETGRBYNAME", /* done */
598 "GETGRBYGID", /* done */
599 "GETHOSTBYNAME", /* done */
600 "GETHOSTBYNAMEv6", /* done */
601 "GETHOSTBYADDR", /* done */
602 "GETHOSTBYADDRv6", /* done */
603 "SHUTDOWN", /* done */
604 "GETSTAT", /* info? */
605 "INVALIDATE", /* done */
606 /* won't do: nscd passes a name of shmem segment
607 * which client can map and "see" the db */
609 "GETFDGR", /* won't do */
610 "GETFDHST", /* won't do */
612 "INITGROUPS", /* done */
613 "GETSERVBYNAME", /* prio 3 (no caching?) */
614 "GETSERVBYPORT", /* prio 3 (no caching?) */
615 "GETFDSERV" /* won't do */
618 extern const char *const typestr[];
620 static const smallint type_to_srv[] = {
621 [GETPWBYNAME ] = SRV_PASSWD,
622 [GETPWBYUID ] = SRV_PASSWD,
623 [GETGRBYNAME ] = SRV_GROUP,
624 [GETGRBYGID ] = SRV_GROUP,
625 [GETHOSTBYNAME ] = SRV_HOSTS,
626 [GETHOSTBYNAMEv6 ] = SRV_HOSTS,
627 [GETHOSTBYADDR ] = SRV_HOSTS,
628 [GETHOSTBYADDRv6 ] = SRV_HOSTS,
629 [GETAI ] = SRV_HOSTS,
630 [INITGROUPS ] = SRV_GROUP,
633 static int unsupported_ureq_type(unsigned type)
635 if (type == GETAI) return 0;
636 if (type == INITGROUPS) return 0;
637 if (type == GETSTAT) return 1;
638 if (type > INVALIDATE) return 1;
643 typedef struct client_info {
644 /* if client_fd != 0, we are waiting for the reply from worker
645 * on pfd[i].fd, and client_fd is saved client's fd
646 * (we need to put it back into pfd[i].fd later) */
648 unsigned bytecnt; /* bytes read from client */
649 unsigned bufidx; /* buffer# in global client_buf[] */
651 unsigned respos; /* response */
652 user_req *resptr; /* response */
653 user_req **cache_pp; /* cache entry address */
654 user_req *ureq; /* request (points to client_buf[x]) */
657 static unsigned g_now_ms;
658 static int min_closed = INT_MAX;
659 static int cnt_closed = 0;
660 static int num_clients = 2; /* two listening sockets are "clients" too */
662 /* We read up to max_reqnum requests in parallel */
663 static unsigned max_reqnum = 14;
665 static char (*client_buf)[MAX_USER_REQ_SIZE];
666 static char *busy_cbuf;
667 static struct pollfd *pfd;
668 static client_info *cinfo;
670 /* Request, response and cache data structures:
672 * cache[] (defined later):
673 * cacheline_t cache[cache_size] array, or in other words,
674 * user_req* cache[cache_size][8] array.
675 * Every client request is hashed, hash value determines which cache[x]
676 * will have the response stored in one of its 8 elements.
677 * Cache entries have this format: request, then padding to 32 bits,
679 * Addresses in cache[x][y] may be NULL or:
680 * (&client_buf[z]) & 1: the cache miss is in progress ("future entry"):
681 * "the data is not in the cache (yet), wait for it to appear"
682 * (&client_buf[z]) & 3: the cache miss is in progress and other clients
683 * also want the same data ("shared future entry")
684 * else (non-NULL but low two bits are 0): cached data in malloc'ed block
686 * Each of these is a [max_reqnum] sized array:
687 * pfd[i] - given to poll() to wait for requests and replies.
688 * .fd: first two pfd[i]: listening Unix domain sockets, else
689 * .fd: open fd to a client, for reading client's request, or
690 * .fd: open fd to a worker, to send request and get response back
691 * cinfo[i] - auxiliary client data for pfd[i]
692 * .client_fd: open fd to a client, in case we already had read its
693 * request and got a cache miss, and created a worker or
694 * wait for another client's worker.
695 * Otherwise, it's 0 and client's fd is in pfd[i].fd
696 * .bufidx: index in client_buf[] we store client's request in
697 * .bytecnt: size of the request
698 * .started_ms: used to time out unresponsive clients
701 * .cache_pp: &cache[x][y] where the response is, or will be stored.
703 * When a client has received its reply (or otherwise closed (timeout etc)),
704 * corresponding pfd[i] and cinfo[i] are removed by shifting [i+1], [i+2] etc
705 * elements down, so that both arrays never have free holes.
706 * [num_clients] is always the first free element.
708 * Each of these also is a [max_reqnum] sized array, but indexes
709 * do not correspond directly to pfd[i] and cinfo[i]:
710 * client_buf[n][MAX_USER_REQ_SIZE] - buffers we read client requests into
711 * busy_cbuf[n] - bool flags marking busy client_buf[]
713 /* Possible reductions:
714 * fd, bufidx - uint8_t
715 * started_ms -> uint16_t started_s
716 * ureq - eliminate (derivable from bufidx?)
719 /* Are special bits 0? is it a true cached entry? */
720 #define CACHED_ENTRY(p) ( ((long)(p) & 3) == 0 )
721 /* Are special bits 11? is it a shared future cache entry? */
722 #define CACHE_SHARED(p) ( ((long)(p) & 3) == 3 )
723 /* Return a ptr with special bits cleared (used for accessing data) */
724 #define CACHE_PTR(p) ( (void*) ((long)(p) & ~(long)3) )
725 /* Return a ptr with special bits set to x1: make future cache entry ptr */
726 #define MAKE_FUTURE_PTR(p) ( (void*) ((long)(p) | 1) )
727 /* Modify ptr, set special bits to 11: shared future cache entry */
728 #define MARK_PTR_SHARED(pp) ( *(long*)(pp) |= 3 )
730 static inline unsigned ureq_size(const user_req *ureq)
732 return sizeof(user_req_header) + ureq->key_len;
735 static unsigned cache_age(const user_req *ureq)
737 if (!CACHED_ENTRY(ureq))
739 return (uint32_t) (g_now_ms - (ureq->timestamp24 << 8));
742 static void set_cache_timestamp(user_req *ureq)
744 ureq->timestamp24 = g_now_ms >> 8;
747 static int alloc_buf_no(void)
752 next_buf = (next_buf + 1) % max_reqnum;
753 if (!busy_cbuf[cur]) {
757 } while (next_buf != n);
758 error_and_die("no free bufs?!");
761 static inline void *bufno2buf(int i)
763 return client_buf[i];
766 static void close_client(unsigned i)
768 log(L_DEBUG, "closing client %u (fd %u,%u)", i, pfd[i].fd, cinfo[i].client_fd);
769 /* Paranoia. We had nasty bugs where client was closed twice. */
770 if (pfd[i].fd == 0) ////
773 if (cinfo[i].client_fd && cinfo[i].client_fd != pfd[i].fd)
774 close(cinfo[i].client_fd);
775 pfd[i].fd = 0; /* flag as unused (coalescing needs this) */
776 busy_cbuf[cinfo[i].bufidx] = 0;
784 ** nscd API <-> C API conversion
787 typedef struct response_header {
788 uint32_t version_or_size;
793 typedef struct initgr_response_header {
794 uint32_t version_or_size;
797 /* code assumes gid_t == int32, let's check that */
798 int32_t gid[sizeof(gid_t) == sizeof(int32_t) ? 0 : -1];
799 /* char user_str[as_needed]; */
800 } initgr_response_header;
802 static initgr_response_header *obtain_initgroups(const char *username)
804 struct initgr_response_header *resp;
806 enum { MAGIC_OFFSET = sizeof(*resp) / sizeof(int32_t) };
810 pw = getpwnam(username);
813 resp->version_or_size = sizeof(*resp);
819 /* getgrouplist may be very expensive, it's much better to allocate
820 * a bit more than to run getgrouplist twice */
824 sz = sizeof(*resp) + sizeof(resp->gid[0]) * ngroups;
825 resp = xrealloc(resp, sz);
826 } while (getgrouplist(username, pw->pw_gid, (gid_t*) &resp->gid, &ngroups) == -1);
827 log(L_DEBUG, "ngroups=%d", ngroups);
829 sz = sizeof(*resp) + sizeof(resp->gid[0]) * ngroups;
830 /* resp = xrealloc(resp, sz); - why bother */
831 resp->version_or_size = sz;
833 resp->ngrps = ngroups;
838 typedef struct pw_response_header {
839 uint32_t version_or_size;
842 int32_t pw_passwd_len;
845 int32_t pw_gecos_len;
847 int32_t pw_shell_len;
848 /* char pw_name[pw_name_len]; */
849 /* char pw_passwd[pw_passwd_len]; */
850 /* char pw_gecos[pw_gecos_len]; */
851 /* char pw_dir[pw_dir_len]; */
852 /* char pw_shell[pw_shell_len]; */
853 } pw_response_header;
855 static pw_response_header *marshal_passwd(struct passwd *pw)
858 pw_response_header *resp;
859 unsigned pw_name_len;
860 unsigned pw_passwd_len;
861 unsigned pw_gecos_len;
863 unsigned pw_shell_len;
864 unsigned sz = sizeof(*resp);
866 sz += (pw_name_len = strsize(pw->pw_name));
867 sz += (pw_passwd_len = strsize(pw->pw_passwd));
868 sz += (pw_gecos_len = strsize(pw->pw_gecos));
869 sz += (pw_dir_len = strsize(pw->pw_dir));
870 sz += (pw_shell_len = strsize(pw->pw_shell));
873 resp->version_or_size = sz;
879 resp->pw_name_len = pw_name_len;
880 resp->pw_passwd_len = pw_passwd_len;
881 resp->pw_uid = pw->pw_uid;
882 resp->pw_gid = pw->pw_gid;
883 resp->pw_gecos_len = pw_gecos_len;
884 resp->pw_dir_len = pw_dir_len;
885 resp->pw_shell_len = pw_shell_len;
886 p = (char*)(resp + 1);
887 strcpy(p, pw->pw_name); p += pw_name_len;
888 strcpy(p, pw->pw_passwd); p += pw_passwd_len;
889 strcpy(p, pw->pw_gecos); p += pw_gecos_len;
890 strcpy(p, pw->pw_dir); p += pw_dir_len;
891 strcpy(p, pw->pw_shell); p += pw_shell_len;
892 log(L_DEBUG, "sz:%u realsz:%u", sz, p - (char*)resp);
897 typedef struct gr_response_header {
898 uint32_t version_or_size;
900 int32_t gr_name_len; /* strlen(gr->gr_name) + 1; */
901 int32_t gr_passwd_len; /* strlen(gr->gr_passwd) + 1; */
902 int32_t gr_gid; /* gr->gr_gid */
903 int32_t gr_mem_cnt; /* while (gr->gr_mem[gr_mem_cnt]) ++gr_mem_cnt; */
904 /* int32_t gr_mem_len[gr_mem_cnt]; */
905 /* char gr_name[gr_name_len]; */
906 /* char gr_passwd[gr_passwd_len]; */
907 /* char gr_mem[gr_mem_cnt][gr_mem_len[i]]; */
908 /* char gr_gid_str[as_needed]; - huh? */
909 /* char orig_key[as_needed]; - needed?? I don't do this ATM... */
911 glibc adds gr_gid_str, but client doesn't get/use it:
912 writev(3, [{"\2\0\0\0\2\0\0\0\5\0\0\0", 12}, {"root\0", 5}], 2) = 17
913 poll([{fd=3, events=POLLIN|POLLERR|POLLHUP, revents=POLLIN}], 1, 5000) = 1
914 read(3, "\2\0\0\0\1\0\0\0\10\0\0\0\4\0\0\0\0\0\0\0\0\0\0\0", 24) = 24
915 readv(3, [{"", 0}, {"root\0\0\0\0\0\0\0\0", 12}], 2) = 12
918 } gr_response_header;
920 static gr_response_header *marshal_group(struct group *gr)
923 gr_response_header *resp;
925 unsigned sz = sizeof(*resp);
927 sz += strsize(gr->gr_name);
928 sz += strsize(gr->gr_passwd);
930 while (gr->gr_mem[gr_mem_cnt]) {
931 sz += strsize(gr->gr_mem[gr_mem_cnt]);
934 /* for int32_t gr_mem_len[gr_mem_cnt]; */
935 sz += gr_mem_cnt * sizeof(int32_t);
938 resp->version_or_size = sz;
944 resp->gr_name_len = strsize(gr->gr_name);
945 resp->gr_passwd_len = strsize(gr->gr_passwd);
946 resp->gr_gid = gr->gr_gid;
947 resp->gr_mem_cnt = gr_mem_cnt;
948 p = (char*)(resp + 1);
949 /* int32_t gr_mem_len[gr_mem_cnt]; */
951 while (gr->gr_mem[gr_mem_cnt]) {
952 *(uint32_t*)p = strsize(gr->gr_mem[gr_mem_cnt]);
956 /* char gr_name[gr_name_len]; */
957 strcpy(p, gr->gr_name);
958 p += strsize(gr->gr_name);
959 /* char gr_passwd[gr_passwd_len]; */
960 strcpy(p, gr->gr_passwd);
961 p += strsize(gr->gr_passwd);
962 /* char gr_mem[gr_mem_cnt][gr_mem_len[i]]; */
964 while (gr->gr_mem[gr_mem_cnt]) {
965 strcpy(p, gr->gr_mem[gr_mem_cnt]);
966 p += strsize(gr->gr_mem[gr_mem_cnt]);
969 log(L_DEBUG, "sz:%u realsz:%u", sz, p - (char*)resp);
974 typedef struct hst_response_header {
975 uint32_t version_or_size;
978 int32_t h_aliases_cnt;
979 int32_t h_addrtype; /* AF_INET or AF_INET6 */
980 int32_t h_length; /* 4 or 16 */
981 int32_t h_addr_list_cnt;
983 /* char h_name[h_name_len]; - we pad it to 4 bytes */
984 /* uint32_t h_aliases_len[h_aliases_cnt]; */
985 /* char h_addr_list[h_addr_list_cnt][h_length]; - every one is the same size [h_length] (4 or 16) */
986 /* char h_aliases[h_aliases_cnt][h_aliases_len[i]]; */
987 } hst_response_header;
989 static hst_response_header *marshal_hostent(struct hostent *h)
992 hst_response_header *resp;
994 unsigned h_aliases_cnt;
995 unsigned h_addr_list_cnt;
996 unsigned sz = sizeof(*resp);
998 /* char h_name[h_name_len] */
999 sz += h_name_len = strsize_aligned4(h->h_name);
1000 h_addr_list_cnt = 0;
1001 while (h->h_addr_list[h_addr_list_cnt]) {
1004 /* char h_addr_list[h_addr_list_cnt][h_length] */
1005 sz += h_addr_list_cnt * h->h_length;
1007 while (h->h_aliases[h_aliases_cnt]) {
1008 /* char h_aliases[h_aliases_cnt][h_aliases_len[i]] */
1009 sz += strsize(h->h_aliases[h_aliases_cnt]);
1012 /* uint32_t h_aliases_len[h_aliases_cnt] */
1013 sz += h_aliases_cnt * 4;
1016 resp->version_or_size = sz;
1018 /*resp->found = 0;*/
1019 resp->error = HOST_NOT_FOUND;
1023 resp->h_name_len = h_name_len;
1024 resp->h_aliases_cnt = h_aliases_cnt;
1025 resp->h_addrtype = h->h_addrtype;
1026 resp->h_length = h->h_length;
1027 resp->h_addr_list_cnt = h_addr_list_cnt;
1028 /*resp->error = 0;*/
1029 p = (char*)(resp + 1);
1030 /* char h_name[h_name_len]; */
1031 strcpy(p, h->h_name);
1033 /* uint32_t h_aliases_len[h_aliases_cnt]; */
1035 while (h->h_aliases[h_aliases_cnt]) {
1036 *(uint32_t*)p = strsize(h->h_aliases[h_aliases_cnt]);
1040 /* char h_addr_list[h_addr_list_cnt][h_length]; */
1041 h_addr_list_cnt = 0;
1042 while (h->h_addr_list[h_addr_list_cnt]) {
1043 memcpy(p, h->h_addr_list[h_addr_list_cnt], h->h_length);
1047 /* char h_aliases[h_aliases_cnt][h_aliases_len[i]]; */
1049 while (h->h_aliases[h_aliases_cnt]) {
1050 strcpy(p, h->h_aliases[h_aliases_cnt]);
1051 p += strsize(h->h_aliases[h_aliases_cnt]);
1054 log(L_DEBUG, "sz:%u realsz:%u", sz, p - (char*)resp);
1059 /* Reply to addrinfo query */
1060 typedef struct ai_response_header {
1061 uint32_t version_or_size;
1067 /* char ai_addr[naddrs][4 or 16]; - addrslen bytes in total */
1068 /* char ai_family[naddrs]; - AF_INET[6] each (determines ai_addr[i] length) */
1069 /* char ai_canonname[canonlen]; */
1070 } ai_response_header;
1072 static ai_response_header *obtain_addrinfo(const char *hostname)
1074 struct addrinfo hints;
1075 struct addrinfo *ai;
1076 struct addrinfo *ap;
1077 ai_response_header *resp;
1081 unsigned naddrs = 0;
1082 unsigned addrslen = 0;
1083 unsigned canonlen = 0;
1085 memset(&hints, 0, sizeof(hints));
1086 hints.ai_flags = AI_CANONNAME;
1087 /* kills dups (one for each possible SOCK_xxx) */
1088 /* this matches glibc behavior */
1089 hints.ai_socktype = SOCK_STREAM;
1090 ai = NULL; /* on failure getaddrinfo may leave it as-is */
1091 err = getaddrinfo(hostname, NULL, &hints, &ai);
1095 if (ai->ai_canonname)
1096 sz += canonlen = strsize(ai->ai_canonname);
1100 addrslen += (ap->ai_family == AF_INET ? 4 : 16);
1103 sz += naddrs + addrslen;
1106 resp->version_or_size = sz;
1109 /*resp->found = 0;*/
1113 resp->naddrs = naddrs;
1114 resp->addrslen = addrslen;
1115 resp->canonlen = canonlen;
1116 p = (char*)(resp + 1);
1117 family = p + addrslen;
1120 /* char ai_family[naddrs]; */
1121 *family++ = ap->ai_family;
1122 /* char ai_addr[naddrs][4 or 16]; */
1123 if (ap->ai_family == AF_INET) {
1124 memcpy(p, &(((struct sockaddr_in*)(ap->ai_addr))->sin_addr), 4);
1127 memcpy(p, &(((struct sockaddr_in6*)(ap->ai_addr))->sin6_addr), 16);
1132 /* char ai_canonname[canonlen]; */
1133 if (ai->ai_canonname)
1134 strcpy(family, ai->ai_canonname);
1135 log(L_DEBUG, "sz:%u realsz:%u", sz, family + strsize(ai->ai_canonname) - (char*)resp);
1137 /* glibc 2.3.6 segfaults here sometimes
1138 * (maybe my mistake, fixed by "ai = NULL;" above).
1139 * Since we are in worker and are going to exit anyway, why bother? */
1140 /*freeaddrinfo(ai);*/
1149 /* one 8-element "cacheline" */
1150 typedef user_req *cacheline_t[8];
1151 static unsigned cache_size;
1152 /* Points to cacheline_t cache[cache_size] array, or in other words,
1153 * points to user_req* cache[cache_size][8] array */
1154 static cacheline_t *cache;
1155 static unsigned cached_cnt;
1156 static unsigned cache_access_cnt = 1; /* prevent division by zero */
1157 static unsigned cache_hit_cnt = 1;
1158 static unsigned last_age_time;
1159 static unsigned aging_interval_ms;
1160 static unsigned min_aging_interval_ms;
1162 static response_header *ureq_response(user_req *ureq)
1164 /* Skip query part, find answer part
1165 * (answer is 32-bit aligned) */
1166 return (void*) ((char*)ureq + ((ureq_size(ureq) + 3) & ~3));
1169 /* This hash is supposed to be good for short textual data */
1170 static uint32_t bernstein_hash(void *p, unsigned sz, uint32_t hash)
1174 hash = (32 * hash + hash) ^ *key++;
1179 static void free_refcounted_ureq(user_req **ureqp)
1181 user_req *ureq = *ureqp;
1183 if (!CACHED_ENTRY(ureq))
1186 if (ureq->refcount) {
1189 log(L_DEBUG2, "refcount == 0, free(%p)", ureq);
1195 static user_req **lookup_in_cache(user_req *ureq)
1197 user_req **cacheline;
1201 unsigned ureq_sz = ureq_size(ureq);
1203 /* prevent overflow and division by zero */
1205 if ((int)cache_access_cnt < 0) {
1206 cache_access_cnt = (cache_access_cnt >> 1) + 1;
1207 cache_hit_cnt = (cache_hit_cnt >> 1) + 1;
1210 hash = bernstein_hash(&ureq->key_len, ureq_sz - offsetof(user_req, key_len), ureq->type);
1211 log(L_DEBUG2, "hash:%08x", hash);
1212 hash = hash % cache_size;
1213 cacheline = cache[hash];
1216 for (i = 0; i < 8; i++) {
1217 user_req *cached = CACHE_PTR(cacheline[i]);
1219 if (free_cache == -1)
1223 /* ureq->version is always 2 and is reused in cache
1224 * for other purposes, we need to skip it here */
1225 if (memcmp(&ureq->type, &cached->type, ureq_sz - offsetof(user_req, type)) == 0) {
1226 log(L_DEBUG, "found in cache[%u][%u]", hash, i);
1228 return &cacheline[i];
1232 if (free_cache >= 0) {
1235 log(L_DEBUG, "not found, using free cache[%u][%u]", hash, i);
1239 unsigned oldest_idx = 0;
1240 unsigned oldest_age = 0;
1241 for (i = 0; i < 8; i++) {
1242 unsigned age = cache_age(cacheline[i]);
1243 if (age > oldest_age) {
1248 if (oldest_age == 0) {
1249 /* All entries in cacheline are "future" entries!
1250 * This is very unlikely, but we must still work correctly.
1251 * We call this "fake cache entry".
1252 * The data will be "cached" only for the duration
1253 * of this client's request lifetime.
1255 log(L_DEBUG, "not found, and cache[%u] is full: using fake cache entry", hash);
1259 log(L_DEBUG, "not found, freeing and reusing cache[%u][%u] (age %u)", hash, i, oldest_age);
1260 free_refcounted_ureq(&cacheline[i]);
1263 cacheline[i] = MAKE_FUTURE_PTR(ureq);
1264 return &cacheline[i];
1267 static void age_cache(unsigned free_all, int srv)
1269 user_req **cp = *cache;
1271 unsigned sv = cached_cnt;
1273 log(L_DEBUG, "aging cache, srv:%d, free_all:%u", srv, free_all);
1274 if (srv == -1 || free_all)
1275 aging_interval_ms = INT_MAX;
1278 user_req *cached = *cp;
1279 if (CACHED_ENTRY(cached) && cached != NULL) {
1280 int csrv = type_to_srv[cached->type];
1281 if (srv == -1 || srv == csrv) {
1284 free_refcounted_ureq(cp);
1286 unsigned age = cache_age(cached);
1287 response_header *resp = ureq_response(cached);
1288 unsigned ttl = (resp->found ? config.pttl : config.nttl)[csrv];
1290 log(L_DEBUG2, "freeing: age %u positive %d ttl %u", age, resp->found, ttl);
1292 free_refcounted_ureq(cp);
1293 } else if (srv == -1) {
1295 if (aging_interval_ms > ttl)
1296 aging_interval_ms = ttl;
1303 log(L_INFO, "aged cache, freed:%u, remain:%u", sv - cached_cnt, cached_cnt);
1304 log(L_DEBUG2, "aging interval now %u ms", aging_interval_ms);
1312 /* Spawns a worker and feeds it with user query on stdin */
1313 /* Returns stdout fd of the worker, in blocking mode */
1314 static int create_and_feed_worker(user_req *ureq)
1320 } to_child, to_parent;
1322 /* NB: these pipe fds are in blocking mode and non-CLOEXECed */
1323 xpipe(&to_child.rd);
1324 xpipe(&to_parent.rd);
1327 if (pid < 0) /* error */
1328 perror_and_die("vfork");
1329 if (!pid) { /* child */
1330 char param[sizeof(int)*3 + 2];
1334 close(to_parent.rd);
1335 xmovefd(to_child.rd, 0);
1336 xmovefd(to_parent.wr, 1);
1337 sprintf(param, "%u", debug);
1338 argv[0] = (char*) "worker_nscd";
1341 /* Re-exec ourself, cleaning up all allocated memory.
1342 * fds in parent are marked CLOEXEC and will be closed too
1344 /* Try link name first: it's better to have comm field
1345 * of "nscd" than "exe" (pgrep reported to fail to find us
1346 * by name when comm field contains "exe") */
1347 execve(self_exe_points_to, argv, argv+2);
1348 xexecve("/proc/self/exe", argv, argv+2);
1353 close(to_parent.wr);
1354 /* We do not expect child to block for any noticeably long time,
1355 * and also we expect write to be one-piece one:
1356 * ureq size is <= 1k and pipes are guaranteed to accept
1357 * at least PIPE_BUF at once */
1358 xsafe_write(to_child.wr, ureq, ureq_size(ureq));
1361 close_on_exec(to_parent.rd);
1362 return to_parent.rd;
1365 static user_req *worker_ureq;
1368 static const char *req_str(unsigned type, const char *buf)
1370 if (type == GETHOSTBYADDR) {
1372 in.s_addr = *((uint32_t*)buf);
1373 return inet_ntoa(in);
1375 if (type == GETHOSTBYADDRv6) {
1381 const char *req_str(unsigned type, const char *buf);
1384 static void worker_signal_handler(int sig)
1387 log(L_INFO, "worker:%d got sig:%d while handling req "
1388 "type:%d(%s) key_len:%d '%s'",
1390 worker_ureq->type, typestr[worker_ureq->type],
1391 worker_ureq->key_len,
1392 req_str(worker_ureq->type, worker_ureq->reqbuf)
1395 log(L_INFO, "worker:%d got sig:%d while handling req "
1396 "type:%d key_len:%d",
1398 worker_ureq->type, worker_ureq->key_len);
1403 static void worker(const char *param) NORETURN;
1404 static void worker(const char *param)
1409 debug = atoi(param);
1411 worker_ureq = &ureq; /* for signal handler */
1413 /* Make sure we won't hang, but rather die */
1414 if (WORKER_TIMEOUT_SEC)
1415 alarm(WORKER_TIMEOUT_SEC);
1417 /* NB: fds 0, 1 are in blocking mode */
1419 /* We block here (for a short time) */
1420 /* Due to ureq size < PIPE_BUF read is atomic */
1421 /* No error or size checking: we trust the parent */
1422 safe_read(0, &ureq, sizeof(ureq));
1424 signal(SIGSEGV, worker_signal_handler);
1425 signal(SIGBUS, worker_signal_handler);
1426 signal(SIGILL, worker_signal_handler);
1427 signal(SIGFPE, worker_signal_handler);
1428 signal(SIGABRT, worker_signal_handler);
1430 signal(SIGSTKFLT, worker_signal_handler);
1433 if (ureq.type == GETHOSTBYNAME
1434 || ureq.type == GETHOSTBYNAMEv6
1436 resp = marshal_hostent(
1437 ureq.type == GETHOSTBYNAME
1438 ? gethostbyname(ureq.reqbuf)
1439 : gethostbyname2(ureq.reqbuf, AF_INET6)
1441 } else if (ureq.type == GETHOSTBYADDR
1442 || ureq.type == GETHOSTBYADDRv6
1444 resp = marshal_hostent(gethostbyaddr(ureq.reqbuf, ureq.key_len,
1445 (ureq.type == GETHOSTBYADDR ? AF_INET : AF_INET6)
1447 } else if (ureq.type == GETPWBYNAME) {
1449 log(L_DEBUG2, "getpwnam('%s')", ureq.reqbuf);
1450 pw = getpwnam(ureq.reqbuf);
1451 log(L_DEBUG2, "getpwnam result:%p", pw);
1452 resp = marshal_passwd(pw);
1453 } else if (ureq.type == GETPWBYUID) {
1454 resp = marshal_passwd(getpwuid(atoi(ureq.reqbuf)));
1455 } else if (ureq.type == GETGRBYNAME) {
1456 struct group *gr = getgrnam(ureq.reqbuf);
1457 resp = marshal_group(gr);
1458 } else if (ureq.type == GETGRBYGID) {
1459 struct group *gr = getgrgid(atoi(ureq.reqbuf));
1460 resp = marshal_group(gr);
1461 } else if (ureq.type == GETAI) {
1462 resp = obtain_addrinfo(ureq.reqbuf);
1463 } else /*if (ureq.type == INITGROUPS)*/ {
1464 resp = obtain_initgroups(ureq.reqbuf);
1467 if (!((response_header*)resp)->found) {
1468 /* Parent knows about this special case */
1469 xfull_write(1, resp, 8);
1471 /* Responses can be big (getgrnam("guest") on a big user db),
1472 * we cannot rely on them being atomic. full_write loops
1474 xfull_write(1, resp, ((response_header*)resp)->version_or_size);
1484 static const char checked_filenames[][sizeof("/etc/passwd")] = {
1485 [SRV_PASSWD] = "/etc/passwd", /* "/etc/shadow"? */
1486 [SRV_GROUP] = "/etc/group",
1487 [SRV_HOSTS] = "/etc/hosts", /* "/etc/resolv.conf" "/etc/nsswitch.conf"? */
1490 static long checked_status[ARRAY_SIZE(checked_filenames)];
1492 static void check_files(int srv)
1495 const char *file = checked_filenames[srv];
1498 memset(&tsb, 0, sizeof(tsb));
1499 stat(file, &tsb); /* ignore errors */
1500 /* Comparing struct stat's was giving false positives.
1501 * Extracting only those fields which are interesting: */
1502 v = (long)tsb.st_mtime ^ (long)tsb.st_size ^ (long)tsb.st_ino; /* ^ (long)tsb.st_dev ? */
1504 if (v != checked_status[srv]) {
1505 checked_status[srv] = v;
1506 log(L_INFO, "detected change in %s", file);
1507 age_cache(/*free_all:*/ 1, srv);
1511 /* Returns 1 if we immediately have the answer */
1512 static int handle_client(int i)
1515 user_req *ureq = cinfo[i].ureq;
1516 user_req **cache_pp;
1517 user_req *ureq_and_resp;
1520 log(L_DEBUG, "version:%d type:%d(%s) key_len:%d '%s'",
1521 ureq->version, ureq->type,
1522 ureq->type < ARRAY_SIZE(typestr) ? typestr[ureq->type] : "BAD",
1523 ureq->key_len, req_str(ureq->type, ureq->reqbuf));
1526 if (ureq->version != NSCD_VERSION) {
1527 log(L_INFO, "wrong version");
1531 if (ureq->key_len > sizeof(ureq->reqbuf)) {
1532 log(L_INFO, "bogus key_len %u - ignoring", ureq->key_len);
1536 if (cinfo[i].bytecnt < USER_HDR_SIZE + ureq->key_len) {
1537 log(L_INFO, "read %d, need to read %d",
1538 cinfo[i].bytecnt, USER_HDR_SIZE + ureq->key_len);
1539 return 0; /* more to read */
1541 if (cinfo[i].bytecnt > USER_HDR_SIZE + ureq->key_len) {
1542 log(L_INFO, "read overflow");
1546 if (unsupported_ureq_type(ureq->type)) {
1547 /* We don't know this request. Just close the connection.
1548 * (glibc client interprets this like "not supported by this nscd")
1549 * Happens very often, thus DEBUG, not INFO */
1550 log(L_DEBUG, "unsupported query, dropping");
1554 srv = type_to_srv[ureq->type];
1555 if (!config.srv_enable[srv]) {
1556 log(L_INFO, "service %d is disabled, dropping", srv);
1561 hex_dump(cinfo[i].ureq, cinfo[i].bytecnt);
1563 if (ureq->type == SHUTDOWN
1564 || ureq->type == INVALIDATE
1567 struct ucred caller;
1568 socklen_t optlen = sizeof(caller);
1569 if (getsockopt(pfd[i].fd, SOL_SOCKET, SO_PEERCRED, &caller, &optlen) < 0) {
1570 log(L_INFO, "ignoring special request - cannot get caller's id: %s", strerror(errno));
1574 if (caller.uid != 0) {
1575 log(L_INFO, "special request from non-root - ignoring");
1580 if (ureq->type == SHUTDOWN) {
1581 log(L_INFO, "got shutdown request, exiting");
1584 if (!ureq->key_len || ureq->reqbuf[ureq->key_len - 1]) {
1585 log(L_INFO, "malformed invalidate request - ignoring");
1589 log(L_INFO, "got invalidate request, flushing cache");
1590 /* Frees entire cache. TODO: replace -1 with service (in ureq->reqbuf) */
1591 age_cache(/*free_all:*/ 1, -1);
1596 if (ureq->type != GETHOSTBYADDR
1597 && ureq->type != GETHOSTBYADDRv6
1599 if (ureq->key_len && ureq->reqbuf[ureq->key_len - 1] != '\0') {
1600 log(L_INFO, "badly terminated buffer");
1606 if (config.check_files[srv]) {
1610 cache_pp = lookup_in_cache(ureq);
1611 ureq_and_resp = cache_pp ? *cache_pp : NULL;
1613 if (ureq_and_resp) {
1614 if (CACHED_ENTRY(ureq_and_resp)) {
1615 /* Found. Save ptr to response into cinfo and return */
1616 response_header *resp = ureq_response(ureq_and_resp);
1617 unsigned sz = resp->version_or_size;
1619 log(L_DEBUG, "sz:%u", sz);
1621 ureq_and_resp->refcount++; /* cache shouldn't free it under us! */
1622 pfd[i].events = POLLOUT; /* we want to write out */
1623 cinfo[i].resptr = ureq_and_resp;
1624 /*cinfo[i].respos = 0; - already is */
1625 /* prevent future matches with anything */
1626 cinfo[i].cache_pp = (void *) 1;
1627 return 1; /* "ready to write data out to client" */
1630 /* Not found. Remember a pointer where it will appear */
1631 cinfo[i].cache_pp = cache_pp;
1633 /* If it does not point to our own ureq buffer... */
1634 if (CACHE_PTR(ureq_and_resp) != ureq) {
1635 /* We are not the first client who wants this */
1636 log(L_DEBUG, "another request is in progress (%p), waiting for its result", ureq_and_resp);
1637 MARK_PTR_SHARED(cache_pp); /* "please inform us when it's ready" */
1638 /* "we do not wait for client anymore" */
1639 cinfo[i].client_fd = pfd[i].fd;
1640 /* Don't wait on fd. Worker response will unblock us */
1644 /* else: lookup_in_cache inserted (ureq & 1) into *cache_pp:
1645 * we are the first client to miss on this ureq. */
1648 /* Start worker thread */
1649 log(L_DEBUG, "stored %p in cache, starting a worker", ureq_and_resp);
1650 /* Now we will wait on worker's fd, not client's! */
1651 cinfo[i].client_fd = pfd[i].fd;
1652 pfd[i].fd = create_and_feed_worker(ureq);
1656 static void prepare_for_writeout(unsigned i, user_req *cached)
1658 log(L_DEBUG2, "client %u: data is ready at %p", i, cached);
1660 if (cinfo[i].client_fd) {
1661 pfd[i].fd = cinfo[i].client_fd;
1662 cinfo[i].client_fd = 0; /* "we don't wait for worker reply" */
1664 pfd[i].events = POLLOUT;
1666 /* Writeout position etc */
1667 cinfo[i].resptr = cached;
1668 /*cinfo[i].respos = 0; - already is */
1669 /* if worker took some time to get info (e.g. DNS query),
1670 * prevent client timeout from triggering at once */
1671 cinfo[i].started_ms = g_now_ms;
1674 /* Worker seems to be ready to write the response.
1675 * When we return, response is fully read and stored in cache,
1676 * worker's fd is closed, pfd[i] and cinfo[i] are updated. */
1677 static void handle_worker_response(int i)
1679 struct { /* struct response_header + small body */
1680 uint32_t version_or_size;
1686 response_header *resp;
1687 unsigned sz, resp_sz;
1688 unsigned ureq_sz_aligned;
1691 ureq = cinfo[i].ureq;
1692 ureq_sz_aligned = (char*)ureq_response(ureq) - (char*)ureq;
1694 sz = full_read(pfd[i].fd, &sz_and_found, sizeof(sz_and_found));
1696 /* worker was killed? */
1697 log(L_DEBUG, "worker gave short reply:%u < 8", sz);
1701 resp_sz = sz_and_found.version_or_size;
1702 if (resp_sz < sz || resp_sz > 0xfffffff) { /* 256 mb */
1703 error("BUG: bad size from worker:%u", resp_sz);
1707 /* Create new block of cached info */
1708 cached = xzalloc(ureq_sz_aligned + resp_sz);
1709 log(L_DEBUG2, "xzalloc(%u):%p", ureq_sz_aligned + resp_sz, cached);
1710 resp = (void*) (((char*) cached) + ureq_sz_aligned);
1711 memcpy(cached, ureq, ureq_size(ureq));
1712 memcpy(resp, &sz_and_found, sz);
1713 if (sz_and_found.found && resp_sz > sz) {
1714 /* We need to read data only if it's found
1715 * (otherwise worker sends only 8 bytes).
1717 * Replies can be big (getgrnam("guest") on a big user db),
1718 * we cannot rely on them being atomic. However, we know
1719 * that worker _always_ gives reply in one full_write(),
1720 * so we loop and read it all
1721 * (looping is implemented inside full_read())
1723 if (full_read(pfd[i].fd, ((char*) resp) + sz, resp_sz - sz) != resp_sz - sz) {
1724 /* worker was killed? */
1725 log(L_DEBUG, "worker gave short reply, free(%p)", cached);
1732 set_cache_timestamp(cached);
1733 hex_dump(resp, resp_sz);
1740 user_req **cache_pp = cinfo[i].cache_pp;
1741 if (cache_pp != NULL) { /* if not a fake entry */
1744 if (CACHE_SHARED(ureq)) {
1745 /* Other clients wait for this response too,
1746 * wake them (and us) up and set refcount = no_of_clients */
1749 for (j = 2; j < num_clients; j++) {
1750 if (cinfo[j].cache_pp == cache_pp) {
1751 /* This client uses the same cache entry */
1753 /* prevent future matches with anything */
1754 cinfo[j].cache_pp = (void *) 1;
1755 prepare_for_writeout(j, cached);
1760 /* prevent future matches with anything */
1761 cinfo[i].cache_pp = (void *) 1;
1765 prepare_for_writeout(i, cached);
1767 /* cache shouldn't free it under us! */
1769 cached->refcount = ref;
1770 aging_interval_ms = min_aging_interval_ms;
1773 static void main_loop(void)
1775 /* 1/2 of smallest negative TTL */
1776 min_aging_interval_ms = config.nttl[0];
1777 if (min_aging_interval_ms > config.nttl[1]) min_aging_interval_ms = config.nttl[1];
1778 if (min_aging_interval_ms > config.nttl[2]) min_aging_interval_ms = config.nttl[2];
1779 min_aging_interval_ms = (min_aging_interval_ms / 2) | 1;
1780 aging_interval_ms = min_aging_interval_ms;
1786 r = SMALL_POLL_TIMEOUT_MS;
1787 if (num_clients <= 2 && !cached_cnt)
1788 r = -1; /* infinite */
1789 else if (num_clients < max_reqnum)
1790 r = aging_interval_ms;
1791 #if 0 /* Debug: leak detector */
1793 static unsigned long long cnt;
1794 static unsigned long low_malloc = -1L;
1795 static unsigned long low_sbrk = -1L;
1796 void *p = malloc(540); /* should not be too small */
1799 if ((unsigned long)p < low_malloc)
1800 low_malloc = (unsigned long)p;
1801 if ((unsigned long)s < low_sbrk)
1802 low_sbrk = (unsigned long)s;
1803 log(L_INFO, "poll %llu (%d ms). clients:%u cached:%u %u/%u malloc:%p (%lu), sbrk:%p (%lu)",
1804 cnt, r, num_clients, cached_cnt, cache_hit_cnt, cache_access_cnt,
1805 p, (unsigned long)p - low_malloc,
1806 s, (unsigned long)s - low_sbrk);
1810 log(L_DEBUG, "poll %d ms. clients:%u cached:%u hit ratio:%u/%u",
1811 r, num_clients, cached_cnt, cache_hit_cnt, cache_access_cnt);
1814 r = poll(pfd, num_clients, r);
1815 log(L_DEBUG2, "poll returns %d", r);
1818 perror_and_die("poll");
1822 /* Everything between polls never sleeps.
1823 * There is no blocking I/O (except when we talk to worker thread
1824 * which is guaranteed to not block us for long) */
1826 g_now_ms = monotonic_ms();
1828 goto skip_fd_checks;
1830 for (i = 0; i < 2; i++) {
1832 if (!pfd[i].revents)
1834 /* pfd[i].revents = 0; - not needed */
1835 cfd = accept(pfd[i].fd, NULL, NULL);
1837 /* odd... poll() says we can accept but accept failed? */
1838 log(L_DEBUG2, "accept failed with %s", strerror(errno));
1843 /* x[num_clients] is next free element, taking it */
1844 log(L_DEBUG2, "new client %d, fd %d", num_clients, cfd);
1845 pfd[num_clients].fd = cfd;
1846 pfd[num_clients].events = POLLIN;
1847 /* this will make us do read() in next for() loop: */
1848 pfd[num_clients].revents = POLLIN;
1849 memset(&cinfo[num_clients], 0, sizeof(cinfo[num_clients]));
1850 /* cinfo[num_clients].bytecnt = 0; - done */
1851 cinfo[num_clients].started_ms = g_now_ms;
1852 cinfo[num_clients].bufidx = alloc_buf_no();
1853 cinfo[num_clients].ureq = bufno2buf(cinfo[num_clients].bufidx);
1855 if (num_clients >= max_reqnum) {
1856 /* stop accepting new connects for now */
1857 pfd[0].events = pfd[0].revents = 0;
1858 pfd[1].events = pfd[1].revents = 0;
1861 for (; i < num_clients; i++) {
1862 if (!pfd[i].revents)
1864 log(L_DEBUG2, "pfd[%d].revents:0x%x", i, pfd[i].revents);
1865 /* pfd[i].revents = 0; - not needed */
1867 /* "Write out result" case */
1868 if (pfd[i].revents == POLLOUT) {
1869 response_header *resp;
1871 if (!cinfo[i].resptr) {
1872 /* corner case: worker gave bad response earlier */
1877 resp = ureq_response(cinfo[i].resptr);
1878 resp_sz = resp->version_or_size;
1879 resp->version_or_size = NSCD_VERSION;
1880 r = safe_write(pfd[i].fd, ((char*) resp) + cinfo[i].respos, resp_sz - cinfo[i].respos);
1881 resp->version_or_size = resp_sz;
1883 if (r < 0 && errno == EAGAIN)
1885 if (r <= 0) { /* client isn't there anymore */
1886 log(L_DEBUG, "client %d is gone (write returned %d)", i, r);
1888 if (cinfo[i].cache_pp == NULL) {
1889 log(L_DEBUG, "client %d: freeing fake cache entry %p", i, cinfo[i].resptr);
1890 free(cinfo[i].resptr);
1892 /* Most of the time, it is not freed here,
1893 * only refcounted--. Freeing happens
1894 * if it was deleted from cache[] but retained
1896 free_refcounted_ureq(&cinfo[i].resptr);
1901 cinfo[i].respos += r;
1902 if (cinfo[i].respos >= resp_sz) {
1903 /* We wrote everything */
1904 /* No point in trying to get next request, it won't come.
1905 * glibc 2.4 client closes its end after each request,
1906 * without testing for EOF from server. strace:
1908 * read(3, "www.google.com\0\0", 16) = 16
1911 log(L_DEBUG, "client %u: sent answer %u bytes", i, cinfo[i].respos);
1912 goto write_out_is_done;
1916 /* "Read reply from worker" case. Worker may be
1917 * already dead, revents may contain other bits too */
1918 if ((pfd[i].revents & POLLIN) && cinfo[i].client_fd) {
1919 log(L_DEBUG, "reading response for client %u", i);
1920 handle_worker_response(i);
1921 /* We can immediately try to write a response
1926 /* POLLHUP means pfd[i].fd is closed by peer.
1927 * POLLHUP+POLLOUT is seen when we switch for writeout
1928 * and see that pfd[i].fd is closed by peer. */
1929 if ((pfd[i].revents & ~POLLOUT) == POLLHUP) {
1930 int is_client = (cinfo[i].client_fd == 0 || cinfo[i].client_fd == pfd[i].fd);
1931 log(L_INFO, "%s %u disappeared (got POLLHUP on fd %d)",
1932 is_client ? "client" : "worker",
1939 /* Read worker output anyway, error handling
1940 * in that function deals with short read.
1941 * Simply closing client is wrong: it leaks
1942 * shared future entries. */
1943 handle_worker_response(i);
1948 /* All strange and unexpected cases */
1949 if (pfd[i].revents != POLLIN) {
1950 /* Not just "can read", but some other bits are there */
1951 log(L_INFO, "client %u revents is strange:%x", i, pfd[i].revents);
1956 /* "Read request from client" case */
1957 r = safe_read(pfd[i].fd, (char*)(cinfo[i].ureq) + cinfo[i].bytecnt, MAX_USER_REQ_SIZE - cinfo[i].bytecnt);
1959 log(L_DEBUG2, "error reading from client: %s", strerror(errno));
1960 if (errno == EAGAIN)
1966 log(L_INFO, "premature EOF from client, dropping");
1970 cinfo[i].bytecnt += r;
1971 if (cinfo[i].bytecnt >= sizeof(user_req_header)) {
1972 if (handle_client(i)) {
1973 /* Response is found in cache! */
1977 } /* for each client[2..num_clients-1] */
1981 if ((g_now_ms - last_age_time) >= aging_interval_ms) {
1982 last_age_time = g_now_ms;
1983 age_cache(/*free_all:*/ 0, -1);
1986 /* Close timed out client connections */
1987 for (i = 2; i < num_clients; i++) {
1988 if (pfd[i].fd != 0 /* not closed yet? */ ////
1989 && cinfo[i].client_fd == 0 /* do we still wait for client, not worker? */
1990 && (g_now_ms - cinfo[i].started_ms) > CLIENT_TIMEOUT_MS
1992 log(L_INFO, "timed out waiting for client %u (%u ms), dropping",
1993 i, (unsigned)(g_now_ms - cinfo[i].started_ms));
2001 /* We closed at least one client, coalesce pfd[], cinfo[] */
2002 if (min_closed + cnt_closed >= num_clients) {
2003 /* clients [min_closed..num_clients-1] are all closed */
2004 /* log(L_DEBUG, "taking shortcut"); - almost always happens */
2009 while (i < num_clients) {
2013 if (++i >= num_clients)
2017 cinfo[j++] = cinfo[i++];
2021 num_clients -= cnt_closed;
2022 log(L_DEBUG, "removing %d closed clients. clients:%d", cnt_closed, num_clients);
2023 min_closed = INT_MAX;
2025 /* start accepting new connects */
2026 pfd[0].events = POLLIN;
2027 pfd[1].events = POLLIN;
2036 #define NSCD_PIDFILE "/var/run/nscd/nscd.pid"
2037 #define NSCD_DIR "/var/run/nscd"
2038 #define NSCD_SOCKET "/var/run/nscd/socket"
2039 #define NSCD_SOCKET_OLD "/var/run/.nscd_socket"
2041 static smallint wrote_pidfile;
2043 static void cleanup_on_signal(int sig)
2046 unlink(NSCD_PIDFILE);
2047 unlink(NSCD_SOCKET_OLD);
2048 unlink(NSCD_SOCKET);
2052 static void write_pid(void)
2054 FILE *pid = fopen(NSCD_PIDFILE, "w");
2057 fprintf(pid, "%d\n", getpid());
2062 /* Open a listening nscd server socket */
2063 static int open_socket(const char *name)
2065 struct sockaddr_un sun;
2066 int sock = socket(AF_UNIX, SOCK_STREAM, 0);
2068 perror_and_die("cannot create unix domain socket");
2070 close_on_exec(sock);
2071 sun.sun_family = AF_UNIX;
2072 strcpy(sun.sun_path, name);
2074 if (bind(sock, (struct sockaddr *) &sun, sizeof(sun)) < 0)
2075 perror_and_die("bind(%s)", name);
2076 if (chmod(name, 0666) < 0)
2077 perror_and_die("chmod(%s)", name);
2078 if (listen(sock, (max_reqnum/8) | 1) < 0)
2079 perror_and_die("listen");
2083 static const struct option longopt[] = {
2084 /* name, has_arg, int *flag, int val */
2085 { "debug" , no_argument , NULL, 'd' },
2086 { "config-file", required_argument, NULL, 'f' },
2087 { "invalidate" , required_argument, NULL, 'i' },
2088 { "shutdown" , no_argument , NULL, 'K' },
2089 { "nthreads" , required_argument, NULL, 't' },
2090 { "version" , no_argument , NULL, 'V' },
2091 { "help" , no_argument , NULL, '?' },
2092 { "usage" , no_argument , NULL, '?' },
2093 /* just exit(0). TODO: "test" connect? */
2094 { "statistic" , no_argument , NULL, 'g' },
2095 { "secure" , no_argument , NULL, 'S' }, /* ? */
2099 static const char *const help[] = {
2100 "Do not daemonize; log to stderr",
2101 "File to read configuration from",
2103 "Shut the server down",
2104 "Serve N requests in parallel",
2108 static void print_help_and_die(void)
2110 const struct option *opt = longopt;
2111 const char *const *h = help;
2113 puts("Usage: nscd [OPTION...]\n"
2114 "Name Service Cache Daemon\n");
2116 printf("\t" "-%c,--%-11s %s\n", opt->val, opt->name, *h);
2119 } while (opt->val != '?');
2123 static char *skip_service(int *srv, const char *s)
2125 if (strcmp("passwd", s) == 0) {
2128 } else if (strcmp("group", s) == 0) {
2130 } else if (strcmp("hosts", s) == 0) {
2135 return skip_whitespace(s + 6);
2138 static void handle_null(const char *str, int srv) {}
2140 static void handle_logfile(const char *str, int srv)
2142 config.logfile = xstrdup(str);
2145 static void handle_debuglvl(const char *str, int srv)
2147 debug |= (uint8_t) getnum(str);
2150 static void handle_threads(const char *str, int srv)
2152 unsigned n = getnum(str);
2157 static void handle_user(const char *str, int srv)
2159 config.user = xstrdup(str);
2162 static void handle_enable(const char *str, int srv)
2164 config.srv_enable[srv] = ((str[0] | 0x20) == 'y');
2167 static void handle_pttl(const char *str, int srv)
2169 config.pttl[srv] = getnum(str);
2172 static void handle_nttl(const char *str, int srv)
2174 config.nttl[srv] = getnum(str);
2177 static void handle_size(const char *str, int srv)
2179 config.size[srv] = getnum(str);
2182 static void handle_chfiles(const char *str, int srv)
2184 config.check_files[srv] = ((str[0] | 0x20) == 'y');
2187 static void parse_conffile(const char *conffile, int warn)
2189 static const struct confword {
2191 void (*handler)(const char *, int);
2193 { "_" "logfile" , handle_logfile },
2194 { "_" "debug-level" , handle_debuglvl },
2195 { "_" "threads" , handle_threads },
2196 { "_" "max-threads" , handle_threads },
2197 { "_" "server-user" , handle_user },
2198 /* ignore: any user can stat */
2199 { "_" "stat-user" , handle_null },
2200 { "_" "paranoia" , handle_null }, /* ? */
2201 /* ignore: design goal is to never crash/hang */
2202 { "_" "reload-count" , handle_null },
2203 { "_" "restart-interval" , handle_null },
2204 { "S" "enable-cache" , handle_enable },
2205 { "S" "positive-time-to-live" , handle_pttl },
2206 { "S" "negative-time-to-live" , handle_nttl },
2207 { "S" "suggested-size" , handle_size },
2208 { "S" "check-files" , handle_chfiles },
2209 { "S" "persistent" , handle_null }, /* ? */
2210 { "S" "shared" , handle_null }, /* ? */
2211 { "S" "auto-propagate" , handle_null }, /* ? */
2216 FILE *file = fopen(conffile, "r");
2220 if (conffile != default_conffile)
2221 perror_and_die("cannot open %s", conffile);
2225 while (fgets(buf, sizeof(buf), file) != NULL) {
2226 const struct confword *word;
2228 int len = strlen(buf);
2232 if (buf[len-1] != '\n') {
2233 if (len >= sizeof(buf) - 1)
2234 error_and_die("%s:%d: line is too long", conffile, lineno);
2235 len++; /* last line, not terminated by '\n' */
2239 p = strchr(buf, '#');
2243 p = skip_whitespace(buf);
2246 *skip_non_whitespace(p) = '\0';
2249 if (strcmp(word->str + 1, p) == 0) {
2251 p = skip_whitespace(p + strlen(p) + 1);
2252 *skip_non_whitespace(p) = '\0';
2253 if (word->str[0] == 'S') {
2254 char *p2 = skip_service(&srv, p);
2257 error("%s:%d: ignoring unknown service name '%s'", conffile, lineno, p);
2261 *skip_non_whitespace(p) = '\0';
2263 word->handler(p, srv);
2269 error("%s:%d: ignoring unknown directive '%s'", conffile, lineno, p);
2278 /* "XX,XX[,XX]..." -> gid_t[] */
2279 static gid_t* env_U_to_uid_and_gids(const char *str, int *sizep)
2290 ug = xmalloc(ng * sizeof(ug[0]));
2298 *gp++ = strtoul(sp, (char**)&sp, 16);
2299 if (errno || (*sp != ',' && *sp != '\0'))
2300 error_and_die("internal error");
2311 static char* user_to_env_U(const char *user)
2318 pw = getpwnam(user);
2320 perror_and_die("user '%s' is not known", user);
2323 /* 0th cell will be used for uid */
2324 ug = xmalloc((1 + ng) * sizeof(ug[0]));
2325 if (getgrouplist(user, pw->pw_gid, &ug[1], &ng) < 0) {
2326 ug = xrealloc(ug, (1 + ng) * sizeof(ug[0]));
2327 if (getgrouplist(user, pw->pw_gid, &ug[1], &ng) < 0)
2328 perror_and_die("can't get groups of user '%s'", user);
2333 /* How much do we need for "-Uxx,xx[,xx]..." string? */
2334 ug_str = xmalloc((sizeof(unsigned long)+1)*2 * ng + 3);
2340 sp += sprintf(sp, "%lx,", (unsigned long)(*gp++));
2349 /* not static - don't inline me, compiler! */
2350 void readlink_self_exe(void)
2352 char buf[PATH_MAX + 1];
2353 ssize_t sz = readlink("/proc/self/exe", buf, sizeof(buf) - 1);
2355 perror_and_die("readlink %s failed", "/proc/self/exe");
2357 self_exe_points_to = xstrdup(buf);
2361 static void special_op(const char *arg) NORETURN;
2362 static void special_op(const char *arg)
2364 static const user_req_header ureq = { NSCD_VERSION, SHUTDOWN, 0 };
2366 struct sockaddr_un addr;
2369 sock = socket(PF_UNIX, SOCK_STREAM, 0);
2371 error_and_die("cannot create AF_UNIX socket");
2373 addr.sun_family = AF_UNIX;
2374 strcpy(addr.sun_path, NSCD_SOCKET);
2375 if (connect(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0)
2376 error_and_die("cannot connect to %s", NSCD_SOCKET);
2378 if (!arg) { /* shutdown */
2379 xfull_write(sock, &ureq, sizeof(ureq));
2380 printf("sent shutdown request, exiting\n");
2381 } else { /* invalidate */
2382 size_t arg_len = strlen(arg) + 1;
2384 user_req_header req;
2387 reqdata.req.version = NSCD_VERSION;
2388 reqdata.req.type = INVALIDATE;
2389 reqdata.req.key_len = arg_len;
2390 memcpy(reqdata.arg, arg, arg_len);
2391 xfull_write(sock, &reqdata, arg_len + sizeof(ureq));
2392 printf("sent invalidate(%s) request, exiting\n", arg);
2398 /* This internal glibc function is called to disable trying to contact nscd.
2399 * We _are_ nscd, so we need to do the lookups, and not recurse. */
2400 void __nss_disable_nscd(void);
2402 int main(int argc, char **argv)
2407 const char *conffile;
2409 /* make sure we don't get recursive calls */
2410 __nss_disable_nscd();
2412 if (argv[0][0] == 'w') /* "worker_nscd" */
2418 /* Make sure stdio is not closed */
2419 n = xopen3("/dev/null", O_RDWR, 0);
2422 /* Close unexpected open file descriptors */
2423 n |= 0xff; /* start from at least fd# 255 */
2428 /* For idiotic kernels which disallow "exec /proc/self/exe" */
2429 readlink_self_exe();
2431 conffile = default_conffile;
2433 while ((n = getopt_long(argc, argv, "df:i:KVgt:", longopt, NULL)) != -1) {
2444 special_op(optarg); /* exits */
2446 /* shutdown server */
2447 special_op(NULL); /* exits */
2449 puts("unscd - nscd which does not hang, v."PROGRAM_VERSION);
2455 max_reqnum = getnum(optarg);
2461 print_help_and_die();
2464 /* Multiple -d can bump debug regardless of nscd.conf:
2465 * no -d or -d: 0, -dd: 1,
2466 * -ddd: 3, -dddd: 7, -ddddd: 15
2469 debug |= (((1U << opt_d_cnt) >> 1) - 1) & L_ALL;
2471 env_U = getenv("U");
2472 /* Avoid duplicate warnings if $U exists */
2473 parse_conffile(conffile, /* warn? */ (env_U == NULL));
2475 /* I have a user report of (broken?) ldap nss library
2476 * opening and never closing a socket to a ldap server,
2477 * even across fork() and exec(). This messes up
2478 * worker child's operations for the reporter.
2480 * This strenghtens my belief that nscd _must not_ trust
2481 * nss libs to be written correctly.
2483 * Here, we need to jump through the hoops to guard against
2484 * such problems. If config file has server-user setting, we need
2485 * to setgroups + setuid. For that, we need to get uid and gid vector.
2486 * And that means possibly using buggy nss libs.
2487 * We will do it here, but then we will re-exec, passing uid+gids
2488 * in an environment variable.
2490 if (!env_U && config.user) {
2491 /* user_to_env_U() does getpwnam and getgrouplist */
2492 if (putenv(user_to_env_U(config.user)))
2493 error_and_die("out of memory");
2494 /* fds leaked by nss will be closed by execed copy */
2495 execv(self_exe_points_to, argv);
2496 xexecve("/proc/self/exe", argv, environ);
2499 /* Allocate dynamically sized stuff */
2500 max_reqnum += 2; /* account for 2 first "fake" clients */
2501 if (max_reqnum < 8) max_reqnum = 8; /* sanitize */
2502 /* Since refcount is a byte, can't serve more than 255-2 clients
2503 * at once. The rest will block in connect() */
2504 if (max_reqnum > 0xff) max_reqnum = 0xff;
2505 client_buf = xzalloc(max_reqnum * sizeof(client_buf[0]));
2506 busy_cbuf = xzalloc(max_reqnum * sizeof(busy_cbuf[0]));
2507 pfd = xzalloc(max_reqnum * sizeof(pfd[0]));
2508 cinfo = xzalloc(max_reqnum * sizeof(cinfo[0]));
2510 cache_size = (config.size[0] + config.size[1] + config.size[2]) / 8;
2511 if (cache_size < 8) cache_size = 8; /* 8*8 = 64 entries min */
2512 if (cache_size > 0xffff) cache_size = 0xffff; /* 8*64k entries max */
2513 cache_size |= 1; /* force it to be odd */
2514 cache = xzalloc(cache_size * sizeof(cache[0]));
2516 /* Register cleanup hooks */
2517 signal(SIGINT, cleanup_on_signal);
2518 signal(SIGTERM, cleanup_on_signal);
2519 /* Don't die if a client closes a socket on us */
2520 signal(SIGPIPE, SIG_IGN);
2521 /* Avoid creating zombies */
2522 signal(SIGCHLD, SIG_IGN);
2524 /* Ensure workers don't have SIGALRM ignored */
2525 signal(SIGALRM, SIG_DFL);
2528 if (mkdir(NSCD_DIR, 0755) == 0) {
2529 /* prevent bad mode of NSCD_DIR if umask is e.g. 077 */
2530 chmod(NSCD_DIR, 0755);
2532 pfd[0].fd = open_socket(NSCD_SOCKET);
2533 pfd[1].fd = open_socket(NSCD_SOCKET_OLD);
2534 pfd[0].events = POLLIN;
2535 pfd[1].events = POLLIN;
2537 if (debug & D_DAEMON) {
2538 daemon(/*nochdir*/ 1, /*noclose*/ 0);
2539 if (config.logfile) {
2540 /* nochdir=1: relative paths still work as expected */
2541 xmovefd(xopen3(config.logfile, O_WRONLY|O_CREAT|O_TRUNC, 0666), 2);
2544 debug = 0; /* why bother? it's /dev/null'ed anyway */
2546 chdir("/"); /* compat */
2549 /* ignore job control signals */
2550 signal(SIGTTOU, SIG_IGN);
2551 signal(SIGTTIN, SIG_IGN);
2552 signal(SIGTSTP, SIG_IGN);
2555 log(L_ALL, "nscd v" PROGRAM_VERSION ", debug level 0x%x", debug & L_ALL);
2556 log(L_DEBUG, "max %u requests in parallel", max_reqnum - 2);
2557 log(L_DEBUG, "cache size %u x 8 entries", cache_size);
2561 gid_t *ug = env_U_to_uid_and_gids(env_U, &size);
2563 if (setgroups(size - 1, &ug[1]) || setgid(ug[1]))
2564 perror_and_die("cannot set groups for user '%s'", config.user);
2567 perror_and_die("cannot set uid to %u", (unsigned)(ug[0]));
2571 for (n = 0; n < 3; n++) {
2572 log(L_DEBUG, "%s cache enabled:%u pttl:%u nttl:%u",
2574 config.srv_enable[n],
2577 config.pttl[n] *= 1000;
2578 config.nttl[n] *= 1000;