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/socket.h>
31 #include <sys/stat.h>
32 #include <sys/types.h>
33 #include <sys/uio.h>
34 #include <sys/un.h>
35
36 #include "nscd.h"
37 #include "dbg_log.h"
38
39 /* Socket 0 in the array is named and exported into the file namespace
40    as a connection point for clients.  */
41 static int sock[MAX_NUM_CONNECTIONS];
42 static int socks_active;
43 static fd_set read_set;
44 static pthread_mutex_t sock_lock = PTHREAD_MUTEX_INITIALIZER;
45
46
47 /* Cleanup.  */
48 void
49 close_sockets (void)
50 {
51   int i;
52
53   if (debug_flag)
54     dbg_log (_("close_sockets called"));
55
56   pthread_mutex_lock (&sock_lock);
57
58   /* Close sockets.  */
59   for (i = 0; i < MAX_NUM_CONNECTIONS; ++i)
60     if (sock[i] != 0)
61       {
62         if (close (sock[i]))
63           dbg_log (_("socket [%d|%d] close: %s"), strerror (errno));
64
65         sock[i] = 0;
66         --socks_active;
67       }
68
69   pthread_mutex_unlock (&sock_lock);
70 }
71
72 void
73 close_socket (int conn)
74 {
75   if (debug_flag > 2)
76     dbg_log (_("close socket (%d|%d)"), conn, sock[conn]);
77
78   pthread_mutex_lock (&sock_lock);
79
80   close (sock[conn]);
81   sock[conn] = 0;
82   --socks_active;
83
84   pthread_mutex_unlock (&sock_lock);
85 }
86
87 /* Local rountine, assigns a socket to a new connection request.  */
88 static void
89 handle_new_connection (void)
90 {
91   int i;
92
93   if (debug_flag > 2)
94     dbg_log (_("handle_new_connection"));
95
96   pthread_mutex_lock (&sock_lock);
97
98   if (socks_active < MAX_NUM_CONNECTIONS)
99     /* Find a free socket entry to use.  */
100     for (i = 1; i < MAX_NUM_CONNECTIONS; ++i)
101       {
102         if (sock[i] == 0)
103           {
104             if ((sock[i] = accept (sock[0], NULL, NULL)) < 0)
105               {
106                 dbg_log (_("socket accept: %s"), strerror (errno));
107                 return;
108               }
109             ++socks_active;
110             FD_SET (sock[i], &read_set);
111             if (debug_flag > 2)
112               dbg_log (_("handle_new_connection used socket %d|%d"), i,
113                        sock[i]);
114             break;
115           }
116       }
117   else
118     {
119       int black_widow_sock;
120       dbg_log (_("Supported number of simultainious connections exceeded"));
121       dbg_log (_("Ignoring client connect request"));
122       /* There has to be a better way to ignore a connection request,..
123          when I get my hands on a sockets wiz I'll modify this.  */
124       black_widow_sock  = accept (sock[0], NULL, NULL);
125       close (black_widow_sock);
126     }
127   pthread_mutex_unlock (&sock_lock);
128 }
129
130 /* Local routine, reads a request off a socket indicated by a selectset.  */
131 static int
132 handle_new_request (fd_set read_selects, int **connp, request_header **reqp,
133                     char **key)
134 {
135   ssize_t nbytes;
136   int i;
137
138   if (debug_flag)
139     dbg_log ("handle_new_request");
140
141   /* Find the descriptor.  */
142   for (i = 1; i < MAX_NUM_CONNECTIONS; ++i)
143     if (FD_ISSET(sock[i], &read_selects))
144       break;
145
146   if (debug_flag > 2)
147     dbg_log (_("handle_new_request uses socket %d"), i);
148
149   /* Read from it.  */
150   nbytes = read (sock[i], *reqp, sizeof (request_header));
151   if (nbytes != sizeof (request_header))
152     {
153       /* Handle non-data read cases.  */
154       if (nbytes == 0)
155         {
156           /* Close socket down.  */
157           if (debug_flag > 2)
158             dbg_log (_("Real close socket %d|%d"), i, sock[i]);
159
160           pthread_mutex_lock (&sock_lock);
161           FD_CLR (sock[i], &read_set);
162           close (sock[i]);
163           sock[i] = 0;
164           --socks_active;
165           pthread_mutex_unlock (&sock_lock);
166         }
167       else
168         if (nbytes < 0)
169           {
170             dbg_log (_("Read(%d|%d) error on get request: %s"),
171                      i, sock[i], strerror (errno));
172             exit (1);
173           }
174         else
175           dbg_log (_("Read, data < request buf size, ignoring data"));
176
177       return -1;
178     }
179   else
180     {
181       *key = malloc ((*reqp)->key_len + 1);
182       /* Read the key from it */
183       nbytes = read (sock[i], *key, (*reqp)->key_len);
184       if (nbytes != (*reqp)->key_len)
185         {
186           /* Handle non-data read cases.  */
187           if (nbytes == 0)
188             {
189               /* Close socket down.  */
190               if (debug_flag > 2)
191                 dbg_log (_("Real close socket %d|%d"), i, sock[i]);
192
193               pthread_mutex_lock (&sock_lock);
194               FD_CLR (sock[i], &read_set);
195               close (sock[i]);
196               sock[i] = 0;
197               --socks_active;
198               pthread_mutex_unlock (&sock_lock);
199             }
200           else
201             if (nbytes < 0)
202               {
203                 perror (_("Read() error on get request"));
204                 return 0;
205               }
206             else
207               fputs (_("Read, data < request buf size, ignoring data"),
208                      stderr);
209
210           free (*key);
211           return -1;
212         }
213       else
214         {
215           /* Ok, have a live one, A real data req buf has been obtained.  */
216           (*key)[(*reqp)->key_len] = '\0';
217           **connp = i;
218           return 0;
219         }
220     }
221 }
222
223 void
224 get_request (int *conn, request_header *req, char **key)
225 {
226   int i, nr, done = 0;
227   fd_set read_selects;
228
229   if (debug_flag)
230     dbg_log ("get_request");
231
232   /* loop, processing new connection requests until a client buffer
233      is read in on an existing connection.  */
234   while (!done)
235     {
236       /* Set up the socket descriptor mask for the select.
237          copy read_set into the local copy.  */
238
239       FD_ZERO (&read_selects);
240       pthread_mutex_lock (&sock_lock);
241       for (i = 0; i < MAX_NUM_CONNECTIONS; ++i)
242         {
243           if (FD_ISSET (sock[i], &read_set))
244             FD_SET (sock[i], &read_selects);
245         }
246       pthread_mutex_unlock (&sock_lock);
247       /* Poll active connections using select().  */
248       nr = select (FD_SETSIZE, &read_selects, NULL, NULL, NULL);
249       if (nr <= 0)
250         {
251           perror (_("Select new reads"));
252           exit (1);
253         }
254       if (FD_ISSET (sock[0], &read_selects))
255         /* Handle the case of a new connection request on the named socket.  */
256         handle_new_connection ();
257       else
258         {
259           /* Read data from client specific descriptor.  */
260           if (handle_new_request (read_selects, &conn, &req, key) == 0)
261             {
262               FD_CLR (sock[*conn], &read_set);
263               done = 1;
264             }
265         }
266     } /* While not_done.  */
267 }
268
269 void
270 init_sockets (void)
271 {
272   struct sockaddr_un sock_addr;
273
274   /* Initialize the connections db.  */
275   socks_active = 0;
276   FD_ZERO (&read_set);
277
278   /* Create the socket.  */
279   sock[0] = socket (AF_UNIX, SOCK_STREAM, 0);
280   if (sock[0] < 0)
281     {
282       perror (_("cannot create socket"));
283       exit (1);
284     }
285   /* Bind a name to the socket.  */
286   sock_addr.sun_family = AF_UNIX;
287   strcpy (sock_addr.sun_path, _PATH_NSCDSOCKET);
288   if (bind (sock[0], (struct sockaddr *) &sock_addr, sizeof (sock_addr)) < 0)
289     {
290       dbg_log ("%s: %s", _PATH_NSCDSOCKET, strerror (errno));
291       exit (1);
292     }
293   /* Set permissions for the socket.  */
294   chmod (_PATH_NSCDSOCKET, 0666);
295
296   /* Set the socket up to accept connections.  */
297   if (listen (sock[0], MAX_NUM_CONNECTIONS) < 0)
298     {
299       perror (_("cannot enable socket to accept connections"));
300       exit (1);
301     }
302
303   /* Add the socket to the server's set of active sockets.  */
304   FD_SET (sock[0], &read_set);
305   ++socks_active;
306 }
307
308 void
309 pw_send_answer (int conn, struct passwd *pwd)
310 {
311   pw_response_header resp;
312
313   resp.version = NSCD_VERSION;
314   if (pwd != NULL)
315     {
316       resp.found = 1;
317       resp.pw_name_len = strlen (pwd->pw_name);
318       resp.pw_passwd_len = strlen (pwd->pw_passwd);
319       resp.pw_uid = pwd->pw_uid;
320       resp.pw_gid = pwd->pw_gid;
321       resp.pw_gecos_len = strlen (pwd->pw_gecos);
322       resp.pw_dir_len = strlen (pwd->pw_dir);
323       resp.pw_shell_len = strlen (pwd->pw_shell);
324     }
325   else
326     {
327       resp.found = 0;
328       resp.pw_name_len = 0;
329       resp.pw_passwd_len = 0;
330       resp.pw_uid = -1;
331       resp.pw_gid = -1;
332       resp.pw_gecos_len = 0;
333       resp.pw_dir_len = 0;
334       resp.pw_shell_len = 0;
335     }
336   if (sock[conn] == 0)
337     {
338       dbg_log (_("bad connection id on send response [%d|%d]"),
339                conn, sock[conn]);
340       return;
341     }
342
343   /* Send response header.  */
344   if (write (sock[conn], &resp, sizeof (pw_response_header)) !=
345       sizeof (pw_response_header))
346     {
347       dbg_log (_("write incomplete on send response: %s"), strerror (errno));
348       return;
349     }
350
351   if (resp.found)
352     {
353       struct iovec vec[5];
354
355       /* Send pw_name.  */
356       vec[0].iov_base = pwd->pw_name;
357       vec[0].iov_len = resp.pw_name_len;
358       /* Send pw_passwd.  */
359       vec[1].iov_base = pwd->pw_passwd;
360       vec[1].iov_len = resp.pw_passwd_len;
361       /* Send pw_gecos.  */
362       vec[2].iov_base = pwd->pw_gecos;
363       vec[2].iov_len = resp.pw_gecos_len;
364       /* Send pw_dir.  */
365       vec[3].iov_base = pwd->pw_dir;
366       vec[3].iov_len = resp.pw_dir_len;
367       /* Send pw_shell.  */
368       vec[4].iov_base = pwd->pw_shell;
369       vec[4].iov_len = resp.pw_shell_len;
370
371       if (writev (sock[conn], vec, 5) != (resp.pw_name_len + resp.pw_passwd_len
372                                           + resp.pw_gecos_len + resp.pw_dir_len
373                                           + resp.pw_shell_len))
374         dbg_log (_("write incomplete on send passwd answer: %s"),
375                  strerror (errno));
376     }
377 }
378
379 void
380 pw_send_disabled (int conn)
381 {
382   pw_response_header resp;
383
384   resp.version = NSCD_VERSION;
385   resp.found = -1;
386   resp.pw_name_len = 0;
387   resp.pw_passwd_len = 0;
388   resp.pw_uid = -1;
389   resp.pw_gid = -1;
390   resp.pw_gecos_len = 0;
391   resp.pw_dir_len = 0;
392   resp.pw_shell_len = 0;
393
394   if (sock[conn] == 0)
395     {
396       dbg_log (_("bad connection id on send response [%d|%d]"),
397                conn, sock[conn]);
398       return;
399     }
400
401   /* Send response header.  */
402   if (write (sock[conn], &resp, sizeof (pw_response_header))
403       != sizeof (pw_response_header))
404     dbg_log (_("write incomplete on send response: %s"), strerror (errno));
405 }
406
407 void
408 gr_send_answer (int conn, struct group *grp)
409 {
410   gr_response_header resp;
411
412   resp.version = NSCD_VERSION;
413   if (grp != NULL)
414     {
415       resp.found = 1;
416       resp.gr_name_len = strlen (grp->gr_name);
417       resp.gr_passwd_len = strlen (grp->gr_passwd);
418       resp.gr_gid = grp->gr_gid;
419       resp.gr_mem_len = 0;
420       while (grp->gr_mem[resp.gr_mem_len])
421         ++resp.gr_mem_len;
422     }
423   else
424     {
425       resp.found = 0;
426       resp.gr_name_len = 0;
427       resp.gr_passwd_len = 0;
428       resp.gr_gid = -1;
429       resp.gr_mem_len = 0;
430     }
431   if (sock[conn] == 0)
432     {
433       dbg_log (_("bad connection id on send response [%d|%d]"),
434                conn, sock[conn]);
435       return;
436     }
437
438   /* Send response header.  */
439   if (write (sock[conn], &resp, sizeof (gr_response_header))
440       != sizeof (gr_response_header))
441     {
442       dbg_log (_("write incomplete on send response: %s"), strerror (errno));
443       return;
444     }
445
446   if (resp.found)
447     {
448       unsigned int l = 0;
449
450       /* Send gr_name.  */
451       if (write (sock[conn], grp->gr_name, resp.gr_name_len)
452           != resp.gr_name_len)
453         {
454           dbg_log (_("write incomplete on send response: %s"),
455                    strerror (errno));
456           return;
457         }
458       /* Send gr_passwd.  */
459       if (write (sock[conn], grp->gr_passwd, resp.gr_passwd_len)
460           != resp.gr_passwd_len)
461         {
462           dbg_log (_("write incomplete on send response: %s"),
463                    strerror (errno));
464           return;
465         }
466
467       while (grp->gr_mem[l])
468         {
469           size_t len = strlen (grp->gr_mem[l]);
470
471           if (write (sock[conn], &len, sizeof (len)) != sizeof (len))
472             {
473               dbg_log (_("write incomplete on send response: %s"),
474                        strerror (errno));
475               return;
476             }
477           if (write (sock[conn], grp->gr_mem[l], len) != len)
478             {
479               dbg_log (_("write incomplete on send response: %s"),
480                        strerror (errno));
481               return;
482             }
483           ++l;
484         }
485     }
486 }
487
488 void
489 gr_send_disabled (int conn)
490 {
491   gr_response_header resp;
492
493   resp.version = NSCD_VERSION;
494   resp.found = -1;
495   resp.gr_name_len = 0;
496   resp.gr_passwd_len = 0;
497   resp.gr_gid = -1;
498   resp.gr_mem_len = 0;
499
500   if (sock[conn] == 0)
501     {
502       dbg_log (_("bad connection id on send gr_disabled response [%d|%d]"),
503                conn, sock[conn]);
504       return;
505     }
506
507   /* Send response header.  */
508   if (write (sock[conn], &resp, sizeof (gr_response_header))
509       != sizeof (gr_response_header))
510     dbg_log (_("write incomplete on send gr_disabled response: %s"),
511              strerror (errno));
512 }
513
514 void
515 stat_send (int conn, stat_response_header *resp)
516 {
517   if (sock[conn] == 0)
518     {
519       dbg_log (_("bad connection id on send stat response [%d|%d]"),
520                conn, sock[conn]);
521       return;
522     }
523
524   /* send response header.  */
525   if (write (sock[conn], resp, sizeof (stat_response_header))
526       != sizeof (stat_response_header))
527     dbg_log (_("write incomplete on send stat response: %s"),
528              strerror (errno));
529 }