]>
Commit | Line | Data |
---|---|---|
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. */ |
40 | static 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. */ | |
53 | const 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. */ | |
68 | static 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. */ |
106 | int nthreads = -1; | |
d67281a7 | 107 | |
67479a70 UD |
108 | /* Socket for incoming connections. */ |
109 | static int sock; | |
d67281a7 | 110 | |
d67281a7 | 111 | |
67479a70 UD |
112 | /* Initialize database information structures. */ |
113 | void | |
114 | nscd_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 | 190 | void |
67479a70 | 191 | close_sockets (void) |
d67281a7 | 192 | { |
67479a70 UD |
193 | close (sock); |
194 | } | |
d67281a7 | 195 | |
67479a70 UD |
196 | |
197 | /* Handle new request. */ | |
198 | static void | |
a1c542bf | 199 | handle_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 (_("\ |
208 | cannot 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. */ | |
355 | static void * | |
356 | __attribute__ ((__noreturn__)) | |
357 | nscd_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 | 470 | void |
67479a70 | 471 | start_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 | } |