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