diff options
Diffstat (limited to 'usr.sbin/amd/amd/srvr_nfs.c')
-rw-r--r-- | usr.sbin/amd/amd/srvr_nfs.c | 719 |
1 files changed, 719 insertions, 0 deletions
diff --git a/usr.sbin/amd/amd/srvr_nfs.c b/usr.sbin/amd/amd/srvr_nfs.c new file mode 100644 index 0000000..4987c95 --- /dev/null +++ b/usr.sbin/amd/amd/srvr_nfs.c @@ -0,0 +1,719 @@ +/* + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)srvr_nfs.c 8.1 (Berkeley) 6/6/93 + * + * $Id: srvr_nfs.c,v 5.2.2.1 1992/02/09 15:09:06 jsp beta $ + * + */ + +/* + * NFS server modeling + */ + +#include "am.h" +#include <netdb.h> +#include <rpc/pmap_prot.h> +#include "mount.h" + +extern qelem nfs_srvr_list; +qelem nfs_srvr_list = { &nfs_srvr_list, &nfs_srvr_list }; + +typedef struct nfs_private { + u_short np_mountd; /* Mount daemon port number */ + char np_mountd_inval; /* Port *may* be invalid */ + int np_ping; /* Number of failed ping attempts */ + time_t np_ttl; /* Time when server is thought dead */ + int np_xid; /* RPC transaction id for pings */ + int np_error; /* Error during portmap request */ +} nfs_private; + +static int np_xid; /* For NFS pings */ +#define NPXID_ALLOC() (++np_xid) +/*#define NPXID_ALLOC() ((++np_xid&0x0fffffff) == 0 ? npxid_gc() : np_xid)*/ + +/* + * Number of pings allowed to fail before host is declared down + * - three-fifths of the allowed mount time... +#define MAX_ALLOWED_PINGS ((((ALLOWED_MOUNT_TIME + 5 * AM_PINGER - 1) * 3) / 5) / AM_PINGER) + */ +#define MAX_ALLOWED_PINGS (3 + /* for luck ... */ 1) + +/* + * How often to ping when starting a new server + */ +#define FAST_NFS_PING 3 + +#if (FAST_NFS_PING * MAX_ALLOWED_PINGS) >= ALLOWED_MOUNT_TIME + #error: sanity check failed +/* + you cannot do things this way... + sufficient fast pings must be given the chance to fail + within the allowed mount time + */ +#endif /* (FAST_NFS_PING * MAX_ALLOWED_PINGS) >= ALLOWED_MOUNT_TIME */ + +static int ping_len; +static char ping_buf[sizeof(struct rpc_msg) + 32]; + +/* + * Flush any cached data + */ +void flush_srvr_nfs_cache P((void)); +void flush_srvr_nfs_cache() +{ + fserver *fs = 0; + + ITER(fs, fserver, &nfs_srvr_list) { + nfs_private *np = (nfs_private *) fs->fs_private; + if (np) { + np->np_mountd_inval = TRUE; + np->np_error = -1; + } + } +} + +/* + * Startup the NFS ping + */ +static void start_ping(P_void); +static void start_ping() +{ + XDR ping_xdr; + struct rpc_msg ping_msg; + + rpc_msg_init(&ping_msg, NFS_PROGRAM, NFS_VERSION, NFSPROC_NULL); + + /* + * Create an XDR endpoint + */ + xdrmem_create(&ping_xdr, ping_buf, sizeof(ping_buf), XDR_ENCODE); + + /* + * Create the NFS ping message + */ + if (!xdr_callmsg(&ping_xdr, &ping_msg)) { + plog(XLOG_ERROR, "Couldn't create ping RPC message"); + going_down(3); + } + + /* + * Find out how long it is + */ + ping_len = xdr_getpos(&ping_xdr); + + /* + * Destroy the XDR endpoint - we don't need it anymore + */ + xdr_destroy(&ping_xdr); +} + + +/* + * Called when a portmap reply arrives + */ +/*ARGSUSED*/ +static void got_portmap P((voidp pkt, int len, struct sockaddr_in *sa, struct sockaddr_in *ia, voidp idv, int done)); +static void got_portmap(pkt, len, sa, ia, idv, done) +voidp pkt; +int len; +struct sockaddr_in *sa; +struct sockaddr_in *ia; +voidp idv; +int done; +{ + fserver *fs2 = (fserver *) idv; + fserver *fs = 0; + + /* + * Find which fileserver we are talking about + */ + ITER(fs, fserver, &nfs_srvr_list) + if (fs == fs2) + break; + + if (fs == fs2) { + u_long port = 0; /* XXX - should be short but protocol is naff */ + int error = done ? pickup_rpc_reply(pkt, len, (voidp) &port, xdr_u_long) : -1; + nfs_private *np = (nfs_private *) fs->fs_private; + if (!error && port) { +#ifdef DEBUG + dlog("got port (%d) for mountd on %s", port, fs->fs_host); +#endif /* DEBUG */ + /* + * Grab the port number. Portmap sends back + * an unsigned long in native ordering, so it + * needs converting to a unsigned short in + * network ordering. + */ + np->np_mountd = htons((u_short) port); + np->np_mountd_inval = FALSE; + np->np_error = 0; + } else { +#ifdef DEBUG + dlog("Error fetching port for mountd on %s", fs->fs_host); +#endif /* DEBUG */ + /* + * Almost certainly no mountd running on remote host + */ + np->np_error = error ? error : ETIMEDOUT; + } + if (fs->fs_flags & FSF_WANT) + wakeup_srvr(fs); + } else if (done) { +#ifdef DEBUG + dlog("Got portmap for old port request"); +#endif /* DEBUG */ + } else { +#ifdef DEBUG + dlog("portmap request timed out"); +#endif /* DEBUG */ + } +} + +/* + * Obtain portmap information + */ +static int call_portmap P((fserver *fs, AUTH *auth, unsigned long prog, unsigned long vers, unsigned long prot)); +static int call_portmap(fs, auth, prog, vers, prot) +fserver *fs; +AUTH *auth; +unsigned long prog, vers, prot; +{ + struct rpc_msg pmap_msg; + int len; + char iobuf[UDPMSGSIZE]; + int error; + struct pmap pmap; + + rpc_msg_init(&pmap_msg, PMAPPROG, PMAPVERS, (unsigned long) 0); + pmap.pm_prog = prog; + pmap.pm_vers = vers; + pmap.pm_prot = prot; + pmap.pm_port = 0; + len = make_rpc_packet(iobuf, sizeof(iobuf), PMAPPROC_GETPORT, + &pmap_msg, (voidp) &pmap, xdr_pmap, auth); + if (len > 0) { + struct sockaddr_in sin; + bzero((voidp) &sin, sizeof(sin)); + sin = *fs->fs_ip; + sin.sin_port = htons(PMAPPORT); + error = fwd_packet(RPC_XID_PORTMAP, (voidp) iobuf, len, + &sin, &sin, (voidp) fs, got_portmap); + } else { + error = -len; + } + return error; +} + +static void nfs_keepalive P((fserver*)); + +static void recompute_portmap P((fserver *fs)); +static void recompute_portmap(fs) +fserver *fs; +{ + int error; + + if (nfs_auth) + error = 0; + else + error = make_nfs_auth(); + + if (error) { + nfs_private *np = (nfs_private *) fs->fs_private; + np->np_error = error; + } else { + call_portmap(fs, nfs_auth, MOUNTPROG, + MOUNTVERS, (unsigned long) IPPROTO_UDP); + } +} + +/* + * This is called when we get a reply to an RPC ping. + * The value of id was taken from the nfs_private + * structure when the ping was transmitted. + */ +/*ARGSUSED*/ +static void nfs_pinged P((voidp pkt, int len, struct sockaddr_in *sp, struct sockaddr_in *tsp, voidp idv, int done)); +static void nfs_pinged(pkt, len, sp, tsp, idv, done) +voidp pkt; +int len; +struct sockaddr_in *sp; +struct sockaddr_in *tsp; +voidp idv; +int done; +{ + int xid = (int) idv; + fserver *fs; +#ifdef DEBUG + int found_map = 0; +#endif /* DEBUG */ + + if (!done) + return; + + /* + * For each node... + */ + ITER(fs, fserver, &nfs_srvr_list) { + nfs_private *np = (nfs_private *) fs->fs_private; + if (np->np_xid == xid) { + /* + * Reset the ping counter. + * Update the keepalive timer. + * Log what happened. + */ + if (fs->fs_flags & FSF_DOWN) { + fs->fs_flags &= ~FSF_DOWN; + if (fs->fs_flags & FSF_VALID) { + srvrlog(fs, "is up"); + } else { + if (np->np_ping > 1) + srvrlog(fs, "ok"); +#ifdef DEBUG + else + srvrlog(fs, "starts up"); +#endif + fs->fs_flags |= FSF_VALID; + } + +#ifdef notdef + /* why ??? */ + if (fs->fs_flags & FSF_WANT) + wakeup_srvr(fs); +#endif /* notdef */ + map_flush_srvr(fs); + } else { + if (fs->fs_flags & FSF_VALID) { +#ifdef DEBUG + dlog("file server %s type nfs is still up", fs->fs_host); +#endif /* DEBUG */ + } else { + if (np->np_ping > 1) + srvrlog(fs, "ok"); + fs->fs_flags |= FSF_VALID; + } + } + + /* + * Adjust ping interval + */ + untimeout(fs->fs_cid); + fs->fs_cid = timeout(fs->fs_pinger, nfs_keepalive, (voidp) fs); + + /* + * Update ttl for this server + */ + np->np_ttl = clocktime() + + (MAX_ALLOWED_PINGS - 1) * FAST_NFS_PING + fs->fs_pinger - 1; + + /* + * New RPC xid... + */ + np->np_xid = NPXID_ALLOC(); + + /* + * Failed pings is zero... + */ + np->np_ping = 0; + + /* + * Recompute portmap information if not known + */ + if (np->np_mountd_inval) + recompute_portmap(fs); + +#ifdef DEBUG + found_map++; +#endif /* DEBUG */ + break; + } + } + +#ifdef DEBUG + if (found_map == 0) + dlog("Spurious ping packet"); +#endif /* DEBUG */ +} + +/* + * Called when no ping-reply received + */ +static void nfs_timed_out P((fserver *fs)); +static void nfs_timed_out(fs) +fserver *fs; +{ + nfs_private *np = (nfs_private *) fs->fs_private; + + /* + * Another ping has failed + */ + np->np_ping++; + + /* + * Not known to be up any longer + */ + if (FSRV_ISUP(fs)) { + fs->fs_flags &= ~FSF_VALID; + if (np->np_ping > 1) + srvrlog(fs, "not responding"); + } + + /* + * If ttl has expired then guess that it is dead + */ + if (np->np_ttl < clocktime()) { + int oflags = fs->fs_flags; + if ((fs->fs_flags & FSF_DOWN) == 0) { + /* + * Server was up, but is now down. + */ + srvrlog(fs, "is down"); + fs->fs_flags |= FSF_DOWN|FSF_VALID; + /* + * Since the server is down, the portmap + * information may now be wrong, so it + * must be flushed from the local cache + */ + flush_nfs_fhandle_cache(fs); + np->np_error = -1; +#ifdef notdef + /* + * Pretend just one ping has failed now + */ + np->np_ping = 1; +#endif + } else { + /* + * Known to be down + */ +#ifdef DEBUG + if ((fs->fs_flags & FSF_VALID) == 0) + srvrlog(fs, "starts down"); +#endif + fs->fs_flags |= FSF_VALID; + } + if (oflags != fs->fs_flags && (fs->fs_flags & FSF_WANT)) + wakeup_srvr(fs); + } else { +#ifdef DEBUG + if (np->np_ping > 1) + dlog("%d pings to %s failed - at most %d allowed", np->np_ping, fs->fs_host, MAX_ALLOWED_PINGS); +#endif /* DEBUG */ + } + + /* + * Run keepalive again + */ + nfs_keepalive(fs); +} + +/* + * Keep track of whether a server is alive + */ +static void nfs_keepalive P((fserver *fs)); +static void nfs_keepalive(fs) +fserver *fs; +{ + int error; + nfs_private *np = (nfs_private *) fs->fs_private; + int fstimeo = -1; + + /* + * Send an NFS ping to this node + */ + + if (ping_len == 0) + start_ping(); + + /* + * Queue the packet... + */ + error = fwd_packet(MK_RPC_XID(RPC_XID_NFSPING, np->np_xid), (voidp) ping_buf, + ping_len, fs->fs_ip, (struct sockaddr_in *) 0, (voidp) np->np_xid, nfs_pinged); + + /* + * See if a hard error occured + */ + switch (error) { + case ENETDOWN: + case ENETUNREACH: + case EHOSTDOWN: + case EHOSTUNREACH: + np->np_ping = MAX_ALLOWED_PINGS; /* immediately down */ + np->np_ttl = (time_t) 0; + /* + * This causes an immediate call to nfs_timed_out + * whenever the server was thought to be up. + * See +++ below. + */ + fstimeo = 0; + break; + + case 0: +#ifdef DEBUG + dlog("Sent NFS ping to %s", fs->fs_host); +#endif /* DEBUG */ + break; + } + +#ifdef DEBUG + /*dlog("keepalive, ping = %d", np->np_ping);*/ +#endif /* DEBUG */ + + /* + * Back off the ping interval if we are not getting replies and + * the remote system is know to be down. + */ + switch (fs->fs_flags & (FSF_DOWN|FSF_VALID)) { + case FSF_VALID: /* Up */ + if (fstimeo < 0) /* +++ see above */ + fstimeo = FAST_NFS_PING; + break; + + case FSF_VALID|FSF_DOWN: /* Down */ + fstimeo = fs->fs_pinger; + break; + + default: /* Unknown */ + fstimeo = FAST_NFS_PING; + break; + } + +#ifdef DEBUG + dlog("NFS timeout in %d seconds", fstimeo); +#endif /* DEBUG */ + + fs->fs_cid = timeout(fstimeo, nfs_timed_out, (voidp) fs); +} + +int nfs_srvr_port P((fserver *fs, u_short *port, voidp wchan)); +int nfs_srvr_port(fs, port, wchan) +fserver *fs; +u_short *port; +voidp wchan; +{ + int error = -1; + if ((fs->fs_flags & FSF_VALID) == FSF_VALID) { + if ((fs->fs_flags & FSF_DOWN) == 0) { + nfs_private *np = (nfs_private *) fs->fs_private; + if (np->np_error == 0) { + *port = np->np_mountd; + error = 0; + } else { + error = np->np_error; + } + /* + * Now go get the port mapping again in case it changed. + * Note that it is used even if (np_mountd_inval) + * is True. The flag is used simply as an + * indication that the mountd may be invalid, not + * that it is known to be invalid. + */ + if (np->np_mountd_inval) + recompute_portmap(fs); + else + np->np_mountd_inval = TRUE; + } else { + error = EWOULDBLOCK; + } + } + if (error < 0 && wchan && !(fs->fs_flags & FSF_WANT)) { + /* + * If a wait channel is supplied, and no + * error has yet occured, then arrange + * that a wakeup is done on the wait channel, + * whenever a wakeup is done on this fs node. + * Wakeup's are done on the fs node whenever + * it changes state - thus causing control to + * come back here and new, better things to happen. + */ + fs->fs_flags |= FSF_WANT; + sched_task(wakeup_task, wchan, (voidp) fs); + } + return error; +} + +static void start_nfs_pings P((fserver *fs, int pingval)); +static void start_nfs_pings(fs, pingval) +fserver *fs; +int pingval; +{ + if (!(fs->fs_flags & FSF_PINGING)) { + fs->fs_flags |= FSF_PINGING; + if (fs->fs_cid) + untimeout(fs->fs_cid); + if (pingval < 0) { + srvrlog(fs, "wired up"); + fs->fs_flags |= FSF_VALID; + fs->fs_flags &= ~FSF_DOWN; + } else { + nfs_keepalive(fs); + } + } else { +#ifdef DEBUG + dlog("Already running pings to %s", fs->fs_host); +#endif /* DEBUG */ + } +} + +/* + * Find an nfs server for a host. + */ +fserver *find_nfs_srvr P((mntfs *mf)); +fserver *find_nfs_srvr(mf) +mntfs *mf; +{ + fserver *fs; + struct hostent *hp = 0; + char *host = mf->mf_fo->opt_rhost; + struct sockaddr_in *ip; + nfs_private *np; + int pingval; + + /* + * Get ping interval from mount options. + * Current only used to decide whether pings + * are required or not. < 0 = no pings. + */ + { struct mntent mnt; + mnt.mnt_opts = mf->mf_mopts; + pingval = hasmntval(&mnt, "ping"); +#ifdef HAS_TCP_NFS + /* + * Over TCP mount, don't bother to do pings. + * This is experimental - maybe you want to + * do pings anyway... + */ + if (pingval == 0 && hasmntopt(&mnt, "tcp")) + pingval = -1; +#endif /* HAS_TCP_NFS */ + } + + + /* + * lookup host address and canonical name + */ + hp = gethostbyname(host); + + /* + * New code from Bob Harris <harris@basil-rathbone.mit.edu> + * Use canonical name to keep track of file server + * information. This way aliases do not generate + * multiple NFS pingers. (Except when we're normalizing + * hosts.) + */ + if (hp && !normalize_hosts) host = hp->h_name; + + ITER(fs, fserver, &nfs_srvr_list) { + if (STREQ(host, fs->fs_host)) { + start_nfs_pings(fs, pingval); + fs->fs_refc++; + return fs; + } + } + + + + /* + * Get here if we can't find an entry + */ + if (hp) { + switch (hp->h_addrtype) { + case AF_INET: + ip = ALLOC(sockaddr_in); + bzero((voidp) ip, sizeof(*ip)); + ip->sin_family = AF_INET; + bcopy((voidp) hp->h_addr, (voidp) &ip->sin_addr, sizeof(ip->sin_addr)); + + ip->sin_port = htons(NFS_PORT); + break; + + default: + ip = 0; + break; + } + } else { + plog(XLOG_USER, "Unknown host: %s", host); + ip = 0; + } + + /* + * Allocate a new server + */ + fs = ALLOC(fserver); + fs->fs_refc = 1; + fs->fs_host = strdup(hp ? hp->h_name : "unknown_hostname"); + if (normalize_hosts) host_normalize(&fs->fs_host); + fs->fs_ip = ip; + fs->fs_cid = 0; + if (ip) { + fs->fs_flags = FSF_DOWN; /* Starts off down */ + } else { + fs->fs_flags = FSF_ERROR|FSF_VALID; + mf->mf_flags |= MFF_ERROR; + mf->mf_error = ENOENT; + } + fs->fs_type = "nfs"; + fs->fs_pinger = AM_PINGER; + np = ALLOC(nfs_private); + bzero((voidp) np, sizeof(*np)); + np->np_mountd_inval = TRUE; + np->np_xid = NPXID_ALLOC(); + np->np_error = -1; + /* + * Initially the server will be deemed dead after + * MAX_ALLOWED_PINGS of the fast variety have failed. + */ + np->np_ttl = clocktime() + MAX_ALLOWED_PINGS * FAST_NFS_PING - 1; + fs->fs_private = (voidp) np; + fs->fs_prfree = (void (*)()) free; + + if (!(fs->fs_flags & FSF_ERROR)) { + /* + * Start of keepalive timer + */ + start_nfs_pings(fs, pingval); + } + + /* + * Add to list of servers + */ + ins_que(&fs->fs_q, &nfs_srvr_list); + + return fs; +} |