Update.
[glibc.git] / nscd / connections.c
blob8e6839a1576eabc256299f461bb31b88c8eb9005
1 /* Inner loops of cache daemon.
2 Copyright (C) 1998 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 <sys/param.h>
28 #include <sys/poll.h>
29 #include <sys/socket.h>
30 #include <sys/stat.h>
31 #include <sys/un.h>
33 #include "nscd.h"
34 #include "dbg_log.h"
37 /* Mapping of request type to database. */
38 static const dbtype serv2db[LASTDBREQ + 1] =
40 [GETPWBYNAME] = pwddb,
41 [GETPWBYUID] = pwddb,
42 [GETGRBYNAME] = grpdb,
43 [GETGRBYGID] = grpdb,
44 [GETHOSTBYNAME] = hstdb,
45 [GETHOSTBYNAMEv6] = hstdb,
46 [GETHOSTBYADDR] = hstdb,
47 [GETHOSTBYADDRv6] = hstdb,
50 /* Map request type to a string. */
51 const char *serv2str[LASTREQ] =
53 [GETPWBYNAME] = "GETPWBYNAME",
54 [GETPWBYUID] = "GETPWBYUID",
55 [GETGRBYNAME] = "GETGRBYNAME",
56 [GETGRBYGID] = "GETGRBYGID",
57 [GETHOSTBYNAME] = "GETHOSTBYNAME",
58 [GETHOSTBYNAMEv6] = "GETHOSTBYNAMEv6",
59 [GETHOSTBYADDR] = "GETHOSTBYADDR",
60 [GETHOSTBYADDRv6] = "GETHOSTBYADDRv6",
61 [SHUTDOWN] = "SHUTDOWN",
62 [GETSTAT] = "GETSTAT"
65 /* The control data structures for the services. */
66 static struct database dbs[lastdb] =
68 [pwddb] = {
69 lock: PTHREAD_RWLOCK_INITIALIZER,
70 enabled: 1,
71 check_file: 1,
72 filename: "/etc/passwd",
73 module: 211,
74 disabled_iov: &pwd_iov_disabled
76 [grpdb] = {
77 lock: PTHREAD_RWLOCK_INITIALIZER,
78 enabled: 1,
79 check_file: 1,
80 filename: "/etc/group",
81 module: 211,
82 disabled_iov: &grp_iov_disabled
84 [hstdb] = {
85 lock: PTHREAD_RWLOCK_INITIALIZER,
86 enabled: 1,
87 check_file: 1,
88 filename: "/etc/hosts",
89 module: 211,
90 disabled_iov: &hst_iov_disabled
94 /* Number of threads to use. */
95 int nthreads = -1;
97 /* Socket for incoming connections. */
98 static int sock;
101 /* Initialize database information structures. */
102 void
103 nscd_init (const char *conffile)
105 struct sockaddr_un sock_addr;
106 size_t cnt;
108 /* Read the configuration file. */
109 if (nscd_parse_file (conffile, dbs) != 0)
111 /* We couldn't read the configuration file. Disable all services
112 by shutting down the srever. */
113 dbg_log (_("cannot read configuration file; this is fatal"));
114 exit (1);
116 if (nthreads == -1)
117 /* No configuration for this value, assume a default. */
118 nthreads = 2 * lastdb;
120 for (cnt = 0; cnt < lastdb; ++cnt)
121 if (dbs[cnt].enabled)
123 pthread_rwlock_init (&dbs[cnt].lock, NULL);
125 dbs[cnt].array = (struct hashentry **)
126 calloc (dbs[cnt].module, sizeof (struct hashentry *));
127 if (dbs[cnt].array == NULL)
128 error (EXIT_FAILURE, errno, "while allocating cache");
130 if (dbs[cnt].check_file)
132 /* We need the modification date of the file. */
133 struct stat st;
135 if (stat (dbs[cnt].filename, &st) < 0)
137 char buf[128];
138 /* We cannot stat() the file, disable file checking. */
139 dbg_log (_("cannot stat() file `%s': %s"),
140 dbs[cnt].filename,
141 strerror_r (errno, buf, sizeof (buf)));
142 dbs[cnt].check_file = 0;
144 else
145 dbs[cnt].file_mtime = st.st_mtime;
149 /* Create the socket. */
150 sock = socket (AF_UNIX, SOCK_STREAM, 0);
151 if (sock < 0)
153 dbg_log (_("cannot open socket: %s"), strerror (errno));
154 exit (1);
156 /* Bind a name to the socket. */
157 sock_addr.sun_family = AF_UNIX;
158 strcpy (sock_addr.sun_path, _PATH_NSCDSOCKET);
159 if (bind (sock, (struct sockaddr *) &sock_addr, sizeof (sock_addr)) < 0)
161 dbg_log ("%s: %s", _PATH_NSCDSOCKET, strerror (errno));
162 exit (1);
165 /* Set permissions for the socket. */
166 chmod (_PATH_NSCDSOCKET, 0666);
168 /* Set the socket up to accept connections. */
169 if (listen (sock, SOMAXCONN) < 0)
171 dbg_log (_("cannot enable socket to accept connections: %s"),
172 strerror (errno));
173 exit (1);
178 /* Close the connections. */
179 void
180 close_sockets (void)
182 close (sock);
186 /* Handle new request. */
187 static void
188 handle_request (int fd, request_header *req, void *key)
190 if (debug_level > 0)
191 dbg_log (_("handle_requests: request received (Version = %d)"),
192 req->version);
194 if (req->version != NSCD_VERSION)
196 dbg_log (_("\
197 cannot handle old request version %d; current version is %d"),
198 req->version, NSCD_VERSION);
199 return;
202 if (req->type >= GETPWBYNAME && req->type <= LASTDBREQ)
204 struct hashentry *cached;
205 struct database *db = &dbs[serv2db[req->type]];
207 if (debug_level > 0)
208 dbg_log ("\t%s (%s)", serv2str[req->type], key);
210 /* Is this service enabled? */
211 if (!db->enabled)
213 /* No sent the prepared record. */
214 if (TEMP_FAILURE_RETRY (write (fd, db->disabled_iov->iov_base,
215 db->disabled_iov->iov_len))
216 != db->disabled_iov->iov_len)
218 /* We have problems sending the result. */
219 char buf[256];
220 dbg_log (_("cannot write result: %s"),
221 strerror_r (errno, buf, sizeof (buf)));
224 return;
227 /* Be sure we can read the data. */
228 pthread_rwlock_rdlock (&db->lock);
230 /* See whether we can handle it from the cache. */
231 cached = (struct hashentry *) cache_search (req->type, key, req->key_len,
232 db);
233 if (cached != NULL)
235 /* Hurray it's in the cache. */
236 if (TEMP_FAILURE_RETRY (write (fd, cached->packet, cached->total))
237 != cached->total)
239 /* We have problems sending the result. */
240 char buf[256];
241 dbg_log (_("cannot write result: %s"),
242 strerror_r (errno, buf, sizeof (buf)));
245 pthread_rwlock_unlock (&db->lock);
247 return;
250 pthread_rwlock_unlock (&db->lock);
252 else
253 if (debug_level > 0)
254 dbg_log ("\t%s", serv2str[req->type]);
256 /* Handle the request. */
257 switch (req->type)
259 case GETPWBYNAME:
260 addpwbyname (&dbs[serv2db[req->type]], fd, req, key);
261 break;
263 case GETPWBYUID:
264 addpwbyuid (&dbs[serv2db[req->type]], fd, req, key);
265 break;
267 case GETGRBYNAME:
268 addgrbyname (&dbs[serv2db[req->type]], fd, req, key);
269 break;
271 case GETGRBYGID:
272 addgrbygid (&dbs[serv2db[req->type]], fd, req, key);
273 break;
275 case GETHOSTBYNAME:
276 addhstbyname (&dbs[serv2db[req->type]], fd, req, key);
277 break;
279 case GETHOSTBYNAMEv6:
280 addhstbynamev6 (&dbs[serv2db[req->type]], fd, req, key);
281 break;
283 case GETHOSTBYADDR:
284 addhstbyaddr (&dbs[serv2db[req->type]], fd, req, key);
285 break;
287 case GETHOSTBYADDRv6:
288 addhstbyaddrv6 (&dbs[serv2db[req->type]], fd, req, key);
289 break;
291 case GETSTAT:
292 send_stats (fd, dbs);
293 break;
295 case SHUTDOWN:
296 termination_handler (0);
297 break;
299 default:
300 abort ();
305 /* This is the main loop. It is replicated in different threads but the
306 `poll' call makes sure only one thread handles an incoming connection. */
307 static void *
308 __attribute__ ((__noreturn__))
309 nscd_run (void *p)
311 int my_number = (int) p;
312 struct pollfd conn;
313 int run_prune = my_number < lastdb && dbs[my_number].enabled;
314 time_t now = time (NULL);
315 time_t next_prune = now + 15;
316 int timeout = run_prune ? 1000 * (next_prune - now) : -1;
318 conn.fd = sock;
319 conn.events = POLLRDNORM;
321 while (1)
323 int nr = poll (&conn, 1, timeout);
325 if (nr == 0)
327 /* The `poll' call timed out. It's time to clean up the cache. */
328 assert (my_number < lastdb);
329 now = time (NULL);
330 prune_cache (&dbs[my_number], now);
331 next_prune = now + 15;
332 timeout = 1000 * (next_prune - now);
333 continue;
336 /* We have a new incoming connection. */
337 if (conn.revents & (POLLRDNORM|POLLERR|POLLHUP|POLLNVAL))
339 /* Accept the connection. */
340 int fd = accept (conn.fd, NULL, NULL);
341 request_header req;
342 char buf[256];
344 if (fd < 0)
346 dbg_log (_("while accepting connection: %s"),
347 strerror_r (errno, buf, sizeof (buf)));
348 continue;
351 /* Now read the request. */
352 if (TEMP_FAILURE_RETRY (read (fd, &req, sizeof (req)))
353 != sizeof (req))
355 dbg_log (_("short read while reading request: %s"),
356 strerror_r (errno, buf, sizeof (buf)));
357 close (fd);
358 continue;
361 /* It should not be possible to crash the nscd with a silly
362 request (i.e., a terribly large key. We limit the size
363 to 1kb. */
364 if (req.key_len < 0 || req.key_len > 1024)
366 dbg_log (_("key length in request to long: %Zd"), req.key_len);
367 close (fd);
368 continue;
370 else
372 /* Get the key. */
373 char keybuf[req.key_len];
375 if (TEMP_FAILURE_RETRY (read (fd, keybuf, req.key_len))
376 != req.key_len)
378 dbg_log (_("short read while reading request key: %s"),
379 strerror_r (errno, buf, sizeof (buf)));
380 close (fd);
381 continue;
384 /* Phew, we got all the data, now process it. */
385 handle_request (fd, &req, keybuf);
387 /* We are done. */
388 close (fd);
392 if (run_prune)
394 now = time (NULL);
395 timeout = now < next_prune ? 1000 * (next_prune - now) : 0;
401 /* Start all the threads we want. The initial process is thread no. 1. */
402 void
403 start_threads (void)
405 int i;
406 pthread_attr_t attr;
407 pthread_t th;
409 pthread_attr_init (&attr);
410 pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
412 /* We allow less than LASTDB threads only for debugging. */
413 if (debug_level == 0)
414 nthreads = MAX (nthreads, lastdb);
416 for (i = 1; i < nthreads; ++i)
417 pthread_create (&th, &attr, nscd_run, (void *) i);
419 pthread_attr_destroy (&attr);
421 nscd_run ((void *) 0);