1 /* Inner loops of cache daemon.
2 Copyright (C) 1998, 1999 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public License as
8 published by the Free Software Foundation; either version 2 of the
9 License, or (at your option) any later version.
11 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
16 You should have received a copy of the GNU Library General Public
17 License along with the GNU C Library; see the file COPYING.LIB. If not,
18 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA. */
28 #include <arpa/inet.h>
29 #include <sys/param.h>
31 #include <sys/socket.h>
39 /* Mapping of request type to database. */
40 static const dbtype serv2db[LASTDBREQ + 1] =
42 [GETPWBYNAME] = pwddb,
44 [GETGRBYNAME] = grpdb,
46 [GETHOSTBYNAME] = hstdb,
47 [GETHOSTBYNAMEv6] = hstdb,
48 [GETHOSTBYADDR] = hstdb,
49 [GETHOSTBYADDRv6] = hstdb,
52 /* Map request type to a string. */
53 const char *serv2str[LASTREQ] =
55 [GETPWBYNAME] = "GETPWBYNAME",
56 [GETPWBYUID] = "GETPWBYUID",
57 [GETGRBYNAME] = "GETGRBYNAME",
58 [GETGRBYGID] = "GETGRBYGID",
59 [GETHOSTBYNAME] = "GETHOSTBYNAME",
60 [GETHOSTBYNAMEv6] = "GETHOSTBYNAMEv6",
61 [GETHOSTBYADDR] = "GETHOSTBYADDR",
62 [GETHOSTBYADDRv6] = "GETHOSTBYADDRv6",
63 [SHUTDOWN] = "SHUTDOWN",
64 [GETSTAT] = "GETSTAT",
65 [INVALIDATE] = "INVALIDATE"
68 /* The control data structures for the services. */
69 static struct database dbs[lastdb] =
72 lock: PTHREAD_RWLOCK_INITIALIZER,
75 filename: "/etc/passwd",
77 disabled_iov: &pwd_iov_disabled,
82 lock: PTHREAD_RWLOCK_INITIALIZER,
85 filename: "/etc/group",
87 disabled_iov: &grp_iov_disabled,
92 lock: PTHREAD_RWLOCK_INITIALIZER,
95 filename: "/etc/hosts",
97 disabled_iov: &hst_iov_disabled,
103 /* Number of seconds between two cache pruning runs. */
104 #define CACHE_PRUNE_INTERVAL 15
106 /* Number of threads to use. */
109 /* Socket for incoming connections. */
113 /* Initialize database information structures. */
115 nscd_init (const char *conffile)
117 struct sockaddr_un sock_addr;
120 /* Read the configuration file. */
121 if (nscd_parse_file (conffile, dbs) != 0)
123 /* We couldn't read the configuration file. Disable all services
124 by shutting down the srever. */
125 dbg_log (_("cannot read configuration file; this is fatal"));
129 /* No configuration for this value, assume a default. */
130 nthreads = 2 * lastdb;
132 for (cnt = 0; cnt < lastdb; ++cnt)
133 if (dbs[cnt].enabled)
135 pthread_rwlock_init (&dbs[cnt].lock, NULL);
137 dbs[cnt].array = (struct hashentry **)
138 calloc (dbs[cnt].module, sizeof (struct hashentry *));
139 if (dbs[cnt].array == NULL)
140 error (EXIT_FAILURE, errno, "while allocating cache");
142 if (dbs[cnt].check_file)
144 /* We need the modification date of the file. */
147 if (stat (dbs[cnt].filename, &st) < 0)
150 /* We cannot stat() the file, disable file checking. */
151 dbg_log (_("cannot stat() file `%s': %s"),
153 strerror_r (errno, buf, sizeof (buf)));
154 dbs[cnt].check_file = 0;
157 dbs[cnt].file_mtime = st.st_mtime;
161 /* Create the socket. */
162 sock = socket (AF_UNIX, SOCK_STREAM, 0);
165 dbg_log (_("cannot open socket: %s"), strerror (errno));
168 /* Bind a name to the socket. */
169 sock_addr.sun_family = AF_UNIX;
170 strcpy (sock_addr.sun_path, _PATH_NSCDSOCKET);
171 if (bind (sock, (struct sockaddr *) &sock_addr, sizeof (sock_addr)) < 0)
173 dbg_log ("%s: %s", _PATH_NSCDSOCKET, strerror (errno));
177 /* Set permissions for the socket. */
178 chmod (_PATH_NSCDSOCKET, 0666);
180 /* Set the socket up to accept connections. */
181 if (listen (sock, SOMAXCONN) < 0)
183 dbg_log (_("cannot enable socket to accept connections: %s"),
190 /* Close the connections. */
198 invalidate_cache (void *key)
202 if (strcmp (key, "passwd") == 0)
204 else if (strcmp (key, "group") == 0)
206 else if (strcmp (key, "hosts") == 0)
210 prune_cache (&dbs[number], LONG_MAX);
214 /* Handle new request. */
216 handle_request (int fd, request_header *req, void *key, uid_t uid)
219 dbg_log (_("handle_request: request received (Version = %d)"),
222 if (req->version != NSCD_VERSION)
225 cannot handle old request version %d; current version is %d"),
226 req->version, NSCD_VERSION);
230 if (req->type >= GETPWBYNAME && req->type <= LASTDBREQ)
232 struct hashentry *cached;
233 struct database *db = &dbs[serv2db[req->type]];
237 if (req->type == GETHOSTBYADDR || req->type == GETHOSTBYADDRv6)
239 char buf[INET6_ADDRSTRLEN];
241 dbg_log ("\t%s (%s)", serv2str[req->type],
242 inet_ntop (req->type == GETHOSTBYADDR
243 ? AF_INET : AF_INET6,
244 key, buf, sizeof (buf)));
247 dbg_log ("\t%s (%s)", serv2str[req->type], key);
250 /* Is this service enabled? */
253 /* No, sent the prepared record. */
254 if (TEMP_FAILURE_RETRY (write (fd, db->disabled_iov->iov_base,
255 db->disabled_iov->iov_len))
256 != db->disabled_iov->iov_len)
258 /* We have problems sending the result. */
260 dbg_log (_("cannot write result: %s"),
261 strerror_r (errno, buf, sizeof (buf)));
267 /* Be sure we can read the data. */
268 pthread_rwlock_rdlock (&db->lock);
270 /* See whether we can handle it from the cache. */
271 cached = (struct hashentry *) cache_search (req->type, key, req->key_len,
275 /* Hurray it's in the cache. */
276 if (TEMP_FAILURE_RETRY (write (fd, cached->packet, cached->total))
279 /* We have problems sending the result. */
281 dbg_log (_("cannot write result: %s"),
282 strerror_r (errno, buf, sizeof (buf)));
285 pthread_rwlock_unlock (&db->lock);
290 pthread_rwlock_unlock (&db->lock);
292 else if (debug_level > 0)
294 if (req->type == INVALIDATE)
295 dbg_log ("\t%s (%s)", serv2str[req->type], key);
297 dbg_log ("\t%s", serv2str[req->type]);
300 /* Handle the request. */
304 addpwbyname (&dbs[serv2db[req->type]], fd, req, key, uid);
308 addpwbyuid (&dbs[serv2db[req->type]], fd, req, key, uid);
312 addgrbyname (&dbs[serv2db[req->type]], fd, req, key, uid);
316 addgrbygid (&dbs[serv2db[req->type]], fd, req, key, uid);
320 addhstbyname (&dbs[serv2db[req->type]], fd, req, key, uid);
323 case GETHOSTBYNAMEv6:
324 addhstbynamev6 (&dbs[serv2db[req->type]], fd, req, key, uid);
328 addhstbyaddr (&dbs[serv2db[req->type]], fd, req, key, uid);
331 case GETHOSTBYADDRv6:
332 addhstbyaddrv6 (&dbs[serv2db[req->type]], fd, req, key, uid);
338 /* Accept shutdown, getstat and invalidate only from root */
339 if (secure_in_use && uid == 0)
341 if (req->type == GETSTAT)
342 send_stats (fd, dbs);
343 else if (req->type == INVALIDATE)
344 invalidate_cache (key);
346 termination_handler (0);
351 socklen_t optlen = sizeof (caller);
353 if (getsockopt (fd, SOL_SOCKET, SO_PEERCRED, &caller, &optlen) < 0)
357 dbg_log (_("error getting callers id: %s"),
358 strerror_r (errno, buf, sizeof (buf)));
363 if (req->type == GETSTAT)
364 send_stats (fd, dbs);
365 else if (req->type == INVALIDATE)
366 invalidate_cache (key);
368 termination_handler (0);
379 /* This is the main loop. It is replicated in different threads but the
380 `poll' call makes sure only one thread handles an incoming connection. */
382 __attribute__ ((__noreturn__))
385 int my_number = (int) p;
387 int run_prune = my_number < lastdb && dbs[my_number].enabled;
388 time_t now = time (NULL);
389 time_t next_prune = now + CACHE_PRUNE_INTERVAL;
390 int timeout = run_prune ? 1000 * (next_prune - now) : -1;
393 conn.events = POLLRDNORM;
397 int nr = poll (&conn, 1, timeout);
401 /* The `poll' call timed out. It's time to clean up the cache. */
402 assert (my_number < lastdb);
404 prune_cache (&dbs[my_number], now);
405 next_prune = now + CACHE_PRUNE_INTERVAL;
406 timeout = 1000 * (next_prune - now);
410 /* We have a new incoming connection. */
411 if (conn.revents & (POLLRDNORM|POLLERR|POLLHUP|POLLNVAL))
413 /* Accept the connection. */
414 int fd = accept (conn.fd, NULL, NULL);
421 dbg_log (_("while accepting connection: %s"),
422 strerror_r (errno, buf, sizeof (buf)));
426 /* Now read the request. */
427 if (TEMP_FAILURE_RETRY (read (fd, &req, sizeof (req)))
430 dbg_log (_("short read while reading request: %s"),
431 strerror_r (errno, buf, sizeof (buf)));
439 socklen_t optlen = sizeof (caller);
441 if (getsockopt (fd, SOL_SOCKET, SO_PEERCRED,
442 &caller, &optlen) < 0)
444 dbg_log (_("error getting callers id: %s"),
445 strerror_r (errno, buf, sizeof (buf)));
450 if (req.type < GETPWBYNAME || req.type > LASTDBREQ
451 || secure[serv2db[req.type]])
455 /* It should not be possible to crash the nscd with a silly
456 request (i.e., a terribly large key. We limit the size
458 if (req.key_len < 0 || req.key_len > 1024)
460 dbg_log (_("key length in request too long: %Zd"), req.key_len);
467 char keybuf[req.key_len];
469 if (TEMP_FAILURE_RETRY (read (fd, keybuf, req.key_len))
472 dbg_log (_("short read while reading request key: %s"),
473 strerror_r (errno, buf, sizeof (buf)));
478 /* Phew, we got all the data, now process it. */
479 handle_request (fd, &req, keybuf, uid);
489 timeout = now < next_prune ? 1000 * (next_prune - now) : 0;
495 /* Start all the threads we want. The initial process is thread no. 1. */
503 pthread_attr_init (&attr);
504 pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
506 /* We allow less than LASTDB threads only for debugging. */
507 if (debug_level == 0)
508 nthreads = MAX (nthreads, lastdb);
510 for (i = 1; i < nthreads; ++i)
511 pthread_create (&th, &attr, nscd_run, (void *) i);
513 pthread_attr_destroy (&attr);
515 nscd_run ((void *) 0);