Update.
[kopensolaris-gnu/glibc.git] / nscd / connections.c
1 /* Copyright (c) 1998 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1998.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Library General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Library General Public License for more details.
14
15    You should have received a copy of the GNU Library General Public
16    License along with the GNU C Library; see the file COPYING.LIB.  If not,
17    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18    Boston, MA 02111-1307, USA. */
19
20 #include <errno.h>
21 #include <error.h>
22 #include <fcntl.h>
23 #include <libintl.h>
24 #include <locale.h>
25 #include <pthread.h>
26 #include <pwd.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <sys/poll.h>
31 #include <sys/socket.h>
32 #include <sys/stat.h>
33 #include <sys/types.h>
34 #include <sys/uio.h>
35 #include <sys/un.h>
36
37 #include "nscd.h"
38 #include "dbg_log.h"
39
40 /* Socket 0 in the array is named and exported into the file namespace
41    as a connection point for clients.  There's a one to one
42    correspondence between sock[i] and read_polls[i].  */
43 static int sock[MAX_NUM_CONNECTIONS];
44 static int socks_active;
45 static struct pollfd read_polls[MAX_NUM_CONNECTIONS];
46 static pthread_mutex_t sock_lock = PTHREAD_MUTEX_INITIALIZER;
47
48
49 /* Cleanup.  */
50 void
51 close_sockets (void)
52 {
53   int i;
54
55   if (debug_flag)
56     dbg_log (_("close_sockets called"));
57
58   pthread_mutex_lock (&sock_lock);
59
60   /* Close sockets.  */
61   for (i = 0; i < MAX_NUM_CONNECTIONS; ++i)
62     if (sock[i] != 0)
63       {
64         if (close (sock[i]))
65           dbg_log (_("socket [%d|%d] close: %s"), i, sock[i], strerror (errno));
66
67         sock[i] = 0;
68         read_polls[i].fd = -1;
69         --socks_active;
70       }
71
72   pthread_mutex_unlock (&sock_lock);
73 }
74
75 void
76 close_socket (int conn)
77 {
78   if (debug_flag > 2)
79     dbg_log (_("close socket (%d|%d)"), conn, sock[conn]);
80
81   pthread_mutex_lock (&sock_lock);
82
83   close (sock[conn]);
84   sock[conn] = 0;
85   read_polls[conn].fd = -1;
86   --socks_active;
87
88   pthread_mutex_unlock (&sock_lock);
89 }
90
91 /* Local routine, assigns a socket to a new connection request.  */
92 static void
93 handle_new_connection (void)
94 {
95   int i;
96
97   if (debug_flag > 2)
98     dbg_log (_("handle_new_connection"));
99
100   pthread_mutex_lock (&sock_lock);
101
102   if (socks_active < MAX_NUM_CONNECTIONS)
103     /* Find a free socket entry to use.  */
104     for (i = 1; i < MAX_NUM_CONNECTIONS; ++i)
105       {
106         if (sock[i] == 0)
107           {
108             if ((sock[i] = accept (sock[0], NULL, NULL)) < 0)
109               {
110                 dbg_log (_("socket accept: %s"), strerror (errno));
111                 return;
112               }
113             ++socks_active;
114             read_polls[i].fd = sock[i];
115             read_polls[i].events = POLLRDNORM;
116             if (debug_flag > 2)
117               dbg_log (_("handle_new_connection used socket %d|%d"), i,
118                        sock[i]);
119             break;
120           }
121       }
122   else
123     {
124       int black_widow_sock;
125       dbg_log (_("Supported number of simultaneous connections exceeded"));
126       dbg_log (_("Ignoring client connect request"));
127       /* There has to be a better way to ignore a connection request,..
128          when I get my hands on a sockets wiz I'll modify this.  */
129       black_widow_sock  = accept (sock[0], NULL, NULL);
130       close (black_widow_sock);
131     }
132   pthread_mutex_unlock (&sock_lock);
133 }
134
135 /* Local routine, reads a request off a socket indicated by read_polls.  */
136 static int
137 handle_new_request (int **connp, request_header **reqp, char **key)
138 {
139   ssize_t nbytes;
140   int i, found = 0;
141
142   if (debug_flag)
143     dbg_log ("handle_new_request");
144
145   /* Find the descriptor.  */
146   for (i = 1; i < MAX_NUM_CONNECTIONS; ++i) {
147     if (read_polls[i].fd >= 0
148         && read_polls[i].revents & (POLLRDNORM|POLLERR|POLLNVAL))
149       {
150         found = i;
151         break;
152       }
153     if (read_polls[i].fd >= 0 && (read_polls[i].revents & POLLHUP))
154       {
155         /* Don't close the socket, we still need to send data.  Just
156            stop polling for more data now.  */
157         read_polls[i].fd = -1;
158       }
159   }
160
161   if (found == 0)
162     {
163       dbg_log (_("No sockets with data found !"));
164       return -1;
165     }
166
167   if (debug_flag > 2)
168     dbg_log (_("handle_new_request uses socket %d"), i);
169
170   /* Read from it.  */
171   nbytes = read (sock[i], *reqp, sizeof (request_header));
172   if (nbytes != sizeof (request_header))
173     {
174       /* Handle non-data read cases.  */
175       if (nbytes == 0)
176         {
177           /* Close socket down.  */
178           if (debug_flag > 2)
179             dbg_log (_("Real close socket %d|%d"), i, sock[i]);
180
181           pthread_mutex_lock (&sock_lock);
182           read_polls[i].fd = -1;
183           close (sock[i]);
184           sock[i] = 0;
185           --socks_active;
186           pthread_mutex_unlock (&sock_lock);
187         }
188       else
189         if (nbytes < 0)
190           {
191             dbg_log (_("Read(%d|%d) error on get request: %s"),
192                      i, sock[i], strerror (errno));
193             exit (1);
194           }
195         else
196           dbg_log (_("Read, data < request buf size, ignoring data"));
197
198       return -1;
199     }
200   else
201     {
202       *key = malloc ((*reqp)->key_len + 1);
203       /* Read the key from it */
204       nbytes = read (sock[i], *key, (*reqp)->key_len);
205       if (nbytes != (*reqp)->key_len)
206         {
207           /* Handle non-data read cases.  */
208           if (nbytes == 0)
209             {
210               /* Close socket down.  */
211               if (debug_flag > 2)
212                 dbg_log (_("Real close socket %d|%d"), i, sock[i]);
213
214               pthread_mutex_lock (&sock_lock);
215               read_polls[i].fd = -1;
216               close (sock[i]);
217               sock[i] = 0;
218               --socks_active;
219               pthread_mutex_unlock (&sock_lock);
220             }
221           else
222             if (nbytes < 0)
223               {
224                 perror (_("Read() error on get request"));
225                 return 0;
226               }
227             else
228               fputs (_("Read, data < request buf size, ignoring data"),
229                      stderr);
230
231           free (*key);
232           return -1;
233         }
234       else
235         {
236           /* Ok, have a live one, A real data req buf has been obtained.  */
237           (*key)[(*reqp)->key_len] = '\0';
238           **connp = i;
239           return 0;
240         }
241     }
242 }
243
244 void
245 get_request (int *conn, request_header *req, char **key)
246 {
247   int done = 0;
248   int nr;
249
250   if (debug_flag)
251     dbg_log ("get_request");
252
253   /* loop, processing new connection requests until a client buffer
254      is read in on an existing connection.  */
255   while (!done)
256     {
257       /* Poll active connections.  */
258       nr = poll (read_polls, MAX_NUM_CONNECTIONS, -1);
259       if (nr <= 0)
260         {
261           perror (_("Poll new reads"));
262           exit (1);
263         }
264       if (read_polls[0].revents & (POLLRDNORM|POLLERR|POLLHUP|POLLNVAL))
265         /* Handle the case of a new connection request on the named socket.  */
266         handle_new_connection ();
267       else
268         {
269           /* Read data from client specific descriptor.  */
270           if (handle_new_request (&conn, &req, key) == 0)
271             done = 1;
272         }
273     } /* While not_done.  */
274 }
275
276 void
277 init_sockets (void)
278 {
279   struct sockaddr_un sock_addr;
280   int i;
281
282   /* Initialize the connections db.  */
283   socks_active = 0;
284
285   /* Initialize the poll array. */
286   for (i = 0; i < MAX_NUM_CONNECTIONS; i++)
287     read_polls[i].fd = -1;
288
289   /* Create the socket.  */
290   sock[0] = socket (AF_UNIX, SOCK_STREAM, 0);
291   if (sock[0] < 0)
292     {
293       perror (_("cannot create socket"));
294       exit (1);
295     }
296   /* Bind a name to the socket.  */
297   sock_addr.sun_family = AF_UNIX;
298   strcpy (sock_addr.sun_path, _PATH_NSCDSOCKET);
299   if (bind (sock[0], (struct sockaddr *) &sock_addr, sizeof (sock_addr)) < 0)
300     {
301       dbg_log ("%s: %s", _PATH_NSCDSOCKET, strerror (errno));
302       exit (1);
303     }
304   /* Set permissions for the socket.  */
305   chmod (_PATH_NSCDSOCKET, 0666);
306
307   /* Set the socket up to accept connections.  */
308   if (listen (sock[0], MAX_NUM_CONNECTIONS) < 0)
309     {
310       perror (_("cannot enable socket to accept connections"));
311       exit (1);
312     }
313
314   /* Add the socket to the server's set of active sockets.  */
315   read_polls[0].fd = sock[0];
316   read_polls[0].events = POLLRDNORM;
317   ++socks_active;
318 }
319
320 void
321 pw_send_answer (int conn, struct passwd *pwd)
322 {
323   struct iovec vec[6];
324   pw_response_header resp;
325   size_t total_len;
326   int nblocks;
327
328   resp.version = NSCD_VERSION;
329   if (pwd != NULL)
330     {
331       resp.found = 1;
332       resp.pw_name_len = strlen (pwd->pw_name);
333       resp.pw_passwd_len = strlen (pwd->pw_passwd);
334       resp.pw_uid = pwd->pw_uid;
335       resp.pw_gid = pwd->pw_gid;
336       resp.pw_gecos_len = strlen (pwd->pw_gecos);
337       resp.pw_dir_len = strlen (pwd->pw_dir);
338       resp.pw_shell_len = strlen (pwd->pw_shell);
339     }
340   else
341     {
342       resp.found = 0;
343       resp.pw_name_len = 0;
344       resp.pw_passwd_len = 0;
345       resp.pw_uid = -1;
346       resp.pw_gid = -1;
347       resp.pw_gecos_len = 0;
348       resp.pw_dir_len = 0;
349       resp.pw_shell_len = 0;
350     }
351   if (sock[conn] == 0)
352     {
353       dbg_log (_("bad connection id on send response [%d|%d]"),
354                conn, sock[conn]);
355       return;
356     }
357
358   /* Add response header.  */
359   vec[0].iov_base = &resp;
360   vec[0].iov_len = sizeof (pw_response_header);
361   total_len = sizeof (pw_response_header);
362   nblocks = 1;
363
364   if (resp.found)
365     {
366       /* Add pw_name.  */
367       vec[1].iov_base = pwd->pw_name;
368       vec[1].iov_len = resp.pw_name_len;
369       total_len += resp.pw_name_len;
370       /* Add pw_passwd.  */
371       vec[2].iov_base = pwd->pw_passwd;
372       vec[2].iov_len = resp.pw_passwd_len;
373       total_len += resp.pw_passwd_len;
374       /* Add pw_gecos.  */
375       vec[3].iov_base = pwd->pw_gecos;
376       vec[3].iov_len = resp.pw_gecos_len;
377       total_len += resp.pw_gecos_len;
378       /* Add pw_dir.  */
379       vec[4].iov_base = pwd->pw_dir;
380       vec[4].iov_len = resp.pw_dir_len;
381       total_len += resp.pw_dir_len;
382       /* Add pw_shell.  */
383       vec[5].iov_base = pwd->pw_shell;
384       vec[5].iov_len = resp.pw_shell_len;
385       total_len += resp.pw_shell_len;
386
387       nblocks = 6;
388     }
389
390   /* Send all the data.  */
391   if (writev (sock[conn], vec, nblocks) != total_len)
392     dbg_log (_("write incomplete on send passwd answer: %s"),
393              strerror (errno));
394 }
395
396 void
397 pw_send_disabled (int conn)
398 {
399   pw_response_header resp;
400
401   resp.version = NSCD_VERSION;
402   resp.found = -1;
403   resp.pw_name_len = 0;
404   resp.pw_passwd_len = 0;
405   resp.pw_uid = -1;
406   resp.pw_gid = -1;
407   resp.pw_gecos_len = 0;
408   resp.pw_dir_len = 0;
409   resp.pw_shell_len = 0;
410
411   if (sock[conn] == 0)
412     {
413       dbg_log (_("bad connection id on send response [%d|%d]"),
414                conn, sock[conn]);
415       return;
416     }
417
418   /* Send response header.  */
419   if (write (sock[conn], &resp, sizeof (pw_response_header))
420       != sizeof (pw_response_header))
421     dbg_log (_("write incomplete on send response: %s"), strerror (errno));
422 }
423
424 void
425 gr_send_answer (int conn, struct group *grp)
426 {
427   struct iovec *vec;
428   size_t *len;
429   gr_response_header resp;
430   size_t total_len, sum;
431   int nblocks;
432   size_t maxiov;
433
434   resp.version = NSCD_VERSION;
435   if (grp != NULL)
436     {
437       resp.found = 1;
438       resp.gr_name_len = strlen (grp->gr_name);
439       resp.gr_passwd_len = strlen (grp->gr_passwd);
440       resp.gr_gid = grp->gr_gid;
441       resp.gr_mem_len = 0;
442       while (grp->gr_mem[resp.gr_mem_len])
443         ++resp.gr_mem_len;
444     }
445   else
446     {
447       resp.found = 0;
448       resp.gr_name_len = 0;
449       resp.gr_passwd_len = 0;
450       resp.gr_gid = -1;
451       resp.gr_mem_len = 0;
452     }
453   if (sock[conn] == 0)
454     {
455       dbg_log (_("bad connection id on send response [%d|%d]"),
456                conn, sock[conn]);
457       return;
458     }
459
460   /* We have no fixed number of records so allocate the IOV here.  */
461   vec = alloca ((3 + 1 + resp.gr_mem_len) * sizeof (struct iovec));
462   len = alloca (resp.gr_mem_len * sizeof (size_t));
463
464   /* Add response header.  */
465   vec[0].iov_base = &resp;
466   vec[0].iov_len = sizeof (gr_response_header);
467   total_len = sizeof (gr_response_header);
468   nblocks = 1;
469
470   if (resp.found)
471     {
472       unsigned int l = 0;
473
474       /* Add gr_name.  */
475       vec[1].iov_base = grp->gr_name;
476       vec[1].iov_len = resp.gr_name_len;
477       total_len += resp.gr_name_len;
478       /* Add gr_passwd.  */
479       vec[2].iov_base = grp->gr_passwd;
480       vec[2].iov_len = resp.gr_passwd_len;
481       total_len += resp.gr_passwd_len;
482       nblocks = 3;
483
484       if (grp->gr_mem[l])
485         {
486           vec[3].iov_base = len;
487           vec[3].iov_len = resp.gr_mem_len * sizeof (size_t);
488           total_len += resp.gr_mem_len * sizeof (size_t);
489           nblocks = 4;
490
491           do
492             {
493               len[l] = strlen (grp->gr_mem[l]);
494
495               vec[nblocks].iov_base = grp->gr_mem[l];
496               vec[nblocks].iov_len = len[l];
497               total_len += len[l];
498
499               ++nblocks;
500             }
501           while (grp->gr_mem[++l]);
502         }
503     }
504
505 #ifdef UIO_MAXIOV
506   maxiov = UIO_MAXIOV;
507 #else
508   maxiov = sysconf (_SC_UIO_MAXIOV);
509 #endif
510
511   /* Send all the data.  */
512   sum = 0;
513   while (nblocks > maxiov)
514     {
515       sum += writev (sock[conn], vec, maxiov);
516       vec += maxiov;
517       nblocks -= maxiov;
518     }
519   if (sum + writev (sock[conn], vec, nblocks) != total_len)
520     dbg_log (_("write incomplete on send group answer: %s"),
521              strerror (errno));
522 }
523
524 void
525 gr_send_disabled (int conn)
526 {
527   gr_response_header resp;
528
529   resp.version = NSCD_VERSION;
530   resp.found = -1;
531   resp.gr_name_len = 0;
532   resp.gr_passwd_len = 0;
533   resp.gr_gid = -1;
534   resp.gr_mem_len = 0;
535
536   if (sock[conn] == 0)
537     {
538       dbg_log (_("bad connection id on send gr_disabled response [%d|%d]"),
539                conn, sock[conn]);
540       return;
541     }
542
543   /* Send response header.  */
544   if (write (sock[conn], &resp, sizeof (gr_response_header))
545       != sizeof (gr_response_header))
546     dbg_log (_("write incomplete on send gr_disabled response: %s"),
547              strerror (errno));
548 }
549
550 void
551 stat_send (int conn, stat_response_header *resp)
552 {
553   if (sock[conn] == 0)
554     {
555       dbg_log (_("bad connection id on send stat response [%d|%d]"),
556                conn, sock[conn]);
557       return;
558     }
559
560   /* send response header.  */
561   if (write (sock[conn], resp, sizeof (stat_response_header))
562       != sizeof (stat_response_header))
563     dbg_log (_("write incomplete on send stat response: %s"),
564              strerror (errno));
565 }