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