Update.
[platform/upstream/glibc.git] / nis / nis_callback.c
1 /* Copyright (C) 1997 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1997.
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 <stdio.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <rpc/pmap_clnt.h>
25 #include <string.h>
26 #include <memory.h>
27 #include <syslog.h>
28 #include <sys/socket.h>
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
31 #include <rpcsvc/nis.h>
32 #include <bits/libc-lock.h>
33
34 #include "nis_intern.h"
35
36 extern void get_myaddress (struct sockaddr_in *addr);
37
38 /* Sorry, we are not able to make this threadsafe. Stupid. But some
39    functions doesn't send us a nis_result obj, so we don't have a
40    cookie. Maybe we could use keys for threads ? Have to learn more
41    about pthreads -- kukuk@vt.uni-paderborn.de */
42
43 #define CB_PROG ((u_long)100302)
44 #define CB_VERS ((u_long)1)
45 #define CBPROC_RECEIVE ((u_long)1)
46 #define CBPROC_FINISH ((u_long)2)
47 #define CBPROC_ERROR ((u_long)3)
48
49 typedef nis_object *obj_p;
50
51 struct cback_data
52   {
53     struct
54       {
55         u_int entries_len;
56         obj_p *entries_val;
57       }
58     entries;
59   };
60 typedef struct cback_data cback_data;
61
62 static nis_cb *data;
63
64 __libc_lock_define_initialized (static, callback)
65
66 static bool_t xdr_cback_data (XDR *, cback_data *);
67
68 static void
69 cb_prog_1 (struct svc_req *rqstp, SVCXPRT *transp)
70 {
71   union
72     {
73       cback_data cbproc_receive_1_arg;
74       nis_error cbproc_error_1_arg;
75     }
76   argument;
77   char *result;
78   xdrproc_t xdr_argument, xdr_result;
79   bool_t bool_result;
80
81   switch (rqstp->rq_proc)
82     {
83     case NULLPROC:
84       (void) svc_sendreply (transp, (xdrproc_t) xdr_void, (char *) NULL);
85       return;
86
87     case CBPROC_RECEIVE:
88       {
89         char name[NIS_MAXNAMELEN + 1];
90         u_long i;
91
92         xdr_argument = (xdrproc_t) xdr_cback_data;
93         xdr_result = (xdrproc_t) xdr_bool;
94         memset (&argument, 0, sizeof (argument));
95         if (!svc_getargs (transp, xdr_argument, (caddr_t) & argument))
96           {
97             svcerr_decode (transp);
98             return;
99           }
100         bool_result = FALSE;
101         for (i = 0; i < argument.cbproc_receive_1_arg.entries.entries_len; ++i)
102           {
103             snprintf (name, NIS_MAXNAMELEN, "%s.%s",
104               argument.cbproc_receive_1_arg.entries.entries_val[i]->zo_name,
105             argument.cbproc_receive_1_arg.entries.entries_val[i]->zo_domain);
106
107             if ((data->callback)
108                 (name, argument.cbproc_receive_1_arg.entries.entries_val[i],
109                  data->userdata))
110               {
111                 bool_result = TRUE;
112                 data->nomore = 1;
113                 data->result = NIS_SUCCESS;
114                 break;
115               }
116           }
117         result = (char *) &bool_result;
118       }
119       break;
120     case CBPROC_FINISH:
121       xdr_argument = (xdrproc_t) xdr_void;
122       xdr_result = (xdrproc_t) xdr_void;
123       memset (&argument, 0, sizeof (argument));
124       if (!svc_getargs (transp, xdr_argument, (caddr_t) & argument))
125         {
126           svcerr_decode (transp);
127           return;
128         }
129       data->nomore = 1;
130       data->result = NIS_SUCCESS;
131       bool_result = TRUE;       /* to make gcc happy, not necessary */
132       result = (char *) &bool_result;
133       break;
134     case CBPROC_ERROR:
135       xdr_argument = (xdrproc_t) xdr_nis_error;
136       xdr_result = (xdrproc_t) xdr_void;
137       memset (&argument, 0, sizeof (argument));
138       if (!svc_getargs (transp, xdr_argument, (caddr_t) & argument))
139         {
140           svcerr_decode (transp);
141           return;
142         }
143       data->nomore = 1;
144       data->result = argument.cbproc_error_1_arg;
145       bool_result = TRUE;       /* to make gcc happy, not necessary */
146       result = (char *) &bool_result;
147       break;
148     default:
149       svcerr_noproc (transp);
150       return;
151     }
152   if (result != NULL && !svc_sendreply (transp, xdr_result, result))
153     svcerr_systemerr (transp);
154   if (!svc_freeargs (transp, xdr_argument, (caddr_t) & argument))
155     {
156       fputs (_ ("unable to free arguments"), stderr);
157       exit (1);
158     }
159   return;
160 }
161
162 static bool_t
163 xdr_obj_p (XDR * xdrs, obj_p *objp)
164 {
165   if (!xdr_pointer (xdrs, (char **) objp, sizeof (nis_object),
166                     (xdrproc_t) xdr_nis_object))
167     return FALSE;
168   return TRUE;
169 }
170
171 static bool_t
172 xdr_cback_data (XDR *xdrs, cback_data *objp)
173 {
174   if (!xdr_array (xdrs, (char **) &objp->entries.entries_val,
175                   (u_int *) & objp->entries.entries_len, ~0, sizeof (obj_p),
176                   (xdrproc_t) xdr_obj_p))
177     return FALSE;
178   return TRUE;
179 }
180
181 static nis_error
182 internal_nis_do_callback (struct dir_binding *bptr, netobj *cookie,
183                           struct nis_cb *cb)
184 {
185   /* Default timeout can be changed using clnt_control() */
186   static struct timeval TIMEOUT = {25, 0};
187 #ifdef FD_SETSIZE
188   fd_set readfds;
189 #else
190   int readfds;
191 #endif /* def FD_SETSIZE */
192   struct timeval tv;
193   bool_t cb_is_running = FALSE;
194
195   data = cb;
196
197   for (;;)
198     {
199 #ifdef FD_SETSIZE
200       readfds = svc_fdset;
201 #else
202       readfds = svc_fds;
203 #endif /* def FD_SETSIZE */
204       tv.tv_sec = 25;
205       tv.tv_usec = 0;
206       switch (select (_rpc_dtablesize (), &readfds, NULL, NULL, &tv))
207         {
208         case -1:
209           if (errno == EINTR)
210             continue;
211           return NIS_CBERROR;
212         case 0:
213           /* See if callback 'thread' in the server is still alive. */
214           memset ((char *) &cb_is_running, 0, sizeof (cb_is_running));
215           if (clnt_call (bptr->clnt, NIS_CALLBACK, (xdrproc_t) xdr_netobj,
216                          (caddr_t) cookie, (xdrproc_t) xdr_bool,
217                          (caddr_t) & cb_is_running, TIMEOUT) != RPC_SUCCESS)
218             cb_is_running = FALSE;
219
220           if (cb_is_running == FALSE)
221             {
222               syslog (LOG_ERR, "NIS+: callback timed out");
223               return NIS_CBERROR;
224             }
225           break;
226         default:
227           svc_getreqset (&readfds);
228           if (data->nomore)
229             return data->result;
230         }
231     }
232 }
233
234 nis_error
235 __nis_do_callback (struct dir_binding *bptr, netobj *cookie,
236                    struct nis_cb *cb)
237 {
238   nis_error result;
239
240   __libc_lock_lock (callback);
241
242   result = internal_nis_do_callback (bptr, cookie, cb);
243
244   __libc_lock_unlock (callback);
245
246   return result;
247 }
248
249 struct nis_cb *
250 __nis_create_callback (int (*callback) (const_nis_name, const nis_object *,
251                                         const void *),
252                        const void *userdata, u_long flags)
253 {
254   struct nis_cb *cb;
255   int sock = RPC_ANYSOCK;
256   struct sockaddr_in sin;
257   int len = sizeof (struct sockaddr_in);
258   char addr[NIS_MAXNAMELEN + 1];
259   unsigned short port;
260
261   cb = (struct nis_cb *) calloc (1, sizeof (struct nis_cb));
262   if (cb == NULL)
263     {
264       syslog (LOG_ERR, "NIS+: out of memory allocating callback");
265       return NULL;
266     }
267
268   cb->serv = (nis_server *) calloc (1, sizeof (nis_server));
269   if (cb->serv == NULL)
270     {
271       free (cb);
272       syslog (LOG_ERR, "NIS+: out of memory allocating callback");
273       return (NULL);
274     }
275   cb->serv->name = strdup (nis_local_host ());
276   cb->serv->ep.ep_val = (endpoint *) calloc (2, sizeof (endpoint));
277   cb->serv->ep.ep_len = 1;
278   cb->serv->ep.ep_val[0].family = strdup ("inet");
279   cb->callback = callback;
280   cb->userdata = userdata;
281
282   /* XXX Sometimes, we should add the public key of the user here ! */
283   cb->serv->key_type = NIS_PK_NONE;
284   cb->serv->pkey.n_bytes = NULL;
285   cb->serv->pkey.n_len = 0;
286
287   if (flags & USE_DGRAM)
288     {
289       cb->serv->ep.ep_val[0].proto = strdup ("udp");
290       cb->xprt = svcudp_bufcreate (sock, 100, 8192);
291     }
292   else
293     {
294       cb->serv->ep.ep_val[0].proto = strdup ("tcp");
295       cb->xprt = svctcp_create (sock, 100, 8192);
296     }
297   cb->sock = cb->xprt->xp_sock;
298   if (!svc_register (cb->xprt, CB_PROG, CB_VERS, cb_prog_1, 0))
299     {
300       xprt_unregister (cb->xprt);
301       svc_destroy (cb->xprt);
302       xdr_free ((xdrproc_t) xdr_nis_server, (char *) cb->serv);
303       free (cb->serv);
304       free (cb);
305       syslog (LOG_ERR, "NIS+: failed to register callback dispatcher");
306       return NULL;
307     }
308
309   if (getsockname (cb->sock, (struct sockaddr *) &sin, &len) == -1)
310     {
311       xprt_unregister (cb->xprt);
312       svc_destroy (cb->xprt);
313       xdr_free ((xdrproc_t) xdr_nis_server, (char *) cb->serv);
314       free (cb->serv);
315       free (cb);
316       syslog (LOG_ERR, "NIS+: failed to read local socket info");
317       return (NULL);
318     }
319   port = sin.sin_port;
320   get_myaddress (&sin);
321   snprintf (addr, sizeof (addr), "%s.%d.%d", inet_ntoa (sin.sin_addr),
322             port & 0x00FF, (port & 0xFF00) >> 8);
323   cb->serv->ep.ep_val[0].uaddr = strdup (addr);
324
325   return cb;
326 }
327
328 nis_error
329 __nis_destroy_callback (struct nis_cb *cb)
330 {
331   xprt_unregister (cb->xprt);
332   svc_destroy (cb->xprt);
333   close (cb->sock);
334   xdr_free ((xdrproc_t) xdr_nis_server, (char *) cb->serv);
335   free (cb->serv);
336   free (cb);
337
338   return NIS_SUCCESS;
339 }