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