Home | History | Annotate | Line # | Download | only in amd
srvr_nfs.c revision 1.1.1.3
      1 /*	$NetBSD: srvr_nfs.c,v 1.1.1.3 2015/01/17 16:34:15 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1997-2014 Erez Zadok
      5  * Copyright (c) 1990 Jan-Simon Pendry
      6  * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
      7  * Copyright (c) 1990 The Regents of the University of California.
      8  * All rights reserved.
      9  *
     10  * This code is derived from software contributed to Berkeley by
     11  * Jan-Simon Pendry at Imperial College, London.
     12  *
     13  * Redistribution and use in source and binary forms, with or without
     14  * modification, are permitted provided that the following conditions
     15  * are met:
     16  * 1. Redistributions of source code must retain the above copyright
     17  *    notice, this list of conditions and the following disclaimer.
     18  * 2. Redistributions in binary form must reproduce the above copyright
     19  *    notice, this list of conditions and the following disclaimer in the
     20  *    documentation and/or other materials provided with the distribution.
     21  * 3. Neither the name of the University nor the names of its contributors
     22  *    may be used to endorse or promote products derived from this software
     23  *    without specific prior written permission.
     24  *
     25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     35  * SUCH DAMAGE.
     36  *
     37  *
     38  * File: am-utils/amd/srvr_nfs.c
     39  *
     40  */
     41 
     42 /*
     43  * NFS server modeling
     44  */
     45 
     46 #ifdef HAVE_CONFIG_H
     47 # include <config.h>
     48 #endif /* HAVE_CONFIG_H */
     49 #include <am_defs.h>
     50 #include <amd.h>
     51 
     52 /*
     53  * Number of pings allowed to fail before host is declared down
     54  * - three-fifths of the allowed mount time...
     55  */
     56 #define	MAX_ALLOWED_PINGS	(3 + /* for luck ... */ 1)
     57 
     58 /*
     59  * How often to ping when starting a new server
     60  */
     61 #define	FAST_NFS_PING		3
     62 
     63 #if (FAST_NFS_PING * MAX_ALLOWED_PINGS) >= ALLOWED_MOUNT_TIME
     64 # error: sanity check failed in srvr_nfs.c
     65 /*
     66  * you cannot do things this way...
     67  * sufficient fast pings must be given the chance to fail
     68  * within the allowed mount time
     69  */
     70 #endif /* (FAST_NFS_PING * MAX_ALLOWED_PINGS) >= ALLOWED_MOUNT_TIME */
     71 
     72 /* structures and typedefs */
     73 typedef struct nfs_private {
     74   u_short np_mountd;		/* Mount daemon port number */
     75   char np_mountd_inval;		/* Port *may* be invalid */
     76   				/* 'Y' invalid, 'N' valid, 'P' permanent */
     77   int np_ping;			/* Number of failed ping attempts */
     78   time_t np_ttl;		/* Time when server is thought dead */
     79   int np_xid;			/* RPC transaction id for pings */
     80   int np_error;			/* Error during portmap request */
     81 } nfs_private;
     82 
     83 /* globals */
     84 qelem nfs_srvr_list = {&nfs_srvr_list, &nfs_srvr_list};
     85 
     86 /* statics */
     87 static int global_xid;		/* For NFS pings */
     88 #define	XID_ALLOC()		(++global_xid)
     89 
     90 #if defined(HAVE_FS_NFS4)
     91 # define NUM_NFS_VERS 3
     92 #elif defined(HAVE_FS_NFS3)
     93 # define NUM_NFS_VERS 2
     94 #else  /* not HAVE_FS_NFS3 */
     95 # define NUM_NFS_VERS 1
     96 #endif /* not HAVE_FS_NFS3 */
     97 static int ping_len[NUM_NFS_VERS];
     98 static char ping_buf[NUM_NFS_VERS][sizeof(struct rpc_msg) + 32];
     99 
    100 #if defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3)
    101 /*
    102  * Protocols we know about, in order of preference.
    103  *
    104  * Note that Solaris 8 and newer NetBSD systems are switching to UDP first,
    105  * so this order may have to be adjusted for Amd in the future once more
    106  * vendors make that change. -Erez 11/24/2000
    107  *
    108  * Or we might simply make this is a platform-specific order. -Ion 09/13/2003
    109  */
    110 static char *protocols[] = { "tcp", "udp", NULL };
    111 #endif /* defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3) */
    112 
    113 /* forward definitions */
    114 static void nfs_keepalive(voidp);
    115 
    116 
    117 /*
    118  * Flush cached data for an fserver (or for all, if fs==NULL)
    119  */
    120 void
    121 flush_srvr_nfs_cache(fserver *fs)
    122 {
    123   fserver *fs2 = NULL;
    124 
    125   ITER(fs2, fserver, &nfs_srvr_list) {
    126     if (fs == NULL || fs == fs2) {
    127       nfs_private *np = (nfs_private *) fs2->fs_private;
    128       if (np && np->np_mountd_inval != 'P') {
    129 	np->np_mountd_inval = 'Y';
    130 	np->np_error = -1;
    131       }
    132     }
    133   }
    134 }
    135 
    136 
    137 /*
    138  * Startup the NFS ping for a particular version.
    139  */
    140 static void
    141 create_ping_payload(u_long nfs_version)
    142 {
    143   XDR ping_xdr;
    144   struct rpc_msg ping_msg;
    145 
    146   /*
    147    * Non nfs mounts like /afs/glue.umd.edu have ended up here.
    148    */
    149   if (nfs_version == 0) {
    150     nfs_version = NFS_VERSION;
    151     plog(XLOG_WARNING, "%s: nfs_version = 0, changed to 2", __func__);
    152   } else
    153     plog(XLOG_INFO, "%s: nfs_version: %d", __func__, (int) nfs_version);
    154 
    155   rpc_msg_init(&ping_msg, NFS_PROGRAM, nfs_version, NFSPROC_NULL);
    156 
    157   /*
    158    * Create an XDR endpoint
    159    */
    160   xdrmem_create(&ping_xdr, ping_buf[nfs_version - NFS_VERSION], sizeof(ping_buf[0]), XDR_ENCODE);
    161 
    162   /*
    163    * Create the NFS ping message
    164    */
    165   if (!xdr_callmsg(&ping_xdr, &ping_msg)) {
    166     plog(XLOG_ERROR, "Couldn't create ping RPC message");
    167     going_down(3);
    168     return;
    169   }
    170   /*
    171    * Find out how long it is
    172    */
    173   ping_len[nfs_version - NFS_VERSION] = xdr_getpos(&ping_xdr);
    174 
    175   /*
    176    * Destroy the XDR endpoint - we don't need it anymore
    177    */
    178   xdr_destroy(&ping_xdr);
    179 }
    180 
    181 
    182 /*
    183  * Called when a portmap reply arrives
    184  */
    185 static void
    186 got_portmap(voidp pkt, int len, struct sockaddr_in *sa, struct sockaddr_in *ia, voidp idv, int done)
    187 {
    188   fserver *fs2 = (fserver *) idv;
    189   fserver *fs = NULL;
    190 
    191   /*
    192    * Find which fileserver we are talking about
    193    */
    194   ITER(fs, fserver, &nfs_srvr_list)
    195     if (fs == fs2)
    196       break;
    197 
    198   if (fs == fs2) {
    199     u_long port = 0;	/* XXX - should be short but protocol is naff */
    200     int error = done ? pickup_rpc_reply(pkt, len, (voidp) &port, (XDRPROC_T_TYPE) xdr_u_long) : -1;
    201     nfs_private *np = (nfs_private *) fs->fs_private;
    202 
    203     if (!error && port) {
    204       dlog("got port (%d) for mountd on %s", (int) port, fs->fs_host);
    205       /*
    206        * Grab the port number.  Portmap sends back
    207        * an u_long in native ordering, so it
    208        * needs converting to a u_short in
    209        * network ordering.
    210        */
    211       np->np_mountd = htons((u_short) port);
    212       np->np_mountd_inval = 'N';
    213       np->np_error = 0;
    214     } else {
    215       dlog("Error fetching port for mountd on %s", fs->fs_host);
    216       dlog("\t error=%d, port=%d", error, (int) port);
    217       /*
    218        * Almost certainly no mountd running on remote host
    219        */
    220       np->np_error = error ? error : ETIMEDOUT;
    221     }
    222 
    223     if (fs->fs_flags & FSF_WANT)
    224       wakeup_srvr(fs);
    225   } else if (done) {
    226     dlog("Got portmap for old port request");
    227   } else {
    228     dlog("portmap request timed out");
    229   }
    230 }
    231 
    232 
    233 /*
    234  * Obtain portmap information
    235  */
    236 static int
    237 call_portmap(fserver *fs, AUTH *auth, u_long prog, u_long vers, u_long prot)
    238 {
    239   struct rpc_msg pmap_msg;
    240   int len;
    241   char iobuf[UDPMSGSIZE];
    242   int error;
    243   struct pmap pmap;
    244 
    245   rpc_msg_init(&pmap_msg, PMAPPROG, PMAPVERS, PMAPPROC_NULL);
    246   pmap.pm_prog = prog;
    247   pmap.pm_vers = vers;
    248   pmap.pm_prot = prot;
    249   pmap.pm_port = 0;
    250   len = make_rpc_packet(iobuf,
    251 			sizeof(iobuf),
    252 			PMAPPROC_GETPORT,
    253 			&pmap_msg,
    254 			(voidp) &pmap,
    255 			(XDRPROC_T_TYPE) xdr_pmap,
    256 			auth);
    257   if (len > 0) {
    258     struct sockaddr_in sin;
    259     memset((voidp) &sin, 0, sizeof(sin));
    260     sin = *fs->fs_ip;
    261     sin.sin_port = htons(PMAPPORT);
    262     error = fwd_packet(RPC_XID_PORTMAP, iobuf, len,
    263 		       &sin, &sin, (voidp) fs, got_portmap);
    264   } else {
    265     error = -len;
    266   }
    267 
    268   return error;
    269 }
    270 
    271 
    272 static void
    273 recompute_portmap(fserver *fs)
    274 {
    275   int error;
    276   u_long mnt_version;
    277 
    278   /*
    279    * No portmap calls for pure WebNFS servers.
    280    */
    281   if (fs->fs_flags & FSF_WEBNFS)
    282     return;
    283 
    284   if (nfs_auth)
    285     error = 0;
    286   else
    287     error = make_nfs_auth();
    288 
    289   if (error) {
    290     nfs_private *np = (nfs_private *) fs->fs_private;
    291     np->np_error = error;
    292     return;
    293   }
    294 
    295   if (fs->fs_version == 0)
    296     plog(XLOG_WARNING, "%s: nfs_version = 0 fixed", __func__);
    297 
    298   plog(XLOG_INFO, "%s: NFS version %d on %s", __func__,
    299        (int) fs->fs_version, fs->fs_host);
    300 #ifdef HAVE_FS_NFS3
    301   if (fs->fs_version == NFS_VERSION3)
    302     mnt_version = AM_MOUNTVERS3;
    303   else
    304 #endif /* HAVE_FS_NFS3 */
    305     mnt_version = MOUNTVERS;
    306 
    307   plog(XLOG_INFO, "Using MOUNT version: %d", (int) mnt_version);
    308   call_portmap(fs, nfs_auth, MOUNTPROG, mnt_version, (u_long) IPPROTO_UDP);
    309 }
    310 
    311 
    312 int
    313 get_mountd_port(fserver *fs, u_short *port, wchan_t wchan)
    314 {
    315   int error = -1;
    316 
    317   if (FSRV_ISDOWN(fs))
    318     return EWOULDBLOCK;
    319 
    320   if (FSRV_ISUP(fs)) {
    321     nfs_private *np = (nfs_private *) fs->fs_private;
    322     if (np->np_error == 0) {
    323       *port = np->np_mountd;
    324       error = 0;
    325     } else {
    326       error = np->np_error;
    327     }
    328     /*
    329      * Now go get the port mapping again in case it changed.
    330      * Note that it is used even if (np_mountd_inval)
    331      * is True.  The flag is used simply as an
    332      * indication that the mountd may be invalid, not
    333      * that it is known to be invalid.
    334      */
    335     switch (np->np_mountd_inval) {
    336     case 'Y':
    337       recompute_portmap(fs);
    338       break;
    339     case 'N':
    340       np->np_mountd_inval = 'Y';
    341       break;
    342     case 'P':
    343       break;
    344     default:
    345       abort();
    346     }
    347   }
    348   if (error < 0 && wchan && !(fs->fs_flags & FSF_WANT)) {
    349     /*
    350      * If a wait channel is supplied, and no
    351      * error has yet occurred, then arrange
    352      * that a wakeup is done on the wait channel,
    353      * whenever a wakeup is done on this fs node.
    354      * Wakeup's are done on the fs node whenever
    355      * it changes state - thus causing control to
    356      * come back here and new, better things to happen.
    357      */
    358     fs->fs_flags |= FSF_WANT;
    359     sched_task(wakeup_task, wchan, (wchan_t) fs);
    360   }
    361   return error;
    362 }
    363 
    364 
    365 /*
    366  * This is called when we get a reply to an RPC ping.
    367  * The value of id was taken from the nfs_private
    368  * structure when the ping was transmitted.
    369  */
    370 static void
    371 nfs_keepalive_callback(voidp pkt, int len, struct sockaddr_in *sp, struct sockaddr_in *tsp, voidp idv, int done)
    372 {
    373   int xid = (long) idv;		/* cast needed for 64-bit archs */
    374   fserver *fs;
    375   int found_map = 0;
    376 
    377   if (!done)
    378     return;
    379 
    380   /*
    381    * For each node...
    382    */
    383   ITER(fs, fserver, &nfs_srvr_list) {
    384     nfs_private *np = (nfs_private *) fs->fs_private;
    385     if (np->np_xid == xid && (fs->fs_flags & FSF_PINGING)) {
    386       /*
    387        * Reset the ping counter.
    388        * Update the keepalive timer.
    389        * Log what happened.
    390        */
    391       if (fs->fs_flags & FSF_DOWN) {
    392 	fs->fs_flags &= ~FSF_DOWN;
    393 	if (fs->fs_flags & FSF_VALID) {
    394 	  srvrlog(fs, "is up");
    395 	} else {
    396 	  if (np->np_ping > 1)
    397 	    srvrlog(fs, "ok");
    398 	  else
    399 	    srvrlog(fs, "starts up");
    400 	  fs->fs_flags |= FSF_VALID;
    401 	}
    402 
    403 	map_flush_srvr(fs);
    404       } else {
    405 	if (fs->fs_flags & FSF_VALID) {
    406 	  dlog("file server %s type nfs is still up", fs->fs_host);
    407 	} else {
    408 	  if (np->np_ping > 1)
    409 	    srvrlog(fs, "ok");
    410 	  fs->fs_flags |= FSF_VALID;
    411 	}
    412       }
    413 
    414       /*
    415        * Adjust ping interval
    416        */
    417       untimeout(fs->fs_cid);
    418       fs->fs_cid = timeout(fs->fs_pinger, nfs_keepalive, (voidp) fs);
    419 
    420       /*
    421        * Update ttl for this server
    422        */
    423       np->np_ttl = clocktime(NULL) +
    424 	(MAX_ALLOWED_PINGS - 1) * FAST_NFS_PING + fs->fs_pinger - 1;
    425 
    426       /*
    427        * New RPC xid...
    428        */
    429       np->np_xid = XID_ALLOC();
    430 
    431       /*
    432        * Failed pings is zero...
    433        */
    434       np->np_ping = 0;
    435 
    436       /*
    437        * Recompute portmap information if not known
    438        */
    439       if (np->np_mountd_inval == 'Y')
    440 	recompute_portmap(fs);
    441 
    442       found_map++;
    443       break;
    444     }
    445   }
    446 
    447   if (found_map == 0)
    448     dlog("Spurious ping packet");
    449 }
    450 
    451 
    452 static void
    453 check_fs_addr_change(fserver *fs)
    454 {
    455   struct hostent *hp = NULL;
    456   struct in_addr ia;
    457   char *old_ipaddr, *new_ipaddr;
    458 
    459   hp = gethostbyname(fs->fs_host);
    460   if (!hp ||
    461       hp->h_addrtype != AF_INET ||
    462       !STREQ((char *) hp->h_name, fs->fs_host) ||
    463       memcmp((voidp) &fs->fs_ip->sin_addr,
    464 	     (voidp) hp->h_addr,
    465 	     sizeof(fs->fs_ip->sin_addr)) == 0)
    466     return;
    467   /* if got here: downed server changed IP address */
    468   old_ipaddr = xstrdup(inet_ntoa(fs->fs_ip->sin_addr));
    469   memmove((voidp) &ia, (voidp) hp->h_addr, sizeof(struct in_addr));
    470   new_ipaddr = inet_ntoa(ia);	/* ntoa uses static buf */
    471   plog(XLOG_WARNING, "EZK: down fileserver %s changed ip: %s -> %s",
    472        fs->fs_host, old_ipaddr, new_ipaddr);
    473   XFREE(old_ipaddr);
    474   /* copy new IP addr */
    475   memmove((voidp) &fs->fs_ip->sin_addr,
    476 	  (voidp) hp->h_addr,
    477 	  sizeof(fs->fs_ip->sin_addr));
    478   /* XXX: do we need to un/set these flags? */
    479   fs->fs_flags &= ~FSF_DOWN;
    480   fs->fs_flags |= FSF_VALID | FSF_WANT;
    481   map_flush_srvr(fs);		/* XXX: a race with flush_srvr_nfs_cache? */
    482   flush_srvr_nfs_cache(fs);
    483   fs->fs_flags |= FSF_FORCE_UNMOUNT;
    484 
    485 #if 0
    486   flush_nfs_fhandle_cache(fs);	/* done in caller: nfs_keepalive_timeout */
    487   /* XXX: need to purge nfs_private so that somehow it will get re-initialized? */
    488 #endif /* 0 */
    489 }
    490 
    491 
    492 /*
    493  * Called when no ping-reply received
    494  */
    495 static void
    496 nfs_keepalive_timeout(voidp v)
    497 {
    498   fserver *fs = v;
    499   nfs_private *np = (nfs_private *) fs->fs_private;
    500 
    501   /*
    502    * Another ping has failed
    503    */
    504   np->np_ping++;
    505   if (np->np_ping > 1)
    506     srvrlog(fs, "not responding");
    507 
    508   /*
    509    * Not known to be up any longer
    510    */
    511   if (FSRV_ISUP(fs))
    512     fs->fs_flags &= ~FSF_VALID;
    513 
    514   /*
    515    * If ttl has expired then guess that it is dead
    516    */
    517   if (np->np_ttl < clocktime(NULL)) {
    518     int oflags = fs->fs_flags;
    519     dlog("ttl has expired");
    520     if ((fs->fs_flags & FSF_DOWN) == 0) {
    521       /*
    522        * Server was up, but is now down.
    523        */
    524       srvrlog(fs, "is down");
    525       fs->fs_flags |= FSF_DOWN | FSF_VALID;
    526       /*
    527        * Since the server is down, the portmap
    528        * information may now be wrong, so it
    529        * must be flushed from the local cache
    530        */
    531       flush_nfs_fhandle_cache(fs);
    532       np->np_error = -1;
    533       check_fs_addr_change(fs); /* check if IP addr of fserver changed */
    534     } else {
    535       /*
    536        * Known to be down
    537        */
    538       if ((fs->fs_flags & FSF_VALID) == 0)
    539 	srvrlog(fs, "starts down");
    540       fs->fs_flags |= FSF_VALID;
    541     }
    542     if (oflags != fs->fs_flags && (fs->fs_flags & FSF_WANT))
    543       wakeup_srvr(fs);
    544     /*
    545      * Reset failed ping count
    546      */
    547     np->np_ping = 0;
    548   } else {
    549     if (np->np_ping > 1)
    550       dlog("%d pings to %s failed - at most %d allowed", np->np_ping, fs->fs_host, MAX_ALLOWED_PINGS);
    551   }
    552 
    553   /*
    554    * New RPC xid, so any late responses to the previous ping
    555    * get ignored...
    556    */
    557   np->np_xid = XID_ALLOC();
    558 
    559   /*
    560    * Run keepalive again
    561    */
    562   nfs_keepalive(fs);
    563 }
    564 
    565 
    566 /*
    567  * Keep track of whether a server is alive
    568  */
    569 static void
    570 nfs_keepalive(voidp v)
    571 {
    572   fserver *fs = v;
    573   int error;
    574   nfs_private *np = (nfs_private *) fs->fs_private;
    575   int fstimeo = -1;
    576   int fs_version = nfs_valid_version(gopt.nfs_vers_ping) &&
    577     gopt.nfs_vers_ping < fs->fs_version ? gopt.nfs_vers_ping : fs->fs_version;
    578 
    579   /*
    580    * Send an NFS ping to this node
    581    */
    582 
    583   if (ping_len[fs_version - NFS_VERSION] == 0)
    584     create_ping_payload(fs_version);
    585 
    586   /*
    587    * Queue the packet...
    588    */
    589   error = fwd_packet(MK_RPC_XID(RPC_XID_NFSPING, np->np_xid),
    590 		     ping_buf[fs_version - NFS_VERSION],
    591 		     ping_len[fs_version - NFS_VERSION],
    592 		     fs->fs_ip,
    593 		     (struct sockaddr_in *) NULL,
    594 		     (voidp) ((long) np->np_xid), /* cast needed for 64-bit archs */
    595 		     nfs_keepalive_callback);
    596 
    597   /*
    598    * See if a hard error occurred
    599    */
    600   switch (error) {
    601   case ENETDOWN:
    602   case ENETUNREACH:
    603   case EHOSTDOWN:
    604   case EHOSTUNREACH:
    605     np->np_ping = MAX_ALLOWED_PINGS;	/* immediately down */
    606     np->np_ttl = (time_t) 0;
    607     /*
    608      * This causes an immediate call to nfs_keepalive_timeout
    609      * whenever the server was thought to be up.
    610      * See +++ below.
    611      */
    612     fstimeo = 0;
    613     break;
    614 
    615   case 0:
    616     dlog("Sent NFS ping to %s", fs->fs_host);
    617     break;
    618   }
    619 
    620   /*
    621    * Back off the ping interval if we are not getting replies and
    622    * the remote system is known to be down.
    623    */
    624   switch (fs->fs_flags & (FSF_DOWN | FSF_VALID)) {
    625   case FSF_VALID:		/* Up */
    626     if (fstimeo < 0)		/* +++ see above */
    627       fstimeo = FAST_NFS_PING;
    628     break;
    629 
    630   case FSF_VALID | FSF_DOWN:	/* Down */
    631     fstimeo = fs->fs_pinger;
    632     break;
    633 
    634   default:			/* Unknown */
    635     fstimeo = FAST_NFS_PING;
    636     break;
    637   }
    638 
    639   dlog("NFS timeout in %d seconds", fstimeo);
    640 
    641   fs->fs_cid = timeout(fstimeo, nfs_keepalive_timeout, (voidp) fs);
    642 }
    643 
    644 
    645 static void
    646 start_nfs_pings(fserver *fs, int pingval)
    647 {
    648   if (pingval == 0)	    /* could be because ping mnt option not found */
    649     pingval = AM_PINGER;
    650   /* if pings haven't been initalized, then init them for first time */
    651   if (fs->fs_flags & FSF_PING_UNINIT) {
    652     fs->fs_flags &= ~FSF_PING_UNINIT;
    653     plog(XLOG_INFO, "initializing %s's pinger to %d sec", fs->fs_host, pingval);
    654     goto do_pings;
    655   }
    656 
    657   if ((fs->fs_flags & FSF_PINGING)  &&  fs->fs_pinger == pingval) {
    658     dlog("already running pings to %s", fs->fs_host);
    659     return;
    660   }
    661 
    662   /* if got here, then we need to update the ping value */
    663   plog(XLOG_INFO, "changing %s's ping value from %d%s to %d%s",
    664        fs->fs_host,
    665        fs->fs_pinger, (fs->fs_pinger < 0 ? " (off)" : ""),
    666        pingval, (pingval < 0 ? " (off)" : ""));
    667  do_pings:
    668   fs->fs_pinger = pingval;
    669 
    670   if (fs->fs_cid)
    671     untimeout(fs->fs_cid);
    672   if (pingval < 0) {
    673     srvrlog(fs, "wired up (pings disabled)");
    674     fs->fs_flags |= FSF_VALID;
    675     fs->fs_flags &= ~FSF_DOWN;
    676   } else {
    677     fs->fs_flags |= FSF_PINGING;
    678     nfs_keepalive(fs);
    679   }
    680 }
    681 
    682 
    683 /*
    684  * Find an nfs server for a host.
    685  */
    686 fserver *
    687 find_nfs_srvr(mntfs *mf)
    688 {
    689   char *host;
    690   fserver *fs;
    691   int pingval;
    692   mntent_t mnt;
    693   nfs_private *np;
    694   struct hostent *hp = NULL;
    695   struct sockaddr_in *ip = NULL;
    696   u_long nfs_version = 0;	/* default is no version specified */
    697   u_long best_nfs_version = 0;
    698   char *nfs_proto = NULL;	/* no IP protocol either */
    699   int nfs_port = 0;
    700   int nfs_port_opt = 0;
    701   int fserver_is_down = 0;
    702 
    703   if (mf->mf_fo == NULL) {
    704     plog(XLOG_ERROR, "%s: NULL mf_fo", __func__);
    705     return NULL;
    706   }
    707   host = mf->mf_fo->opt_rhost;
    708   /*
    709    * Get ping interval from mount options.
    710    * Current only used to decide whether pings
    711    * are required or not.  < 0 = no pings.
    712    */
    713   mnt.mnt_opts = mf->mf_mopts;
    714   pingval = hasmntval(&mnt, "ping");
    715 
    716   if (mf->mf_flags & MFF_NFS_SCALEDOWN) {
    717     /*
    718      * the server granted us a filehandle, but we were unable to mount it.
    719      * therefore, scale down to NFSv2/UDP and try again.
    720      */
    721     nfs_version = NFS_VERSION;
    722     nfs_proto = "udp";
    723     plog(XLOG_WARNING, "%s: NFS mount failed, trying again with NFSv2/UDP",
    724       __func__);
    725     mf->mf_flags &= ~MFF_NFS_SCALEDOWN;
    726   } else {
    727     /*
    728      * Get the NFS version from the mount options. This is used
    729      * to decide the highest NFS version to try.
    730      */
    731 #ifdef MNTTAB_OPT_VERS
    732     nfs_version = hasmntval(&mnt, MNTTAB_OPT_VERS);
    733 #endif /* MNTTAB_OPT_VERS */
    734 
    735 #ifdef MNTTAB_OPT_PROTO
    736     {
    737       char *proto_opt = hasmnteq(&mnt, MNTTAB_OPT_PROTO);
    738       if (proto_opt) {
    739 	char **p;
    740 	for (p = protocols; *p; p++)
    741 	  if (NSTREQ(proto_opt, *p, strlen(*p))) {
    742 	    nfs_proto = *p;
    743 	    break;
    744 	  }
    745 	if (*p == NULL)
    746 	  plog(XLOG_WARNING, "ignoring unknown protocol option for %s:%s",
    747 	       host, mf->mf_fo->opt_rfs);
    748       }
    749     }
    750 #endif /* MNTTAB_OPT_PROTO */
    751 
    752 #ifdef HAVE_NFS_NFSV2_H
    753     /* allow overriding if nfsv2 option is specified in mount options */
    754     if (amu_hasmntopt(&mnt, "nfsv2")) {
    755       nfs_version = NFS_VERSION;/* nullify any ``vers=X'' statements */
    756       nfs_proto = "udp";	/* nullify any ``proto=tcp'' statements */
    757       plog(XLOG_WARNING, "found compatibility option \"nfsv2\": set options vers=2,proto=udp for host %s", host);
    758     }
    759 #endif /* HAVE_NFS_NFSV2_H */
    760 
    761     /* check if we've globally overridden the NFS version/protocol */
    762     if (gopt.nfs_vers) {
    763       nfs_version = gopt.nfs_vers;
    764       plog(XLOG_INFO, "%s: force NFS version to %d", __func__,
    765 	   (int) nfs_version);
    766     }
    767     if (gopt.nfs_proto) {
    768       nfs_proto = gopt.nfs_proto;
    769       plog(XLOG_INFO, "%s: force NFS protocol transport to %s", __func__,
    770 	nfs_proto);
    771     }
    772   }
    773 
    774   /*
    775    * lookup host address and canonical name
    776    */
    777   hp = gethostbyname(host);
    778 
    779   /*
    780    * New code from Bob Harris <harris (at) basil-rathbone.mit.edu>
    781    * Use canonical name to keep track of file server
    782    * information.  This way aliases do not generate
    783    * multiple NFS pingers.  (Except when we're normalizing
    784    * hosts.)
    785    */
    786   if (hp && !(gopt.flags & CFM_NORMALIZE_HOSTNAMES))
    787     host = (char *) hp->h_name;
    788 
    789   if (hp) {
    790     switch (hp->h_addrtype) {
    791     case AF_INET:
    792       ip = CALLOC(struct sockaddr_in);
    793       memset((voidp) ip, 0, sizeof(*ip));
    794       /* as per POSIX, sin_len need not be set (used internally by kernel) */
    795       ip->sin_family = AF_INET;
    796       memmove((voidp) &ip->sin_addr, (voidp) hp->h_addr, sizeof(ip->sin_addr));
    797       break;
    798 
    799     default:
    800       plog(XLOG_USER, "No IP address for host %s", host);
    801       goto no_dns;
    802     }
    803   } else {
    804     plog(XLOG_USER, "Unknown host: %s", host);
    805     goto no_dns;
    806   }
    807 
    808   /*
    809    * This may not be the best way to do things, but it really doesn't make
    810    * sense to query a file server which is marked as 'down' for any
    811    * version/proto combination.
    812    */
    813   ITER(fs, fserver, &nfs_srvr_list) {
    814     if (FSRV_ISDOWN(fs) &&
    815 	STREQ(host, fs->fs_host)) {
    816       plog(XLOG_WARNING, "fileserver %s is already hung - not running NFS proto/version discovery", host);
    817       fs->fs_refc++;
    818       XFREE(ip);
    819       return fs;
    820     }
    821   }
    822 
    823   /*
    824    * Get the NFS Version, and verify server is up.
    825    * If the client only supports NFSv2, hardcode it but still try to
    826    * contact the remote portmapper to see if the service is running.
    827    */
    828 #ifndef HAVE_FS_NFS3
    829   nfs_version = NFS_VERSION;
    830   nfs_proto = "udp";
    831   plog(XLOG_INFO, "The client supports only NFS(2,udp)");
    832 #endif /* not HAVE_FS_NFS3 */
    833 
    834 
    835   if (amu_hasmntopt(&mnt, MNTTAB_OPT_PUBLIC)) {
    836     /*
    837      * Use WebNFS to obtain file handles.
    838      */
    839     mf->mf_flags |= MFF_WEBNFS;
    840     plog(XLOG_INFO, "%s option used, NOT contacting the portmapper on %s",
    841 	 MNTTAB_OPT_PUBLIC, host);
    842     /*
    843      * Prefer NFSv4/tcp if the client supports it (cf. RFC 2054, 7).
    844      */
    845     if (!nfs_version) {
    846 #if defined(HAVE_FS_NFS4)
    847       nfs_version = NFS_VERSION4;
    848 #elif defined(HAVE_FS_NFS3)
    849       nfs_version = NFS_VERSION3;
    850 #else /* not HAVE_FS_NFS3 */
    851       nfs_version = NFS_VERSION;
    852 #endif /* not HAVE_FS_NFS3 */
    853       plog(XLOG_INFO, "No NFS version specified, will use NFSv%d",
    854 	   (int) nfs_version);
    855     }
    856     if (!nfs_proto) {
    857 #if defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3) || defined(HAVE_FS_NFS4)
    858       nfs_proto = "tcp";
    859 #else /* not defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3) || defined(HAVE_FS_NFS4) */
    860       nfs_proto = "udp";
    861 #endif /* not defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3) || defined(HAVE_FS_NFS4) */
    862       plog(XLOG_INFO, "No NFS protocol transport specified, will use %s",
    863 	   nfs_proto);
    864     }
    865   } else {
    866     /*
    867      * Find the best combination of NFS version and protocol.
    868      * When given a choice, use the highest available version,
    869      * and use TCP over UDP if available.
    870      */
    871     if (check_pmap_up(host, ip)) {
    872       if (nfs_proto) {
    873 	best_nfs_version = get_nfs_version(host, ip, nfs_version, nfs_proto,
    874 	  gopt.nfs_vers);
    875 	nfs_port = ip->sin_port;
    876       }
    877 #ifdef MNTTAB_OPT_PROTO
    878       else {
    879 	u_int proto_nfs_version;
    880 	char **p;
    881 
    882 	for (p = protocols; *p; p++) {
    883 	  proto_nfs_version = get_nfs_version(host, ip, nfs_version, *p,
    884 	    gopt.nfs_vers);
    885 	  if (proto_nfs_version > best_nfs_version) {
    886 	    best_nfs_version = proto_nfs_version;
    887 	    nfs_proto = *p;
    888 	    nfs_port = ip->sin_port;
    889 	  }
    890 	}
    891       }
    892 #endif /* MNTTAB_OPT_PROTO */
    893     } else {
    894       plog(XLOG_INFO, "portmapper service not running on %s", host);
    895     }
    896 
    897     /* use the portmapper results only nfs_version is not set yet */
    898     if (!best_nfs_version) {
    899       /*
    900        * If the NFS server is down or does not support the portmapper call
    901        * (such as certain Novell NFS servers) we mark it as version 2 and we
    902        * let the nfs code deal with the case when it is down.  If/when the
    903        * server comes back up and it can support NFSv3 and/or TCP, it will
    904        * use those.
    905        */
    906       if (nfs_version == 0) {
    907 	nfs_version = NFS_VERSION;
    908 	nfs_proto = "udp";
    909       }
    910       plog(XLOG_INFO, "NFS service not running on %s", host);
    911       fserver_is_down = 1;
    912     } else {
    913       if (nfs_version == 0)
    914 	nfs_version = best_nfs_version;
    915       plog(XLOG_INFO, "Using NFS version %d, protocol %s on host %s",
    916 	   (int) nfs_version, nfs_proto, host);
    917     }
    918   }
    919 
    920   /*
    921    * Determine the NFS port.
    922    *
    923    * A valid "port" mount option overrides anything else.
    924    * If the port has been determined from the portmapper, use that.
    925    * Default to NFS_PORT otherwise (cf. RFC 2054, 3).
    926    */
    927   nfs_port_opt = hasmntval(&mnt, MNTTAB_OPT_PORT);
    928   if (nfs_port_opt > 0)
    929     nfs_port = htons(nfs_port_opt);
    930   if (!nfs_port)
    931     nfs_port = htons(NFS_PORT);
    932 
    933   dlog("%s: using port %d for nfs on %s", __func__,
    934     (int) ntohs(nfs_port), host);
    935   ip->sin_port = nfs_port;
    936 
    937 no_dns:
    938   /*
    939    * Try to find an existing fs server structure for this host.
    940    * Note that differing versions or protocols have their own structures.
    941    * XXX: Need to fix the ping mechanism to actually use the NFS protocol
    942    * chosen here (right now it always uses datagram sockets).
    943    */
    944   ITER(fs, fserver, &nfs_srvr_list) {
    945     if (STREQ(host, fs->fs_host) &&
    946  	nfs_version == fs->fs_version &&
    947 	STREQ(nfs_proto, fs->fs_proto)) {
    948       /*
    949        * fill in the IP address -- this is only needed
    950        * if there is a chance an IP address will change
    951        * between mounts.
    952        * Mike Mitchell, mcm (at) unx.sas.com, 09/08/93
    953        */
    954       if (hp && fs->fs_ip &&
    955 	  memcmp((voidp) &fs->fs_ip->sin_addr,
    956 		 (voidp) hp->h_addr,
    957 		 sizeof(fs->fs_ip->sin_addr)) != 0) {
    958 	struct in_addr ia;
    959 	char *old_ipaddr, *new_ipaddr;
    960 	old_ipaddr = xstrdup(inet_ntoa(fs->fs_ip->sin_addr));
    961 	memmove((voidp) &ia, (voidp) hp->h_addr, sizeof(struct in_addr));
    962 	new_ipaddr = inet_ntoa(ia);	/* ntoa uses static buf */
    963 	plog(XLOG_WARNING, "fileserver %s changed ip: %s -> %s",
    964 	     fs->fs_host, old_ipaddr, new_ipaddr);
    965 	XFREE(old_ipaddr);
    966 	flush_nfs_fhandle_cache(fs);
    967 	memmove((voidp) &fs->fs_ip->sin_addr, (voidp) hp->h_addr, sizeof(fs->fs_ip->sin_addr));
    968       }
    969 
    970       /*
    971        * If the new file systems doesn't use WebNFS, the nfs pings may
    972        * try to contact the portmapper.
    973        */
    974       if (!(mf->mf_flags & MFF_WEBNFS))
    975 	fs->fs_flags &= ~FSF_WEBNFS;
    976 
    977       /* check if pingval needs to be updated/set/reset */
    978       start_nfs_pings(fs, pingval);
    979 
    980       /*
    981        * Following if statement from Mike Mitchell <mcm (at) unx.sas.com>
    982        * Initialize the ping data if we aren't pinging now.  The np_ttl and
    983        * np_ping fields are especially important.
    984        */
    985       if (!(fs->fs_flags & FSF_PINGING)) {
    986 	np = (nfs_private *) fs->fs_private;
    987 	if (np->np_mountd_inval != 'P') {
    988 	  np->np_mountd_inval = TRUE;
    989 	  np->np_xid = XID_ALLOC();
    990 	  np->np_error = -1;
    991 	  np->np_ping = 0;
    992 	  /*
    993 	   * Initially the server will be deemed dead
    994 	   * after MAX_ALLOWED_PINGS of the fast variety
    995 	   * have failed.
    996 	   */
    997 	  np->np_ttl = MAX_ALLOWED_PINGS * FAST_NFS_PING + clocktime(NULL) - 1;
    998 	  start_nfs_pings(fs, pingval);
    999 	  if (fserver_is_down)
   1000 	    fs->fs_flags |= FSF_VALID | FSF_DOWN;
   1001 	} else {
   1002 	  fs->fs_flags = FSF_VALID;
   1003 	}
   1004 
   1005       }
   1006 
   1007       fs->fs_refc++;
   1008       XFREE(ip);
   1009       return fs;
   1010     }
   1011   }
   1012 
   1013   /*
   1014    * Get here if we can't find an entry
   1015    */
   1016 
   1017   /*
   1018    * Allocate a new server
   1019    */
   1020   fs = ALLOC(struct fserver);
   1021   fs->fs_refc = 1;
   1022   fs->fs_host = xstrdup(hp ? hp->h_name : "unknown_hostname");
   1023   if (gopt.flags & CFM_NORMALIZE_HOSTNAMES)
   1024     host_normalize(&fs->fs_host);
   1025   fs->fs_ip = ip;
   1026   fs->fs_cid = 0;
   1027   if (ip) {
   1028     fs->fs_flags = FSF_DOWN;	/* Starts off down */
   1029   } else {
   1030     fs->fs_flags = FSF_ERROR | FSF_VALID;
   1031     mf->mf_flags |= MFF_ERROR;
   1032     mf->mf_error = ENOENT;
   1033   }
   1034   if (mf->mf_flags & MFF_WEBNFS)
   1035     fs->fs_flags |= FSF_WEBNFS;
   1036   fs->fs_version = nfs_version;
   1037   fs->fs_proto = nfs_proto;
   1038   fs->fs_type = MNTTAB_TYPE_NFS;
   1039   fs->fs_pinger = AM_PINGER;
   1040   fs->fs_flags |= FSF_PING_UNINIT; /* pinger hasn't been initialized */
   1041   np = ALLOC(struct nfs_private);
   1042   memset((voidp) np, 0, sizeof(*np));
   1043   np->np_mountd = htons(hasmntval(&mnt, "mountport"));
   1044   if (np->np_mountd == 0) {
   1045     np->np_mountd_inval = 'Y';
   1046     np->np_xid = XID_ALLOC();
   1047     np->np_error = -1;
   1048   } else {
   1049     plog(XLOG_INFO, "%s: using mountport: %d", __func__,
   1050       (int) ntohs(np->np_mountd));
   1051     np->np_mountd_inval = 'P';
   1052     np->np_xid = 0;
   1053     np->np_error = 0;
   1054   }
   1055 
   1056   /*
   1057    * Initially the server will be deemed dead after
   1058    * MAX_ALLOWED_PINGS of the fast variety have failed.
   1059    */
   1060   np->np_ttl = clocktime(NULL) + MAX_ALLOWED_PINGS * FAST_NFS_PING - 1;
   1061   fs->fs_private = (voidp) np;
   1062   fs->fs_prfree = (void (*)(voidp)) free;
   1063 
   1064   if (!FSRV_ERROR(fs)) {
   1065     /* start of keepalive timer, first updating pingval */
   1066     start_nfs_pings(fs, pingval);
   1067     if (fserver_is_down)
   1068       fs->fs_flags |= FSF_VALID | FSF_DOWN;
   1069   }
   1070 
   1071   /*
   1072    * Add to list of servers
   1073    */
   1074   ins_que(&fs->fs_q, &nfs_srvr_list);
   1075 
   1076   return fs;
   1077 }
   1078