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