]> git.ipfire.org Git - thirdparty/glibc.git/blame - nscd/connections.c
Update.
[thirdparty/glibc.git] / nscd / connections.c
CommitLineData
67479a70 1/* Inner loops of cache daemon.
8d8c6efa 2 Copyright (C) 1998, 1999 Free Software Foundation, Inc.
d67281a7 3 This file is part of the GNU C Library.
67479a70 4 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
d67281a7
UD
5
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.
10
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.
15
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,
67479a70 19 Boston, MA 02111-1307, USA. */
d67281a7 20
67479a70 21#include <assert.h>
d67281a7 22#include <error.h>
67479a70 23#include <errno.h>
d67281a7 24#include <pthread.h>
d67281a7
UD
25#include <stdlib.h>
26#include <unistd.h>
4360eafd 27#include <libintl.h>
8d8c6efa 28#include <arpa/inet.h>
67479a70 29#include <sys/param.h>
a53bad16 30#include <sys/poll.h>
d67281a7
UD
31#include <sys/socket.h>
32#include <sys/stat.h>
d67281a7
UD
33#include <sys/un.h>
34
35#include "nscd.h"
36#include "dbg_log.h"
37
d67281a7 38
67479a70
UD
39/* Mapping of request type to database. */
40static const dbtype serv2db[LASTDBREQ + 1] =
d67281a7 41{
67479a70
UD
42 [GETPWBYNAME] = pwddb,
43 [GETPWBYUID] = pwddb,
44 [GETGRBYNAME] = grpdb,
45 [GETGRBYGID] = grpdb,
46 [GETHOSTBYNAME] = hstdb,
47 [GETHOSTBYNAMEv6] = hstdb,
48 [GETHOSTBYADDR] = hstdb,
49 [GETHOSTBYADDRv6] = hstdb,
50};
51
52/* Map request type to a string. */
53const char *serv2str[LASTREQ] =
d67281a7 54{
67479a70
UD
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};
66
67/* The control data structures for the services. */
68static struct database dbs[lastdb] =
69{
70 [pwddb] = {
71 lock: PTHREAD_RWLOCK_INITIALIZER,
34489d95 72 enabled: 0,
67479a70
UD
73 check_file: 1,
74 filename: "/etc/passwd",
75 module: 211,
34489d95
UD
76 disabled_iov: &pwd_iov_disabled,
77 postimeout: 3600,
78 negtimeout: 20
67479a70
UD
79 },
80 [grpdb] = {
81 lock: PTHREAD_RWLOCK_INITIALIZER,
34489d95 82 enabled: 0,
67479a70
UD
83 check_file: 1,
84 filename: "/etc/group",
85 module: 211,
34489d95
UD
86 disabled_iov: &grp_iov_disabled,
87 postimeout: 3600,
88 negtimeout: 60
67479a70
UD
89 },
90 [hstdb] = {
91 lock: PTHREAD_RWLOCK_INITIALIZER,
34489d95 92 enabled: 0,
67479a70
UD
93 check_file: 1,
94 filename: "/etc/hosts",
95 module: 211,
34489d95
UD
96 disabled_iov: &hst_iov_disabled,
97 postimeout: 3600,
98 negtimeout: 20
67479a70
UD
99 }
100};
d67281a7 101
34489d95
UD
102/* Number of seconds between two cache pruning runs. */
103#define CACHE_PRUNE_INTERVAL 15
104
67479a70
UD
105/* Number of threads to use. */
106int nthreads = -1;
d67281a7 107
67479a70
UD
108/* Socket for incoming connections. */
109static int sock;
d67281a7 110
d67281a7 111
67479a70
UD
112/* Initialize database information structures. */
113void
114nscd_init (const char *conffile)
d67281a7 115{
67479a70
UD
116 struct sockaddr_un sock_addr;
117 size_t cnt;
d67281a7 118
67479a70
UD
119 /* Read the configuration file. */
120 if (nscd_parse_file (conffile, dbs) != 0)
d67281a7 121 {
67479a70
UD
122 /* We couldn't read the configuration file. Disable all services
123 by shutting down the srever. */
124 dbg_log (_("cannot read configuration file; this is fatal"));
125 exit (1);
d67281a7 126 }
67479a70
UD
127 if (nthreads == -1)
128 /* No configuration for this value, assume a default. */
129 nthreads = 2 * lastdb;
d67281a7 130
67479a70
UD
131 for (cnt = 0; cnt < lastdb; ++cnt)
132 if (dbs[cnt].enabled)
9db29cde 133 {
67479a70 134 pthread_rwlock_init (&dbs[cnt].lock, NULL);
264d5b94 135
67479a70
UD
136 dbs[cnt].array = (struct hashentry **)
137 calloc (dbs[cnt].module, sizeof (struct hashentry *));
138 if (dbs[cnt].array == NULL)
139 error (EXIT_FAILURE, errno, "while allocating cache");
d67281a7 140
67479a70 141 if (dbs[cnt].check_file)
d67281a7 142 {
67479a70
UD
143 /* We need the modification date of the file. */
144 struct stat st;
d67281a7 145
67479a70 146 if (stat (dbs[cnt].filename, &st) < 0)
d67281a7 147 {
67479a70
UD
148 char buf[128];
149 /* We cannot stat() the file, disable file checking. */
150 dbg_log (_("cannot stat() file `%s': %s"),
151 dbs[cnt].filename,
152 strerror_r (errno, buf, sizeof (buf)));
153 dbs[cnt].check_file = 0;
d67281a7
UD
154 }
155 else
67479a70
UD
156 dbs[cnt].file_mtime = st.st_mtime;
157 }
158 }
d67281a7
UD
159
160 /* Create the socket. */
67479a70
UD
161 sock = socket (AF_UNIX, SOCK_STREAM, 0);
162 if (sock < 0)
d67281a7 163 {
67479a70 164 dbg_log (_("cannot open socket: %s"), strerror (errno));
d67281a7
UD
165 exit (1);
166 }
167 /* Bind a name to the socket. */
168 sock_addr.sun_family = AF_UNIX;
169 strcpy (sock_addr.sun_path, _PATH_NSCDSOCKET);
67479a70 170 if (bind (sock, (struct sockaddr *) &sock_addr, sizeof (sock_addr)) < 0)
d67281a7
UD
171 {
172 dbg_log ("%s: %s", _PATH_NSCDSOCKET, strerror (errno));
173 exit (1);
174 }
67479a70 175
d67281a7
UD
176 /* Set permissions for the socket. */
177 chmod (_PATH_NSCDSOCKET, 0666);
178
179 /* Set the socket up to accept connections. */
67479a70 180 if (listen (sock, SOMAXCONN) < 0)
d67281a7 181 {
67479a70
UD
182 dbg_log (_("cannot enable socket to accept connections: %s"),
183 strerror (errno));
d67281a7
UD
184 exit (1);
185 }
d67281a7
UD
186}
187
67479a70
UD
188
189/* Close the connections. */
d67281a7 190void
67479a70 191close_sockets (void)
d67281a7 192{
67479a70
UD
193 close (sock);
194}
d67281a7 195
67479a70
UD
196
197/* Handle new request. */
198static void
a1c542bf 199handle_request (int fd, request_header *req, void *key, uid_t uid)
67479a70
UD
200{
201 if (debug_level > 0)
ce85d65b 202 dbg_log (_("handle_request: request received (Version = %d)"),
67479a70
UD
203 req->version);
204
205 if (req->version != NSCD_VERSION)
d67281a7 206 {
67479a70
UD
207 dbg_log (_("\
208cannot handle old request version %d; current version is %d"),
209 req->version, NSCD_VERSION);
d67281a7
UD
210 return;
211 }
212
67479a70 213 if (req->type >= GETPWBYNAME && req->type <= LASTDBREQ)
d67281a7 214 {
67479a70
UD
215 struct hashentry *cached;
216 struct database *db = &dbs[serv2db[req->type]];
264d5b94 217
67479a70 218 if (debug_level > 0)
8d8c6efa
UD
219 {
220 if (req->type == GETHOSTBYADDR || req->type == GETHOSTBYADDRv6)
221 {
222 char buf[INET6_ADDRSTRLEN];
223
224 dbg_log ("\t%s (%s)", serv2str[req->type],
225 inet_ntop (req->type == GETHOSTBYADDR
226 ? AF_INET : AF_INET6,
227 key, buf, sizeof (buf)));
228 }
229 else
230 dbg_log ("\t%s (%s)", serv2str[req->type], key);
231 }
d67281a7 232
67479a70
UD
233 /* Is this service enabled? */
234 if (!db->enabled)
235 {
ce85d65b 236 /* No, sent the prepared record. */
67479a70
UD
237 if (TEMP_FAILURE_RETRY (write (fd, db->disabled_iov->iov_base,
238 db->disabled_iov->iov_len))
239 != db->disabled_iov->iov_len)
240 {
241 /* We have problems sending the result. */
242 char buf[256];
243 dbg_log (_("cannot write result: %s"),
244 strerror_r (errno, buf, sizeof (buf)));
245 }
d67281a7 246
67479a70
UD
247 return;
248 }
d67281a7 249
67479a70
UD
250 /* Be sure we can read the data. */
251 pthread_rwlock_rdlock (&db->lock);
252
253 /* See whether we can handle it from the cache. */
254 cached = (struct hashentry *) cache_search (req->type, key, req->key_len,
a1c542bf 255 db, uid);
67479a70
UD
256 if (cached != NULL)
257 {
258 /* Hurray it's in the cache. */
259 if (TEMP_FAILURE_RETRY (write (fd, cached->packet, cached->total))
260 != cached->total)
261 {
262 /* We have problems sending the result. */
263 char buf[256];
264 dbg_log (_("cannot write result: %s"),
265 strerror_r (errno, buf, sizeof (buf)));
266 }
267
268 pthread_rwlock_unlock (&db->lock);
269
270 return;
271 }
272
273 pthread_rwlock_unlock (&db->lock);
d67281a7
UD
274 }
275 else
67479a70
UD
276 if (debug_level > 0)
277 dbg_log ("\t%s", serv2str[req->type]);
278
279 /* Handle the request. */
280 switch (req->type)
d67281a7 281 {
67479a70 282 case GETPWBYNAME:
a1c542bf 283 addpwbyname (&dbs[serv2db[req->type]], fd, req, key, uid);
67479a70
UD
284 break;
285
286 case GETPWBYUID:
a1c542bf 287 addpwbyuid (&dbs[serv2db[req->type]], fd, req, key, uid);
67479a70
UD
288 break;
289
290 case GETGRBYNAME:
a1c542bf 291 addgrbyname (&dbs[serv2db[req->type]], fd, req, key, uid);
67479a70
UD
292 break;
293
294 case GETGRBYGID:
a1c542bf 295 addgrbygid (&dbs[serv2db[req->type]], fd, req, key, uid);
67479a70
UD
296 break;
297
298 case GETHOSTBYNAME:
a1c542bf 299 addhstbyname (&dbs[serv2db[req->type]], fd, req, key, uid);
67479a70
UD
300 break;
301
302 case GETHOSTBYNAMEv6:
a1c542bf 303 addhstbynamev6 (&dbs[serv2db[req->type]], fd, req, key, uid);
67479a70
UD
304 break;
305
306 case GETHOSTBYADDR:
a1c542bf 307 addhstbyaddr (&dbs[serv2db[req->type]], fd, req, key, uid);
67479a70
UD
308 break;
309
310 case GETHOSTBYADDRv6:
a1c542bf 311 addhstbyaddrv6 (&dbs[serv2db[req->type]], fd, req, key, uid);
67479a70
UD
312 break;
313
314 case GETSTAT:
67479a70 315 case SHUTDOWN:
54000924 316 /* Accept shutdown and getstat only from root */
a1c542bf 317 if (secure_in_use && uid == 0)
54000924
UD
318 {
319 if (req->type == GETSTAT)
320 send_stats (fd, dbs);
321 else
322 termination_handler (0);
323 }
a1c542bf
UD
324 else
325 {
326 struct ucred caller;
1709e27a 327 socklen_t optlen = sizeof (caller);
a1c542bf
UD
328
329 if (getsockopt (fd, SOL_SOCKET, SO_PEERCRED, &caller, &optlen) < 0)
330 {
331 char buf[256];
332
333 dbg_log (_("error getting callers id: %s"),
334 strerror_r (errno, buf, sizeof (buf)));
335 }
54000924
UD
336 else
337 if (caller.uid == 0)
338 {
339 if (req->type == GETSTAT)
340 send_stats (fd, dbs);
341 else
342 termination_handler (0);
343 }
a1c542bf 344 }
67479a70
UD
345 break;
346
347 default:
348 abort ();
d67281a7 349 }
67479a70
UD
350}
351
d67281a7 352
67479a70
UD
353/* This is the main loop. It is replicated in different threads but the
354 `poll' call makes sure only one thread handles an incoming connection. */
355static void *
356__attribute__ ((__noreturn__))
357nscd_run (void *p)
358{
359 int my_number = (int) p;
360 struct pollfd conn;
361 int run_prune = my_number < lastdb && dbs[my_number].enabled;
362 time_t now = time (NULL);
34489d95 363 time_t next_prune = now + CACHE_PRUNE_INTERVAL;
67479a70 364 int timeout = run_prune ? 1000 * (next_prune - now) : -1;
264d5b94 365
67479a70
UD
366 conn.fd = sock;
367 conn.events = POLLRDNORM;
d67281a7 368
67479a70 369 while (1)
d67281a7 370 {
67479a70
UD
371 int nr = poll (&conn, 1, timeout);
372
373 if (nr == 0)
374 {
375 /* The `poll' call timed out. It's time to clean up the cache. */
376 assert (my_number < lastdb);
377 now = time (NULL);
378 prune_cache (&dbs[my_number], now);
34489d95 379 next_prune = now + CACHE_PRUNE_INTERVAL;
67479a70
UD
380 timeout = 1000 * (next_prune - now);
381 continue;
382 }
383
384 /* We have a new incoming connection. */
385 if (conn.revents & (POLLRDNORM|POLLERR|POLLHUP|POLLNVAL))
d67281a7 386 {
67479a70
UD
387 /* Accept the connection. */
388 int fd = accept (conn.fd, NULL, NULL);
389 request_header req;
390 char buf[256];
a1c542bf 391 uid_t uid = 0;
d67281a7 392
67479a70 393 if (fd < 0)
d67281a7 394 {
67479a70
UD
395 dbg_log (_("while accepting connection: %s"),
396 strerror_r (errno, buf, sizeof (buf)));
397 continue;
398 }
264d5b94 399
67479a70
UD
400 /* Now read the request. */
401 if (TEMP_FAILURE_RETRY (read (fd, &req, sizeof (req)))
402 != sizeof (req))
403 {
404 dbg_log (_("short read while reading request: %s"),
405 strerror_r (errno, buf, sizeof (buf)));
406 close (fd);
407 continue;
408 }
264d5b94 409
a1c542bf
UD
410 if (secure_in_use)
411 {
412 struct ucred caller;
176c4992 413 socklen_t optlen = sizeof (caller);
a1c542bf
UD
414
415 if (getsockopt (fd, SOL_SOCKET, SO_PEERCRED,
416 &caller, &optlen) < 0)
417 {
418 dbg_log (_("error getting callers id: %s"),
419 strerror_r (errno, buf, sizeof (buf)));
420 close (fd);
421 continue;
422 }
423
397d46fd 424 if (req.type < GETPWBYNAME || req.type > LASTDBREQ
a1c542bf
UD
425 || secure[serv2db[req.type]])
426 uid = caller.uid;
427 }
428
67479a70
UD
429 /* It should not be possible to crash the nscd with a silly
430 request (i.e., a terribly large key. We limit the size
431 to 1kb. */
432 if (req.key_len < 0 || req.key_len > 1024)
433 {
ce85d65b 434 dbg_log (_("key length in request too long: %Zd"), req.key_len);
67479a70
UD
435 close (fd);
436 continue;
437 }
438 else
439 {
440 /* Get the key. */
441 char keybuf[req.key_len];
442
443 if (TEMP_FAILURE_RETRY (read (fd, keybuf, req.key_len))
444 != req.key_len)
445 {
446 dbg_log (_("short read while reading request key: %s"),
447 strerror_r (errno, buf, sizeof (buf)));
448 close (fd);
449 continue;
450 }
451
452 /* Phew, we got all the data, now process it. */
a1c542bf 453 handle_request (fd, &req, keybuf, uid);
67479a70
UD
454
455 /* We are done. */
456 close (fd);
d67281a7 457 }
d67281a7 458 }
264d5b94 459
67479a70
UD
460 if (run_prune)
461 {
462 now = time (NULL);
463 timeout = now < next_prune ? 1000 * (next_prune - now) : 0;
464 }
67308730 465 }
d67281a7
UD
466}
467
67479a70
UD
468
469/* Start all the threads we want. The initial process is thread no. 1. */
d67281a7 470void
67479a70 471start_threads (void)
d67281a7 472{
67479a70
UD
473 int i;
474 pthread_attr_t attr;
475 pthread_t th;
d67281a7 476
67479a70
UD
477 pthread_attr_init (&attr);
478 pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
d67281a7 479
67479a70
UD
480 /* We allow less than LASTDB threads only for debugging. */
481 if (debug_level == 0)
482 nthreads = MAX (nthreads, lastdb);
d67281a7 483
67479a70
UD
484 for (i = 1; i < nthreads; ++i)
485 pthread_create (&th, &attr, nscd_run, (void *) i);
d67281a7 486
67479a70 487 pthread_attr_destroy (&attr);
d67281a7 488
67479a70 489 nscd_run ((void *) 0);
d67281a7 490}