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