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>