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>