xref: /csrg-svn/usr.sbin/amd/amd/srvr_nfs.c (revision 47526)
144626Smckusick /*
2*47526Spendry  * $Id: srvr_nfs.c,v 5.2.1.3 91/03/17 17:44:37 jsp Alpha $
344626Smckusick  *
444626Smckusick  * Copyright (c) 1990 Jan-Simon Pendry
544626Smckusick  * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
644626Smckusick  * Copyright (c) 1990 The Regents of the University of California.
744626Smckusick  * All rights reserved.
844626Smckusick  *
944626Smckusick  * This code is derived from software contributed to Berkeley by
1044626Smckusick  * Jan-Simon Pendry at Imperial College, London.
1144626Smckusick  *
1244626Smckusick  * %sccs.include.redist.c%
1344626Smckusick  *
14*47526Spendry  *	@(#)srvr_nfs.c	5.2 (Berkeley) 03/17/91
1544626Smckusick  */
1644626Smckusick 
1744626Smckusick /*
1844626Smckusick  * NFS server modeling
1944626Smckusick  */
2044626Smckusick 
2144626Smckusick #include "am.h"
2244626Smckusick #include <netdb.h>
2344626Smckusick #include <rpc/pmap_prot.h>
2444626Smckusick #include "mount.h"
2544626Smckusick 
2644626Smckusick extern qelem nfs_srvr_list;
2744626Smckusick qelem nfs_srvr_list = { &nfs_srvr_list, &nfs_srvr_list };
2844626Smckusick 
2944626Smckusick typedef struct nfs_private {
3044626Smckusick 	u_short np_mountd;	/* Mount daemon port number */
31*47526Spendry 	char np_mountd_inval;	/* Port *may* be invalid */
3244626Smckusick 	int np_ping;		/* Number of failed ping attempts */
3344626Smckusick 	time_t np_ttl;		/* Time when server is thought dead */
3444626Smckusick 	int np_xid;		/* RPC transaction id for pings */
3544626Smckusick 	int np_error;		/* Error during portmap request */
3644626Smckusick } nfs_private;
3744626Smckusick 
3844626Smckusick static int np_xid;	/* For NFS pings */
3944626Smckusick #define	NPXID_ALLOC()	(++np_xid)
4044626Smckusick /*#define	NPXID_ALLOC()	((++np_xid&0x0fffffff) == 0 ? npxid_gc() : np_xid)*/
4144626Smckusick 
4244626Smckusick /*
4344626Smckusick  * Number of pings allowed to fail before host is declared down
4444626Smckusick  * - three-fifths of the allowed mount time...
4544626Smckusick #define	MAX_ALLOWED_PINGS	((((ALLOWED_MOUNT_TIME + 5 * AM_PINGER - 1) * 3) / 5) / AM_PINGER)
4644626Smckusick  */
4744626Smckusick #define	MAX_ALLOWED_PINGS	(3 + /* for luck ... */ 1)
4844626Smckusick 
4944626Smckusick /*
5044626Smckusick  * How often to ping when starting a new server
5144626Smckusick  */
5244626Smckusick #define	FAST_NFS_PING		3
5344626Smckusick 
5444626Smckusick #if (FAST_NFS_PING * MAX_ALLOWED_PINGS) >= ALLOWED_MOUNT_TIME
5544626Smckusick  #error: sanity check failed
5644626Smckusick /*
5744626Smckusick  you cannot do things this way...
5844626Smckusick  sufficient fast pings must be given the chance to fail
5944626Smckusick  within the allowed mount time
6044626Smckusick  */
6144626Smckusick #endif /* (FAST_NFS_PING * MAX_ALLOWED_PINGS) >= ALLOWED_MOUNT_TIME */
6244626Smckusick 
6344626Smckusick static int ping_len;
6444626Smckusick static char ping_buf[sizeof(struct rpc_msg) + 32];
6544626Smckusick 
6644626Smckusick /*
67*47526Spendry  * Flush any cached data
68*47526Spendry  */
69*47526Spendry void flush_srvr_nfs_cache P((void));
70*47526Spendry void flush_srvr_nfs_cache()
71*47526Spendry {
72*47526Spendry 	fserver *fs = 0;
73*47526Spendry 
74*47526Spendry 	ITER(fs, fserver, &nfs_srvr_list) {
75*47526Spendry 		nfs_private *np = (nfs_private *) fs->fs_private;
76*47526Spendry 		if (np) {
77*47526Spendry 			np->np_mountd_inval = TRUE;
78*47526Spendry 			np->np_error = -1;
79*47526Spendry 		}
80*47526Spendry 	}
81*47526Spendry }
82*47526Spendry 
83*47526Spendry /*
8444626Smckusick  * Startup the NFS ping
8544626Smckusick  */
86*47526Spendry static void start_ping(P_void);
8744626Smckusick static void start_ping()
8844626Smckusick {
8944626Smckusick 	XDR ping_xdr;
9044626Smckusick 	struct rpc_msg ping_msg;
9144626Smckusick 
9244626Smckusick 	rpc_msg_init(&ping_msg, NFS_PROGRAM, NFS_VERSION, NFSPROC_NULL);
9344626Smckusick 
9444626Smckusick 	/*
9544626Smckusick 	 * Create an XDR endpoint
9644626Smckusick 	 */
9744626Smckusick 	xdrmem_create(&ping_xdr, ping_buf, sizeof(ping_buf), XDR_ENCODE);
9844626Smckusick 
9944626Smckusick 	/*
10044626Smckusick 	 * Create the NFS ping message
10144626Smckusick 	 */
10244626Smckusick 	if (!xdr_callmsg(&ping_xdr, &ping_msg)) {
10344626Smckusick 		plog(XLOG_ERROR, "Couldn't create ping RPC message");
10444626Smckusick 		going_down(3);
10544626Smckusick 	}
10644626Smckusick 
10744626Smckusick 	/*
10844626Smckusick 	 * Find out how long it is
10944626Smckusick 	 */
11044626Smckusick 	ping_len = xdr_getpos(&ping_xdr);
11144626Smckusick 
11244626Smckusick 	/*
11344626Smckusick 	 * Destroy the XDR endpoint - we don't need it anymore
11444626Smckusick 	 */
11544626Smckusick 	xdr_destroy(&ping_xdr);
11644626Smckusick }
11744626Smckusick 
11844626Smckusick 
11944626Smckusick /*
12044626Smckusick  * Called when a portmap reply arrives
12144626Smckusick  */
122*47526Spendry /*ARGSUSED*/
123*47526Spendry static void got_portmap P((voidp pkt, int len, struct sockaddr_in *sa, struct sockaddr_in *ia, voidp idv, int done));
12444626Smckusick static void got_portmap(pkt, len, sa, ia, idv, done)
12544626Smckusick voidp pkt;
12644626Smckusick int len;
127*47526Spendry struct sockaddr_in *sa;
128*47526Spendry struct sockaddr_in *ia;
12944626Smckusick voidp idv;
13044626Smckusick int done;
13144626Smckusick {
13244626Smckusick 	fserver *fs2 = (fserver *) idv;
13344626Smckusick 	fserver *fs = 0;
134*47526Spendry 
135*47526Spendry 	/*
136*47526Spendry 	 * Find which fileserver we are talking about
137*47526Spendry 	 */
13844626Smckusick 	ITER(fs, fserver, &nfs_srvr_list)
13944626Smckusick 		if (fs == fs2)
14044626Smckusick 			break;
14144626Smckusick 
14244626Smckusick 	if (fs == fs2) {
14344626Smckusick 		u_long port = 0;	/* XXX - should be short but protocol is naff */
14444626Smckusick 		int error = done ? pickup_rpc_reply(pkt, len, (voidp) &port, xdr_u_long) : -1;
14544626Smckusick 		nfs_private *np = (nfs_private *) fs->fs_private;
14644626Smckusick 		if (!error && port) {
14744626Smckusick #ifdef DEBUG
14844626Smckusick 			dlog("got port (%d) for mountd on %s", port, fs->fs_host);
14944626Smckusick #endif /* DEBUG */
15044626Smckusick 			/*
15144626Smckusick 			 * Grab the port number.  Portmap sends back
15244626Smckusick 			 * an unsigned long in native ordering, so it
15344626Smckusick 			 * needs converting to a unsigned short in
15444626Smckusick 			 * network ordering.
15544626Smckusick 			 */
15644626Smckusick 			np->np_mountd = htons((u_short) port);
15744626Smckusick 			np->np_mountd_inval = FALSE;
15844626Smckusick 			np->np_error = 0;
15944626Smckusick 		} else {
16044626Smckusick #ifdef DEBUG
16144626Smckusick 			dlog("Error fetching port for mountd on %s", fs->fs_host);
16244626Smckusick #endif /* DEBUG */
16344626Smckusick 			/*
16444626Smckusick 			 * Almost certainly no mountd running on remote host
16544626Smckusick 			 */
16644626Smckusick 			np->np_error = error ? error : ETIMEDOUT;
16744626Smckusick 		}
16844626Smckusick 		if (fs->fs_flags & FSF_WANT)
16944626Smckusick 			wakeup_srvr(fs);
17044626Smckusick 	} else if (done) {
17144626Smckusick #ifdef DEBUG
17244626Smckusick 		dlog("Got portmap for old port request");
17344626Smckusick #endif /* DEBUG */
17444626Smckusick 	} else {
17544626Smckusick #ifdef DEBUG
17644626Smckusick 		dlog("portmap request timed out");
17744626Smckusick #endif /* DEBUG */
17844626Smckusick 	}
17944626Smckusick }
18044626Smckusick 
18144626Smckusick /*
18244626Smckusick  * Obtain portmap information
18344626Smckusick  */
184*47526Spendry static int call_portmap P((fserver *fs, AUTH *auth, unsigned long prog, unsigned long vers, unsigned long prot));
18544626Smckusick static int call_portmap(fs, auth, prog, vers, prot)
18644626Smckusick fserver *fs;
18744626Smckusick AUTH *auth;
18844626Smckusick unsigned long prog, vers, prot;
18944626Smckusick {
19044626Smckusick 	struct rpc_msg pmap_msg;
19144626Smckusick 	int len;
19244626Smckusick 	char iobuf[UDPMSGSIZE];
19344626Smckusick 	int error;
19444626Smckusick 	struct pmap pmap;
19544626Smckusick 
19644626Smckusick 	rpc_msg_init(&pmap_msg, PMAPPROG, PMAPVERS, (unsigned long) 0);
19744626Smckusick 	pmap.pm_prog = prog;
19844626Smckusick 	pmap.pm_vers = vers;
19944626Smckusick 	pmap.pm_prot = prot;
20044626Smckusick 	pmap.pm_port = 0;
20144626Smckusick 	len = make_rpc_packet(iobuf, sizeof(iobuf), PMAPPROC_GETPORT,
20244626Smckusick 			&pmap_msg, (voidp) &pmap, xdr_pmap, auth);
20344626Smckusick 	if (len > 0) {
20444626Smckusick 		struct sockaddr_in sin;
20544626Smckusick 		bzero((voidp) &sin, sizeof(sin));
20644626Smckusick 		sin = *fs->fs_ip;
20744626Smckusick 		sin.sin_port = htons(PMAPPORT);
20844626Smckusick 		error = fwd_packet(RPC_XID_PORTMAP, (voidp) iobuf, len,
20944626Smckusick 				&sin, &sin, (voidp) fs, got_portmap);
21044626Smckusick 	} else {
21144626Smckusick 		error = -len;
21244626Smckusick 	}
21344626Smckusick 	return error;
21444626Smckusick }
21544626Smckusick 
21644626Smckusick static void nfs_keepalive P((fserver*));
21744626Smckusick 
21844626Smckusick static void recompute_portmap P((fserver *fs));
21944626Smckusick static void recompute_portmap(fs)
22044626Smckusick fserver *fs;
22144626Smckusick {
22244626Smckusick 	if (!nfs_auth)
22344626Smckusick 		nfs_auth = authunix_create_default();
22444626Smckusick 	if (!nfs_auth) {
22544626Smckusick 		nfs_private *np = (nfs_private *) fs->fs_private;
22644626Smckusick 		np->np_error = ENOBUFS;
22744626Smckusick 	} else {
22844626Smckusick 		call_portmap(fs, nfs_auth, MOUNTPROG,
22944626Smckusick 			MOUNTVERS, (unsigned long) IPPROTO_UDP);
23044626Smckusick 	}
23144626Smckusick }
23244626Smckusick 
23344626Smckusick /*
23444626Smckusick  * This is called when we get a reply to an RPC ping.
23544626Smckusick  * The value of id was taken from the nfs_private
23644626Smckusick  * structure when the ping was transmitted.
23744626Smckusick  */
23844626Smckusick /*ARGSUSED*/
239*47526Spendry static void nfs_pinged P((voidp pkt, int len, struct sockaddr_in *sp, struct sockaddr_in *tsp, voidp idv, int done));
24044626Smckusick static void nfs_pinged(pkt, len, sp, tsp, idv, done)
24144626Smckusick voidp pkt;
24244626Smckusick int len;
243*47526Spendry struct sockaddr_in *sp;
244*47526Spendry struct sockaddr_in *tsp;
24544626Smckusick voidp idv;
24644626Smckusick int done;
24744626Smckusick {
24844626Smckusick 	int xid = (int) idv;
24944626Smckusick 	fserver *fs;
250*47526Spendry #ifdef DEBUG
25144626Smckusick 	int found_map = 0;
252*47526Spendry #endif /* DEBUG */
25344626Smckusick 
25444626Smckusick 	if (!done)
25544626Smckusick 		return;
25644626Smckusick 
25744626Smckusick 	/*
25844626Smckusick 	 * For each node...
25944626Smckusick 	 */
26044626Smckusick 	ITER(fs, fserver, &nfs_srvr_list) {
26144626Smckusick 		nfs_private *np = (nfs_private *) fs->fs_private;
26244626Smckusick 		if (np->np_xid == xid) {
26344626Smckusick 			/*
26444626Smckusick 			 * Reset the ping counter.
26544626Smckusick 			 * Update the keepalive timer.
26644626Smckusick 			 * Log what happened.
26744626Smckusick 			 */
26844626Smckusick 			if (fs->fs_flags & FSF_DOWN) {
26944626Smckusick 				fs->fs_flags &= ~FSF_DOWN;
27044626Smckusick 				if (fs->fs_flags & FSF_VALID) {
27144626Smckusick 					srvrlog(fs, "is up");
27244626Smckusick 				} else {
273*47526Spendry 					if (np->np_ping > 1)
274*47526Spendry 						srvrlog(fs, "ok");
275*47526Spendry #ifdef DEBUG
276*47526Spendry 					else
277*47526Spendry 						srvrlog(fs, "starts up");
278*47526Spendry #endif
27944626Smckusick 					fs->fs_flags |= FSF_VALID;
28044626Smckusick 				}
28144626Smckusick 
28244626Smckusick #ifdef notdef
28344626Smckusick 				/* why ??? */
28444626Smckusick 				if (fs->fs_flags & FSF_WANT)
28544626Smckusick 					wakeup_srvr(fs);
28644626Smckusick #endif /* notdef */
28744626Smckusick 			} else {
28844626Smckusick 				if (fs->fs_flags & FSF_VALID) {
28944626Smckusick #ifdef DEBUG
29044626Smckusick 					dlog("file server %s type nfs is still up", fs->fs_host);
29144626Smckusick #endif /* DEBUG */
29244626Smckusick 				} else {
293*47526Spendry 					if (np->np_ping > 1)
294*47526Spendry 						srvrlog(fs, "ok");
29544626Smckusick 					fs->fs_flags |= FSF_VALID;
29644626Smckusick 				}
29744626Smckusick 			}
29844626Smckusick 
29944626Smckusick 			/*
30044626Smckusick 			 * Adjust ping interval
30144626Smckusick 			 */
30244626Smckusick 			untimeout(fs->fs_cid);
30344626Smckusick 			fs->fs_cid = timeout(fs->fs_pinger, nfs_keepalive, (voidp) fs);
30444626Smckusick 
30544626Smckusick 			/*
30644626Smckusick 			 * Update ttl for this server
30744626Smckusick 			 */
30844626Smckusick 			np->np_ttl = clocktime() +
30944626Smckusick 				(MAX_ALLOWED_PINGS - 1) * FAST_NFS_PING + fs->fs_pinger - 1;
31044626Smckusick 
31144626Smckusick 			/*
31244626Smckusick 			 * New RPC xid...
31344626Smckusick 			 */
31444626Smckusick 			np->np_xid = NPXID_ALLOC();
31544626Smckusick 
31644626Smckusick 			/*
31744626Smckusick 			 * Failed pings is zero...
31844626Smckusick 			 */
31944626Smckusick 			np->np_ping = 0;
32044626Smckusick 
32144626Smckusick 			/*
32244626Smckusick 			 * Recompute portmap information if not known
32344626Smckusick 			 */
32444626Smckusick 			if (np->np_mountd_inval)
32544626Smckusick 				recompute_portmap(fs);
32644626Smckusick 
327*47526Spendry #ifdef DEBUG
32844626Smckusick 			found_map++;
329*47526Spendry #endif /* DEBUG */
33044626Smckusick 			break;
33144626Smckusick 		}
33244626Smckusick 	}
33344626Smckusick 
33444626Smckusick #ifdef DEBUG
33544626Smckusick 	if (found_map == 0)
33644626Smckusick 		dlog("Spurious ping packet");
33744626Smckusick #endif /* DEBUG */
33844626Smckusick }
33944626Smckusick 
34044626Smckusick /*
34144626Smckusick  * Called when no ping-reply received
34244626Smckusick  */
34344626Smckusick static void nfs_timed_out P((fserver *fs));
34444626Smckusick static void nfs_timed_out(fs)
34544626Smckusick fserver *fs;
34644626Smckusick {
34744626Smckusick 	nfs_private *np = (nfs_private *) fs->fs_private;
34844626Smckusick 
34944626Smckusick 	/*
350*47526Spendry 	 * Another ping has failed
351*47526Spendry 	 */
352*47526Spendry 	np->np_ping++;
353*47526Spendry 
354*47526Spendry 	/*
35544626Smckusick 	 * Not known to be up any longer
35644626Smckusick 	 */
35744626Smckusick 	if (FSRV_ISUP(fs)) {
35844626Smckusick 		fs->fs_flags &= ~FSF_VALID;
359*47526Spendry 		if (np->np_ping > 1)
360*47526Spendry 			srvrlog(fs, "not responding");
36144626Smckusick 	}
36244626Smckusick 
36344626Smckusick 	/*
36444626Smckusick 	 * If ttl has expired then guess that it is dead
36544626Smckusick 	 */
36644626Smckusick 	if (np->np_ttl < clocktime()) {
36744626Smckusick 		if ((fs->fs_flags & FSF_DOWN) == 0) {
36844626Smckusick 			/*
36944626Smckusick 			 * Server was up, but is now down.
37044626Smckusick 			 */
37144626Smckusick 			srvrlog(fs, "is down");
37244626Smckusick 			fs->fs_flags |= FSF_DOWN|FSF_VALID;
37344626Smckusick 			/*
37444626Smckusick 			 * Since the server is down, the portmap
37544626Smckusick 			 * information may now be wrong, so it
37644626Smckusick 			 * must be flushed from the local cache
37744626Smckusick 			 */
37844626Smckusick 			flush_nfs_fhandle_cache(fs);
37944626Smckusick 			np->np_error = -1;
380*47526Spendry #ifdef notdef
38144626Smckusick 			/*
38244626Smckusick 			 * Pretend just one ping has failed now
38344626Smckusick 			 */
38444626Smckusick 			np->np_ping = 1;
385*47526Spendry #endif
38644626Smckusick 		} else {
38744626Smckusick 			/*
38844626Smckusick 			 * Known to be down
38944626Smckusick 			 */
39044626Smckusick 			fs->fs_flags |= FSF_VALID;
391*47526Spendry #ifdef DEBUG
392*47526Spendry 			srvrlog(fs, "starts down");
393*47526Spendry #endif
39444626Smckusick 		}
395*47526Spendry 		if (fs->fs_flags & FSF_WANT)
396*47526Spendry 			wakeup_srvr(fs);
39744626Smckusick 	} else {
39844626Smckusick #ifdef DEBUG
39944626Smckusick 		if (np->np_ping > 1)
40044626Smckusick 			dlog("%d pings to %s failed - at most %d allowed", np->np_ping, fs->fs_host, MAX_ALLOWED_PINGS);
40144626Smckusick #endif /* DEBUG */
40244626Smckusick 	}
40344626Smckusick 
40444626Smckusick 	/*
40544626Smckusick 	 * Run keepalive again
40644626Smckusick 	 */
40744626Smckusick 	nfs_keepalive(fs);
40844626Smckusick }
40944626Smckusick 
41044626Smckusick /*
41144626Smckusick  * Keep track of whether a server is alive
41244626Smckusick  */
41344626Smckusick static void nfs_keepalive P((fserver *fs));
41444626Smckusick static void nfs_keepalive(fs)
41544626Smckusick fserver *fs;
41644626Smckusick {
41744626Smckusick 	int error;
41844626Smckusick 	nfs_private *np = (nfs_private *) fs->fs_private;
41944626Smckusick 	int fstimeo = -1;
42044626Smckusick 
42144626Smckusick 	/*
42244626Smckusick 	 * Send an NFS ping to this node
42344626Smckusick 	 */
42444626Smckusick 
42544626Smckusick 	if (ping_len == 0)
42644626Smckusick 		start_ping();
42744626Smckusick 
42844626Smckusick 	/*
42944626Smckusick 	 * Queue the packet...
43044626Smckusick 	 */
43144626Smckusick 	error = fwd_packet(MK_RPC_XID(RPC_XID_NFSPING, np->np_xid), (voidp) ping_buf,
43244626Smckusick 		ping_len, fs->fs_ip, (struct sockaddr_in *) 0, (voidp) np->np_xid, nfs_pinged);
43344626Smckusick 
43444626Smckusick 	/*
43544626Smckusick 	 * See if a hard error occured
43644626Smckusick 	 */
43744626Smckusick 	switch (error) {
43844626Smckusick 	case ENETDOWN:
43944626Smckusick 	case ENETUNREACH:
44044626Smckusick 	case EHOSTDOWN:
44144626Smckusick 	case EHOSTUNREACH:
44244626Smckusick 		np->np_ping = MAX_ALLOWED_PINGS;	/* immediately down */
44344626Smckusick 		np->np_ttl = (time_t) 0;
44444626Smckusick 		/*
44544626Smckusick 		 * This causes an immediate call to nfs_timed_out
44644626Smckusick 		 * whenever the server was thought to be up.
44744626Smckusick 		 * See +++ below.
44844626Smckusick 		 */
44944626Smckusick 		fstimeo = 0;
45044626Smckusick 		break;
45144626Smckusick 
45244626Smckusick 	case 0:
45344626Smckusick #ifdef DEBUG
45444626Smckusick 		dlog("Sent NFS ping to %s", fs->fs_host);
45544626Smckusick #endif /* DEBUG */
45644626Smckusick 		break;
45744626Smckusick 	}
45844626Smckusick 
45944626Smckusick #ifdef DEBUG
46044626Smckusick 	/*dlog("keepalive, ping = %d", np->np_ping);*/
46144626Smckusick #endif /* DEBUG */
46244626Smckusick 
46344626Smckusick 	/*
46444626Smckusick 	 * Back off the ping interval if we are not getting replies and
46544626Smckusick 	 * the remote system is know to be down.
46644626Smckusick 	 */
46744626Smckusick 	switch (fs->fs_flags & (FSF_DOWN|FSF_VALID)) {
46844626Smckusick 	case FSF_VALID:			/* Up */
46944626Smckusick 		if (fstimeo < 0)	/* +++ see above */
47044626Smckusick 			fstimeo = FAST_NFS_PING;
47144626Smckusick 		break;
47244626Smckusick 
47344626Smckusick 	case FSF_VALID|FSF_DOWN:	/* Down */
47444626Smckusick 		fstimeo = fs->fs_pinger;
47544626Smckusick 		break;
47644626Smckusick 
47744626Smckusick 	default:			/* Unknown */
47844626Smckusick 		fstimeo = FAST_NFS_PING;
47944626Smckusick 		break;
48044626Smckusick 	}
48144626Smckusick 
48244626Smckusick #ifdef DEBUG
48344626Smckusick 	dlog("NFS timeout in %d seconds", fstimeo);
48444626Smckusick #endif /* DEBUG */
48544626Smckusick 
48644626Smckusick 	fs->fs_cid = timeout(fstimeo, nfs_timed_out, (voidp) fs);
48744626Smckusick }
48844626Smckusick 
489*47526Spendry int nfs_srvr_port P((fserver *fs, u_short *port, voidp wchan));
49044626Smckusick int nfs_srvr_port(fs, port, wchan)
49144626Smckusick fserver *fs;
49244626Smckusick u_short *port;
49344626Smckusick voidp wchan;
49444626Smckusick {
49544626Smckusick 	int error = -1;
49644626Smckusick 	if ((fs->fs_flags & FSF_VALID) == FSF_VALID) {
49744626Smckusick 		if ((fs->fs_flags & FSF_DOWN) == 0) {
49844626Smckusick 			nfs_private *np = (nfs_private *) fs->fs_private;
49944626Smckusick 			if (np->np_error == 0) {
50044626Smckusick 				*port = np->np_mountd;
50144626Smckusick 				error = 0;
50244626Smckusick 			} else {
50344626Smckusick 				error = np->np_error;
50444626Smckusick 			}
505*47526Spendry 			/*
506*47526Spendry 			 * Now go get the port mapping again in case it changed.
507*47526Spendry 			 * Note that it is used even if (np_mountd_inval)
508*47526Spendry 			 * is True.  The flag is used simply as an
509*47526Spendry 			 * indication that the mountd may be invalid, not
510*47526Spendry 			 * that it is known to be invalid.
511*47526Spendry 			 */
512*47526Spendry 			if (np->np_mountd_inval)
513*47526Spendry 				recompute_portmap(fs);
514*47526Spendry 			else
515*47526Spendry 				np->np_mountd_inval = TRUE;
51644626Smckusick 		} else {
51744626Smckusick 			error = EWOULDBLOCK;
51844626Smckusick 		}
51944626Smckusick 	}
52044626Smckusick 	if (error < 0 && wchan && !(fs->fs_flags & FSF_WANT)) {
52144626Smckusick 		/*
52244626Smckusick 		 * If a wait channel is supplied, and no
52344626Smckusick 		 * error has yet occured, then arrange
52444626Smckusick 		 * that a wakeup is done on the wait channel,
52544626Smckusick 		 * whenever a wakeup is done on this fs node.
52644626Smckusick 		 * Wakeup's are done on the fs node whenever
52744626Smckusick 		 * it changes state - thus causing control to
52844626Smckusick 		 * come back here and new, better things to happen.
52944626Smckusick 		 */
53044626Smckusick 		fs->fs_flags |= FSF_WANT;
53144626Smckusick 		sched_task(wakeup_task, wchan, (voidp) fs);
53244626Smckusick 	}
53344626Smckusick 	return error;
53444626Smckusick }
53544626Smckusick 
53644626Smckusick static void start_nfs_pings P((fserver *fs, int pingval));
53744626Smckusick static void start_nfs_pings(fs, pingval)
53844626Smckusick fserver *fs;
53944626Smckusick int pingval;
54044626Smckusick {
54144626Smckusick 	if (!(fs->fs_flags & FSF_PINGING)) {
54244626Smckusick 		fs->fs_flags |= FSF_PINGING;
54344626Smckusick 		if (fs->fs_cid)
54444626Smckusick 			untimeout(fs->fs_cid);
54544626Smckusick 		if (pingval < 0) {
54644626Smckusick 			srvrlog(fs, "wired up");
54744626Smckusick 			fs->fs_flags |= FSF_VALID;
54844626Smckusick 			fs->fs_flags &= ~FSF_DOWN;
54944626Smckusick 		} else {
55044626Smckusick 			nfs_keepalive(fs);
55144626Smckusick 		}
55244626Smckusick 	} else {
55344626Smckusick #ifdef DEBUG
55444626Smckusick 		dlog("Already running pings to %s", fs->fs_host);
55544626Smckusick #endif /* DEBUG */
55644626Smckusick 	}
55744626Smckusick }
55844626Smckusick 
55944626Smckusick /*
56044626Smckusick  * Find an nfs server for a host.
56144626Smckusick  */
56244626Smckusick fserver *find_nfs_srvr P((mntfs *mf));
56344626Smckusick fserver *find_nfs_srvr(mf)
56444626Smckusick mntfs *mf;
56544626Smckusick {
56644626Smckusick 	fserver *fs;
56744626Smckusick 	struct hostent *hp = 0;
56844626Smckusick 	char *host = mf->mf_fo->opt_rhost;
56944626Smckusick 	struct sockaddr_in *ip;
57044626Smckusick 	nfs_private *np;
57144626Smckusick 	int pingval;
57244626Smckusick 
57344626Smckusick 	/*
57444626Smckusick 	 * Get ping interval from mount options.
57544626Smckusick 	 * Current only used to decide whether pings
57644626Smckusick 	 * are required or not.  < 0 = no pings.
57744626Smckusick 	 */
57844626Smckusick 	{ struct mntent mnt;
579*47526Spendry 	  mnt.mnt_opts = mf->mf_mopts;
58044626Smckusick 	  pingval = hasmntval(&mnt, "ping");
58144626Smckusick #ifdef HAS_TCP_NFS
58244626Smckusick 	  /*
58344626Smckusick 	   * Over TCP mount, don't bother to do pings.
58444626Smckusick 	   * This is experimental - maybe you want to
58544626Smckusick 	   * do pings anyway...
58644626Smckusick 	   */
58744626Smckusick 	  if (pingval == 0 && hasmntopt(&mnt, "tcp"))
58844626Smckusick 		pingval = -1;
58944626Smckusick #endif /* HAS_TCP_NFS */
59044626Smckusick 	}
59144626Smckusick 
59244626Smckusick 
59344626Smckusick 	/*
594*47526Spendry 	 * lookup host address and canonical name
59544626Smckusick 	 */
596*47526Spendry 	hp = gethostbyname(host);
597*47526Spendry 
598*47526Spendry 	/*
599*47526Spendry 	 * New code from Bob Harris <harris@basil-rathbone.mit.edu>
600*47526Spendry 	 * Use canonical name to keep track of file server
601*47526Spendry 	 * information.  This way aliases do not generate
602*47526Spendry 	 * multiple NFS pingers.  (Except when we're normalizing
603*47526Spendry 	 * hosts.)
604*47526Spendry 	 */
605*47526Spendry 	if (hp && !normalize_hosts) host = hp->h_name;
606*47526Spendry 
60744626Smckusick 	ITER(fs, fserver, &nfs_srvr_list) {
60844626Smckusick 		if (STREQ(host, fs->fs_host)) {
60944626Smckusick 			start_nfs_pings(fs, pingval);
61044626Smckusick 			fs->fs_refc++;
61144626Smckusick 			return fs;
61244626Smckusick 		}
61344626Smckusick 	}
61444626Smckusick 
61544626Smckusick 
616*47526Spendry 
61744626Smckusick 	/*
61844626Smckusick 	 * Get here if we can't find an entry
61944626Smckusick 	 */
62044626Smckusick 	if (hp) {
62144626Smckusick 		switch (hp->h_addrtype) {
62244626Smckusick 		case AF_INET:
62344626Smckusick 			ip = ALLOC(sockaddr_in);
62444626Smckusick 			bzero((voidp) ip, sizeof(*ip));
62544626Smckusick 			ip->sin_family = AF_INET;
626*47526Spendry 			bcopy((voidp) hp->h_addr, (voidp) &ip->sin_addr, sizeof(ip->sin_addr));
627*47526Spendry 
62844626Smckusick 			ip->sin_port = htons(NFS_PORT);
62944626Smckusick 			break;
63044626Smckusick 
63144626Smckusick 		default:
63244626Smckusick 			ip = 0;
63344626Smckusick 			break;
63444626Smckusick 		}
63544626Smckusick 	} else {
636*47526Spendry 		plog(XLOG_USER, "Unknown host: %s", host);
63744626Smckusick 		ip = 0;
63844626Smckusick 	}
63944626Smckusick 
64044626Smckusick 	/*
64144626Smckusick 	 * Allocate a new server
64244626Smckusick 	 */
64344626Smckusick 	fs = ALLOC(fserver);
64444626Smckusick 	fs->fs_refc = 1;
64544626Smckusick 	fs->fs_host = strdup(hp ? hp->h_name : "unknown_hostname");
646*47526Spendry 	if (normalize_hosts) host_normalize(&fs->fs_host);
64744626Smckusick 	fs->fs_ip = ip;
64844626Smckusick 	fs->fs_cid = 0;
64944626Smckusick 	if (ip) {
65044626Smckusick 		fs->fs_flags = FSF_DOWN;	/* Starts off down */
65144626Smckusick 	} else {
65244626Smckusick 		fs->fs_flags = FSF_ERROR|FSF_VALID;
65344626Smckusick 		mf->mf_flags |= MFF_ERROR;
65444626Smckusick 		mf->mf_error = ENOENT;
65544626Smckusick 	}
65644626Smckusick 	fs->fs_type = "nfs";
65744626Smckusick 	fs->fs_pinger = AM_PINGER;
65844626Smckusick 	np = ALLOC(nfs_private);
65944626Smckusick 	bzero((voidp) np, sizeof(*np));
66044626Smckusick 	np->np_mountd_inval = TRUE;
66144626Smckusick 	np->np_xid = NPXID_ALLOC();
66244626Smckusick 	np->np_error = -1;
66344626Smckusick 	/*
66444626Smckusick 	 * Initially the server will be deemed dead after
66544626Smckusick 	 * MAX_ALLOWED_PINGS of the fast variety have failed.
66644626Smckusick 	 */
66744626Smckusick 	np->np_ttl = clocktime() + MAX_ALLOWED_PINGS * FAST_NFS_PING - 1;
66844626Smckusick 	fs->fs_private = (voidp) np;
66944626Smckusick 	fs->fs_prfree = (void (*)()) free;
67044626Smckusick 
67144626Smckusick 	if (!(fs->fs_flags & FSF_ERROR)) {
67244626Smckusick 		/*
67344626Smckusick 		 * Start of keepalive timer
67444626Smckusick 		 */
67544626Smckusick 		start_nfs_pings(fs, pingval);
67644626Smckusick 	}
67744626Smckusick 
67844626Smckusick 	/*
67944626Smckusick 	 * Add to list of servers
68044626Smckusick 	 */
68144626Smckusick 	ins_que(&fs->fs_q, &nfs_srvr_list);
68244626Smckusick 
68344626Smckusick 	return fs;
68444626Smckusick }
685