9d1b4d366ef816dcce043cd401309871f499e5ee
[kopensolaris-gnu/glibc.git] / nscd / connections.c
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.
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 <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>
33
34 #include "nscd.h"
35 #include "dbg_log.h"
36
37
38 /* Mapping of request type to database.  */
39 static const dbtype serv2db[LASTDBREQ + 1] =
40 {
41   [GETPWBYNAME] = pwddb,
42   [GETPWBYUID] = pwddb,
43   [GETGRBYNAME] = grpdb,
44   [GETGRBYGID] = grpdb,
45   [GETHOSTBYNAME] = hstdb,
46   [GETHOSTBYNAMEv6] = hstdb,
47   [GETHOSTBYADDR] = hstdb,
48   [GETHOSTBYADDRv6] = hstdb,
49 };
50
51 /* Map request type to a string.  */
52 const char *serv2str[LASTREQ] =
53 {
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"
64 };
65
66 /* The control data structures for the services.  */
67 static struct database dbs[lastdb] =
68 {
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
78   },
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
88   },
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
98   }
99 };
100
101 /* Number of seconds between two cache pruning runs.  */
102 #define CACHE_PRUNE_INTERVAL    15
103
104 /* Number of threads to use.  */
105 int nthreads = -1;
106
107 /* Socket for incoming connections.  */
108 static int sock;
109
110
111 /* Initialize database information structures.  */
112 void
113 nscd_init (const char *conffile)
114 {
115   struct sockaddr_un sock_addr;
116   size_t cnt;
117
118   /* Read the configuration file.  */
119   if (nscd_parse_file (conffile, dbs) != 0)
120     {
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);
125     }
126   if (nthreads == -1)
127     /* No configuration for this value, assume a default.  */
128     nthreads = 2 * lastdb;
129
130   for (cnt = 0; cnt < lastdb; ++cnt)
131     if (dbs[cnt].enabled)
132       {
133         pthread_rwlock_init (&dbs[cnt].lock, NULL);
134
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");
139
140         if (dbs[cnt].check_file)
141           {
142             /* We need the modification date of the file.  */
143             struct stat st;
144
145             if (stat (dbs[cnt].filename, &st) < 0)
146               {
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;
153               }
154             else
155               dbs[cnt].file_mtime = st.st_mtime;
156           }
157       }
158
159   /* Create the socket.  */
160   sock = socket (AF_UNIX, SOCK_STREAM, 0);
161   if (sock < 0)
162     {
163       dbg_log (_("cannot open socket: %s"), strerror (errno));
164       exit (1);
165     }
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)
170     {
171       dbg_log ("%s: %s", _PATH_NSCDSOCKET, strerror (errno));
172       exit (1);
173     }
174
175   /* Set permissions for the socket.  */
176   chmod (_PATH_NSCDSOCKET, 0666);
177
178   /* Set the socket up to accept connections.  */
179   if (listen (sock, SOMAXCONN) < 0)
180     {
181       dbg_log (_("cannot enable socket to accept connections: %s"),
182                strerror (errno));
183       exit (1);
184     }
185 }
186
187
188 /* Close the connections.  */
189 void
190 close_sockets (void)
191 {
192   close (sock);
193 }
194
195
196 /* Handle new request.  */
197 static void
198 handle_request (int fd, request_header *req, void *key, uid_t uid)
199 {
200   if (debug_level > 0)
201     dbg_log (_("handle_request: request received (Version = %d)"),
202              req->version);
203
204   if (req->version != NSCD_VERSION)
205     {
206       dbg_log (_("\
207 cannot handle old request version %d; current version is %d"),
208                req->version, NSCD_VERSION);
209       return;
210     }
211
212   if (req->type >= GETPWBYNAME && req->type <= LASTDBREQ)
213     {
214       struct hashentry *cached;
215       struct database *db = &dbs[serv2db[req->type]];
216
217       if (debug_level > 0)
218         {
219           if (req->type == GETHOSTBYADDR || req->type == GETHOSTBYADDRv6)
220             {
221               char buf[INET6_ADDRSTRLEN];
222
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)));
227             }
228           else
229             dbg_log ("\t%s (%s)", serv2str[req->type], key);
230         }
231
232       /* Is this service enabled?  */
233       if (!db->enabled)
234         {
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)
239             {
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)));
244             }
245
246           return;
247         }
248
249       /* Be sure we can read the data.  */
250       pthread_rwlock_rdlock (&db->lock);
251
252       /* See whether we can handle it from the cache.  */
253       cached = (struct hashentry *) cache_search (req->type, key, req->key_len,
254                                                   db, uid);
255       if (cached != NULL)
256         {
257           /* Hurray it's in the cache.  */
258           if (TEMP_FAILURE_RETRY (write (fd, cached->packet, cached->total))
259               != cached->total)
260             {
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)));
265             }
266
267           pthread_rwlock_unlock (&db->lock);
268
269           return;
270         }
271
272       pthread_rwlock_unlock (&db->lock);
273     }
274   else
275     if (debug_level > 0)
276       dbg_log ("\t%s", serv2str[req->type]);
277
278   /* Handle the request.  */
279   switch (req->type)
280     {
281     case GETPWBYNAME:
282       addpwbyname (&dbs[serv2db[req->type]], fd, req, key, uid);
283       break;
284
285     case GETPWBYUID:
286       addpwbyuid (&dbs[serv2db[req->type]], fd, req, key, uid);
287       break;
288
289     case GETGRBYNAME:
290       addgrbyname (&dbs[serv2db[req->type]], fd, req, key, uid);
291       break;
292
293     case GETGRBYGID:
294       addgrbygid (&dbs[serv2db[req->type]], fd, req, key, uid);
295       break;
296
297     case GETHOSTBYNAME:
298       addhstbyname (&dbs[serv2db[req->type]], fd, req, key, uid);
299       break;
300
301     case GETHOSTBYNAMEv6:
302       addhstbynamev6 (&dbs[serv2db[req->type]], fd, req, key, uid);
303       break;
304
305     case GETHOSTBYADDR:
306       addhstbyaddr (&dbs[serv2db[req->type]], fd, req, key, uid);
307       break;
308
309     case GETHOSTBYADDRv6:
310       addhstbyaddrv6 (&dbs[serv2db[req->type]], fd, req, key, uid);
311       break;
312
313     case GETSTAT:
314     case SHUTDOWN:
315       /* Accept shutdown and getstat only from root */
316       if (secure_in_use && uid == 0)
317         {
318           if (req->type == GETSTAT)
319             send_stats (fd, dbs);
320           else
321             termination_handler (0);
322         }
323       else
324         {
325           struct ucred caller;
326           int optlen = sizeof (caller);
327
328           if (getsockopt (fd, SOL_SOCKET, SO_PEERCRED, &caller, &optlen) < 0)
329             {
330               char buf[256];
331
332               dbg_log (_("error getting callers id: %s"),
333                        strerror_r (errno, buf, sizeof (buf)));
334             }
335           else
336             if (caller.uid == 0)
337               {
338                 if (req->type == GETSTAT)
339                   send_stats (fd, dbs);
340                 else
341                   termination_handler (0);
342               }
343         }
344       break;
345
346     default:
347       abort ();
348     }
349 }
350
351
352 /* This is the main loop.  It is replicated in different threads but the
353    `poll' call makes sure only one thread handles an incoming connection.  */
354 static void *
355 __attribute__ ((__noreturn__))
356 nscd_run (void *p)
357 {
358   int my_number = (int) p;
359   struct pollfd conn;
360   int run_prune = my_number < lastdb && dbs[my_number].enabled;
361   time_t now = time (NULL);
362   time_t next_prune = now + CACHE_PRUNE_INTERVAL;
363   int timeout = run_prune ? 1000 * (next_prune - now) : -1;
364
365   conn.fd = sock;
366   conn.events = POLLRDNORM;
367
368   while (1)
369     {
370       int nr = poll (&conn, 1, timeout);
371
372       if (nr == 0)
373         {
374           /* The `poll' call timed out.  It's time to clean up the cache.  */
375           assert (my_number < lastdb);
376           now = time (NULL);
377           prune_cache (&dbs[my_number], now);
378           next_prune = now + CACHE_PRUNE_INTERVAL;
379           timeout = 1000 * (next_prune - now);
380           continue;
381         }
382
383       /* We have a new incoming connection.  */
384       if (conn.revents & (POLLRDNORM|POLLERR|POLLHUP|POLLNVAL))
385         {
386           /* Accept the connection.  */
387           int fd = accept (conn.fd, NULL, NULL);
388           request_header req;
389           char buf[256];
390           uid_t uid = 0;
391
392           if (fd < 0)
393             {
394               dbg_log (_("while accepting connection: %s"),
395                        strerror_r (errno, buf, sizeof (buf)));
396               continue;
397             }
398
399           /* Now read the request.  */
400           if (TEMP_FAILURE_RETRY (read (fd, &req, sizeof (req)))
401               != sizeof (req))
402             {
403               dbg_log (_("short read while reading request: %s"),
404                        strerror_r (errno, buf, sizeof (buf)));
405               close (fd);
406               continue;
407             }
408
409           if (secure_in_use)
410             {
411               struct ucred caller;
412               int optlen = sizeof (caller);
413
414               if (getsockopt (fd, SOL_SOCKET, SO_PEERCRED,
415                               &caller, &optlen) < 0)
416                 {
417                   dbg_log (_("error getting callers id: %s"),
418                            strerror_r (errno, buf, sizeof (buf)));
419                   close (fd);
420                   continue;
421                 }
422
423               if (req.type < GETPWBYNAME || req.type > LASTDBREQ
424                   || secure[serv2db[req.type]])
425                 uid = caller.uid;
426             }
427
428           /* It should not be possible to crash the nscd with a silly
429              request (i.e., a terribly large key.  We limit the size
430              to 1kb.  */
431           if (req.key_len < 0 || req.key_len > 1024)
432             {
433               dbg_log (_("key length in request too long: %Zd"), req.key_len);
434               close (fd);
435               continue;
436             }
437           else
438             {
439               /* Get the key.  */
440               char keybuf[req.key_len];
441
442               if (TEMP_FAILURE_RETRY (read (fd, keybuf, req.key_len))
443                   != req.key_len)
444                 {
445                   dbg_log (_("short read while reading request key: %s"),
446                            strerror_r (errno, buf, sizeof (buf)));
447                   close (fd);
448                   continue;
449                 }
450
451               /* Phew, we got all the data, now process it.  */
452               handle_request (fd, &req, keybuf, uid);
453
454               /* We are done.  */
455               close (fd);
456             }
457         }
458
459       if (run_prune)
460         {
461           now = time (NULL);
462           timeout = now < next_prune ? 1000 * (next_prune - now) : 0;
463         }
464     }
465 }
466
467
468 /* Start all the threads we want.  The initial process is thread no. 1.  */
469 void
470 start_threads (void)
471 {
472   int i;
473   pthread_attr_t attr;
474   pthread_t th;
475
476   pthread_attr_init (&attr);
477   pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
478
479   /* We allow less than LASTDB threads only for debugging.  */
480   if (debug_level == 0)
481     nthreads = MAX (nthreads, lastdb);
482
483   for (i = 1; i < nthreads; ++i)
484     pthread_create (&th, &attr, nscd_run, (void *) i);
485
486   pthread_attr_destroy (&attr);
487
488   nscd_run ((void *) 0);
489 }