xref: /csrg-svn/usr.sbin/amd/amd/srvr_nfs.c (revision 61776)
144626Smckusick /*
244626Smckusick  * Copyright (c) 1990 Jan-Simon Pendry
344626Smckusick  * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
4*61776Sbostic  * Copyright (c) 1990, 1993
5*61776Sbostic  *	The Regents of the University of California.  All rights reserved.
644626Smckusick  *
744626Smckusick  * This code is derived from software contributed to Berkeley by
844626Smckusick  * Jan-Simon Pendry at Imperial College, London.
944626Smckusick  *
1044626Smckusick  * %sccs.include.redist.c%
1144626Smckusick  *
12*61776Sbostic  *	@(#)srvr_nfs.c	8.1 (Berkeley) 06/06/93
1349683Spendry  *
1452460Spendry  * $Id: srvr_nfs.c,v 5.2.2.1 1992/02/09 15:09:06 jsp beta $
1549683Spendry  *
1644626Smckusick  */
1744626Smckusick 
1844626Smckusick /*
1944626Smckusick  * NFS server modeling
2044626Smckusick  */
2144626Smckusick 
2244626Smckusick #include "am.h"
2344626Smckusick #include <netdb.h>
2444626Smckusick #include <rpc/pmap_prot.h>
2544626Smckusick #include "mount.h"
2644626Smckusick 
2744626Smckusick extern qelem nfs_srvr_list;
2844626Smckusick qelem nfs_srvr_list = { &nfs_srvr_list, &nfs_srvr_list };
2944626Smckusick 
3044626Smckusick typedef struct nfs_private {
3144626Smckusick 	u_short np_mountd;	/* Mount daemon port number */
3247526Spendry 	char np_mountd_inval;	/* Port *may* be invalid */
3344626Smckusick 	int np_ping;		/* Number of failed ping attempts */
3444626Smckusick 	time_t np_ttl;		/* Time when server is thought dead */
3544626Smckusick 	int np_xid;		/* RPC transaction id for pings */
3644626Smckusick 	int np_error;		/* Error during portmap request */
3744626Smckusick } nfs_private;
3844626Smckusick 
3944626Smckusick static int np_xid;	/* For NFS pings */
4044626Smckusick #define	NPXID_ALLOC()	(++np_xid)
4144626Smckusick /*#define	NPXID_ALLOC()	((++np_xid&0x0fffffff) == 0 ? npxid_gc() : np_xid)*/
4244626Smckusick 
4344626Smckusick /*
4444626Smckusick  * Number of pings allowed to fail before host is declared down
4544626Smckusick  * - three-fifths of the allowed mount time...
4644626Smckusick #define	MAX_ALLOWED_PINGS	((((ALLOWED_MOUNT_TIME + 5 * AM_PINGER - 1) * 3) / 5) / AM_PINGER)
4744626Smckusick  */
4844626Smckusick #define	MAX_ALLOWED_PINGS	(3 + /* for luck ... */ 1)
4944626Smckusick 
5044626Smckusick /*
5144626Smckusick  * How often to ping when starting a new server
5244626Smckusick  */
5344626Smckusick #define	FAST_NFS_PING		3
5444626Smckusick 
5544626Smckusick #if (FAST_NFS_PING * MAX_ALLOWED_PINGS) >= ALLOWED_MOUNT_TIME
5644626Smckusick  #error: sanity check failed
5744626Smckusick /*
5844626Smckusick  you cannot do things this way...
5944626Smckusick  sufficient fast pings must be given the chance to fail
6044626Smckusick  within the allowed mount time
6144626Smckusick  */
6244626Smckusick #endif /* (FAST_NFS_PING * MAX_ALLOWED_PINGS) >= ALLOWED_MOUNT_TIME */
6344626Smckusick 
6444626Smckusick static int ping_len;
6544626Smckusick static char ping_buf[sizeof(struct rpc_msg) + 32];
6644626Smckusick 
6744626Smckusick /*
6847526Spendry  * Flush any cached data
6947526Spendry  */
7047526Spendry void flush_srvr_nfs_cache P((void));
flush_srvr_nfs_cache()7147526Spendry void flush_srvr_nfs_cache()
7247526Spendry {
7347526Spendry 	fserver *fs = 0;
7447526Spendry 
7547526Spendry 	ITER(fs, fserver, &nfs_srvr_list) {
7647526Spendry 		nfs_private *np = (nfs_private *) fs->fs_private;
7747526Spendry 		if (np) {
7847526Spendry 			np->np_mountd_inval = TRUE;
7947526Spendry 			np->np_error = -1;
8047526Spendry 		}
8147526Spendry 	}
8247526Spendry }
8347526Spendry 
8447526Spendry /*
8544626Smckusick  * Startup the NFS ping
8644626Smckusick  */
8747526Spendry static void start_ping(P_void);
start_ping()8844626Smckusick static void start_ping()
8944626Smckusick {
9044626Smckusick 	XDR ping_xdr;
9144626Smckusick 	struct rpc_msg ping_msg;
9244626Smckusick 
9344626Smckusick 	rpc_msg_init(&ping_msg, NFS_PROGRAM, NFS_VERSION, NFSPROC_NULL);
9444626Smckusick 
9544626Smckusick 	/*
9644626Smckusick 	 * Create an XDR endpoint
9744626Smckusick 	 */
9844626Smckusick 	xdrmem_create(&ping_xdr, ping_buf, sizeof(ping_buf), XDR_ENCODE);
9944626Smckusick 
10044626Smckusick 	/*
10144626Smckusick 	 * Create the NFS ping message
10244626Smckusick 	 */
10344626Smckusick 	if (!xdr_callmsg(&ping_xdr, &ping_msg)) {
10444626Smckusick 		plog(XLOG_ERROR, "Couldn't create ping RPC message");
10544626Smckusick 		going_down(3);
10644626Smckusick 	}
10744626Smckusick 
10844626Smckusick 	/*
10944626Smckusick 	 * Find out how long it is
11044626Smckusick 	 */
11144626Smckusick 	ping_len = xdr_getpos(&ping_xdr);
11244626Smckusick 
11344626Smckusick 	/*
11444626Smckusick 	 * Destroy the XDR endpoint - we don't need it anymore
11544626Smckusick 	 */
11644626Smckusick 	xdr_destroy(&ping_xdr);
11744626Smckusick }
11844626Smckusick 
11944626Smckusick 
12044626Smckusick /*
12144626Smckusick  * Called when a portmap reply arrives
12244626Smckusick  */
12347526Spendry /*ARGSUSED*/
12447526Spendry static void got_portmap P((voidp pkt, int len, struct sockaddr_in *sa, struct sockaddr_in *ia, voidp idv, int done));
got_portmap(pkt,len,sa,ia,idv,done)12544626Smckusick static void got_portmap(pkt, len, sa, ia, idv, done)
12644626Smckusick voidp pkt;
12744626Smckusick int len;
12847526Spendry struct sockaddr_in *sa;
12947526Spendry struct sockaddr_in *ia;
13044626Smckusick voidp idv;
13144626Smckusick int done;
13244626Smckusick {
13344626Smckusick 	fserver *fs2 = (fserver *) idv;
13444626Smckusick 	fserver *fs = 0;
13547526Spendry 
13647526Spendry 	/*
13747526Spendry 	 * Find which fileserver we are talking about
13847526Spendry 	 */
13944626Smckusick 	ITER(fs, fserver, &nfs_srvr_list)
14044626Smckusick 		if (fs == fs2)
14144626Smckusick 			break;
14244626Smckusick 
14344626Smckusick 	if (fs == fs2) {
14444626Smckusick 		u_long port = 0;	/* XXX - should be short but protocol is naff */
14544626Smckusick 		int error = done ? pickup_rpc_reply(pkt, len, (voidp) &port, xdr_u_long) : -1;
14644626Smckusick 		nfs_private *np = (nfs_private *) fs->fs_private;
14744626Smckusick 		if (!error && port) {
14844626Smckusick #ifdef DEBUG
14944626Smckusick 			dlog("got port (%d) for mountd on %s", port, fs->fs_host);
15044626Smckusick #endif /* DEBUG */
15144626Smckusick 			/*
15244626Smckusick 			 * Grab the port number.  Portmap sends back
15344626Smckusick 			 * an unsigned long in native ordering, so it
15444626Smckusick 			 * needs converting to a unsigned short in
15544626Smckusick 			 * network ordering.
15644626Smckusick 			 */
15744626Smckusick 			np->np_mountd = htons((u_short) port);
15844626Smckusick 			np->np_mountd_inval = FALSE;
15944626Smckusick 			np->np_error = 0;
16044626Smckusick 		} else {
16144626Smckusick #ifdef DEBUG
16244626Smckusick 			dlog("Error fetching port for mountd on %s", fs->fs_host);
16344626Smckusick #endif /* DEBUG */
16444626Smckusick 			/*
16544626Smckusick 			 * Almost certainly no mountd running on remote host
16644626Smckusick 			 */
16744626Smckusick 			np->np_error = error ? error : ETIMEDOUT;
16844626Smckusick 		}
16944626Smckusick 		if (fs->fs_flags & FSF_WANT)
17044626Smckusick 			wakeup_srvr(fs);
17144626Smckusick 	} else if (done) {
17244626Smckusick #ifdef DEBUG
17344626Smckusick 		dlog("Got portmap for old port request");
17444626Smckusick #endif /* DEBUG */
17544626Smckusick 	} else {
17644626Smckusick #ifdef DEBUG
17744626Smckusick 		dlog("portmap request timed out");
17844626Smckusick #endif /* DEBUG */
17944626Smckusick 	}
18044626Smckusick }
18144626Smckusick 
18244626Smckusick /*
18344626Smckusick  * Obtain portmap information
18444626Smckusick  */
18547526Spendry static int call_portmap P((fserver *fs, AUTH *auth, unsigned long prog, unsigned long vers, unsigned long prot));
call_portmap(fs,auth,prog,vers,prot)18644626Smckusick static int call_portmap(fs, auth, prog, vers, prot)
18744626Smckusick fserver *fs;
18844626Smckusick AUTH *auth;
18944626Smckusick unsigned long prog, vers, prot;
19044626Smckusick {
19144626Smckusick 	struct rpc_msg pmap_msg;
19244626Smckusick 	int len;
19344626Smckusick 	char iobuf[UDPMSGSIZE];
19444626Smckusick 	int error;
19544626Smckusick 	struct pmap pmap;
19644626Smckusick 
19744626Smckusick 	rpc_msg_init(&pmap_msg, PMAPPROG, PMAPVERS, (unsigned long) 0);
19844626Smckusick 	pmap.pm_prog = prog;
19944626Smckusick 	pmap.pm_vers = vers;
20044626Smckusick 	pmap.pm_prot = prot;
20144626Smckusick 	pmap.pm_port = 0;
20244626Smckusick 	len = make_rpc_packet(iobuf, sizeof(iobuf), PMAPPROC_GETPORT,
20344626Smckusick 			&pmap_msg, (voidp) &pmap, xdr_pmap, auth);
20444626Smckusick 	if (len > 0) {
20544626Smckusick 		struct sockaddr_in sin;
20644626Smckusick 		bzero((voidp) &sin, sizeof(sin));
20744626Smckusick 		sin = *fs->fs_ip;
20844626Smckusick 		sin.sin_port = htons(PMAPPORT);
20944626Smckusick 		error = fwd_packet(RPC_XID_PORTMAP, (voidp) iobuf, len,
21044626Smckusick 				&sin, &sin, (voidp) fs, got_portmap);
21144626Smckusick 	} else {
21244626Smckusick 		error = -len;
21344626Smckusick 	}
21444626Smckusick 	return error;
21544626Smckusick }
21644626Smckusick 
21744626Smckusick static void nfs_keepalive P((fserver*));
21844626Smckusick 
21944626Smckusick static void recompute_portmap P((fserver *fs));
recompute_portmap(fs)22044626Smckusick static void recompute_portmap(fs)
22144626Smckusick fserver *fs;
22244626Smckusick {
22349683Spendry 	int error;
22449683Spendry 
22549683Spendry 	if (nfs_auth)
22649683Spendry 		error = 0;
22749683Spendry 	else
22849683Spendry 		error = make_nfs_auth();
22949683Spendry 
23049683Spendry 	if (error) {
23144626Smckusick 		nfs_private *np = (nfs_private *) fs->fs_private;
23249683Spendry 		np->np_error = error;
23344626Smckusick 	} else {
23444626Smckusick 		call_portmap(fs, nfs_auth, MOUNTPROG,
23544626Smckusick 			MOUNTVERS, (unsigned long) IPPROTO_UDP);
23644626Smckusick 	}
23744626Smckusick }
23844626Smckusick 
23944626Smckusick /*
24044626Smckusick  * This is called when we get a reply to an RPC ping.
24144626Smckusick  * The value of id was taken from the nfs_private
24244626Smckusick  * structure when the ping was transmitted.
24344626Smckusick  */
24444626Smckusick /*ARGSUSED*/
24547526Spendry static void nfs_pinged P((voidp pkt, int len, struct sockaddr_in *sp, struct sockaddr_in *tsp, voidp idv, int done));
nfs_pinged(pkt,len,sp,tsp,idv,done)24644626Smckusick static void nfs_pinged(pkt, len, sp, tsp, idv, done)
24744626Smckusick voidp pkt;
24844626Smckusick int len;
24947526Spendry struct sockaddr_in *sp;
25047526Spendry struct sockaddr_in *tsp;
25144626Smckusick voidp idv;
25244626Smckusick int done;
25344626Smckusick {
25444626Smckusick 	int xid = (int) idv;
25544626Smckusick 	fserver *fs;
25647526Spendry #ifdef DEBUG
25744626Smckusick 	int found_map = 0;
25847526Spendry #endif /* DEBUG */
25944626Smckusick 
26044626Smckusick 	if (!done)
26144626Smckusick 		return;
26244626Smckusick 
26344626Smckusick 	/*
26444626Smckusick 	 * For each node...
26544626Smckusick 	 */
26644626Smckusick 	ITER(fs, fserver, &nfs_srvr_list) {
26744626Smckusick 		nfs_private *np = (nfs_private *) fs->fs_private;
26844626Smckusick 		if (np->np_xid == xid) {
26944626Smckusick 			/*
27044626Smckusick 			 * Reset the ping counter.
27144626Smckusick 			 * Update the keepalive timer.
27244626Smckusick 			 * Log what happened.
27344626Smckusick 			 */
27444626Smckusick 			if (fs->fs_flags & FSF_DOWN) {
27544626Smckusick 				fs->fs_flags &= ~FSF_DOWN;
27644626Smckusick 				if (fs->fs_flags & FSF_VALID) {
27744626Smckusick 					srvrlog(fs, "is up");
27844626Smckusick 				} else {
27947526Spendry 					if (np->np_ping > 1)
28047526Spendry 						srvrlog(fs, "ok");
28147526Spendry #ifdef DEBUG
28247526Spendry 					else
28347526Spendry 						srvrlog(fs, "starts up");
28447526Spendry #endif
28544626Smckusick 					fs->fs_flags |= FSF_VALID;
28644626Smckusick 				}
28744626Smckusick 
28844626Smckusick #ifdef notdef
28944626Smckusick 				/* why ??? */
29044626Smckusick 				if (fs->fs_flags & FSF_WANT)
29144626Smckusick 					wakeup_srvr(fs);
29244626Smckusick #endif /* notdef */
29349683Spendry 				map_flush_srvr(fs);
29444626Smckusick 			} else {
29544626Smckusick 				if (fs->fs_flags & FSF_VALID) {
29644626Smckusick #ifdef DEBUG
29744626Smckusick 					dlog("file server %s type nfs is still up", fs->fs_host);
29844626Smckusick #endif /* DEBUG */
29944626Smckusick 				} else {
30047526Spendry 					if (np->np_ping > 1)
30147526Spendry 						srvrlog(fs, "ok");
30244626Smckusick 					fs->fs_flags |= FSF_VALID;
30344626Smckusick 				}
30444626Smckusick 			}
30544626Smckusick 
30644626Smckusick 			/*
30744626Smckusick 			 * Adjust ping interval
30844626Smckusick 			 */
30944626Smckusick 			untimeout(fs->fs_cid);
31044626Smckusick 			fs->fs_cid = timeout(fs->fs_pinger, nfs_keepalive, (voidp) fs);
31144626Smckusick 
31244626Smckusick 			/*
31344626Smckusick 			 * Update ttl for this server
31444626Smckusick 			 */
31544626Smckusick 			np->np_ttl = clocktime() +
31644626Smckusick 				(MAX_ALLOWED_PINGS - 1) * FAST_NFS_PING + fs->fs_pinger - 1;
31744626Smckusick 
31844626Smckusick 			/*
31944626Smckusick 			 * New RPC xid...
32044626Smckusick 			 */
32144626Smckusick 			np->np_xid = NPXID_ALLOC();
32244626Smckusick 
32344626Smckusick 			/*
32444626Smckusick 			 * Failed pings is zero...
32544626Smckusick 			 */
32644626Smckusick 			np->np_ping = 0;
32744626Smckusick 
32844626Smckusick 			/*
32944626Smckusick 			 * Recompute portmap information if not known
33044626Smckusick 			 */
33144626Smckusick 			if (np->np_mountd_inval)
33244626Smckusick 				recompute_portmap(fs);
33344626Smckusick 
33447526Spendry #ifdef DEBUG
33544626Smckusick 			found_map++;
33647526Spendry #endif /* DEBUG */
33744626Smckusick 			break;
33844626Smckusick 		}
33944626Smckusick 	}
34044626Smckusick 
34144626Smckusick #ifdef DEBUG
34244626Smckusick 	if (found_map == 0)
34344626Smckusick 		dlog("Spurious ping packet");
34444626Smckusick #endif /* DEBUG */
34544626Smckusick }
34644626Smckusick 
34744626Smckusick /*
34844626Smckusick  * Called when no ping-reply received
34944626Smckusick  */
35044626Smckusick static void nfs_timed_out P((fserver *fs));
nfs_timed_out(fs)35144626Smckusick static void nfs_timed_out(fs)
35244626Smckusick fserver *fs;
35344626Smckusick {
35444626Smckusick 	nfs_private *np = (nfs_private *) fs->fs_private;
35544626Smckusick 
35644626Smckusick 	/*
35747526Spendry 	 * Another ping has failed
35847526Spendry 	 */
35947526Spendry 	np->np_ping++;
36047526Spendry 
36147526Spendry 	/*
36244626Smckusick 	 * Not known to be up any longer
36344626Smckusick 	 */
36444626Smckusick 	if (FSRV_ISUP(fs)) {
36544626Smckusick 		fs->fs_flags &= ~FSF_VALID;
36647526Spendry 		if (np->np_ping > 1)
36747526Spendry 			srvrlog(fs, "not responding");
36844626Smckusick 	}
36944626Smckusick 
37044626Smckusick 	/*
37144626Smckusick 	 * If ttl has expired then guess that it is dead
37244626Smckusick 	 */
37344626Smckusick 	if (np->np_ttl < clocktime()) {
37449683Spendry 		int oflags = fs->fs_flags;
37544626Smckusick 		if ((fs->fs_flags & FSF_DOWN) == 0) {
37644626Smckusick 			/*
37744626Smckusick 			 * Server was up, but is now down.
37844626Smckusick 			 */
37944626Smckusick 			srvrlog(fs, "is down");
38044626Smckusick 			fs->fs_flags |= FSF_DOWN|FSF_VALID;
38144626Smckusick 			/*
38244626Smckusick 			 * Since the server is down, the portmap
38344626Smckusick 			 * information may now be wrong, so it
38444626Smckusick 			 * must be flushed from the local cache
38544626Smckusick 			 */
38644626Smckusick 			flush_nfs_fhandle_cache(fs);
38744626Smckusick 			np->np_error = -1;
38847526Spendry #ifdef notdef
38944626Smckusick 			/*
39044626Smckusick 			 * Pretend just one ping has failed now
39144626Smckusick 			 */
39244626Smckusick 			np->np_ping = 1;
39347526Spendry #endif
39444626Smckusick 		} else {
39544626Smckusick 			/*
39644626Smckusick 			 * Known to be down
39744626Smckusick 			 */
39847526Spendry #ifdef DEBUG
39949683Spendry 			if ((fs->fs_flags & FSF_VALID) == 0)
40049683Spendry 				srvrlog(fs, "starts down");
40147526Spendry #endif
40249683Spendry 			fs->fs_flags |= FSF_VALID;
40344626Smckusick 		}
40449683Spendry 		if (oflags != fs->fs_flags && (fs->fs_flags & FSF_WANT))
40547526Spendry 			wakeup_srvr(fs);
40644626Smckusick 	} else {
40744626Smckusick #ifdef DEBUG
40844626Smckusick 		if (np->np_ping > 1)
40944626Smckusick 			dlog("%d pings to %s failed - at most %d allowed", np->np_ping, fs->fs_host, MAX_ALLOWED_PINGS);
41044626Smckusick #endif /* DEBUG */
41144626Smckusick 	}
41244626Smckusick 
41344626Smckusick 	/*
41444626Smckusick 	 * Run keepalive again
41544626Smckusick 	 */
41644626Smckusick 	nfs_keepalive(fs);
41744626Smckusick }
41844626Smckusick 
41944626Smckusick /*
42044626Smckusick  * Keep track of whether a server is alive
42144626Smckusick  */
42244626Smckusick static void nfs_keepalive P((fserver *fs));
nfs_keepalive(fs)42344626Smckusick static void nfs_keepalive(fs)
42444626Smckusick fserver *fs;
42544626Smckusick {
42644626Smckusick 	int error;
42744626Smckusick 	nfs_private *np = (nfs_private *) fs->fs_private;
42844626Smckusick 	int fstimeo = -1;
42944626Smckusick 
43044626Smckusick 	/*
43144626Smckusick 	 * Send an NFS ping to this node
43244626Smckusick 	 */
43344626Smckusick 
43444626Smckusick 	if (ping_len == 0)
43544626Smckusick 		start_ping();
43644626Smckusick 
43744626Smckusick 	/*
43844626Smckusick 	 * Queue the packet...
43944626Smckusick 	 */
44044626Smckusick 	error = fwd_packet(MK_RPC_XID(RPC_XID_NFSPING, np->np_xid), (voidp) ping_buf,
44144626Smckusick 		ping_len, fs->fs_ip, (struct sockaddr_in *) 0, (voidp) np->np_xid, nfs_pinged);
44244626Smckusick 
44344626Smckusick 	/*
44444626Smckusick 	 * See if a hard error occured
44544626Smckusick 	 */
44644626Smckusick 	switch (error) {
44744626Smckusick 	case ENETDOWN:
44844626Smckusick 	case ENETUNREACH:
44944626Smckusick 	case EHOSTDOWN:
45044626Smckusick 	case EHOSTUNREACH:
45144626Smckusick 		np->np_ping = MAX_ALLOWED_PINGS;	/* immediately down */
45244626Smckusick 		np->np_ttl = (time_t) 0;
45344626Smckusick 		/*
45444626Smckusick 		 * This causes an immediate call to nfs_timed_out
45544626Smckusick 		 * whenever the server was thought to be up.
45644626Smckusick 		 * See +++ below.
45744626Smckusick 		 */
45844626Smckusick 		fstimeo = 0;
45944626Smckusick 		break;
46044626Smckusick 
46144626Smckusick 	case 0:
46244626Smckusick #ifdef DEBUG
46344626Smckusick 		dlog("Sent NFS ping to %s", fs->fs_host);
46444626Smckusick #endif /* DEBUG */
46544626Smckusick 		break;
46644626Smckusick 	}
46744626Smckusick 
46844626Smckusick #ifdef DEBUG
46944626Smckusick 	/*dlog("keepalive, ping = %d", np->np_ping);*/
47044626Smckusick #endif /* DEBUG */
47144626Smckusick 
47244626Smckusick 	/*
47344626Smckusick 	 * Back off the ping interval if we are not getting replies and
47444626Smckusick 	 * the remote system is know to be down.
47544626Smckusick 	 */
47644626Smckusick 	switch (fs->fs_flags & (FSF_DOWN|FSF_VALID)) {
47744626Smckusick 	case FSF_VALID:			/* Up */
47844626Smckusick 		if (fstimeo < 0)	/* +++ see above */
47944626Smckusick 			fstimeo = FAST_NFS_PING;
48044626Smckusick 		break;
48144626Smckusick 
48244626Smckusick 	case FSF_VALID|FSF_DOWN:	/* Down */
48344626Smckusick 		fstimeo = fs->fs_pinger;
48444626Smckusick 		break;
48544626Smckusick 
48644626Smckusick 	default:			/* Unknown */
48744626Smckusick 		fstimeo = FAST_NFS_PING;
48844626Smckusick 		break;
48944626Smckusick 	}
49044626Smckusick 
49144626Smckusick #ifdef DEBUG
49244626Smckusick 	dlog("NFS timeout in %d seconds", fstimeo);
49344626Smckusick #endif /* DEBUG */
49444626Smckusick 
49544626Smckusick 	fs->fs_cid = timeout(fstimeo, nfs_timed_out, (voidp) fs);
49644626Smckusick }
49744626Smckusick 
49847526Spendry int nfs_srvr_port P((fserver *fs, u_short *port, voidp wchan));
nfs_srvr_port(fs,port,wchan)49944626Smckusick int nfs_srvr_port(fs, port, wchan)
50044626Smckusick fserver *fs;
50144626Smckusick u_short *port;
50244626Smckusick voidp wchan;
50344626Smckusick {
50444626Smckusick 	int error = -1;
50544626Smckusick 	if ((fs->fs_flags & FSF_VALID) == FSF_VALID) {
50644626Smckusick 		if ((fs->fs_flags & FSF_DOWN) == 0) {
50744626Smckusick 			nfs_private *np = (nfs_private *) fs->fs_private;
50844626Smckusick 			if (np->np_error == 0) {
50944626Smckusick 				*port = np->np_mountd;
51044626Smckusick 				error = 0;
51144626Smckusick 			} else {
51244626Smckusick 				error = np->np_error;
51344626Smckusick 			}
51447526Spendry 			/*
51547526Spendry 			 * Now go get the port mapping again in case it changed.
51647526Spendry 			 * Note that it is used even if (np_mountd_inval)
51747526Spendry 			 * is True.  The flag is used simply as an
51847526Spendry 			 * indication that the mountd may be invalid, not
51947526Spendry 			 * that it is known to be invalid.
52047526Spendry 			 */
52147526Spendry 			if (np->np_mountd_inval)
52247526Spendry 				recompute_portmap(fs);
52347526Spendry 			else
52447526Spendry 				np->np_mountd_inval = TRUE;
52544626Smckusick 		} else {
52644626Smckusick 			error = EWOULDBLOCK;
52744626Smckusick 		}
52844626Smckusick 	}
52944626Smckusick 	if (error < 0 && wchan && !(fs->fs_flags & FSF_WANT)) {
53044626Smckusick 		/*
53144626Smckusick 		 * If a wait channel is supplied, and no
53244626Smckusick 		 * error has yet occured, then arrange
53344626Smckusick 		 * that a wakeup is done on the wait channel,
53444626Smckusick 		 * whenever a wakeup is done on this fs node.
53544626Smckusick 		 * Wakeup's are done on the fs node whenever
53644626Smckusick 		 * it changes state - thus causing control to
53744626Smckusick 		 * come back here and new, better things to happen.
53844626Smckusick 		 */
53944626Smckusick 		fs->fs_flags |= FSF_WANT;
54044626Smckusick 		sched_task(wakeup_task, wchan, (voidp) fs);
54144626Smckusick 	}
54244626Smckusick 	return error;
54344626Smckusick }
54444626Smckusick 
54544626Smckusick static void start_nfs_pings P((fserver *fs, int pingval));
start_nfs_pings(fs,pingval)54644626Smckusick static void start_nfs_pings(fs, pingval)
54744626Smckusick fserver *fs;
54844626Smckusick int pingval;
54944626Smckusick {
55044626Smckusick 	if (!(fs->fs_flags & FSF_PINGING)) {
55144626Smckusick 		fs->fs_flags |= FSF_PINGING;
55244626Smckusick 		if (fs->fs_cid)
55344626Smckusick 			untimeout(fs->fs_cid);
55444626Smckusick 		if (pingval < 0) {
55544626Smckusick 			srvrlog(fs, "wired up");
55644626Smckusick 			fs->fs_flags |= FSF_VALID;
55744626Smckusick 			fs->fs_flags &= ~FSF_DOWN;
55844626Smckusick 		} else {
55944626Smckusick 			nfs_keepalive(fs);
56044626Smckusick 		}
56144626Smckusick 	} else {
56244626Smckusick #ifdef DEBUG
56344626Smckusick 		dlog("Already running pings to %s", fs->fs_host);
56444626Smckusick #endif /* DEBUG */
56544626Smckusick 	}
56644626Smckusick }
56744626Smckusick 
56844626Smckusick /*
56944626Smckusick  * Find an nfs server for a host.
57044626Smckusick  */
57144626Smckusick fserver *find_nfs_srvr P((mntfs *mf));
find_nfs_srvr(mf)57244626Smckusick fserver *find_nfs_srvr(mf)
57344626Smckusick mntfs *mf;
57444626Smckusick {
57544626Smckusick 	fserver *fs;
57644626Smckusick 	struct hostent *hp = 0;
57744626Smckusick 	char *host = mf->mf_fo->opt_rhost;
57844626Smckusick 	struct sockaddr_in *ip;
57944626Smckusick 	nfs_private *np;
58044626Smckusick 	int pingval;
58144626Smckusick 
58244626Smckusick 	/*
58344626Smckusick 	 * Get ping interval from mount options.
58444626Smckusick 	 * Current only used to decide whether pings
58544626Smckusick 	 * are required or not.  < 0 = no pings.
58644626Smckusick 	 */
58744626Smckusick 	{ struct mntent mnt;
58847526Spendry 	  mnt.mnt_opts = mf->mf_mopts;
58944626Smckusick 	  pingval = hasmntval(&mnt, "ping");
59044626Smckusick #ifdef HAS_TCP_NFS
59144626Smckusick 	  /*
59244626Smckusick 	   * Over TCP mount, don't bother to do pings.
59344626Smckusick 	   * This is experimental - maybe you want to
59444626Smckusick 	   * do pings anyway...
59544626Smckusick 	   */
59644626Smckusick 	  if (pingval == 0 && hasmntopt(&mnt, "tcp"))
59744626Smckusick 		pingval = -1;
59844626Smckusick #endif /* HAS_TCP_NFS */
59944626Smckusick 	}
60044626Smckusick 
60144626Smckusick 
60244626Smckusick 	/*
60347526Spendry 	 * lookup host address and canonical name
60444626Smckusick 	 */
60547526Spendry 	hp = gethostbyname(host);
60647526Spendry 
60747526Spendry 	/*
60847526Spendry 	 * New code from Bob Harris <harris@basil-rathbone.mit.edu>
60947526Spendry 	 * Use canonical name to keep track of file server
61047526Spendry 	 * information.  This way aliases do not generate
61147526Spendry 	 * multiple NFS pingers.  (Except when we're normalizing
61247526Spendry 	 * hosts.)
61347526Spendry 	 */
61447526Spendry 	if (hp && !normalize_hosts) host = hp->h_name;
61547526Spendry 
61644626Smckusick 	ITER(fs, fserver, &nfs_srvr_list) {
61744626Smckusick 		if (STREQ(host, fs->fs_host)) {
61844626Smckusick 			start_nfs_pings(fs, pingval);
61944626Smckusick 			fs->fs_refc++;
62044626Smckusick 			return fs;
62144626Smckusick 		}
62244626Smckusick 	}
62344626Smckusick 
62444626Smckusick 
62547526Spendry 
62644626Smckusick 	/*
62744626Smckusick 	 * Get here if we can't find an entry
62844626Smckusick 	 */
62944626Smckusick 	if (hp) {
63044626Smckusick 		switch (hp->h_addrtype) {
63144626Smckusick 		case AF_INET:
63244626Smckusick 			ip = ALLOC(sockaddr_in);
63344626Smckusick 			bzero((voidp) ip, sizeof(*ip));
63444626Smckusick 			ip->sin_family = AF_INET;
63547526Spendry 			bcopy((voidp) hp->h_addr, (voidp) &ip->sin_addr, sizeof(ip->sin_addr));
63647526Spendry 
63744626Smckusick 			ip->sin_port = htons(NFS_PORT);
63844626Smckusick 			break;
63944626Smckusick 
64044626Smckusick 		default:
64144626Smckusick 			ip = 0;
64244626Smckusick 			break;
64344626Smckusick 		}
64444626Smckusick 	} else {
64547526Spendry 		plog(XLOG_USER, "Unknown host: %s", host);
64644626Smckusick 		ip = 0;
64744626Smckusick 	}
64844626Smckusick 
64944626Smckusick 	/*
65044626Smckusick 	 * Allocate a new server
65144626Smckusick 	 */
65244626Smckusick 	fs = ALLOC(fserver);
65344626Smckusick 	fs->fs_refc = 1;
65444626Smckusick 	fs->fs_host = strdup(hp ? hp->h_name : "unknown_hostname");
65547526Spendry 	if (normalize_hosts) host_normalize(&fs->fs_host);
65644626Smckusick 	fs->fs_ip = ip;
65744626Smckusick 	fs->fs_cid = 0;
65844626Smckusick 	if (ip) {
65944626Smckusick 		fs->fs_flags = FSF_DOWN;	/* Starts off down */
66044626Smckusick 	} else {
66144626Smckusick 		fs->fs_flags = FSF_ERROR|FSF_VALID;
66244626Smckusick 		mf->mf_flags |= MFF_ERROR;
66344626Smckusick 		mf->mf_error = ENOENT;
66444626Smckusick 	}
66544626Smckusick 	fs->fs_type = "nfs";
66644626Smckusick 	fs->fs_pinger = AM_PINGER;
66744626Smckusick 	np = ALLOC(nfs_private);
66844626Smckusick 	bzero((voidp) np, sizeof(*np));
66944626Smckusick 	np->np_mountd_inval = TRUE;
67044626Smckusick 	np->np_xid = NPXID_ALLOC();
67144626Smckusick 	np->np_error = -1;
67244626Smckusick 	/*
67344626Smckusick 	 * Initially the server will be deemed dead after
67444626Smckusick 	 * MAX_ALLOWED_PINGS of the fast variety have failed.
67544626Smckusick 	 */
67644626Smckusick 	np->np_ttl = clocktime() + MAX_ALLOWED_PINGS * FAST_NFS_PING - 1;
67744626Smckusick 	fs->fs_private = (voidp) np;
67844626Smckusick 	fs->fs_prfree = (void (*)()) free;
67944626Smckusick 
68044626Smckusick 	if (!(fs->fs_flags & FSF_ERROR)) {
68144626Smckusick 		/*
68244626Smckusick 		 * Start of keepalive timer
68344626Smckusick 		 */
68444626Smckusick 		start_nfs_pings(fs, pingval);
68544626Smckusick 	}
68644626Smckusick 
68744626Smckusick 	/*
68844626Smckusick 	 * Add to list of servers
68944626Smckusick 	 */
69044626Smckusick 	ins_que(&fs->fs_q, &nfs_srvr_list);
69144626Smckusick 
69244626Smckusick 	return fs;
69344626Smckusick }
694