[BACK]Return to RPC_TCP_Connector.c++ CVS log [TXT][DIR] Up to [Development] / fam / fam

Annotation of fam/fam/RPC_TCP_Connector.c++, Revision 1.1.1.1

1.1       trev        1: //  Copyright (C) 1999 Silicon Graphics, Inc.  All Rights Reserved.
                      2: //
                      3: //  This program is free software; you can redistribute it and/or modify it
                      4: //  under the terms of version 2 of the GNU General Public License as
                      5: //  published by the Free Software Foundation.
                      6: //
                      7: //  This program is distributed in the hope that it would be useful, but
                      8: //  WITHOUT ANY WARRANTY; without even the implied warranty of
                      9: //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  Further, any
                     10: //  license provided herein, whether implied or otherwise, is limited to
                     11: //  this program in accordance with the express provisions of the GNU
                     12: //  General Public License.  Patent licenses, if any, provided herein do not
                     13: //  apply to combinations of this program with other product or programs, or
                     14: //  any other product whatsoever.  This program is distributed without any
                     15: //  warranty that the program is delivered free of the rightful claim of any
                     16: //  third person by way of infringement or the like.  See the GNU General
                     17: //  Public License for more details.
                     18: //
                     19: //  You should have received a copy of the GNU General Public License along
                     20: //  with this program; if not, write the Free Software Foundation, Inc., 59
                     21: //  Temple Place - Suite 330, Boston MA 02111-1307, USA.
                     22:
                     23: #include "RPC_TCP_Connector.h"
                     24:
                     25: #include <errno.h>
                     26: #include <rpc/rpc.h>
                     27: #include <rpc/pmap_prot.h>
                     28: #include <sys/ioctl.h>
                     29: #include <sys/socket.h>
                     30: #include <unistd.h>
                     31: #include <string.h>
                     32: #include <netdb.h>  // for rresvport
                     33:
                     34: #include "Log.h"
                     35: #include "Scheduler.h"
                     36: #include "Cred.h"  // for Cred::SuperUser
                     37:
                     38: RPC_TCP_Connector::RPC_TCP_Connector(unsigned long p,
                     39:                                      unsigned long v,
                     40:                                      unsigned long host,
                     41: 				     ConnectHandler ch,
                     42:                                      void *cl)
                     43:     : state(IDLE), sockfd(-1), program(p), version(v),
                     44:       connect_handler(ch), closure(cl)
                     45: {
                     46:     memset(&address, 0, sizeof address);
                     47:     address.sin_family = AF_INET;
                     48:     address.sin_addr.s_addr = host;
                     49: }
                     50:
                     51: RPC_TCP_Connector::~RPC_TCP_Connector()
                     52: {
                     53:     deactivate();
                     54: }
                     55:
                     56: //////////////////////////////////////////////////////////////////////////////
                     57:
                     58: void
                     59: RPC_TCP_Connector::activate()
                     60: {
                     61:     assert(state == IDLE);
                     62:     state = PMAPPING;
                     63:     address.sin_port = htons(PMAPPORT);
                     64:     retry_interval = INITIAL_RETRY_INTERVAL;
                     65:     try_to_connect();
                     66: }
                     67:
                     68: void
                     69: RPC_TCP_Connector::deactivate()
                     70: {
                     71:     if (sockfd >= 0)
                     72:     {   (void) Scheduler::remove_write_handler(sockfd);
                     73: 	(void) close(sockfd);
                     74: 	sockfd = -1;
                     75:     }
                     76:     if (state == PAUSING)
                     77: 	(void) Scheduler::remove_onetime_task(retry_task, this);
                     78:     state = IDLE;
                     79: }
                     80:
                     81: //////////////////////////////////////////////////////////////////////////////
                     82:
                     83: void
                     84: RPC_TCP_Connector::try_to_connect()
                     85: {
                     86:     assert(sockfd == -1);
                     87:     assert(state == PMAPPING || state == CONNECTING);
                     88:
                     89:     Cred::SuperUser.become_user();  //  So we can have a privileged port.
                     90:     int lport = IPPORT_RESERVED - 1;
                     91:     int fd = rresvport(&lport);
                     92:     if (fd < 0)
                     93:     {   Log::perror("rresvport");
                     94: 	try_again();
                     95: 	return;
                     96:     }
                     97:     int yes = 1;
                     98:     int rc = ioctl(fd, FIONBIO, &yes);
                     99:     if (rc < 0)
                    100:     {   Log::perror("FIONBIO");
                    101:         deactivate();
                    102: 	return;
                    103:     }
                    104:     rc = connect(fd, (const sockaddr *)&address, sizeof address);
                    105:     if (rc == 0)
                    106:     {   sockfd = fd;
                    107: 	write_handler(fd, this);
                    108:     }
                    109:     else if (errno == EINPROGRESS)
                    110:     {   (void) Scheduler::install_write_handler(fd, write_handler, this);
                    111: 	sockfd = fd;
                    112:     }
                    113:     else
                    114:     {   Log::perror("connect");
                    115: 	(void) close(fd);
                    116: 	try_again();
                    117:     }
                    118: }
                    119:
                    120: void
                    121: RPC_TCP_Connector::write_handler(int fd, void *closure)
                    122: {
                    123:     (void) Scheduler::remove_write_handler(fd);
                    124:     RPC_TCP_Connector *conn = (RPC_TCP_Connector *) closure;
                    125:     assert(fd == conn->sockfd);
                    126:     int rc = connect(fd, (const sockaddr *)(&conn->address), sizeof conn->address);
                    127:     if (rc < 0 && errno != EISCONN)
                    128:     {
                    129: 	Log::perror("connect");
                    130: 	(void) close(conn->sockfd);
                    131: 	conn->sockfd = -1;
                    132: 	conn->try_again();
                    133: 	return;
                    134:     }
                    135:     switch (conn->state)
                    136:     {
                    137:     case PMAPPING:
                    138:     {
                    139: 	//  We have connected with portmapper; make a PMAP_GETPORT
                    140: 	//  call.
                    141:
                    142: 	sockaddr_in addr;
                    143: 	addr.sin_port = htons(PMAPPORT);
                    144: 	CLIENT *client = clnttcp_create(&addr, PMAPPROG, PMAPVERS, &fd,
                    145: 					RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
                    146: 	clnt_stat rc = (clnt_stat) ~RPC_SUCCESS;
                    147: 	unsigned short port = 0;
                    148: 	if (client)
                    149: 	{   struct pmap pmp = { conn->program, conn->version, IPPROTO_TCP, 0 };
                    150: 	    timeval timeout = { PMAP_TIMEOUT, 0 };
                    151: 	    rc = CLNT_CALL(client, PMAPPROC_GETPORT, (xdrproc_t) xdr_pmap,
                    152: 			   (caddr_t) &pmp, (xdrproc_t) xdr_u_short, (caddr_t) &port, timeout);
                    153: 	    if (rc != RPC_SUCCESS)
                    154: 		Log::info("Portmapper call failed: %s", clnt_sperrno(rc));
                    155: 	    CLNT_DESTROY(client);
                    156: 	}
                    157: 	else
                    158: 	    Log::info("Couldn't create RPC TCP/IP client: %m");
                    159: 	(void) close(fd);
                    160: 	conn->sockfd = -1;
                    161: 	if (rc == RPC_SUCCESS && port != 0)
                    162: 	{   conn->state = CONNECTING;
                    163: 	    conn->address.sin_port = htons(port);
                    164: 	    conn->try_to_connect();
                    165: 	}
                    166: 	else
                    167: 	    conn->try_again();
                    168: 	break;
                    169:     }
                    170:     case CONNECTING:
                    171:
                    172: 	conn->state = IDLE;
                    173: 	conn->sockfd = -1;
                    174: 	(*conn->connect_handler)(fd, conn->closure);
                    175: 	break;
                    176:
                    177:     default:
                    178:
                    179: 	int unknown_connector_state = 0; assert(unknown_connector_state);
                    180: 	break;
                    181:     }
                    182: }
                    183:
                    184: //////////////////////////////////////////////////////////////////////////////
                    185:
                    186: //  Implement an exponential falloff.  Start trying once a second,
                    187: //  slow to once every 1024 seconds (~17 minutes).  Time required by
                    188: //  each connection attempt is added in, so if other host is down and
                    189: //  TCP has a two minute timeout, we start by polling every 2:01, and
                    190: //  slow to every 19:05.
                    191:
                    192: void
                    193: RPC_TCP_Connector::try_again()
                    194: {
                    195:     assert(state == PMAPPING || state == CONNECTING);
                    196:     state = PAUSING;
                    197:
                    198:     timeval next_time;
                    199:     (void) gettimeofday(&next_time, NULL);
                    200:     next_time.tv_sec += retry_interval;
                    201:     if (retry_interval < MAX_RETRY_INTERVAL)
                    202: 	retry_interval *= 2;
                    203:
                    204:     Scheduler::install_onetime_task(next_time, retry_task, this);
                    205: }
                    206:
                    207: void
                    208: RPC_TCP_Connector::retry_task(void *closure)
                    209: {
                    210:     RPC_TCP_Connector *conn = (RPC_TCP_Connector *) closure;
                    211:     assert(conn->state == PAUSING);
                    212:     conn->state = PMAPPING;
                    213:     conn->address.sin_port = htons(PMAPPORT);
                    214:     conn->try_to_connect();
                    215: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>