(dbs): Define defualt values for timeouts and set enabled to 0 by default.
[kopensolaris-gnu/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_requests: 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 to 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 }