]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/amd/amd/srvr_nfs.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / amd / amd / srvr_nfs.c
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 */
75 typedef 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 */
85 qelem nfs_srvr_list = {&nfs_srvr_list, &nfs_srvr_list};
86
87 /* statics */
88 static 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 */
96 static int ping_len[NUM_NFS_VERS];
97 static 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  */
109 static char *protocols[] = { "tcp", "udp", NULL };
110 #endif /* defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3) */
111
112 /* forward definitions */
113 static void nfs_keepalive(voidp);
114
115
116 /*
117  * Flush cached data for an fserver (or for all, if fs==NULL)
118  */
119 void
120 flush_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  */
139 static void
140 create_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  */
183 static void
184 got_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  */
234 static int
235 call_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
270 static void
271 recompute_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
310 int
311 get_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  */
359 static void
360 nfs_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
441 static void
442 check_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  */
484 static void
485 nfs_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  */
558 static void
559 nfs_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
632 static void
633 start_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  */
673 fserver *
674 find_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
915 no_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 }