1
#ident "$Id: rpc_subs.c,v 1.7 2004/11/20 15:08:38 raven Exp $"
2
/* ----------------------------------------------------------------------- *
4
* rpc_subs.c - routines for rpc discovery
6
* Copyright 2004 Ian Kent <raven@themaw.net> - All Rights Reserved
7
* Copyright 2004 Jeff Moyer <jmoyer@redaht.com> - All Rights Reserved
9
* This program is free software; you can redistribute it and/or modify
10
* it under the terms of the GNU General Public License as published by
11
* the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
12
* USA; either version 2 of the License, or (at your option) any later
13
* version; incorporated herein by reference.
15
* ----------------------------------------------------------------------- */
18
#include <rpc/pmap_prot.h>
20
#include <linux/nfs2.h>
21
#include <linux/nfs3.h>
25
#include <sys/socket.h>
27
#include <netinet/in.h>
28
#include <sys/fcntl.h>
31
#include "automount.h"
33
#define PMAP_TOUT_UDP 2
34
#define PMAP_TOUT_TCP 3
39
unsigned long program;
40
unsigned long version;
41
struct protoent *proto;
44
struct timeval timeout;
48
* Create a UDP RPC client
50
static CLIENT* create_udp_client(struct conn_info *info)
54
struct sockaddr_in laddr, raddr;
57
if (info->proto->p_proto != IPPROTO_UDP)
60
memset(&laddr, 0, sizeof(laddr));
61
memset(&raddr, 0, sizeof(raddr));
63
hp = gethostbyname(info->host);
67
raddr.sin_family = AF_INET;
68
raddr.sin_port = htons(info->port);
69
memcpy(&raddr.sin_addr.s_addr, hp->h_addr, hp->h_length);
72
* bind to any unused port. If we left this up to the rpc
73
* layer, it would bind to a reserved port, which has been shown
74
* to exhaust the reserved port range in some situations.
76
fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
79
laddr.sin_family = AF_INET;
81
laddr.sin_addr.s_addr = htonl(INADDR_ANY);
83
if (bind(fd, (struct sockaddr *)&laddr,
84
sizeof(struct sockaddr_in)) < 0) {
90
client = clntudp_bufcreate(&raddr,
91
info->program, info->version,
93
info->send_sz, info->recv_sz);
95
clnt_control(client, CLSET_FD_CLOSE, NULL);
101
* Perform a non-blocking connect on the socket fd.
103
* tout contains the timeout. It will be modified to contain the time
104
* remaining (i.e. time provided - time elasped).
106
static int connect_nb(int fd, struct sockaddr_in *addr, struct timeval *tout)
111
flags = fcntl(fd, F_GETFL, 0);
115
ret = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
120
* From here on subsequent sys calls could change errno so
121
* we set ret = -errno to capture it in case we decide to
124
ret = connect(fd, (struct sockaddr *)addr, sizeof(struct sockaddr));
125
if (ret < 0 && errno != EINPROGRESS) {
138
ret = select(fd + 1, &rset, &wset, NULL, tout);
147
if (FD_ISSET(fd, &rset) || FD_ISSET(fd, &wset)) {
151
stat = getsockopt(fd, SOL_SOCKET, SO_ERROR, &ret, &len);
157
/* Oops - something wrong with connect */
163
fcntl(fd, F_SETFL, flags);
168
* Create a TCP RPC client using non-blocking connect
170
static CLIENT* create_tcp_client(struct conn_info *info)
174
struct sockaddr_in addr;
178
if (info->proto->p_proto != IPPROTO_TCP)
181
memset(&addr, 0, sizeof(addr));
183
hp = gethostbyname(info->host);
187
addr.sin_family = AF_INET;
188
addr.sin_port = htons(info->port);
189
memcpy(&addr.sin_addr.s_addr, hp->h_addr, hp->h_length);
191
fd = socket(PF_INET, SOCK_STREAM, info->proto->p_proto);
195
ret = connect_nb(fd, &addr, &info->timeout);
199
client = clnttcp_create(&addr,
200
info->program, info->version, &fd,
201
info->send_sz, info->recv_sz);
205
/* Close socket fd on destroy, as is default for rpcowned fds */
206
if (!clnt_control(client, CLSET_FD_CLOSE, NULL)) {
207
clnt_destroy(client);
218
static unsigned short portmap_getport(struct conn_info *info)
220
struct conn_info pmap_info;
221
unsigned short port = 0;
226
pmap_info.host = info->host;
227
pmap_info.port = PMAPPORT;
228
pmap_info.program = PMAPPROG;
229
pmap_info.version = PMAPVERS;
230
pmap_info.proto = info->proto;
231
pmap_info.send_sz = RPCSMALLMSGSIZE;
232
pmap_info.recv_sz = RPCSMALLMSGSIZE;
233
pmap_info.timeout.tv_sec = PMAP_TOUT_UDP;
234
pmap_info.timeout.tv_usec = 0;
236
if (info->proto->p_proto == IPPROTO_TCP) {
237
pmap_info.timeout.tv_sec = PMAP_TOUT_TCP;
238
client = create_tcp_client(&pmap_info);
240
client = create_udp_client(&pmap_info);
245
parms.pm_prog = info->program;
246
parms.pm_vers = info->version;
247
parms.pm_prot = info->proto->p_proto;
250
stat = clnt_call(client, PMAPPROC_GETPORT,
251
(xdrproc_t) xdr_pmap, (caddr_t) &parms,
252
(xdrproc_t) xdr_u_short, (caddr_t) &port,
255
clnt_destroy(client);
257
if (stat != RPC_SUCCESS)
263
static int rpc_ping_proto(const char *host,
264
unsigned long nfs_version,
266
long seconds, long micros)
268
struct conn_info info;
271
struct protoent *prot;
273
prot = getprotobyname(proto);
278
info.program = NFS_PROGRAM;
279
info.version = nfs_version;
283
info.timeout.tv_sec = seconds;
284
info.timeout.tv_usec = micros;
286
info.port = portmap_getport(&info);
290
if (prot->p_proto == IPPROTO_UDP) {
291
info.send_sz = UDPMSGSIZE;
292
info.recv_sz = UDPMSGSIZE;
293
client = create_udp_client(&info);
295
client = create_tcp_client(&info);
300
clnt_control(client, CLSET_TIMEOUT, (char *) &info.timeout);
301
clnt_control(client, CLSET_RETRY_TIMEOUT, (char *) &info.timeout);
303
stat = clnt_call(client, NFSPROC_NULL,
304
(xdrproc_t) xdr_void, 0, (xdrproc_t) xdr_void, 0,
307
clnt_destroy(client);
309
if (stat != RPC_SUCCESS)
315
static unsigned int rpc_ping_v2(const char *host, long seconds, long micros)
317
unsigned int status = RPC_PING_FAIL;
319
status = rpc_ping_proto(host, NFS2_VERSION, "udp", seconds, micros);
321
return RPC_PING_V2 | RPC_PING_UDP;
323
status = rpc_ping_proto(host, NFS2_VERSION, "tcp", seconds, micros);
325
return RPC_PING_V2 | RPC_PING_TCP;
330
static unsigned int rpc_ping_v3(const char *host, long seconds, long micros)
332
unsigned int status = RPC_PING_FAIL;
334
status = rpc_ping_proto(host, NFS3_VERSION, "udp", seconds, micros);
336
return RPC_PING_V3 | RPC_PING_UDP;
338
status = rpc_ping_proto(host, NFS3_VERSION, "tcp", seconds, micros);
340
return RPC_PING_V3 | RPC_PING_TCP;
345
unsigned int rpc_ping(const char *host, long seconds, long micros)
349
status = rpc_ping_v2(host, seconds, micros);
353
status = rpc_ping_v3(host, seconds, micros);
358
static double elapsed(struct timeval start, struct timeval end)
361
t1 = (double)start.tv_sec + (double)start.tv_usec/(1000*1000);
362
t2 = (double)end.tv_sec + (double)end.tv_usec/(1000*1000);
366
int rpc_time(const char *host,
367
unsigned int ping_vers, unsigned int ping_proto,
368
long seconds, long micros, double *result)
372
struct timeval start, end;
374
char *proto = (ping_proto & RPC_PING_UDP) ? "udp" : "tcp";
376
gettimeofday(&start, &tz);
377
status = rpc_ping_proto(host, ping_vers, proto, seconds, micros);
378
gettimeofday(&end, &tz);
384
taken = elapsed(start, end);
395
int main(int argc, char **argv)
400
ret = rpc_ping("budgie", 10, 0);
401
printf("ret = %d\n", ret);
404
ret = rpc_time("raven", NFS2_VERSION, RPC_PING_TCP, 10, 0, &res);
405
printf("v2 tcp ret = %d, res = %f\n", ret, res);
408
ret = rpc_time("raven", NFS3_VERSION, RPC_PING_TCP, 10, 0, &res);
409
printf("v3 tcp ret = %d, res = %f\n", ret, res);
412
ret = rpc_time("raven", NFS2_VERSION, RPC_PING_UDP, 10, 0, &res);
413
printf("v2 udp ret = %d, res = %f\n", ret, res);
416
ret = rpc_time("raven", NFS3_VERSION, RPC_PING_UDP, 10, 0, &res);
417
printf("v3 udp ret = %d, res = %f\n", ret, res);