nfs_cast.c revision 1160694128cd3980cc06abe31af529a887efd310
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* nfs_cast.c : broadcast to a specific group of NFS servers
*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <syslog.h>
#include <errno.h>
#include <string.h>
#include <sys/resource.h>
#include <unistd.h>
#include <stdlib.h>
#include <rpc/clnt_soc.h>
#include <rpc/pmap_prot.h>
#include <netconfig.h>
#include <netdir.h>
#define NFSCLIENT
#include <locale.h>
#include "automount.h"
#define PENALTY_WEIGHT 100000
struct tstamps {
int ts_penalty;
int ts_inx;
int ts_rcvd;
struct timeval ts_timeval;
};
/* A list of addresses - all belonging to the same transport */
struct addrs {
struct nd_addrlist *addr_addrs;
struct tstamps *addr_if_tstamps;
};
/* A list of connectionless transports */
struct transp {
int tr_fd;
char *tr_device;
};
/* A list of map entries and their roundtrip times, for sorting */
struct sm {
};
static void free_transports(struct transp *);
static void calc_resp_time(struct timeval *);
static int host_sm(const void *, const void *b);
static int time_sm(const void *, const void *b);
struct mapfs **);
/*
* This routine is designed to be able to "ping"
* a list of hosts and create a list of responding
* hosts sorted by response time.
* This must be done without any prior
* contact with the host - therefore the "ping"
* must be to a "well-known" address. The outstanding
* candidate here is the address of "rpcbind".
*
* A response to a ping is no guarantee that the host
* is running NFS, has a mount daemon, or exports
* the required filesystem. If the subsequent
* mount attempt fails then the host will be marked
* "ignore" and the host list will be re-pinged
* (sans the bad host). This process continues
* until a successful mount is achieved or until
* there are no hosts left to try.
*/
enum clnt_stat
{
int outlen;
int if_inx;
int tsec;
int flag;
register int i;
struct timeval t, rcv_timeout;
struct nd_hostserv hs;
struct nd_addrlist *retaddrs;
int dtbsize;
/*
* For each connectionless transport get a list of
* host addresses. Any single host may have
* addresses on several transports.
*/
/*
* Set the default select size to be the maximum FD_SETSIZE, unless
* the current rlimit is lower.
*/
}
prev_trans = NULL;
if (trace > 2)
nc = setnetconfig();
stat = RPC_CANTSEND;
goto done_broad;
}
continue;
stat = RPC_CANTSEND;
goto done_broad;
}
else
prev_trans = trans;
stat = RPC_CANTSEND;
goto done_broad;
}
stat = RPC_CANTSEND;
goto done_broad;
}
/* LINTED pointer alignment */
goto done_broad;
}
if_inx = 0;
/*
* If mfs->ignore is previously set for
* this map, clear it. Because a host can
* have either v6 or v4 address
*/
mfs->mfs_ignore = 0;
if (a == NULL) {
stat = RPC_CANTSEND;
goto done_broad;
}
(void) memset(a, 0, sizeof (*a));
else
prev_addr = a;
a->addr_if_tstamps = NULL;
a->addr_addrs = retaddrs;
while (if_cnt--) {
stat = RPC_CANTSEND;
goto done_broad;
}
if (a->addr_if_tstamps == NULL)
a->addr_if_tstamps = ts;
else
addr_cnt++;
}
break;
} else {
if (verbose)
"%s:%s address not known",
}
} /* while */
} /* for */
if (addr_cnt == 0) {
stat = RPC_CANTSEND;
goto done_broad;
}
(void) gettimeofday(&t, (struct timezone *)0);
t.tv_usec = 0;
/* serialize the RPC header */
/*
* we can not use RPCBVERS here since it doesn't exist in 4.X,
* the fix to bug 1139883 has made the 4.X portmapper silent to
* version mismatches. This causes the RPC call to the remote
* portmapper to simply be ignored if it's not Version 2.
*/
goto done_broad;
}
goto done_broad;
}
/*
* Basic loop: send packet to all hosts and wait for response(s).
* The response timeout grows larger per iteration.
* A unique xid is assigned to each address in order to
* correctly match the replies.
*/
if (timeout <= 0)
rcv_timeout.tv_usec = 0;
sent = 0;
a->addr_addrs->n_addrs;
ts = a->addr_if_tstamps;
while (if_cnt--) {
/*
* xid is the first thing in
* preserialized buffer
*/
/* LINTED pointer alignment */
(struct timezone *)0);
/*
* Check if already received
* from a previous iteration.
*/
sent++;
continue;
}
&t_udata) == 0) {
sent++;
}
}
}
}
if (sent == 0) { /* no packets sent ? */
stat = RPC_CANTSEND;
goto done_broad;
}
/*
* Have sent all the packets. Now collect the responses...
*/
rcvd = 0;
case 0: /* Timed out */
/*
* If we got at least one response in the
* last interval, then don't wait for any
* more. In theory we should wait for
* the max weighting (penalty) value so
* that a very slow server has a chance to
* respond but this could take a long time
* if the admin has set a high weighting
* value.
*/
if (rcvd > 0)
goto done_broad;
stat = RPC_TIMEDOUT;
continue;
case -1: /* some kind of error */
goto recv_again;
if (rcvd == 0)
stat = RPC_CANTRECV;
goto done_broad;
} /* end of select results switch */
break;
}
goto recv_again;
goto try_again;
stat = RPC_CANTRECV;
continue;
}
goto recv_again;
"nfscast: t_rcvudata: %s: buffer overflow",
goto recv_again;
}
/*
* see if reply transaction id matches sent id.
* If so, decode the results.
* Note: received addr is ignored, it could be
* different from the send addr if the host has
* more than one addr.
*/
stat = RPC_SUCCESS;
rcvd++;
break;
}
}
} /* otherwise, we just ignore the errors ... */
}
goto done_broad;
else
goto recv_again;
}
if (!rcvd)
stat = RPC_TIMEDOUT;
if (rcvd) {
stat = RPC_SUCCESS;
}
if (nc)
return (stat);
}
/*
* Go through all the responses and sort fastest to slowest.
* Note that any penalty is added to the response time - so the
* fastest response isn't necessarily the one that arrived first.
*/
static struct mapfs *
{
struct transp *t;
struct addrs *a;
if (!buffer) {
return (NULL);
}
for (ti = a->addr_if_tstamps;
continue;
}
allocsize += 10;
if (!buffer) {
"sort_responses: malloc error.\n");
return (NULL);
}
}
size++;
}
}
}
#ifdef DEBUG
if (trace > 3) {
for (i = 0; i < size; i++)
trace_prt(0, "\n");
}
#endif
/*
* Cope with multiply listed hosts by choosing first time
*/
for (i = 1; i < size; i++) {
#ifdef DEBUG
if (trace > 3) {
}
#endif
sizeof (struct timeval));
}
if (trace > 3)
trace_prt(0, "\n");
#ifdef DEBUG
if (trace > 3) {
for (i = 0; i < size; i++)
trace_prt(0, "\n");
}
#endif
#ifdef DEBUG
if (trace > 3) {
for (i = 0; i < size; i++)
trace_prt(0, "\n");
}
#endif
for (i = 0; i < size; i++) {
#ifdef DEBUG
if (trace > 3) {
}
#endif
if (!p)
return (NULL);
}
return (mfs_head);
}
/*
* Comparison routines called by qsort(3).
*/
static int host_sm(const void *a, const void *b)
{
}
static int time_sm(const void *a, const void *b)
{
return (-1);
return (1);
else
return (0);
}
/*
* Given send_time which is the time a request
* was transmitted to a server, subtract it
* from the time "now" thereby converting it
* to an elapsed time.
*/
static void
{
}
}
static void
{
if (t->tr_taddr)
if (t->tr_fd > 0)
}
free(a);
}
free(t);
}
}