.
[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)
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);
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);
283       break;
284
285     case GETPWBYUID:
286       addpwbyuid (&dbs[serv2db[req->type]], fd, req, key);
287       break;
288
289     case GETGRBYNAME:
290       addgrbyname (&dbs[serv2db[req->type]], fd, req, key);
291       break;
292
293     case GETGRBYGID:
294       addgrbygid (&dbs[serv2db[req->type]], fd, req, key);
295       break;
296
297     case GETHOSTBYNAME:
298       addhstbyname (&dbs[serv2db[req->type]], fd, req, key);
299       break;
300
301     case GETHOSTBYNAMEv6:
302       addhstbynamev6 (&dbs[serv2db[req->type]], fd, req, key);
303       break;
304
305     case GETHOSTBYADDR:
306       addhstbyaddr (&dbs[serv2db[req->type]], fd, req, key);
307       break;
308
309     case GETHOSTBYADDRv6:
310       addhstbyaddrv6 (&dbs[serv2db[req->type]], fd, req, key);
311       break;
312
313     case GETSTAT:
314       send_stats (fd, dbs);
315       break;
316
317     case SHUTDOWN:
318       termination_handler (0);
319       break;
320
321     default:
322       abort ();
323     }
324 }
325
326
327 /* This is the main loop.  It is replicated in different threads but the
328    `poll' call makes sure only one thread handles an incoming connection.  */
329 static void *
330 __attribute__ ((__noreturn__))
331 nscd_run (void *p)
332 {
333   int my_number = (int) p;
334   struct pollfd conn;
335   int run_prune = my_number < lastdb && dbs[my_number].enabled;
336   time_t now = time (NULL);
337   time_t next_prune = now + CACHE_PRUNE_INTERVAL;
338   int timeout = run_prune ? 1000 * (next_prune - now) : -1;
339
340   conn.fd = sock;
341   conn.events = POLLRDNORM;
342
343   while (1)
344     {
345       int nr = poll (&conn, 1, timeout);
346
347       if (nr == 0)
348         {
349           /* The `poll' call timed out.  It's time to clean up the cache.  */
350           assert (my_number < lastdb);
351           now = time (NULL);
352           prune_cache (&dbs[my_number], now);
353           next_prune = now + CACHE_PRUNE_INTERVAL;
354           timeout = 1000 * (next_prune - now);
355           continue;
356         }
357
358       /* We have a new incoming connection.  */
359       if (conn.revents & (POLLRDNORM|POLLERR|POLLHUP|POLLNVAL))
360         {
361           /* Accept the connection.  */
362           int fd = accept (conn.fd, NULL, NULL);
363           request_header req;
364           char buf[256];
365
366           if (fd < 0)
367             {
368               dbg_log (_("while accepting connection: %s"),
369                        strerror_r (errno, buf, sizeof (buf)));
370               continue;
371             }
372
373           /* Now read the request.  */
374           if (TEMP_FAILURE_RETRY (read (fd, &req, sizeof (req)))
375               != sizeof (req))
376             {
377               dbg_log (_("short read while reading request: %s"),
378                        strerror_r (errno, buf, sizeof (buf)));
379               close (fd);
380               continue;
381             }
382
383           /* It should not be possible to crash the nscd with a silly
384              request (i.e., a terribly large key.  We limit the size
385              to 1kb.  */
386           if (req.key_len < 0 || req.key_len > 1024)
387             {
388               dbg_log (_("key length in request too long: %Zd"), req.key_len);
389               close (fd);
390               continue;
391             }
392           else
393             {
394               /* Get the key.  */
395               char keybuf[req.key_len];
396
397               if (TEMP_FAILURE_RETRY (read (fd, keybuf, req.key_len))
398                   != req.key_len)
399                 {
400                   dbg_log (_("short read while reading request key: %s"),
401                            strerror_r (errno, buf, sizeof (buf)));
402                   close (fd);
403                   continue;
404                 }
405
406               /* Phew, we got all the data, now process it.  */
407               handle_request (fd, &req, keybuf);
408
409               /* We are done.  */
410               close (fd);
411             }
412         }
413
414       if (run_prune)
415         {
416           now = time (NULL);
417           timeout = now < next_prune ? 1000 * (next_prune - now) : 0;
418         }
419     }
420 }
421
422
423 /* Start all the threads we want.  The initial process is thread no. 1.  */
424 void
425 start_threads (void)
426 {
427   int i;
428   pthread_attr_t attr;
429   pthread_t th;
430
431   pthread_attr_init (&attr);
432   pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
433
434   /* We allow less than LASTDB threads only for debugging.  */
435   if (debug_level == 0)
436     nthreads = MAX (nthreads, lastdb);
437
438   for (i = 1; i < nthreads; ++i)
439     pthread_create (&th, &attr, nscd_run, (void *) i);
440
441   pthread_attr_destroy (&attr);
442
443   nscd_run ((void *) 0);
444 }