[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     ! 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>