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