138494Sobrien/*
2174313Sobrien * Copyright (c) 1997-2006 Erez Zadok
338494Sobrien * Copyright (c) 1990 Jan-Simon Pendry
438494Sobrien * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
538494Sobrien * Copyright (c) 1990 The Regents of the University of California.
638494Sobrien * All rights reserved.
738494Sobrien *
838494Sobrien * This code is derived from software contributed to Berkeley by
938494Sobrien * Jan-Simon Pendry at Imperial College, London.
1038494Sobrien *
1138494Sobrien * Redistribution and use in source and binary forms, with or without
1238494Sobrien * modification, are permitted provided that the following conditions
1338494Sobrien * are met:
1438494Sobrien * 1. Redistributions of source code must retain the above copyright
1538494Sobrien *    notice, this list of conditions and the following disclaimer.
1638494Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1738494Sobrien *    notice, this list of conditions and the following disclaimer in the
1838494Sobrien *    documentation and/or other materials provided with the distribution.
1938494Sobrien * 3. All advertising materials mentioning features or use of this software
2042633Sobrien *    must display the following acknowledgment:
2138494Sobrien *      This product includes software developed by the University of
2238494Sobrien *      California, Berkeley and its contributors.
2338494Sobrien * 4. Neither the name of the University nor the names of its contributors
2438494Sobrien *    may be used to endorse or promote products derived from this software
2538494Sobrien *    without specific prior written permission.
2638494Sobrien *
2738494Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2838494Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2938494Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
3038494Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
3138494Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3238494Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3338494Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3438494Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3538494Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3638494Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3738494Sobrien * SUCH DAMAGE.
3838494Sobrien *
3938494Sobrien *
40174313Sobrien * File: am-utils/amd/srvr_nfs.c
4138494Sobrien *
4238494Sobrien */
4338494Sobrien
4438494Sobrien/*
4538494Sobrien * NFS server modeling
4638494Sobrien */
4738494Sobrien
4838494Sobrien#ifdef HAVE_CONFIG_H
4938494Sobrien# include <config.h>
5038494Sobrien#endif /* HAVE_CONFIG_H */
5138494Sobrien#include <am_defs.h>
5238494Sobrien#include <amd.h>
5338494Sobrien
5438494Sobrien/*
5538494Sobrien * Number of pings allowed to fail before host is declared down
5638494Sobrien * - three-fifths of the allowed mount time...
5738494Sobrien */
5838494Sobrien#define	MAX_ALLOWED_PINGS	(3 + /* for luck ... */ 1)
5938494Sobrien
6038494Sobrien/*
6138494Sobrien * How often to ping when starting a new server
6238494Sobrien */
6338494Sobrien#define	FAST_NFS_PING		3
6438494Sobrien
6538494Sobrien#if (FAST_NFS_PING * MAX_ALLOWED_PINGS) >= ALLOWED_MOUNT_TIME
6638494Sobrien# error: sanity check failed in srvr_nfs.c
6738494Sobrien/*
6838494Sobrien * you cannot do things this way...
6938494Sobrien * sufficient fast pings must be given the chance to fail
7038494Sobrien * within the allowed mount time
7138494Sobrien */
7238494Sobrien#endif /* (FAST_NFS_PING * MAX_ALLOWED_PINGS) >= ALLOWED_MOUNT_TIME */
7338494Sobrien
7438494Sobrien/* structures and typedefs */
7538494Sobrientypedef struct nfs_private {
7638494Sobrien  u_short np_mountd;		/* Mount daemon port number */
7738494Sobrien  char np_mountd_inval;		/* Port *may* be invalid */
7838494Sobrien  int np_ping;			/* Number of failed ping attempts */
7938494Sobrien  time_t np_ttl;		/* Time when server is thought dead */
8038494Sobrien  int np_xid;			/* RPC transaction id for pings */
8138494Sobrien  int np_error;			/* Error during portmap request */
8238494Sobrien} nfs_private;
8338494Sobrien
8438494Sobrien/* globals */
8538494Sobrienqelem nfs_srvr_list = {&nfs_srvr_list, &nfs_srvr_list};
8638494Sobrien
8738494Sobrien/* statics */
88174313Sobrienstatic int global_xid;		/* For NFS pings */
89174313Sobrien#define	XID_ALLOC()		(++global_xid)
9038494Sobrien
91174313Sobrien#ifdef HAVE_FS_NFS3
92174313Sobrien# define NUM_NFS_VERS 2
93174313Sobrien#else  /* not HAVE_FS_NFS3 */
94174313Sobrien# define NUM_NFS_VERS 1
95174313Sobrien#endif /* not HAVE_FS_NFS3 */
96174313Sobrienstatic int ping_len[NUM_NFS_VERS];
97174313Sobrienstatic char ping_buf[NUM_NFS_VERS][sizeof(struct rpc_msg) + 32];
98174313Sobrien
9938494Sobrien#if defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3)
10082800Sobrien/*
10182800Sobrien * Protocols we know about, in order of preference.
10282800Sobrien *
10382800Sobrien * Note that Solaris 8 and newer NetBSD systems are switching to UDP first,
10482800Sobrien * so this order may have to be adjusted for Amd in the future once more
10582800Sobrien * vendors make that change. -Erez 11/24/2000
106174313Sobrien *
107174313Sobrien * Or we might simply make this is a platform-specific order. -Ion 09/13/2003
10882800Sobrien */
10938494Sobrienstatic char *protocols[] = { "tcp", "udp", NULL };
11038494Sobrien#endif /* defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3) */
11138494Sobrien
11238494Sobrien/* forward definitions */
11338494Sobrienstatic void nfs_keepalive(voidp);
11438494Sobrien
11538494Sobrien
11638494Sobrien/*
117174313Sobrien * Flush cached data for an fserver (or for all, if fs==NULL)
11838494Sobrien */
11938494Sobrienvoid
120174313Sobrienflush_srvr_nfs_cache(fserver *fs)
12138494Sobrien{
122174313Sobrien  fserver *fs2 = NULL;
12338494Sobrien
124174313Sobrien  ITER(fs2, fserver, &nfs_srvr_list) {
125174313Sobrien    if (fs == NULL || fs == fs2) {
126174313Sobrien      nfs_private *np = (nfs_private *) fs2->fs_private;
127174313Sobrien      if (np) {
128174313Sobrien	np->np_mountd_inval = TRUE;
129174313Sobrien	np->np_error = -1;
130174313Sobrien      }
13138494Sobrien    }
13238494Sobrien  }
13338494Sobrien}
13438494Sobrien
13538494Sobrien
13638494Sobrien/*
13738494Sobrien * Startup the NFS ping for a particular version.
13838494Sobrien */
13938494Sobrienstatic void
140174313Sobriencreate_ping_payload(u_long nfs_version)
14138494Sobrien{
14238494Sobrien  XDR ping_xdr;
14338494Sobrien  struct rpc_msg ping_msg;
14438494Sobrien
14538494Sobrien  /*
14638494Sobrien   * Non nfs mounts like /afs/glue.umd.edu have ended up here.
14738494Sobrien   */
14838494Sobrien  if (nfs_version == 0) {
14938494Sobrien    nfs_version = NFS_VERSION;
150174313Sobrien    plog(XLOG_WARNING, "create_ping_payload: nfs_version = 0, changed to 2");
151174313Sobrien  } else
152174313Sobrien    plog(XLOG_INFO, "create_ping_payload: nfs_version: %d", (int) nfs_version);
15338494Sobrien
15438494Sobrien  rpc_msg_init(&ping_msg, NFS_PROGRAM, nfs_version, NFSPROC_NULL);
15538494Sobrien
15638494Sobrien  /*
15738494Sobrien   * Create an XDR endpoint
15838494Sobrien   */
159174313Sobrien  xdrmem_create(&ping_xdr, ping_buf[nfs_version - NFS_VERSION], sizeof(ping_buf[0]), XDR_ENCODE);
16038494Sobrien
16138494Sobrien  /*
16238494Sobrien   * Create the NFS ping message
16338494Sobrien   */
16438494Sobrien  if (!xdr_callmsg(&ping_xdr, &ping_msg)) {
16538494Sobrien    plog(XLOG_ERROR, "Couldn't create ping RPC message");
16638494Sobrien    going_down(3);
16738494Sobrien  }
16838494Sobrien  /*
16938494Sobrien   * Find out how long it is
17038494Sobrien   */
171174313Sobrien  ping_len[nfs_version - NFS_VERSION] = xdr_getpos(&ping_xdr);
17238494Sobrien
17338494Sobrien  /*
17438494Sobrien   * Destroy the XDR endpoint - we don't need it anymore
17538494Sobrien   */
17638494Sobrien  xdr_destroy(&ping_xdr);
17738494Sobrien}
17838494Sobrien
17938494Sobrien
18038494Sobrien/*
18138494Sobrien * Called when a portmap reply arrives
18238494Sobrien */
18338494Sobrienstatic void
18482800Sobriengot_portmap(voidp pkt, int len, struct sockaddr_in *sa, struct sockaddr_in *ia, voidp idv, int done)
18538494Sobrien{
18638494Sobrien  fserver *fs2 = (fserver *) idv;
18738494Sobrien  fserver *fs = 0;
18838494Sobrien
18938494Sobrien  /*
19038494Sobrien   * Find which fileserver we are talking about
19138494Sobrien   */
19238494Sobrien  ITER(fs, fserver, &nfs_srvr_list)
193174313Sobrien    if (fs == fs2)
19438494Sobrien      break;
19538494Sobrien
19638494Sobrien  if (fs == fs2) {
19738494Sobrien    u_long port = 0;	/* XXX - should be short but protocol is naff */
19838494Sobrien    int error = done ? pickup_rpc_reply(pkt, len, (voidp) &port, (XDRPROC_T_TYPE) xdr_u_long) : -1;
19938494Sobrien    nfs_private *np = (nfs_private *) fs->fs_private;
20038494Sobrien
20138494Sobrien    if (!error && port) {
20251300Sobrien      dlog("got port (%d) for mountd on %s", (int) port, fs->fs_host);
20338494Sobrien      /*
20438494Sobrien       * Grab the port number.  Portmap sends back
20538494Sobrien       * an u_long in native ordering, so it
20638494Sobrien       * needs converting to a u_short in
20738494Sobrien       * network ordering.
20838494Sobrien       */
20938494Sobrien      np->np_mountd = htons((u_short) port);
21038494Sobrien      np->np_mountd_inval = FALSE;
21138494Sobrien      np->np_error = 0;
21238494Sobrien    } else {
21338494Sobrien      dlog("Error fetching port for mountd on %s", fs->fs_host);
21451300Sobrien      dlog("\t error=%d, port=%d", error, (int) port);
21538494Sobrien      /*
21638494Sobrien       * Almost certainly no mountd running on remote host
21738494Sobrien       */
21838494Sobrien      np->np_error = error ? error : ETIMEDOUT;
21938494Sobrien    }
22038494Sobrien
22138494Sobrien    if (fs->fs_flags & FSF_WANT)
22238494Sobrien      wakeup_srvr(fs);
22338494Sobrien  } else if (done) {
22438494Sobrien    dlog("Got portmap for old port request");
22538494Sobrien  } else {
22638494Sobrien    dlog("portmap request timed out");
22738494Sobrien  }
22838494Sobrien}
22938494Sobrien
23038494Sobrien
23138494Sobrien/*
23238494Sobrien * Obtain portmap information
23338494Sobrien */
23438494Sobrienstatic int
23582800Sobriencall_portmap(fserver *fs, AUTH *auth, u_long prog, u_long vers, u_long prot)
23638494Sobrien{
23738494Sobrien  struct rpc_msg pmap_msg;
23838494Sobrien  int len;
23938494Sobrien  char iobuf[UDPMSGSIZE];
24038494Sobrien  int error;
24138494Sobrien  struct pmap pmap;
24238494Sobrien
24338494Sobrien  rpc_msg_init(&pmap_msg, PMAPPROG, PMAPVERS, PMAPPROC_NULL);
24438494Sobrien  pmap.pm_prog = prog;
24538494Sobrien  pmap.pm_vers = vers;
24638494Sobrien  pmap.pm_prot = prot;
24738494Sobrien  pmap.pm_port = 0;
24838494Sobrien  len = make_rpc_packet(iobuf,
24938494Sobrien			sizeof(iobuf),
25038494Sobrien			PMAPPROC_GETPORT,
25138494Sobrien			&pmap_msg,
25238494Sobrien			(voidp) &pmap,
25338494Sobrien			(XDRPROC_T_TYPE) xdr_pmap,
25438494Sobrien			auth);
25538494Sobrien  if (len > 0) {
25638494Sobrien    struct sockaddr_in sin;
25738494Sobrien    memset((voidp) &sin, 0, sizeof(sin));
25838494Sobrien    sin = *fs->fs_ip;
25938494Sobrien    sin.sin_port = htons(PMAPPORT);
260174313Sobrien    error = fwd_packet(RPC_XID_PORTMAP, iobuf, len,
26138494Sobrien		       &sin, &sin, (voidp) fs, got_portmap);
26238494Sobrien  } else {
26338494Sobrien    error = -len;
26438494Sobrien  }
26538494Sobrien
26638494Sobrien  return error;
26738494Sobrien}
26838494Sobrien
26938494Sobrien
27038494Sobrienstatic void
27138494Sobrienrecompute_portmap(fserver *fs)
27238494Sobrien{
27338494Sobrien  int error;
27438494Sobrien  u_long mnt_version;
27538494Sobrien
276174313Sobrien  /*
277174313Sobrien   * No portmap calls for pure WebNFS servers.
278174313Sobrien   */
279174313Sobrien  if (fs->fs_flags & FSF_WEBNFS)
280174313Sobrien    return;
281174313Sobrien
28238494Sobrien  if (nfs_auth)
28338494Sobrien    error = 0;
28438494Sobrien  else
28538494Sobrien    error = make_nfs_auth();
28638494Sobrien
28738494Sobrien  if (error) {
28838494Sobrien    nfs_private *np = (nfs_private *) fs->fs_private;
28938494Sobrien    np->np_error = error;
29038494Sobrien    return;
29138494Sobrien  }
29238494Sobrien
29338494Sobrien  if (fs->fs_version == 0)
29438494Sobrien    plog(XLOG_WARNING, "recompute_portmap: nfs_version = 0 fixed");
29538494Sobrien
296174313Sobrien  plog(XLOG_INFO, "recompute_portmap: NFS version %d on %s",
297174313Sobrien       (int) fs->fs_version, fs->fs_host);
29838494Sobrien#ifdef HAVE_FS_NFS3
29938494Sobrien  if (fs->fs_version == NFS_VERSION3)
300174313Sobrien    mnt_version = AM_MOUNTVERS3;
30138494Sobrien  else
30238494Sobrien#endif /* HAVE_FS_NFS3 */
30338494Sobrien    mnt_version = MOUNTVERS;
30438494Sobrien
30551300Sobrien  plog(XLOG_INFO, "Using MOUNT version: %d", (int) mnt_version);
30638494Sobrien  call_portmap(fs, nfs_auth, MOUNTPROG, mnt_version, (u_long) IPPROTO_UDP);
30738494Sobrien}
30838494Sobrien
30938494Sobrien
310174313Sobrienint
311174313Sobrienget_mountd_port(fserver *fs, u_short *port, wchan_t wchan)
312174313Sobrien{
313174313Sobrien  int error = -1;
314174313Sobrien  if (FSRV_ISDOWN(fs))
315174313Sobrien    return EWOULDBLOCK;
316174313Sobrien
317174313Sobrien  if (FSRV_ISUP(fs)) {
318174313Sobrien    nfs_private *np = (nfs_private *) fs->fs_private;
319174313Sobrien    if (np->np_error == 0) {
320174313Sobrien      *port = np->np_mountd;
321174313Sobrien      error = 0;
322174313Sobrien    } else {
323174313Sobrien      error = np->np_error;
324174313Sobrien    }
325174313Sobrien    /*
326174313Sobrien     * Now go get the port mapping again in case it changed.
327174313Sobrien     * Note that it is used even if (np_mountd_inval)
328174313Sobrien     * is True.  The flag is used simply as an
329174313Sobrien     * indication that the mountd may be invalid, not
330174313Sobrien     * that it is known to be invalid.
331174313Sobrien     */
332174313Sobrien    if (np->np_mountd_inval)
333174313Sobrien      recompute_portmap(fs);
334174313Sobrien    else
335174313Sobrien      np->np_mountd_inval = TRUE;
336174313Sobrien  }
337174313Sobrien  if (error < 0 && wchan && !(fs->fs_flags & FSF_WANT)) {
338174313Sobrien    /*
339174313Sobrien     * If a wait channel is supplied, and no
340174313Sobrien     * error has yet occurred, then arrange
341174313Sobrien     * that a wakeup is done on the wait channel,
342174313Sobrien     * whenever a wakeup is done on this fs node.
343174313Sobrien     * Wakeup's are done on the fs node whenever
344174313Sobrien     * it changes state - thus causing control to
345174313Sobrien     * come back here and new, better things to happen.
346174313Sobrien     */
347174313Sobrien    fs->fs_flags |= FSF_WANT;
348174313Sobrien    sched_task(wakeup_task, wchan, (wchan_t) fs);
349174313Sobrien  }
350174313Sobrien  return error;
351174313Sobrien}
352174313Sobrien
353174313Sobrien
35438494Sobrien/*
35538494Sobrien * This is called when we get a reply to an RPC ping.
35638494Sobrien * The value of id was taken from the nfs_private
35738494Sobrien * structure when the ping was transmitted.
35838494Sobrien */
35938494Sobrienstatic void
360174313Sobriennfs_keepalive_callback(voidp pkt, int len, struct sockaddr_in *sp, struct sockaddr_in *tsp, voidp idv, int done)
36138494Sobrien{
362174313Sobrien  int xid = (long) idv;		/* cast needed for 64-bit archs */
36338494Sobrien  fserver *fs;
36438494Sobrien  int found_map = 0;
36538494Sobrien
36638494Sobrien  if (!done)
36738494Sobrien    return;
36838494Sobrien
36938494Sobrien  /*
37038494Sobrien   * For each node...
37138494Sobrien   */
37238494Sobrien  ITER(fs, fserver, &nfs_srvr_list) {
37338494Sobrien    nfs_private *np = (nfs_private *) fs->fs_private;
37438494Sobrien    if (np->np_xid == xid && (fs->fs_flags & FSF_PINGING)) {
37538494Sobrien      /*
37638494Sobrien       * Reset the ping counter.
37738494Sobrien       * Update the keepalive timer.
37838494Sobrien       * Log what happened.
37938494Sobrien       */
38038494Sobrien      if (fs->fs_flags & FSF_DOWN) {
38138494Sobrien	fs->fs_flags &= ~FSF_DOWN;
38238494Sobrien	if (fs->fs_flags & FSF_VALID) {
38338494Sobrien	  srvrlog(fs, "is up");
38438494Sobrien	} else {
38538494Sobrien	  if (np->np_ping > 1)
38638494Sobrien	    srvrlog(fs, "ok");
38738494Sobrien	  else
38838494Sobrien	    srvrlog(fs, "starts up");
38938494Sobrien	  fs->fs_flags |= FSF_VALID;
39038494Sobrien	}
39138494Sobrien
39238494Sobrien	map_flush_srvr(fs);
39338494Sobrien      } else {
39438494Sobrien	if (fs->fs_flags & FSF_VALID) {
39538494Sobrien	  dlog("file server %s type nfs is still up", fs->fs_host);
39638494Sobrien	} else {
39738494Sobrien	  if (np->np_ping > 1)
39838494Sobrien	    srvrlog(fs, "ok");
39938494Sobrien	  fs->fs_flags |= FSF_VALID;
40038494Sobrien	}
40138494Sobrien      }
40238494Sobrien
40338494Sobrien      /*
40438494Sobrien       * Adjust ping interval
40538494Sobrien       */
40638494Sobrien      untimeout(fs->fs_cid);
40738494Sobrien      fs->fs_cid = timeout(fs->fs_pinger, nfs_keepalive, (voidp) fs);
40838494Sobrien
40938494Sobrien      /*
41038494Sobrien       * Update ttl for this server
41138494Sobrien       */
412174313Sobrien      np->np_ttl = clocktime(NULL) +
41338494Sobrien	(MAX_ALLOWED_PINGS - 1) * FAST_NFS_PING + fs->fs_pinger - 1;
41438494Sobrien
41538494Sobrien      /*
41638494Sobrien       * New RPC xid...
41738494Sobrien       */
418174313Sobrien      np->np_xid = XID_ALLOC();
41938494Sobrien
42038494Sobrien      /*
42138494Sobrien       * Failed pings is zero...
42238494Sobrien       */
42338494Sobrien      np->np_ping = 0;
42438494Sobrien
42538494Sobrien      /*
42638494Sobrien       * Recompute portmap information if not known
42738494Sobrien       */
42838494Sobrien      if (np->np_mountd_inval)
42938494Sobrien	recompute_portmap(fs);
43038494Sobrien
43138494Sobrien      found_map++;
43238494Sobrien      break;
43338494Sobrien    }
43438494Sobrien  }
43538494Sobrien
43638494Sobrien  if (found_map == 0)
43738494Sobrien    dlog("Spurious ping packet");
43838494Sobrien}
43938494Sobrien
44038494Sobrien
441174313Sobrienstatic void
442174313Sobriencheck_fs_addr_change(fserver *fs)
443174313Sobrien{
444174313Sobrien  struct hostent *hp = NULL;
445174313Sobrien  struct in_addr ia;
446174313Sobrien  char *old_ipaddr, *new_ipaddr;
447174313Sobrien
448174313Sobrien  hp = gethostbyname(fs->fs_host);
449174313Sobrien  if (!hp ||
450174313Sobrien      hp->h_addrtype != AF_INET ||
451174313Sobrien      !STREQ((char *) hp->h_name, fs->fs_host) ||
452174313Sobrien      memcmp((voidp) &fs->fs_ip->sin_addr,
453174313Sobrien	     (voidp) hp->h_addr,
454174313Sobrien	     sizeof(fs->fs_ip->sin_addr)) == 0)
455174313Sobrien    return;
456174313Sobrien  /* if got here: downed server changed IP address */
457174313Sobrien  old_ipaddr = strdup(inet_ntoa(fs->fs_ip->sin_addr));
458174313Sobrien  memmove((voidp) &ia, (voidp) hp->h_addr, sizeof(struct in_addr));
459174313Sobrien  new_ipaddr = inet_ntoa(ia);	/* ntoa uses static buf */
460174313Sobrien  plog(XLOG_WARNING, "EZK: down fileserver %s changed ip: %s -> %s",
461174313Sobrien       fs->fs_host, old_ipaddr, new_ipaddr);
462174313Sobrien  XFREE(old_ipaddr);
463174313Sobrien  /* copy new IP addr */
464174313Sobrien  memmove((voidp) &fs->fs_ip->sin_addr,
465174313Sobrien	  (voidp) hp->h_addr,
466174313Sobrien	  sizeof(fs->fs_ip->sin_addr));
467174313Sobrien  /* XXX: do we need to un/set these flags? */
468174313Sobrien  fs->fs_flags &= ~FSF_DOWN;
469174313Sobrien  fs->fs_flags |= FSF_VALID | FSF_WANT;
470174313Sobrien  map_flush_srvr(fs);		/* XXX: a race with flush_srvr_nfs_cache? */
471174313Sobrien  flush_srvr_nfs_cache(fs);
472174313Sobrien  fs->fs_flags |= FSF_FORCE_UNMOUNT;
473174313Sobrien
474174313Sobrien#if 0
475174313Sobrien  flush_nfs_fhandle_cache(fs);	/* done in caller: nfs_keepalive_timeout */
476174313Sobrien  /* XXX: need to purge nfs_private so that somehow it will get re-initialized? */
477174313Sobrien#endif
478174313Sobrien}
479174313Sobrien
480174313Sobrien
48138494Sobrien/*
48238494Sobrien * Called when no ping-reply received
48338494Sobrien */
48438494Sobrienstatic void
485174313Sobriennfs_keepalive_timeout(voidp v)
48638494Sobrien{
48738494Sobrien  fserver *fs = v;
48838494Sobrien  nfs_private *np = (nfs_private *) fs->fs_private;
48938494Sobrien
49038494Sobrien  /*
49138494Sobrien   * Another ping has failed
49238494Sobrien   */
49338494Sobrien  np->np_ping++;
494119682Smbr  if (np->np_ping > 1)
495119682Smbr    srvrlog(fs, "not responding");
49638494Sobrien
49738494Sobrien  /*
49838494Sobrien   * Not known to be up any longer
49938494Sobrien   */
500119682Smbr  if (FSRV_ISUP(fs))
50138494Sobrien    fs->fs_flags &= ~FSF_VALID;
50238494Sobrien
50338494Sobrien  /*
50438494Sobrien   * If ttl has expired then guess that it is dead
50538494Sobrien   */
506174313Sobrien  if (np->np_ttl < clocktime(NULL)) {
50738494Sobrien    int oflags = fs->fs_flags;
508119682Smbr    dlog("ttl has expired");
50938494Sobrien    if ((fs->fs_flags & FSF_DOWN) == 0) {
51038494Sobrien      /*
51138494Sobrien       * Server was up, but is now down.
51238494Sobrien       */
51338494Sobrien      srvrlog(fs, "is down");
51438494Sobrien      fs->fs_flags |= FSF_DOWN | FSF_VALID;
51538494Sobrien      /*
51638494Sobrien       * Since the server is down, the portmap
51738494Sobrien       * information may now be wrong, so it
51838494Sobrien       * must be flushed from the local cache
51938494Sobrien       */
52038494Sobrien      flush_nfs_fhandle_cache(fs);
52138494Sobrien      np->np_error = -1;
522174313Sobrien      check_fs_addr_change(fs); /* check if IP addr of fserver changed */
52338494Sobrien    } else {
52438494Sobrien      /*
52538494Sobrien       * Known to be down
52638494Sobrien       */
52738494Sobrien      if ((fs->fs_flags & FSF_VALID) == 0)
52838494Sobrien	srvrlog(fs, "starts down");
52938494Sobrien      fs->fs_flags |= FSF_VALID;
53038494Sobrien    }
53138494Sobrien    if (oflags != fs->fs_flags && (fs->fs_flags & FSF_WANT))
53238494Sobrien      wakeup_srvr(fs);
533119682Smbr    /*
534119682Smbr     * Reset failed ping count
535119682Smbr     */
536119682Smbr    np->np_ping = 0;
53738494Sobrien  } else {
53838494Sobrien    if (np->np_ping > 1)
53938494Sobrien      dlog("%d pings to %s failed - at most %d allowed", np->np_ping, fs->fs_host, MAX_ALLOWED_PINGS);
54038494Sobrien  }
54138494Sobrien
54238494Sobrien  /*
543119682Smbr   * New RPC xid, so any late responses to the previous ping
544119682Smbr   * get ignored...
545119682Smbr   */
546174313Sobrien  np->np_xid = XID_ALLOC();
547119682Smbr
548119682Smbr  /*
54938494Sobrien   * Run keepalive again
55038494Sobrien   */
55138494Sobrien  nfs_keepalive(fs);
55238494Sobrien}
55338494Sobrien
55438494Sobrien
55538494Sobrien/*
55638494Sobrien * Keep track of whether a server is alive
55738494Sobrien */
55838494Sobrienstatic void
55938494Sobriennfs_keepalive(voidp v)
56038494Sobrien{
56138494Sobrien  fserver *fs = v;
56238494Sobrien  int error;
56338494Sobrien  nfs_private *np = (nfs_private *) fs->fs_private;
56438494Sobrien  int fstimeo = -1;
56538494Sobrien
56638494Sobrien  /*
56738494Sobrien   * Send an NFS ping to this node
56838494Sobrien   */
56938494Sobrien
570174313Sobrien  if (ping_len[fs->fs_version - NFS_VERSION] == 0)
571174313Sobrien    create_ping_payload(fs->fs_version);
57238494Sobrien
57338494Sobrien  /*
57438494Sobrien   * Queue the packet...
57538494Sobrien   */
57638494Sobrien  error = fwd_packet(MK_RPC_XID(RPC_XID_NFSPING, np->np_xid),
577174313Sobrien		     ping_buf[fs->fs_version - NFS_VERSION],
578174313Sobrien		     ping_len[fs->fs_version - NFS_VERSION],
57938494Sobrien		     fs->fs_ip,
58038494Sobrien		     (struct sockaddr_in *) 0,
581174313Sobrien		     (voidp) ((long) np->np_xid), /* cast needed for 64-bit archs */
582174313Sobrien		     nfs_keepalive_callback);
58338494Sobrien
58438494Sobrien  /*
58542633Sobrien   * See if a hard error occurred
58638494Sobrien   */
58738494Sobrien  switch (error) {
58838494Sobrien  case ENETDOWN:
58938494Sobrien  case ENETUNREACH:
59038494Sobrien  case EHOSTDOWN:
59138494Sobrien  case EHOSTUNREACH:
59238494Sobrien    np->np_ping = MAX_ALLOWED_PINGS;	/* immediately down */
59338494Sobrien    np->np_ttl = (time_t) 0;
59438494Sobrien    /*
595174313Sobrien     * This causes an immediate call to nfs_keepalive_timeout
59638494Sobrien     * whenever the server was thought to be up.
59738494Sobrien     * See +++ below.
59838494Sobrien     */
59938494Sobrien    fstimeo = 0;
60038494Sobrien    break;
60138494Sobrien
60238494Sobrien  case 0:
60338494Sobrien    dlog("Sent NFS ping to %s", fs->fs_host);
60438494Sobrien    break;
60538494Sobrien  }
60638494Sobrien
60738494Sobrien  /*
60838494Sobrien   * Back off the ping interval if we are not getting replies and
609174313Sobrien   * the remote system is known to be down.
61038494Sobrien   */
61138494Sobrien  switch (fs->fs_flags & (FSF_DOWN | FSF_VALID)) {
61238494Sobrien  case FSF_VALID:		/* Up */
61338494Sobrien    if (fstimeo < 0)		/* +++ see above */
61438494Sobrien      fstimeo = FAST_NFS_PING;
61538494Sobrien    break;
61638494Sobrien
61738494Sobrien  case FSF_VALID | FSF_DOWN:	/* Down */
61838494Sobrien    fstimeo = fs->fs_pinger;
61938494Sobrien    break;
62038494Sobrien
62138494Sobrien  default:			/* Unknown */
62238494Sobrien    fstimeo = FAST_NFS_PING;
62338494Sobrien    break;
62438494Sobrien  }
62538494Sobrien
62638494Sobrien  dlog("NFS timeout in %d seconds", fstimeo);
62738494Sobrien
628174313Sobrien  fs->fs_cid = timeout(fstimeo, nfs_keepalive_timeout, (voidp) fs);
62938494Sobrien}
63038494Sobrien
63138494Sobrien
632174313Sobrienstatic void
633174313Sobrienstart_nfs_pings(fserver *fs, int pingval)
63438494Sobrien{
635174313Sobrien  if (pingval == 0)	    /* could be because ping mnt option not found */
636174313Sobrien    pingval = AM_PINGER;
637174313Sobrien  /* if pings haven't been initalized, then init them for first time */
638174313Sobrien  if (fs->fs_flags & FSF_PING_UNINIT) {
639174313Sobrien    fs->fs_flags &= ~FSF_PING_UNINIT;
640174313Sobrien    plog(XLOG_INFO, "initializing %s's pinger to %d sec", fs->fs_host, pingval);
641174313Sobrien    goto do_pings;
64238494Sobrien  }
64338494Sobrien
644174313Sobrien  if ((fs->fs_flags & FSF_PINGING)  &&  fs->fs_pinger == pingval) {
645174313Sobrien    dlog("already running pings to %s", fs->fs_host);
646119682Smbr    return;
64738494Sobrien  }
648119682Smbr
649174313Sobrien  /* if got here, then we need to update the ping value */
650174313Sobrien  plog(XLOG_INFO, "changing %s's ping value from %d%s to %d%s",
651174313Sobrien       fs->fs_host,
652174313Sobrien       fs->fs_pinger, (fs->fs_pinger < 0 ? " (off)" : ""),
653174313Sobrien       pingval, (pingval < 0 ? " (off)" : ""));
654174313Sobrien do_pings:
655174313Sobrien  fs->fs_pinger = pingval;
656174313Sobrien
657119682Smbr  if (fs->fs_cid)
658119682Smbr    untimeout(fs->fs_cid);
659119682Smbr  if (pingval < 0) {
660174313Sobrien    srvrlog(fs, "wired up (pings disabled)");
661119682Smbr    fs->fs_flags |= FSF_VALID;
662119682Smbr    fs->fs_flags &= ~FSF_DOWN;
663119682Smbr  } else {
664119682Smbr    fs->fs_flags |= FSF_PINGING;
665119682Smbr    nfs_keepalive(fs);
666119682Smbr  }
66738494Sobrien}
66838494Sobrien
66938494Sobrien
67038494Sobrien/*
67138494Sobrien * Find an nfs server for a host.
67238494Sobrien */
67338494Sobrienfserver *
67438494Sobrienfind_nfs_srvr(mntfs *mf)
67538494Sobrien{
67638494Sobrien  char *host = mf->mf_fo->opt_rhost;
67738494Sobrien  fserver *fs;
67838494Sobrien  int pingval;
67938494Sobrien  mntent_t mnt;
68038494Sobrien  nfs_private *np;
681174313Sobrien  struct hostent *hp = NULL;
682174313Sobrien  struct sockaddr_in *ip = NULL;
68338494Sobrien  u_long nfs_version = 0;	/* default is no version specified */
684174313Sobrien  u_long best_nfs_version = 0;
685174313Sobrien  char *nfs_proto = NULL;	/* no IP protocol either */
686174313Sobrien  int nfs_port = 0;
687174313Sobrien  int nfs_port_opt = 0;
688174313Sobrien  int fserver_is_down = 0;
68938494Sobrien
69038494Sobrien  /*
69138494Sobrien   * Get ping interval from mount options.
69238494Sobrien   * Current only used to decide whether pings
69338494Sobrien   * are required or not.  < 0 = no pings.
69438494Sobrien   */
69538494Sobrien  mnt.mnt_opts = mf->mf_mopts;
69638494Sobrien  pingval = hasmntval(&mnt, "ping");
69738494Sobrien
698174313Sobrien  if (mf->mf_flags & MFF_NFS_SCALEDOWN) {
699174313Sobrien    /*
700174313Sobrien     * the server granted us a filehandle, but we were unable to mount it.
701174313Sobrien     * therefore, scale down to NFSv2/UDP and try again.
702174313Sobrien     */
703174313Sobrien    nfs_version = NFS_VERSION;
704174313Sobrien    nfs_proto = "udp";
705174313Sobrien    plog(XLOG_WARNING, "find_nfs_srvr: NFS mount failed, trying again with NFSv2/UDP");
706174313Sobrien    mf->mf_flags &= ~MFF_NFS_SCALEDOWN;
707174313Sobrien  } else {
708174313Sobrien    /*
709174313Sobrien     * Get the NFS version from the mount options. This is used
710174313Sobrien     * to decide the highest NFS version to try.
711174313Sobrien     */
71238494Sobrien#ifdef MNTTAB_OPT_VERS
713174313Sobrien    nfs_version = hasmntval(&mnt, MNTTAB_OPT_VERS);
71438494Sobrien#endif /* MNTTAB_OPT_VERS */
71538494Sobrien
71638494Sobrien#ifdef MNTTAB_OPT_PROTO
717174313Sobrien    {
718174313Sobrien      char *proto_opt = hasmnteq(&mnt, MNTTAB_OPT_PROTO);
719174313Sobrien      if (proto_opt) {
720174313Sobrien	char **p;
721174313Sobrien	for (p = protocols; *p; p++)
722174313Sobrien	  if (NSTREQ(proto_opt, *p, strlen(*p))) {
723174313Sobrien	    nfs_proto = *p;
724174313Sobrien	    break;
725174313Sobrien	  }
726174313Sobrien	if (*p == NULL)
727174313Sobrien	  plog(XLOG_WARNING, "ignoring unknown protocol option for %s:%s",
728174313Sobrien	       host, mf->mf_fo->opt_rfs);
729174313Sobrien      }
73038494Sobrien    }
73138494Sobrien#endif /* MNTTAB_OPT_PROTO */
73238494Sobrien
73342633Sobrien#ifdef HAVE_NFS_NFSV2_H
734174313Sobrien    /* allow overriding if nfsv2 option is specified in mount options */
735174313Sobrien    if (amu_hasmntopt(&mnt, "nfsv2")) {
736174313Sobrien      nfs_version = NFS_VERSION;/* nullify any ``vers=X'' statements */
737174313Sobrien      nfs_proto = "udp";	/* nullify any ``proto=tcp'' statements */
738174313Sobrien      plog(XLOG_WARNING, "found compatibility option \"nfsv2\": set options vers=2,proto=udp for host %s", host);
739174313Sobrien    }
74042633Sobrien#endif /* HAVE_NFS_NFSV2_H */
74139159Sobrien
742174313Sobrien    /* check if we've globally overridden the NFS version/protocol */
743174313Sobrien    if (gopt.nfs_vers) {
744174313Sobrien      nfs_version = gopt.nfs_vers;
745174313Sobrien      plog(XLOG_INFO, "find_nfs_srvr: force NFS version to %d",
746174313Sobrien	   (int) nfs_version);
747174313Sobrien    }
748174313Sobrien    if (gopt.nfs_proto) {
749174313Sobrien      nfs_proto = gopt.nfs_proto;
750174313Sobrien      plog(XLOG_INFO, "find_nfs_srvr: force NFS protocol transport to %s", nfs_proto);
751174313Sobrien    }
75282800Sobrien  }
75382800Sobrien
75438494Sobrien  /*
75538494Sobrien   * lookup host address and canonical name
75638494Sobrien   */
75738494Sobrien  hp = gethostbyname(host);
75838494Sobrien
75938494Sobrien  /*
76038494Sobrien   * New code from Bob Harris <harris@basil-rathbone.mit.edu>
76138494Sobrien   * Use canonical name to keep track of file server
76238494Sobrien   * information.  This way aliases do not generate
76338494Sobrien   * multiple NFS pingers.  (Except when we're normalizing
76438494Sobrien   * hosts.)
76538494Sobrien   */
76638494Sobrien  if (hp && !(gopt.flags & CFM_NORMALIZE_HOSTNAMES))
76738494Sobrien    host = (char *) hp->h_name;
76838494Sobrien
76938494Sobrien  if (hp) {
77038494Sobrien    switch (hp->h_addrtype) {
77138494Sobrien    case AF_INET:
77238494Sobrien      ip = ALLOC(struct sockaddr_in);
77338494Sobrien      memset((voidp) ip, 0, sizeof(*ip));
774174313Sobrien      /* as per POSIX, sin_len need not be set (used internally by kernel) */
77538494Sobrien      ip->sin_family = AF_INET;
77638494Sobrien      memmove((voidp) &ip->sin_addr, (voidp) hp->h_addr, sizeof(ip->sin_addr));
77738494Sobrien      break;
77838494Sobrien
77938494Sobrien    default:
780174313Sobrien      plog(XLOG_USER, "No IP address for host %s", host);
781174313Sobrien      goto no_dns;
78238494Sobrien    }
78338494Sobrien  } else {
78438494Sobrien    plog(XLOG_USER, "Unknown host: %s", host);
785174313Sobrien    goto no_dns;
78638494Sobrien  }
78738494Sobrien
78838494Sobrien  /*
789174313Sobrien   * This may not be the best way to do things, but it really doesn't make
790174313Sobrien   * sense to query a file server which is marked as 'down' for any
791174313Sobrien   * version/proto combination.
79238494Sobrien   */
793174313Sobrien  ITER(fs, fserver, &nfs_srvr_list) {
794174313Sobrien    if (FSRV_ISDOWN(fs) &&
795174313Sobrien	STREQ(host, fs->fs_host)) {
796174313Sobrien      plog(XLOG_WARNING, "fileserver %s is already hung - not running NFS proto/version discovery", host);
797174313Sobrien      fs->fs_refc++;
798174313Sobrien      if (ip)
799174313Sobrien	XFREE(ip);
800174313Sobrien      return fs;
801174313Sobrien    }
802174313Sobrien  }
803174313Sobrien
804174313Sobrien  /*
805174313Sobrien   * Get the NFS Version, and verify server is up.
806174313Sobrien   * If the client only supports NFSv2, hardcode it but still try to
807174313Sobrien   * contact the remote portmapper to see if the service is running.
808174313Sobrien   */
809174313Sobrien#ifndef HAVE_FS_NFS3
810174313Sobrien  nfs_version = NFS_VERSION;
811174313Sobrien  nfs_proto = "udp";
812174313Sobrien  plog(XLOG_INFO, "The client supports only NFS(2,udp)");
813174313Sobrien#endif /* not HAVE_FS_NFS3 */
814174313Sobrien
815174313Sobrien
816174313Sobrien  if (amu_hasmntopt(&mnt, MNTTAB_OPT_PUBLIC)) {
817174313Sobrien    /*
818174313Sobrien     * Use WebNFS to obtain file handles.
819174313Sobrien     */
820174313Sobrien    mf->mf_flags |= MFF_WEBNFS;
821174313Sobrien    plog(XLOG_INFO, "%s option used, NOT contacting the portmapper on %s",
822174313Sobrien	 MNTTAB_OPT_PUBLIC, host);
823174313Sobrien    /*
824174313Sobrien     * Prefer NFSv3/tcp if the client supports it (cf. RFC 2054, 7).
825174313Sobrien     */
826174313Sobrien    if (!nfs_version) {
82738494Sobrien#ifdef HAVE_FS_NFS3
828174313Sobrien      nfs_version = NFS_VERSION3;
829174313Sobrien#else /* not HAVE_FS_NFS3 */
830174313Sobrien      nfs_version = NFS_VERSION;
831174313Sobrien#endif /* not HAVE_FS_NFS3 */
832174313Sobrien      plog(XLOG_INFO, "No NFS version specified, will use NFSv%d",
833174313Sobrien	   (int) nfs_version);
834174313Sobrien    }
835174313Sobrien    if (!nfs_proto) {
836174313Sobrien#if defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3)
837174313Sobrien      nfs_proto = "tcp";
838174313Sobrien#else /* not defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3) */
839174313Sobrien      nfs_proto = "udp";
840174313Sobrien#endif /* not defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3) */
841174313Sobrien      plog(XLOG_INFO, "No NFS protocol transport specified, will use %s",
842174313Sobrien	   nfs_proto);
843174313Sobrien    }
844174313Sobrien  } else {
84538494Sobrien    /*
84638494Sobrien     * Find the best combination of NFS version and protocol.
84738494Sobrien     * When given a choice, use the highest available version,
84838494Sobrien     * and use TCP over UDP if available.
84938494Sobrien     */
850174313Sobrien    if (check_pmap_up(host, ip)) {
851174313Sobrien      if (nfs_proto) {
852174313Sobrien	best_nfs_version = get_nfs_version(host, ip, nfs_version, nfs_proto);
853174313Sobrien	nfs_port = ip->sin_port;
854174313Sobrien      }
855174313Sobrien#ifdef MNTTAB_OPT_PROTO
856174313Sobrien      else {
857174313Sobrien	u_int proto_nfs_version;
858174313Sobrien	char **p;
85938494Sobrien
860174313Sobrien	for (p = protocols; *p; p++) {
861174313Sobrien	  proto_nfs_version = get_nfs_version(host, ip, nfs_version, *p);
86238494Sobrien
863174313Sobrien	  if (proto_nfs_version > best_nfs_version) {
864174313Sobrien	    best_nfs_version = proto_nfs_version;
865174313Sobrien	    nfs_proto = *p;
866174313Sobrien	    nfs_port = ip->sin_port;
867174313Sobrien	  }
86838494Sobrien	}
86938494Sobrien      }
870174313Sobrien#endif /* MNTTAB_OPT_PROTO */
871174313Sobrien    } else {
872174313Sobrien      plog(XLOG_INFO, "portmapper service not running on %s", host);
87338494Sobrien    }
87438494Sobrien
875174313Sobrien    /* use the portmapper results only nfs_version is not set yet */
876174313Sobrien    if (!best_nfs_version) {
87738494Sobrien      /*
87838494Sobrien       * If the NFS server is down or does not support the portmapper call
87938494Sobrien       * (such as certain Novell NFS servers) we mark it as version 2 and we
880174313Sobrien       * let the nfs code deal with the case when it is down.  If/when the
881174313Sobrien       * server comes back up and it can support NFSv3 and/or TCP, it will
88238494Sobrien       * use those.
88338494Sobrien       */
884174313Sobrien      if (nfs_version == 0) {
885174313Sobrien	nfs_version = NFS_VERSION;
886174313Sobrien	nfs_proto = "udp";
887174313Sobrien      }
888174313Sobrien      plog(XLOG_INFO, "NFS service not running on %s", host);
889174313Sobrien      fserver_is_down = 1;
890174313Sobrien    } else {
891174313Sobrien      if (nfs_version == 0)
892174313Sobrien	nfs_version = best_nfs_version;
893174313Sobrien      plog(XLOG_INFO, "Using NFS version %d, protocol %s on host %s",
894174313Sobrien	   (int) nfs_version, nfs_proto, host);
89538494Sobrien    }
89638494Sobrien  }
89738494Sobrien
898174313Sobrien  /*
899174313Sobrien   * Determine the NFS port.
900174313Sobrien   *
901174313Sobrien   * A valid "port" mount option overrides anything else.
902174313Sobrien   * If the port has been determined from the portmapper, use that.
903174313Sobrien   * Default to NFS_PORT otherwise (cf. RFC 2054, 3).
904174313Sobrien   */
905174313Sobrien  nfs_port_opt = hasmntval(&mnt, MNTTAB_OPT_PORT);
906174313Sobrien  if (nfs_port_opt > 0)
907174313Sobrien    nfs_port = htons(nfs_port_opt);
908174313Sobrien  if (!nfs_port)
909174313Sobrien    nfs_port = htons(NFS_PORT);
91038494Sobrien
911174313Sobrien  dlog("find_nfs_srvr: using port %d for nfs on %s",
912174313Sobrien       (int) ntohs(nfs_port), host);
913174313Sobrien  ip->sin_port = nfs_port;
91438494Sobrien
915174313Sobrienno_dns:
91638494Sobrien  /*
91742633Sobrien   * Try to find an existing fs server structure for this host.
91838494Sobrien   * Note that differing versions or protocols have their own structures.
91938494Sobrien   * XXX: Need to fix the ping mechanism to actually use the NFS protocol
92038494Sobrien   * chosen here (right now it always uses datagram sockets).
92138494Sobrien   */
92238494Sobrien  ITER(fs, fserver, &nfs_srvr_list) {
92338494Sobrien    if (STREQ(host, fs->fs_host) &&
92438494Sobrien 	nfs_version == fs->fs_version &&
92538494Sobrien	STREQ(nfs_proto, fs->fs_proto)) {
92638494Sobrien      /*
927174313Sobrien       * fill in the IP address -- this is only needed
928174313Sobrien       * if there is a chance an IP address will change
929174313Sobrien       * between mounts.
930174313Sobrien       * Mike Mitchell, mcm@unx.sas.com, 09/08/93
93138494Sobrien       */
932174313Sobrien      if (hp && fs->fs_ip &&
933174313Sobrien	  memcmp((voidp) &fs->fs_ip->sin_addr,
934174313Sobrien		 (voidp) hp->h_addr,
935174313Sobrien		 sizeof(fs->fs_ip->sin_addr)) != 0) {
936174313Sobrien	struct in_addr ia;
937174313Sobrien	char *old_ipaddr, *new_ipaddr;
938174313Sobrien	old_ipaddr = strdup(inet_ntoa(fs->fs_ip->sin_addr));
939174313Sobrien	memmove((voidp) &ia, (voidp) hp->h_addr, sizeof(struct in_addr));
940174313Sobrien	new_ipaddr = inet_ntoa(ia);	/* ntoa uses static buf */
941174313Sobrien	plog(XLOG_WARNING, "fileserver %s changed ip: %s -> %s",
942174313Sobrien	     fs->fs_host, old_ipaddr, new_ipaddr);
943174313Sobrien	XFREE(old_ipaddr);
944174313Sobrien	flush_nfs_fhandle_cache(fs);
945174313Sobrien	memmove((voidp) &fs->fs_ip->sin_addr, (voidp) hp->h_addr, sizeof(fs->fs_ip->sin_addr));
946174313Sobrien      }
947174313Sobrien
948174313Sobrien      /*
949174313Sobrien       * If the new file systems doesn't use WebNFS, the nfs pings may
950174313Sobrien       * try to contact the portmapper.
951174313Sobrien       */
952174313Sobrien      if (!(mf->mf_flags & MFF_WEBNFS))
953174313Sobrien	fs->fs_flags &= ~FSF_WEBNFS;
954174313Sobrien
955174313Sobrien      /* check if pingval needs to be updated/set/reset */
956174313Sobrien      start_nfs_pings(fs, pingval);
957174313Sobrien
958174313Sobrien      /*
959174313Sobrien       * Following if statement from Mike Mitchell <mcm@unx.sas.com>
960174313Sobrien       * Initialize the ping data if we aren't pinging now.  The np_ttl and
961174313Sobrien       * np_ping fields are especially important.
962174313Sobrien       */
96338494Sobrien      if (!(fs->fs_flags & FSF_PINGING)) {
96438494Sobrien	np = (nfs_private *) fs->fs_private;
96538494Sobrien	np->np_mountd_inval = TRUE;
966174313Sobrien	np->np_xid = XID_ALLOC();
96738494Sobrien	np->np_error = -1;
96838494Sobrien	np->np_ping = 0;
96938494Sobrien	/*
97038494Sobrien	 * Initially the server will be deemed dead
97138494Sobrien	 * after MAX_ALLOWED_PINGS of the fast variety
97238494Sobrien	 * have failed.
97338494Sobrien	 */
974174313Sobrien	np->np_ttl = MAX_ALLOWED_PINGS * FAST_NFS_PING + clocktime(NULL) - 1;
975174313Sobrien	start_nfs_pings(fs, pingval);
976174313Sobrien	if (fserver_is_down)
977174313Sobrien	  fs->fs_flags |= FSF_VALID | FSF_DOWN;
97838494Sobrien      }
97938494Sobrien
98038494Sobrien      fs->fs_refc++;
98138494Sobrien      if (ip)
98238494Sobrien	XFREE(ip);
98338494Sobrien      return fs;
98438494Sobrien    }
98538494Sobrien  }
98638494Sobrien
98738494Sobrien  /*
98838494Sobrien   * Get here if we can't find an entry
98938494Sobrien   */
99038494Sobrien
99138494Sobrien  /*
99238494Sobrien   * Allocate a new server
99338494Sobrien   */
99438494Sobrien  fs = ALLOC(struct fserver);
99538494Sobrien  fs->fs_refc = 1;
99638494Sobrien  fs->fs_host = strdup(hp ? hp->h_name : "unknown_hostname");
99738494Sobrien  if (gopt.flags & CFM_NORMALIZE_HOSTNAMES)
99838494Sobrien    host_normalize(&fs->fs_host);
99938494Sobrien  fs->fs_ip = ip;
100038494Sobrien  fs->fs_cid = 0;
100138494Sobrien  if (ip) {
100238494Sobrien    fs->fs_flags = FSF_DOWN;	/* Starts off down */
100338494Sobrien  } else {
100438494Sobrien    fs->fs_flags = FSF_ERROR | FSF_VALID;
100538494Sobrien    mf->mf_flags |= MFF_ERROR;
100638494Sobrien    mf->mf_error = ENOENT;
100738494Sobrien  }
1008174313Sobrien  if (mf->mf_flags & MFF_WEBNFS)
1009174313Sobrien    fs->fs_flags |= FSF_WEBNFS;
101038494Sobrien  fs->fs_version = nfs_version;
101138494Sobrien  fs->fs_proto = nfs_proto;
101238494Sobrien  fs->fs_type = MNTTAB_TYPE_NFS;
101338494Sobrien  fs->fs_pinger = AM_PINGER;
1014174313Sobrien  fs->fs_flags |= FSF_PING_UNINIT; /* pinger hasn't been initialized */
101538494Sobrien  np = ALLOC(struct nfs_private);
101638494Sobrien  memset((voidp) np, 0, sizeof(*np));
101738494Sobrien  np->np_mountd_inval = TRUE;
1018174313Sobrien  np->np_xid = XID_ALLOC();
101938494Sobrien  np->np_error = -1;
102038494Sobrien
102138494Sobrien  /*
102238494Sobrien   * Initially the server will be deemed dead after
102338494Sobrien   * MAX_ALLOWED_PINGS of the fast variety have failed.
102438494Sobrien   */
1025174313Sobrien  np->np_ttl = clocktime(NULL) + MAX_ALLOWED_PINGS * FAST_NFS_PING - 1;
102638494Sobrien  fs->fs_private = (voidp) np;
102738494Sobrien  fs->fs_prfree = (void (*)(voidp)) free;
102838494Sobrien
1029174313Sobrien  if (!FSRV_ERROR(fs)) {
1030174313Sobrien    /* start of keepalive timer, first updating pingval */
103138494Sobrien    start_nfs_pings(fs, pingval);
1032174313Sobrien    if (fserver_is_down)
1033174313Sobrien      fs->fs_flags |= FSF_VALID | FSF_DOWN;
103438494Sobrien  }
103538494Sobrien
103638494Sobrien  /*
103738494Sobrien   * Add to list of servers
103838494Sobrien   */
103938494Sobrien  ins_que(&fs->fs_q, &nfs_srvr_list);
104038494Sobrien
104138494Sobrien  return fs;
104238494Sobrien}
1043