Add GNU LGPL headers to all .c .C and .h files
[oweals/cde.git] / cde / lib / tt / slib / mp_rpc_server.C
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 //%%  (c) Copyright 1993, 1994 Hewlett-Packard Company                  
24 //%%  (c) Copyright 1993, 1994 International Business Machines Corp.    
25 //%%  (c) Copyright 1993, 1994 Sun Microsystems, Inc.                   
26 //%%  (c) Copyright 1993, 1994 Novell, Inc.                             
27 //%%  $TOG: mp_rpc_server.C /main/11 1999/08/30 11:03:00 mgreess $                                                      
28 /*
29  *
30  * @(#)mp_rpc_server.C  1.46    94/11/17
31  *
32  * Copyright (c) 1990 by Sun Microsystems, Inc.
33  */
34 #include "tt_options.h"
35
36 #include <stdio.h>
37 #include <sys/time.h>
38 #include <sys/resource.h>
39 #include <errno.h>
40
41 #include "mp_rpc_server.h"
42 #include "util/tt_port.h"
43 #include "util/tt_gettext.h"
44 #include "util/tt_global_env.h"
45 #include "mp/mp_mp.h"
46 #include "mp/mp_rpc.h"
47
48 #if defined(OPT_TLI)
49 #include <netdir.h>
50 static int      gettransient(int, netconfig *, netbuf *);
51 #if defined(OPT_BUG_SUNOS_5) || defined(OPT_BUG_UXP)
52 extern "C" { char *     nc_sperror(); }
53 #endif
54 # if defined(OPT_BUG_USL) || defined(OPT_BUG_UXP)
55     extern int   t_errno;
56     extern char *t_strerror(int t_errno);
57 # endif
58 #else 
59 #include <rpc/pmap_clnt.h>
60 #include <netinet/tcp.h>
61 static int      gettransient(int,int,int *);
62 #endif /* OPT_TLI */
63
64 #if defined(OPT_BUG_USL)
65      typedef void (*SERVICE_FN_TYPE)(const struct svc_req *, const SVCXPRT*);
66 #elif defined(OPT_BUG_AIX)
67      typedef void (*SERVICE_FN_TYPE)();
68 #else
69      typedef void (*SERVICE_FN_TYPE)(struct svc_req *, SVCXPRT*);
70 #endif
71
72
73 /* 
74  * Constructs an rpc server for the given program, version and socket.
75  */
76 _Tt_rpc_server::
77 _Tt_rpc_server(int program, int version, int Rsocket, _Tt_auth &auth)
78 {
79         _version = version;
80         _socket = Rsocket;
81         _program = program;
82         _auth = auth;
83 }
84
85
86 /* 
87  * Destroys an rpc server. Unsets the program,version mapping in the
88  * portmapper. 
89  */
90 _Tt_rpc_server::
91 ~_Tt_rpc_server()
92 {
93 #ifndef OPT_TLI 
94 /* 
95  *      pmap_unset(_program, _version);
96  */
97 #else
98         for (int version = _version; version >= 1; version--) {
99                 rpcb_unset(_program, version, (netconfig *)0);
100         }
101 #endif                          // OPT_TLI
102 }
103
104
105 /* 
106  * Initializes an rpc server with a service function. If _program is set
107  * to -1 then an unused program number is obtained using the gettransient
108  * function. If _socket is anything other than RPC_ANYSOCK then it will
109  * be used to create the rpc transport using svfd_create and the rpc
110  * numbers will not be registered with the portmapper.
111  */
112 int _Tt_rpc_server::
113 init(void (*service_fn)(struct svc_req *, SVCXPRT *))
114 {
115         char            *bufopt = (char *)0;
116
117 #ifndef OPT_TLI
118
119         bufopt = getenv("TT_BUFSIZE");
120         unsigned int buffersize = (bufopt != (char *)0) ? atoi(bufopt) : 32000;
121
122         if (_socket != RPC_ANYSOCK) {
123                 _transp = svcfd_create(_socket, buffersize, buffersize);
124                 if (_transp == (SVCXPRT *)0) {
125                         return(0);
126                 }
127                 if (!svc_register(_transp, _program, _version,
128                                   (SERVICE_FN_TYPE)service_fn, 0))
129                 {
130                         _tt_syslog(0, LOG_ERR, "svc_register(): %m");
131                         return(0);
132                 }
133
134                 return(1);
135         }
136
137                 
138         if (_program == -1) {
139                 if (! (_program =
140                        gettransient(IPPROTO_TCP, _version, &_socket))) {
141                         return(0);
142                 }
143         } else {
144                 _socket = socket(AF_INET, SOCK_STREAM, 0);
145                 if (_socket < 0) {
146                         _tt_syslog(0, LOG_ERR,
147                                    "_Tt_rpc_server::init(): socket(): %m");
148                         return 0;
149                 }
150         }
151         int optval = 1;
152         if (setsockopt(_socket, IPPROTO_TCP, TCP_NODELAY,
153                        (char *)&optval, sizeof(int)) == -1) {
154                 _tt_syslog(0, LOG_ERR, "setsockopt(TCP_NODELAY): %m");
155         }
156         if (setsockopt(_socket, SOL_SOCKET, SO_RCVBUF, (char *)&buffersize,
157                        sizeof(int)) == -1) {
158                 _tt_syslog(0, LOG_ERR, "setsockopt(SO_RCVBUF): %m");
159         }
160         if (setsockopt(_socket, SOL_SOCKET, SO_SNDBUF, (char *)&buffersize,
161                        sizeof(int)) == -1) {
162                 _tt_syslog(0, LOG_ERR, "setsockopt(SO_SNDBUF): %m");
163         }
164         _transp = svctcp_create(_socket, buffersize, buffersize);
165         if (_transp == (SVCXPRT *)0) {
166                 return(0);
167         }
168         if (   !svc_register(_transp, _program, _version,
169                              (SERVICE_FN_TYPE)service_fn, IPPROTO_TCP)
170             || !svc_register(_transp, _program, 1,
171                              (SERVICE_FN_TYPE)service_fn, 0))
172         {
173                 _tt_syslog(0, LOG_ERR, "svc_register(): %m");
174                 return(0);
175         }
176 #else
177         netconfig               *nconf;
178         void                    *handlep;
179         t_info                  tinfo;
180         int                     fd;
181
182
183         if ((handlep = setnetconfig()) == (void *)0) {
184                 _tt_syslog(0, LOG_ERR, "setnetconfig(): %s", nc_sperror());
185                 return(0);
186         }
187
188         // Find a connection-oriented transport.
189         while (nconf = getnetconfig(handlep)) {
190                 if ((nconf->nc_semantics == NC_TPI_COTS) ||
191                     (nconf->nc_semantics == NC_TPI_COTS_ORD)) {
192
193                         // Make sure this netconfig maps to an address
194                         if (0 == strcmp(nconf->nc_protofmly, NC_INET))
195                             break;
196                 }
197         }
198
199         // If we failed to find a suitable transport, exit.
200         if (nconf == (netconfig *)0) {
201                 endnetconfig(handlep);
202                 _tt_syslog(0, LOG_ERR,
203                            catgets(_ttcatd, 2, 3,
204                                    "No connection-oriented transport"));
205                 return(0);
206         }
207         
208         fd = t_open(nconf->nc_device, O_RDWR, &tinfo);
209         if (fd == -1) {
210                 _tt_syslog(0, LOG_ERR,
211                            "_Tt_rpc_server::init(): t_open(): %s",
212                            t_strerror( t_errno ) );
213                 endnetconfig(handlep);
214                 return 0;
215         }
216         // No longer need to try to set NODELAY here as TIRPC does it for us
217         // tinfo.tsdu can be negative, but that's not a valid buf size.
218         u_int buf_size = 0;
219         if (tinfo.tsdu > 0) {
220                 buf_size = (u_int)tinfo.tsdu;
221         }
222         _transp = svc_tli_create(fd, nconf, (struct t_bind *)0,
223                                  buf_size, buf_size);
224         if (_transp == (SVCXPRT *)0) {
225                 _tt_syslog(0, LOG_ERR, "svc_tli_create(): 0");
226                 (void)t_close(fd);
227                 return(0);
228         }
229         if (_program == -1 &&
230             (! (_program = gettransient(_version, nconf,
231                                         &_transp->xp_ltaddr)))) {
232                 _tt_syslog(0, LOG_ERR, "gettransient(): 0");
233                 return(0);
234         }
235         for (int version = _version; version >= 1; version--) {
236                 if (!svc_reg(_transp, _program, version,
237                              (SERVICE_FN_TYPE)service_fn, nconf)) {
238                         _tt_syslog(0, LOG_ERR, "svc_reg(,,%d): 0", version);
239                         return(0);
240                 }
241         }
242         // it is important to not call endnetconfig until one is done
243         // using nconf as endnetconfig frees the nconf storage.
244         (void)endnetconfig(handlep);
245 #endif                          /* OPT_TLI */
246         // now figure out what fd the rpc package is using
247         int maxfds = _tt_global->maxfds();
248         for (int i=0; i < maxfds; i++) {
249                 if (FD_ISSET(i, &svc_fdset)) {
250                         _rpc_fd = i;
251                 }
252         }
253
254         return(1);
255 }
256
257
258 /* 
259  * Runs an rpc server. If a non-negative timeout is given then this
260  * function will return if the timeout expired before any rpc requests
261  * came in. The values returned are: -1 for error, 0 for timeout, 1
262  * for when timeout is 0 and an rpc request was serviced.
263  */
264 _Tt_rpcsrv_err _Tt_rpc_server::
265 run_until(int *stop, int timeout, _Tt_int_rec_list_ptr &efds)
266 {
267         fd_set                  readfds;
268         timeval                 tmout;
269         int                     fd;
270         int                     done = 0;
271         int                     select_stat;
272         _Tt_rpcsrv_err          status = _TT_RPCSRV_OK;
273
274         tmout.tv_sec = timeout;
275         tmout.tv_usec = 0;
276         _Tt_int_rec_list_cursor efds_c(efds);
277         do {
278                 // Add our fd's to a copy of the rpc fdset.
279                 readfds = svc_fdset;
280                 efds_c.reset();
281                 while (efds_c.next()) {
282                         fd = efds_c->val;
283                         // NOTE that it is crucially important that the bit
284                         // for fd 0 not be set.  fd 0 (stdin) is always set
285                         // to /dev/null, which is always active.
286                         // The reason fd 0 is in efds at all is that 
287                         // _Tt_self_procid uses it as a dummy entry
288                         // for ttsession itself, which doesn\'t need a
289                         // signalling channel.
290
291                         // I haven\'t verified this, but I bet it\'s possible
292                         // for negative entries to be in the efds list too,
293                         // representing signalling channels that were found
294                         // active on a previous pass but are not yet cleared
295                         // out.
296                         if (fd > 0) {
297                                 FD_SET(fd, &readfds);
298                         }
299                 }
300
301                 // Drop the global mutex around any polling or RPC calls.
302                 
303                 _tt_global->drop_mutex();
304                 
305                 select_stat = 
306                         select(FD_SETSIZE,&readfds, 0, 0,
307                                (timeout >= 0) ? &tmout : (timeval *)0);
308
309                 _tt_global->grab_mutex();
310
311                 switch (select_stat) {
312                       case -1:
313                         return(_TT_RPCSRV_ERR);
314                       case 0:
315                         return(_TT_RPCSRV_TMOUT);
316                       default:
317                         // check for exception fds
318                         efds_c.reset();
319                         while (efds_c.next()) {
320                                 fd = efds_c->val;
321                                 if (fd < 0) continue;   // -1 => not valid fd
322                                 if (FD_ISSET(fd, &readfds)) {
323                                         efds_c->val = (0 - fd);
324                                         status = _TT_RPCSRV_FDERR;
325                                         done = 1;
326                                 }
327
328                                 // Clear our fd from the fdset so
329                                 // svc_getreqset() won't get confused (bug
330                                 // 2000972).
331
332                                 FD_CLR(fd, &readfds);
333                         }
334                         svc_getreqset(&readfds);
335                 }
336         } while ((! done) && ((stop == 0) || (! *stop)));
337         return status;
338 }
339
340
341 /* 
342  * Returns an unused transient program number. Definition taken out of
343  * the RPC manual.
344  */
345 #ifdef OPT_TLI
346 static int
347 gettransient(int vers, netconfig *nconf, netbuf *address)
348 #else
349 static int
350 gettransient(int proto, int vers, int *sockp)
351 #endif                          /* OPT_TLI */
352 {
353         int                     prognum;
354
355 #ifndef OPT_TLI
356         int                     found;
357         int                     s;
358         int                     len;
359         int                     socktype;
360         sockaddr_in             addr;
361         sockaddr_in             tport;
362         sockaddr_in             uport;
363
364         switch (proto) {
365               case IPPROTO_UDP:
366                 socktype = SOCK_DGRAM;
367                 break;
368               case IPPROTO_TCP:
369                 socktype = SOCK_STREAM;
370                 break;
371               default:
372                 return(0);
373         }
374         if (*sockp == RPC_ANYSOCK) {
375                 s = socket(AF_INET, socktype, 0);
376                 if (s < 0) {
377                         _tt_syslog(0, LOG_ERR, "gettransient(): socket(): %m");
378                         return 0;
379                 }
380                 *sockp = s;
381         } else {
382                 s = *sockp;
383         }
384         memset(&addr, 0, sizeof(addr));
385         addr.sin_addr.s_addr = htonl(INADDR_ANY);
386         addr.sin_port = htons(0);
387         addr.sin_family = AF_INET;
388         len = sizeof(addr);
389         bind(s, (sockaddr *)&addr, len);
390 #if defined (_AIX) && (OSMAJORVERSION==4) && (OSMINORVERSION==2)
391         if (getsockname(s, (sockaddr *)&addr, (size_t *)&len) < 0) {
392 #else
393         if (getsockname(s, (sockaddr *)&addr, &len) < 0) {
394 #endif
395                 _tt_syslog(0, LOG_ERR, "getsockname(): %m");
396                 return(0);
397         }
398
399         int optval = 0;
400 #if !defined(linux)
401         if (setsockopt(s, SOL_SOCKET, SO_USELOOPBACK,
402                        (char *)&optval, sizeof(optval)) == -1) {
403         }
404 #endif
405 #endif                          /* !OPT_TLI */
406
407
408         // Search for a transient rpc number in the range 0x40000000 -
409         // 0x5fffffff by starting in the middle of the range searching
410         // up and then searching down if that fails. The reason for
411         // this is to make it less likely for other programs to grab
412         // this transient number (since pmap_getport doesn't complain
413         // if you try to grab a number for udp and we have it grabbed
414         // for tcp).
415
416         // search up in the range 0x4fffffff - 0x5fffffff
417         for (prognum = 0x4fffffff; prognum <= 0x5fffffff; prognum++) {
418                 /* XXX: pmap_set allows the same prognum for different  */
419                 /* protocols so we hack around that by attemptint to    */
420                 /* set both tcp and udp. */
421
422 #ifndef OPT_TLI
423                 found = (!pmap_getport(&uport, prognum, vers,
424                                        IPPROTO_UDP) &&
425                          !pmap_getport(&tport, prognum, vers, proto));
426                 if (found &&
427                     (found = pmap_set(prognum,
428                                       vers,
429                                       proto,
430                                       ntohs(addr.sin_port))) &&
431                     (vers==1 || (found = pmap_set(prognum,
432                                                   1,
433                                                   proto,
434                                                   ntohs(addr.sin_port))))) {
435                         return(prognum);
436                 }
437 #else
438                 if (rpcb_set(prognum, vers, nconf, address) &&
439                     (vers==1 || rpcb_set(prognum, 1, nconf, address))) {
440                         return(prognum);
441                 }
442 #endif                          /* !OPT_TLI */
443         }
444
445         // search down in the range 0x4ffffffe - 0x40000000
446         for (prognum = 0x4ffffffe; prognum >= 0x40000000; prognum--) {
447                 /* XXX: pmap_set allows the same prognum for different  */
448                 /* protocols so we hack around that by attemptint to    */
449                 /* set both tcp and udp. */
450 #ifndef OPT_TLI
451                 found = (!pmap_getport(&uport, prognum, vers,
452                                        IPPROTO_UDP) &&
453                          !pmap_getport(&tport, prognum, vers, proto));
454                 if (found &&
455                     (found = pmap_set(prognum,
456                                       vers,
457                                       proto,
458                                       ntohs(addr.sin_port)))) {
459                         return(prognum);
460                 }
461 #else
462                 if (rpcb_set(prognum, vers, nconf, address)) {
463                         return(prognum);
464                 }
465 #endif                          /* !OPT_TLI */
466         }
467
468         return(0);
469 }
470
471
472
473