2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
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)
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
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
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 $
30 * @(#)mp_rpc_server.C 1.46 94/11/17
32 * Copyright (c) 1990 by Sun Microsystems, Inc.
34 #include "tt_options.h"
38 #include <sys/resource.h>
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"
46 #include "mp/mp_rpc.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(); }
54 # if defined(OPT_BUG_USL) || defined(OPT_BUG_UXP)
56 extern char *t_strerror(int t_errno);
59 #include <rpc/pmap_clnt.h>
60 #include <netinet/tcp.h>
61 static int gettransient(int,int,int *);
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)();
69 typedef void (*SERVICE_FN_TYPE)(struct svc_req *, SVCXPRT*);
74 * Constructs an rpc server for the given program, version and socket.
77 _Tt_rpc_server(int program, int version, int Rsocket, _Tt_auth &auth)
87 * Destroys an rpc server. Unsets the program,version mapping in the
95 * pmap_unset(_program, _version);
98 for (int version = _version; version >= 1; version--) {
99 rpcb_unset(_program, version, (netconfig *)0);
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.
113 init(void (*service_fn)(struct svc_req *, SVCXPRT *))
115 char *bufopt = (char *)0;
119 bufopt = getenv("TT_BUFSIZE");
120 unsigned int buffersize = (bufopt != (char *)0) ? atoi(bufopt) : 32000;
122 if (_socket != RPC_ANYSOCK) {
123 _transp = svcfd_create(_socket, buffersize, buffersize);
124 if (_transp == (SVCXPRT *)0) {
127 if (!svc_register(_transp, _program, _version,
128 (SERVICE_FN_TYPE)service_fn, 0))
130 _tt_syslog(0, LOG_ERR, "svc_register(): %m");
138 if (_program == -1) {
140 gettransient(IPPROTO_TCP, _version, &_socket))) {
144 _socket = socket(AF_INET, SOCK_STREAM, 0);
146 _tt_syslog(0, LOG_ERR,
147 "_Tt_rpc_server::init(): socket(): %m");
152 if (setsockopt(_socket, IPPROTO_TCP, TCP_NODELAY,
153 (char *)&optval, sizeof(int)) == -1) {
154 _tt_syslog(0, LOG_ERR, "setsockopt(TCP_NODELAY): %m");
156 if (setsockopt(_socket, SOL_SOCKET, SO_RCVBUF, (char *)&buffersize,
157 sizeof(int)) == -1) {
158 _tt_syslog(0, LOG_ERR, "setsockopt(SO_RCVBUF): %m");
160 if (setsockopt(_socket, SOL_SOCKET, SO_SNDBUF, (char *)&buffersize,
161 sizeof(int)) == -1) {
162 _tt_syslog(0, LOG_ERR, "setsockopt(SO_SNDBUF): %m");
164 _transp = svctcp_create(_socket, buffersize, buffersize);
165 if (_transp == (SVCXPRT *)0) {
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))
173 _tt_syslog(0, LOG_ERR, "svc_register(): %m");
183 if ((handlep = setnetconfig()) == (void *)0) {
184 _tt_syslog(0, LOG_ERR, "setnetconfig(): %s", nc_sperror());
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)) {
193 // Make sure this netconfig maps to an address
194 if (0 == strcmp(nconf->nc_protofmly, NC_INET))
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"));
208 fd = t_open(nconf->nc_device, O_RDWR, &tinfo);
210 _tt_syslog(0, LOG_ERR,
211 "_Tt_rpc_server::init(): t_open(): %s",
212 t_strerror( t_errno ) );
213 endnetconfig(handlep);
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.
219 if (tinfo.tsdu > 0) {
220 buf_size = (u_int)tinfo.tsdu;
222 _transp = svc_tli_create(fd, nconf, (struct t_bind *)0,
224 if (_transp == (SVCXPRT *)0) {
225 _tt_syslog(0, LOG_ERR, "svc_tli_create(): 0");
229 if (_program == -1 &&
230 (! (_program = gettransient(_version, nconf,
231 &_transp->xp_ltaddr)))) {
232 _tt_syslog(0, LOG_ERR, "gettransient(): 0");
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);
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);
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)) {
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.
264 _Tt_rpcsrv_err _Tt_rpc_server::
265 run_until(int *stop, int timeout, _Tt_int_rec_list_ptr &efds)
272 _Tt_rpcsrv_err status = _TT_RPCSRV_OK;
274 tmout.tv_sec = timeout;
276 _Tt_int_rec_list_cursor efds_c(efds);
278 // Add our fd's to a copy of the rpc fdset.
281 while (efds_c.next()) {
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.
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
297 FD_SET(fd, &readfds);
301 // Drop the global mutex around any polling or RPC calls.
303 _tt_global->drop_mutex();
306 select(FD_SETSIZE,&readfds, 0, 0,
307 (timeout >= 0) ? &tmout : (timeval *)0);
309 _tt_global->grab_mutex();
311 switch (select_stat) {
313 return(_TT_RPCSRV_ERR);
315 return(_TT_RPCSRV_TMOUT);
317 // check for exception fds
319 while (efds_c.next()) {
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;
328 // Clear our fd from the fdset so
329 // svc_getreqset() won't get confused (bug
332 FD_CLR(fd, &readfds);
334 svc_getreqset(&readfds);
336 } while ((! done) && ((stop == 0) || (! *stop)));
342 * Returns an unused transient program number. Definition taken out of
347 gettransient(int vers, netconfig *nconf, netbuf *address)
350 gettransient(int proto, int vers, int *sockp)
358 #if defined(linux) || defined(CSRG_BASED)
370 socktype = SOCK_DGRAM;
373 socktype = SOCK_STREAM;
378 if (*sockp == RPC_ANYSOCK) {
379 s = socket(AF_INET, socktype, 0);
381 _tt_syslog(0, LOG_ERR, "gettransient(): socket(): %m");
388 memset(&addr, 0, sizeof(addr));
389 addr.sin_addr.s_addr = htonl(INADDR_ANY);
390 addr.sin_port = htons(0);
391 addr.sin_family = AF_INET;
393 bind(s, (sockaddr *)&addr, len);
394 #if defined (_AIX) && (OSMAJORVERSION==4) && (OSMINORVERSION==2)
395 if (getsockname(s, (sockaddr *)&addr, (size_t *)&len) < 0) {
397 if (getsockname(s, (sockaddr *)&addr, &len) < 0) {
399 _tt_syslog(0, LOG_ERR, "getsockname(): %m");
405 if (setsockopt(s, SOL_SOCKET, SO_USELOOPBACK,
406 (char *)&optval, sizeof(optval)) == -1) {
409 #endif /* !OPT_TLI */
412 // Search for a transient rpc number in the range 0x40000000 -
413 // 0x5fffffff by starting in the middle of the range searching
414 // up and then searching down if that fails. The reason for
415 // this is to make it less likely for other programs to grab
416 // this transient number (since pmap_getport doesn't complain
417 // if you try to grab a number for udp and we have it grabbed
420 // JET - this is way too many pnums to search, and causes what
421 // appears to be an infinite loop (though it isn't) if the
422 // user is running an rpcbind in secure mode and not using
423 // libtirpc - a common error. The end result is staring at
424 // the dthello welcome screen. So - rather than search this
425 // immense space, we will only search from start to +-50
426 // before bailing. If a hundred attmepts to get a transient
427 // fail, I don't see that doing approximately 537 million
428 // attempts are worth it :)
429 #define MAX_TRANS_RANGE 50
431 // search up in the range 0x4fffffff to 0x4fffffff + MAX_TRANS_RANGE
432 for (prognum = 0x4fffffff; prognum <= (0x4fffffff + MAX_TRANS_RANGE); prognum++) {
433 /* XXX: pmap_set allows the same prognum for different */
434 /* protocols so we hack around that by attemptint to */
435 /* set both tcp and udp. */
438 found = (!pmap_getport(&uport, prognum, vers,
440 !pmap_getport(&tport, prognum, vers, proto));
442 (found = pmap_set(prognum,
445 ntohs(addr.sin_port))) &&
446 (vers==1 || (found = pmap_set(prognum,
449 ntohs(addr.sin_port))))) {
453 if (rpcb_set(prognum, vers, nconf, address) &&
454 (vers==1 || rpcb_set(prognum, 1, nconf, address))) {
457 #endif /* !OPT_TLI */
460 // search down in the range 0x4ffffffe - 0x40000000
461 for (prognum = 0x4ffffffe; prognum >= (0x4ffffffe - MAX_TRANS_RANGE); prognum--) {
462 /* XXX: pmap_set allows the same prognum for different */
463 /* protocols so we hack around that by attemptint to */
464 /* set both tcp and udp. */
466 found = (!pmap_getport(&uport, prognum, vers,
468 !pmap_getport(&tport, prognum, vers, proto));
470 (found = pmap_set(prognum,
473 ntohs(addr.sin_port)))) {
477 if (rpcb_set(prognum, vers, nconf, address)) {
480 #endif /* !OPT_TLI */