1999-03-19 Andreas Jaeger <aj@arthur.rhein-neckar.de>
[glibc.git] / nscd / connections.c
blobca139fbddf290dd3427dc8dfb0af161788861701
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. */
21 #include <assert.h>
22 #include <error.h>
23 #include <errno.h>
24 #include <pthread.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <arpa/inet.h>
28 #include <sys/param.h>
29 #include <sys/poll.h>
30 #include <sys/socket.h>
31 #include <sys/stat.h>
32 #include <sys/un.h>
34 #include "nscd.h"
35 #include "dbg_log.h"
38 /* Mapping of request type to database. */
39 static const dbtype serv2db[LASTDBREQ + 1] =
41 [GETPWBYNAME] = pwddb,
42 [GETPWBYUID] = pwddb,
43 [GETGRBYNAME] = grpdb,
44 [GETGRBYGID] = grpdb,
45 [GETHOSTBYNAME] = hstdb,
46 [GETHOSTBYNAMEv6] = hstdb,
47 [GETHOSTBYADDR] = hstdb,
48 [GETHOSTBYADDRv6] = hstdb,
51 /* Map request type to a string. */
52 const char *serv2str[LASTREQ] =
54 [GETPWBYNAME] = "GETPWBYNAME",
55 [GETPWBYUID] = "GETPWBYUID",
56 [GETGRBYNAME] = "GETGRBYNAME",
57 [GETGRBYGID] = "GETGRBYGID",
58 [GETHOSTBYNAME] = "GETHOSTBYNAME",
59 [GETHOSTBYNAMEv6] = "GETHOSTBYNAMEv6",
60 [GETHOSTBYADDR] = "GETHOSTBYADDR",
61 [GETHOSTBYADDRv6] = "GETHOSTBYADDRv6",
62 [SHUTDOWN] = "SHUTDOWN",
63 [GETSTAT] = "GETSTAT"
66 /* The control data structures for the services. */
67 static struct database dbs[lastdb] =
69 [pwddb] = {
70 lock: PTHREAD_RWLOCK_INITIALIZER,
71 enabled: 0,
72 check_file: 1,
73 filename: "/etc/passwd",
74 module: 211,
75 disabled_iov: &pwd_iov_disabled,
76 postimeout: 3600,
77 negtimeout: 20
79 [grpdb] = {
80 lock: PTHREAD_RWLOCK_INITIALIZER,
81 enabled: 0,
82 check_file: 1,
83 filename: "/etc/group",
84 module: 211,
85 disabled_iov: &grp_iov_disabled,
86 postimeout: 3600,
87 negtimeout: 60
89 [hstdb] = {
90 lock: PTHREAD_RWLOCK_INITIALIZER,
91 enabled: 0,
92 check_file: 1,
93 filename: "/etc/hosts",
94 module: 211,
95 disabled_iov: &hst_iov_disabled,
96 postimeout: 3600,
97 negtimeout: 20
101 /* Number of seconds between two cache pruning runs. */
102 #define CACHE_PRUNE_INTERVAL 15
104 /* Number of threads to use. */
105 int nthreads = -1;
107 /* Socket for incoming connections. */
108 static int sock;
111 /* Initialize database information structures. */
112 void
113 nscd_init (const char *conffile)
115 struct sockaddr_un sock_addr;
116 size_t cnt;
118 /* Read the configuration file. */
119 if (nscd_parse_file (conffile, dbs) != 0)
121 /* We couldn't read the configuration file. Disable all services
122 by shutting down the srever. */
123 dbg_log (_("cannot read configuration file; this is fatal"));
124 exit (1);
126 if (nthreads == -1)
127 /* No configuration for this value, assume a default. */
128 nthreads = 2 * lastdb;
130 for (cnt = 0; cnt < lastdb; ++cnt)
131 if (dbs[cnt].enabled)
133 pthread_rwlock_init (&dbs[cnt].lock, NULL);
135 dbs[cnt].array = (struct hashentry **)
136 calloc (dbs[cnt].module, sizeof (struct hashentry *));
137 if (dbs[cnt].array == NULL)
138 error (EXIT_FAILURE, errno, "while allocating cache");
140 if (dbs[cnt].check_file)
142 /* We need the modification date of the file. */
143 struct stat st;
145 if (stat (dbs[cnt].filename, &st) < 0)
147 char buf[128];
148 /* We cannot stat() the file, disable file checking. */
149 dbg_log (_("cannot stat() file `%s': %s"),
150 dbs[cnt].filename,
151 strerror_r (errno, buf, sizeof (buf)));
152 dbs[cnt].check_file = 0;
154 else
155 dbs[cnt].file_mtime = st.st_mtime;
159 /* Create the socket. */
160 sock = socket (AF_UNIX, SOCK_STREAM, 0);
161 if (sock < 0)
163 dbg_log (_("cannot open socket: %s"), strerror (errno));
164 exit (1);
166 /* Bind a name to the socket. */
167 sock_addr.sun_family = AF_UNIX;
168 strcpy (sock_addr.sun_path, _PATH_NSCDSOCKET);
169 if (bind (sock, (struct sockaddr *) &sock_addr, sizeof (sock_addr)) < 0)
171 dbg_log ("%s: %s", _PATH_NSCDSOCKET, strerror (errno));
172 exit (1);
175 /* Set permissions for the socket. */
176 chmod (_PATH_NSCDSOCKET, 0666);
178 /* Set the socket up to accept connections. */
179 if (listen (sock, SOMAXCONN) < 0)
181 dbg_log (_("cannot enable socket to accept connections: %s"),
182 strerror (errno));
183 exit (1);
188 /* Close the connections. */
189 void
190 close_sockets (void)
192 close (sock);
196 /* Handle new request. */
197 static void
198 handle_request (int fd, request_header *req, void *key)
200 if (debug_level > 0)
201 dbg_log (_("handle_request: request received (Version = %d)"),
202 req->version);
204 if (req->version != NSCD_VERSION)
206 dbg_log (_("\
207 cannot handle old request version %d; current version is %d"),
208 req->version, NSCD_VERSION);
209 return;
212 if (req->type >= GETPWBYNAME && req->type <= LASTDBREQ)
214 struct hashentry *cached;
215 struct database *db = &dbs[serv2db[req->type]];
217 if (debug_level > 0)
219 if (req->type == GETHOSTBYADDR || req->type == GETHOSTBYADDRv6)
221 char buf[INET6_ADDRSTRLEN];
223 dbg_log ("\t%s (%s)", serv2str[req->type],
224 inet_ntop (req->type == GETHOSTBYADDR
225 ? AF_INET : AF_INET6,
226 key, buf, sizeof (buf)));
228 else
229 dbg_log ("\t%s (%s)", serv2str[req->type], key);
232 /* Is this service enabled? */
233 if (!db->enabled)
235 /* No, sent the prepared record. */
236 if (TEMP_FAILURE_RETRY (write (fd, db->disabled_iov->iov_base,
237 db->disabled_iov->iov_len))
238 != db->disabled_iov->iov_len)
240 /* We have problems sending the result. */
241 char buf[256];
242 dbg_log (_("cannot write result: %s"),
243 strerror_r (errno, buf, sizeof (buf)));
246 return;
249 /* Be sure we can read the data. */
250 pthread_rwlock_rdlock (&db->lock);
252 /* See whether we can handle it from the cache. */
253 cached = (struct hashentry *) cache_search (req->type, key, req->key_len,
254 db);
255 if (cached != NULL)
257 /* Hurray it's in the cache. */
258 if (TEMP_FAILURE_RETRY (write (fd, cached->packet, cached->total))
259 != cached->total)
261 /* We have problems sending the result. */
262 char buf[256];
263 dbg_log (_("cannot write result: %s"),
264 strerror_r (errno, buf, sizeof (buf)));
267 pthread_rwlock_unlock (&db->lock);
269 return;
272 pthread_rwlock_unlock (&db->lock);
274 else
275 if (debug_level > 0)
276 dbg_log ("\t%s", serv2str[req->type]);
278 /* Handle the request. */
279 switch (req->type)
281 case GETPWBYNAME:
282 addpwbyname (&dbs[serv2db[req->type]], fd, req, key);
283 break;
285 case GETPWBYUID:
286 addpwbyuid (&dbs[serv2db[req->type]], fd, req, key);
287 break;
289 case GETGRBYNAME:
290 addgrbyname (&dbs[serv2db[req->type]], fd, req, key);
291 break;
293 case GETGRBYGID:
294 addgrbygid (&dbs[serv2db[req->type]], fd, req, key);
295 break;
297 case GETHOSTBYNAME:
298 addhstbyname (&dbs[serv2db[req->type]], fd, req, key);
299 break;
301 case GETHOSTBYNAMEv6:
302 addhstbynamev6 (&dbs[serv2db[req->type]], fd, req, key);
303 break;
305 case GETHOSTBYADDR:
306 addhstbyaddr (&dbs[serv2db[req->type]], fd, req, key);
307 break;
309 case GETHOSTBYADDRv6:
310 addhstbyaddrv6 (&dbs[serv2db[req->type]], fd, req, key);
311 break;
313 case GETSTAT:
314 send_stats (fd, dbs);
315 break;
317 case SHUTDOWN:
318 termination_handler (0);
319 break;
321 default:
322 abort ();
327 /* This is the main loop. It is replicated in different threads but the
328 `poll' call makes sure only one thread handles an incoming connection. */
329 static void *
330 __attribute__ ((__noreturn__))
331 nscd_run (void *p)
333 int my_number = (int) p;
334 struct pollfd conn;
335 int run_prune = my_number < lastdb && dbs[my_number].enabled;
336 time_t now = time (NULL);
337 time_t next_prune = now + CACHE_PRUNE_INTERVAL;
338 int timeout = run_prune ? 1000 * (next_prune - now) : -1;
340 conn.fd = sock;
341 conn.events = POLLRDNORM;
343 while (1)
345 int nr = poll (&conn, 1, timeout);
347 if (nr == 0)
349 /* The `poll' call timed out. It's time to clean up the cache. */
350 assert (my_number < lastdb);
351 now = time (NULL);
352 prune_cache (&dbs[my_number], now);
353 next_prune = now + CACHE_PRUNE_INTERVAL;
354 timeout = 1000 * (next_prune - now);
355 continue;
358 /* We have a new incoming connection. */
359 if (conn.revents & (POLLRDNORM|POLLERR|POLLHUP|POLLNVAL))
361 /* Accept the connection. */
362 int fd = accept (conn.fd, NULL, NULL);
363 request_header req;
364 char buf[256];
366 if (fd < 0)
368 dbg_log (_("while accepting connection: %s"),
369 strerror_r (errno, buf, sizeof (buf)));
370 continue;
373 /* Now read the request. */
374 if (TEMP_FAILURE_RETRY (read (fd, &req, sizeof (req)))
375 != sizeof (req))
377 dbg_log (_("short read while reading request: %s"),
378 strerror_r (errno, buf, sizeof (buf)));
379 close (fd);
380 continue;
383 /* It should not be possible to crash the nscd with a silly
384 request (i.e., a terribly large key. We limit the size
385 to 1kb. */
386 if (req.key_len < 0 || req.key_len > 1024)
388 dbg_log (_("key length in request too long: %Zd"), req.key_len);
389 close (fd);
390 continue;
392 else
394 /* Get the key. */
395 char keybuf[req.key_len];
397 if (TEMP_FAILURE_RETRY (read (fd, keybuf, req.key_len))
398 != req.key_len)
400 dbg_log (_("short read while reading request key: %s"),
401 strerror_r (errno, buf, sizeof (buf)));
402 close (fd);
403 continue;
406 /* Phew, we got all the data, now process it. */
407 handle_request (fd, &req, keybuf);
409 /* We are done. */
410 close (fd);
414 if (run_prune)
416 now = time (NULL);
417 timeout = now < next_prune ? 1000 * (next_prune - now) : 0;
423 /* Start all the threads we want. The initial process is thread no. 1. */
424 void
425 start_threads (void)
427 int i;
428 pthread_attr_t attr;
429 pthread_t th;
431 pthread_attr_init (&attr);
432 pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
434 /* We allow less than LASTDB threads only for debugging. */
435 if (debug_level == 0)
436 nthreads = MAX (nthreads, lastdb);
438 for (i = 1; i < nthreads; ++i)
439 pthread_create (&th, &attr, nscd_run, (void *) i);
441 pthread_attr_destroy (&attr);
443 nscd_run ((void *) 0);