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