home *** CD-ROM | disk | FTP | other *** search
- /*
- * $Id: srvr_nfs.c,v 5.1.1.2 90/01/11 17:21:08 jsp Exp Locker: jsp $
- *
- * Copyright (c) 1990 Jan-Simon Pendry
- * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
- * Copyright (c) 1990 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 are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Imperial College of Science, Technology and Medicine, London, UK.
- * The names of the College and University may not be used to endorse
- * or promote products derived from this software without specific
- * prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- *
- * %W% (Berkeley) %G%
- */
-
- /*
- * 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 */
- int np_ping; /* Number of failed ping attempts */
- 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)
- /*
- * How often to ping when starting a new server
- */
- #define FAST_NFS_PING 3
-
- static int ping_len;
- static char ping_buf[sizeof(struct rpc_msg) + 32];
-
- /*
- * Startup the NFS ping
- */
- 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
- */
- static void got_portmap(pkt, len, sa, ia, idv, done)
- voidp pkt;
- int len;
- struct sockaddr_in *sa, *ia;
- voidp idv;
- int done;
- {
- fserver *fs2 = (fserver *) idv;
- fserver *fs = 0;
- 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
- /*
- * 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_error = 0;
- } else {
- #ifdef DEBUG
- dlog("Error fetching port for mountd on %s", fs->fs_host);
- #endif
- /*
- * 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
- } else {
- #ifdef DEBUG
- dlog("portmap request timed out");
- #endif
- }
- }
-
- /*
- * Obtain portmap information
- */
- 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*));
-
- /*
- * This is called when we get a reply to an RPC ping.
- * The value of id wass taken from the nfs_private
- * structure when the ping was transmitted.
- */
- /*ARGSUSED*/
- static void nfs_pinged(pkt, len, sp, tsp, idv, done)
- voidp pkt;
- int len;
- struct sockaddr_in *sp, *tsp;
- voidp idv;
- int done;
- {
- int xid = (int) idv;
- fserver *fs;
- int found_map = 0;
-
- 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) {
- plog(XLOG_INFO, "file server %s type nfs is up", fs->fs_host);
- } else {
- plog(XLOG_INFO, "file server %s type nfs starts up", fs->fs_host);
- fs->fs_flags |= FSF_VALID;
- }
- /*if (fs->fs_flags & FSF_WANT)
- wakeup_srvr(fs);*/
- } else {
- #ifdef DEBUG
- dlog("file server %s type nfs is still up", fs->fs_host);
- #endif
- }
-
- /*
- * Speed up the pings again
- */
- if (np->np_ping >= MAX_ALLOWED_PINGS) {
- untimeout(fs->fs_cid);
- fs->fs_cid = timeout(fs->fs_pinger, nfs_keepalive, (voidp) fs);
- }
-
- /*
- * 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_error < 0) {
- if (!nfs_auth)
- nfs_auth = authunix_create_default();
- if (!nfs_auth)
- np->np_error = ENOBUFS;
- else
- call_portmap(fs, nfs_auth, MOUNTPROG,
- MOUNTVERS, (unsigned long) IPPROTO_UDP);
- }
- found_map++;
- break;
- }
- }
-
- #ifdef DEBUG
- if (found_map == 0)
- dlog("Spurious ping packet");
- #endif
- }
-
-
- /*
- * Keep track of whether a server is alive
- */
- static void nfs_keepalive(fs)
- fserver *fs;
- {
- int error;
- nfs_private *np = (nfs_private *) fs->fs_private;
- int fstimeo;
-
- /*
- * 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 */
- break;
-
- case 0:
- #ifdef DEBUG
- dlog("Sent NFS ping to %s", fs->fs_host);
- #endif
- break;
- }
-
- /*
- * If N pings have failed then guess that it is dead
- */
- if (np->np_ping >= MAX_ALLOWED_PINGS) {
- if (!(fs->fs_flags & FSF_VALID)) {
- /*
- * Starts off down
- */
- plog(XLOG_INFO, "file server %s type nfs starts down", fs->fs_host);
- fs->fs_flags |= FSF_VALID;
- if (fs->fs_flags & FSF_WANT)
- wakeup_srvr(fs);
- }
-
- if ((fs->fs_flags & FSF_DOWN) == 0) {
- /*
- * Server was up, but is now down.
- */
- plog(XLOG_INFO, "file server %s type nfs is down", fs->fs_host);
- fs->fs_flags |= FSF_DOWN;
- if (fs->fs_flags & FSF_WANT)
- wakeup_srvr(fs);
- /*
- * Since the server is down, the portmap
- * information may now be wrong, so it
- * must be flushed from the local cache
- */
- flush_fhandle_cache(fs);
- np->np_error = -1;
- np->np_ping = 1;
- }
- } else {
- np->np_ping++;
- #ifdef DEBUG
- if (np->np_ping > 1)
- dlog("%d pings to %s failed - max %d allowed", np->np_ping, fs->fs_host, MAX_ALLOWED_PINGS);
- #endif
- }
-
- /*
- * 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 */
- fstimeo = fs->fs_pinger;
- break;
- case FSF_VALID|FSF_DOWN: /* Down */
- fstimeo = np->np_ping * fs->fs_pinger;
- break;
- default: /* Unknown */
- fstimeo = FAST_NFS_PING;
- break;
- }
- fs->fs_cid = timeout(fstimeo, nfs_keepalive, (voidp) fs);
- }
-
- 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;
- /*
- * Now go get it again in case it changed
- */
- np->np_error = -1;
- error = 0;
- } else {
- error = np->np_error;
- }
- } 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(fs)
- fserver *fs;
- {
- if (!(fs->fs_flags & FSF_PINGING)) {
- fs->fs_flags |= FSF_PINGING;
- if (fs->fs_cid)
- untimeout(fs->fs_cid);
- nfs_keepalive(fs);
- } else {
- #ifdef DEBUG
- dlog("Already running pings to %s", fs->fs_host);
- #endif
- }
- }
-
- /*
- * Find an nfs server for a host.
- */
- 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;
-
- top:
- /*
- * Scan the list of known servers looking
- * for one with the same name
- */
- ITER(fs, fserver, &nfs_srvr_list) {
- if (STREQ(host, fs->fs_host)) {
- start_nfs_pings(fs);
- fs->fs_refc++;
- return fs;
- }
- }
-
- /*
- * If the name is not known, it may be
- * because it was an alternate name for
- * the same machine. So do a lookup and
- * try again with the primary name if that
- * is different.
- * All that assuming it wasn't normalized
- * earlier of course...
- */
- if (hp == 0) {
- hp = gethostbyname(host);
- if (hp && !STREQ(host, hp->h_name) && !normalize_hosts) {
- host = hp->h_name;
- goto top;
- }
- }
-
- /*
- * 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;
- ip->sin_addr = *(struct in_addr *) hp->h_addr;
- ip->sin_port = htons(NFS_PORT);
- break;
-
- default:
- ip = 0;
- break;
- }
- } else {
- ip = 0;
- }
-
- /*
- * Allocate a new server
- */
- fs = ALLOC(fserver);
- fs->fs_refc = 1;
- fs->fs_host = strdup(hp ? hp->h_name : "unknown_hostname");
- 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_pinger = AM_PINGER;
- np = ALLOC(nfs_private);
- np->np_xid = NPXID_ALLOC();
- bzero((voidp) np, sizeof(*np));
- np->np_error = -1;
- fs->fs_private = (voidp) np;
- fs->fs_prfree = free;
-
- if (!(fs->fs_flags & FSF_ERROR)) {
- /*
- * Start of keepalive timer
- */
- start_nfs_pings(fs);
- }
-
- /*
- * Add to list of servers
- */
- ins_que(&fs->fs_q, &nfs_srvr_list);
-
- return fs;
- }
-