Merge branch 'vendor/TNFTP'
[dragonfly.git] / contrib / amd / amd / srvr_nfs.c
1 /*
2  * Copyright (c) 1997-1999 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  *      %W% (Berkeley) %G%
40  *
41  * $Id: srvr_nfs.c,v 1.5 1999/09/08 23:36:39 ezk Exp $
42  * $FreeBSD: src/contrib/amd/amd/srvr_nfs.c,v 1.4 1999/09/15 05:45:13 obrien Exp $
43  * $DragonFly: src/contrib/amd/amd/srvr_nfs.c,v 1.2 2003/06/17 04:23:56 dillon Exp $
44  *
45  */
46
47 /*
48  * NFS server modeling
49  */
50
51 #ifdef HAVE_CONFIG_H
52 # include <config.h>
53 #endif /* HAVE_CONFIG_H */
54 #include <am_defs.h>
55 #include <amd.h>
56
57 /*
58  * Number of pings allowed to fail before host is declared down
59  * - three-fifths of the allowed mount time...
60  */
61 #define MAX_ALLOWED_PINGS       (3 + /* for luck ... */ 1)
62
63 /*
64  * How often to ping when starting a new server
65  */
66 #define FAST_NFS_PING           3
67
68 #if (FAST_NFS_PING * MAX_ALLOWED_PINGS) >= ALLOWED_MOUNT_TIME
69 # error: sanity check failed in srvr_nfs.c
70 /*
71  * you cannot do things this way...
72  * sufficient fast pings must be given the chance to fail
73  * within the allowed mount time
74  */
75 #endif /* (FAST_NFS_PING * MAX_ALLOWED_PINGS) >= ALLOWED_MOUNT_TIME */
76
77 #define NPXID_ALLOC(struct )    (++np_xid)
78
79 /* structures and typedefs */
80 typedef struct nfs_private {
81   u_short np_mountd;            /* Mount daemon port number */
82   char np_mountd_inval;         /* Port *may* be invalid */
83   int np_ping;                  /* Number of failed ping attempts */
84   time_t np_ttl;                /* Time when server is thought dead */
85   int np_xid;                   /* RPC transaction id for pings */
86   int np_error;                 /* Error during portmap request */
87 } nfs_private;
88
89 /* globals */
90 qelem nfs_srvr_list = {&nfs_srvr_list, &nfs_srvr_list};
91
92 /* statics */
93 static int np_xid;              /* For NFS pings */
94 static int ping_len;
95 static char ping_buf[sizeof(struct rpc_msg) + 32];
96
97 #if defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3)
98 /* protocols we know about, in order of preference */
99 static char *protocols[] = { "tcp", "udp", NULL };
100 #endif /* defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3) */
101
102 /* forward definitions */
103 static void nfs_keepalive(voidp);
104
105
106
107 /*
108  * Flush any cached data
109  */
110 void
111 flush_srvr_nfs_cache(void)
112 {
113   fserver *fs = 0;
114
115   ITER(fs, fserver, &nfs_srvr_list) {
116     nfs_private *np = (nfs_private *) fs->fs_private;
117     if (np) {
118       np->np_mountd_inval = TRUE;
119       np->np_error = -1;
120     }
121   }
122 }
123
124
125 /*
126  * Startup the NFS ping for a particular version.
127  */
128 static void
129 start_ping(u_long nfs_version)
130 {
131   XDR ping_xdr;
132   struct rpc_msg ping_msg;
133
134   /*
135    * Non nfs mounts like /afs/glue.umd.edu have ended up here.
136    */
137   if (nfs_version == 0) {
138     nfs_version = NFS_VERSION;
139     plog(XLOG_WARNING, "start_ping: nfs_version = 0 fixed");
140   }
141   plog(XLOG_INFO, "start_ping: nfs_version: %d", (int) nfs_version);
142
143   rpc_msg_init(&ping_msg, NFS_PROGRAM, nfs_version, NFSPROC_NULL);
144
145   /*
146    * Create an XDR endpoint
147    */
148   xdrmem_create(&ping_xdr, ping_buf, sizeof(ping_buf), XDR_ENCODE);
149
150   /*
151    * Create the NFS ping message
152    */
153   if (!xdr_callmsg(&ping_xdr, &ping_msg)) {
154     plog(XLOG_ERROR, "Couldn't create ping RPC message");
155     going_down(3);
156   }
157   /*
158    * Find out how long it is
159    */
160   ping_len = xdr_getpos(&ping_xdr);
161
162   /*
163    * Destroy the XDR endpoint - we don't need it anymore
164    */
165   xdr_destroy(&ping_xdr);
166 }
167
168
169 /*
170  * Called when a portmap reply arrives
171  */
172 static void
173 got_portmap(voidp pkt, int len, struct sockaddr_in * sa, struct sockaddr_in * ia, voidp idv, int done)
174 {
175   fserver *fs2 = (fserver *) idv;
176   fserver *fs = 0;
177
178   /*
179    * Find which fileserver we are talking about
180    */
181   ITER(fs, fserver, &nfs_srvr_list)
182   if (fs == fs2)
183       break;
184
185   if (fs == fs2) {
186     u_long port = 0;    /* XXX - should be short but protocol is naff */
187     int error = done ? pickup_rpc_reply(pkt, len, (voidp) &port, (XDRPROC_T_TYPE) xdr_u_long) : -1;
188     nfs_private *np = (nfs_private *) fs->fs_private;
189
190     if (!error && port) {
191 #ifdef DEBUG
192       dlog("got port (%d) for mountd on %s", (int) port, fs->fs_host);
193 #endif /* DEBUG */
194       /*
195        * Grab the port number.  Portmap sends back
196        * an u_long in native ordering, so it
197        * needs converting to a u_short in
198        * network ordering.
199        */
200       np->np_mountd = htons((u_short) port);
201       np->np_mountd_inval = FALSE;
202       np->np_error = 0;
203     } else {
204 #ifdef DEBUG
205       dlog("Error fetching port for mountd on %s", fs->fs_host);
206       dlog("\t error=%d, port=%d", error, (int) port);
207 #endif /* DEBUG */
208       /*
209        * Almost certainly no mountd running on remote host
210        */
211       np->np_error = error ? error : ETIMEDOUT;
212     }
213
214     if (fs->fs_flags & FSF_WANT)
215       wakeup_srvr(fs);
216   } else if (done) {
217 #ifdef DEBUG
218     dlog("Got portmap for old port request");
219 #endif /* DEBUG */
220   } else {
221 #ifdef DEBUG
222     dlog("portmap request timed out");
223 #endif /* DEBUG */
224   }
225 }
226
227
228 /*
229  * Obtain portmap information
230  */
231 static int
232 call_portmap(fserver *fs, AUTH * auth, u_long prog, u_long vers, u_long prot)
233 {
234   struct rpc_msg pmap_msg;
235   int len;
236   char iobuf[UDPMSGSIZE];
237   int error;
238   struct pmap pmap;
239
240   rpc_msg_init(&pmap_msg, PMAPPROG, PMAPVERS, PMAPPROC_NULL);
241   pmap.pm_prog = prog;
242   pmap.pm_vers = vers;
243   pmap.pm_prot = prot;
244   pmap.pm_port = 0;
245   len = make_rpc_packet(iobuf,
246                         sizeof(iobuf),
247                         PMAPPROC_GETPORT,
248                         &pmap_msg,
249                         (voidp) &pmap,
250                         (XDRPROC_T_TYPE) xdr_pmap,
251                         auth);
252   if (len > 0) {
253     struct sockaddr_in sin;
254     memset((voidp) &sin, 0, sizeof(sin));
255     sin = *fs->fs_ip;
256     sin.sin_port = htons(PMAPPORT);
257     error = fwd_packet(RPC_XID_PORTMAP, (voidp) iobuf, len,
258                        &sin, &sin, (voidp) fs, got_portmap);
259   } else {
260     error = -len;
261   }
262
263   return error;
264 }
265
266
267 static void
268 recompute_portmap(fserver *fs)
269 {
270   int error;
271   u_long mnt_version;
272
273   if (nfs_auth)
274     error = 0;
275   else
276     error = make_nfs_auth();
277
278   if (error) {
279     nfs_private *np = (nfs_private *) fs->fs_private;
280     np->np_error = error;
281     return;
282   }
283
284   if (fs->fs_version == 0)
285     plog(XLOG_WARNING, "recompute_portmap: nfs_version = 0 fixed");
286
287   plog(XLOG_INFO, "recompute_portmap: NFS version %d", (int) fs->fs_version);
288 #ifdef HAVE_FS_NFS3
289   if (fs->fs_version == NFS_VERSION3)
290     mnt_version = MOUNTVERS3;
291   else
292 #endif /* HAVE_FS_NFS3 */
293     mnt_version = MOUNTVERS;
294
295   plog(XLOG_INFO, "Using MOUNT version: %d", (int) mnt_version);
296   call_portmap(fs, nfs_auth, MOUNTPROG, mnt_version, (u_long) IPPROTO_UDP);
297 }
298
299
300 /*
301  * This is called when we get a reply to an RPC ping.
302  * The value of id was taken from the nfs_private
303  * structure when the ping was transmitted.
304  */
305 static void
306 nfs_pinged(voidp pkt, int len, struct sockaddr_in * sp, struct sockaddr_in * tsp, voidp idv, int done)
307 {
308   int xid = (long) idv;         /* for 64-bit archs */
309   fserver *fs;
310 #ifdef DEBUG
311   int found_map = 0;
312 #endif /* DEBUG */
313
314   if (!done)
315     return;
316
317   /*
318    * For each node...
319    */
320   ITER(fs, fserver, &nfs_srvr_list) {
321     nfs_private *np = (nfs_private *) fs->fs_private;
322     if (np->np_xid == xid && (fs->fs_flags & FSF_PINGING)) {
323       /*
324        * Reset the ping counter.
325        * Update the keepalive timer.
326        * Log what happened.
327        */
328       if (fs->fs_flags & FSF_DOWN) {
329         fs->fs_flags &= ~FSF_DOWN;
330         if (fs->fs_flags & FSF_VALID) {
331           srvrlog(fs, "is up");
332         } else {
333           if (np->np_ping > 1)
334             srvrlog(fs, "ok");
335 #ifdef DEBUG
336           else
337             srvrlog(fs, "starts up");
338 #endif /* DEBUG */
339           fs->fs_flags |= FSF_VALID;
340         }
341
342         map_flush_srvr(fs);
343       } else {
344         if (fs->fs_flags & FSF_VALID) {
345 #ifdef DEBUG
346           dlog("file server %s type nfs is still up", fs->fs_host);
347 #endif /* DEBUG */
348         } else {
349           if (np->np_ping > 1)
350             srvrlog(fs, "ok");
351           fs->fs_flags |= FSF_VALID;
352         }
353       }
354
355       /*
356        * Adjust ping interval
357        */
358       untimeout(fs->fs_cid);
359       fs->fs_cid = timeout(fs->fs_pinger, nfs_keepalive, (voidp) fs);
360
361       /*
362        * Update ttl for this server
363        */
364       np->np_ttl = clocktime() +
365         (MAX_ALLOWED_PINGS - 1) * FAST_NFS_PING + fs->fs_pinger - 1;
366
367       /*
368        * New RPC xid...
369        */
370       np->np_xid = NPXID_ALLOC(struct );
371
372       /*
373        * Failed pings is zero...
374        */
375       np->np_ping = 0;
376
377       /*
378        * Recompute portmap information if not known
379        */
380       if (np->np_mountd_inval)
381         recompute_portmap(fs);
382
383 #ifdef DEBUG
384       found_map++;
385 #endif /* DEBUG */
386       break;
387     }
388   }
389
390 #ifdef DEBUG
391   if (found_map == 0)
392     dlog("Spurious ping packet");
393 #endif /* DEBUG */
394 }
395
396
397 /*
398  * Called when no ping-reply received
399  */
400 static void
401 nfs_timed_out(voidp v)
402 {
403   fserver *fs = v;
404   nfs_private *np = (nfs_private *) fs->fs_private;
405
406   /*
407    * Another ping has failed
408    */
409   np->np_ping++;
410
411   /*
412    * Not known to be up any longer
413    */
414   if (FSRV_ISUP(fs)) {
415     fs->fs_flags &= ~FSF_VALID;
416     if (np->np_ping > 1)
417       srvrlog(fs, "not responding");
418   }
419
420   /*
421    * If ttl has expired then guess that it is dead
422    */
423   if (np->np_ttl < clocktime()) {
424     int oflags = fs->fs_flags;
425     if ((fs->fs_flags & FSF_DOWN) == 0) {
426       /*
427        * Server was up, but is now down.
428        */
429       srvrlog(fs, "is down");
430       fs->fs_flags |= FSF_DOWN | FSF_VALID;
431       /*
432        * Since the server is down, the portmap
433        * information may now be wrong, so it
434        * must be flushed from the local cache
435        */
436       flush_nfs_fhandle_cache(fs);
437       np->np_error = -1;
438     } else {
439       /*
440        * Known to be down
441        */
442 #ifdef DEBUG
443       if ((fs->fs_flags & FSF_VALID) == 0)
444         srvrlog(fs, "starts down");
445 #endif /* DEBUG */
446       fs->fs_flags |= FSF_VALID;
447     }
448     if (oflags != fs->fs_flags && (fs->fs_flags & FSF_WANT))
449       wakeup_srvr(fs);
450   } else {
451 #ifdef DEBUG
452     if (np->np_ping > 1)
453       dlog("%d pings to %s failed - at most %d allowed", np->np_ping, fs->fs_host, MAX_ALLOWED_PINGS);
454 #endif /* DEBUG */
455   }
456
457   /*
458    * Run keepalive again
459    */
460   nfs_keepalive(fs);
461 }
462
463
464 /*
465  * Keep track of whether a server is alive
466  */
467 static void
468 nfs_keepalive(voidp v)
469 {
470   fserver *fs = v;
471   int error;
472   nfs_private *np = (nfs_private *) fs->fs_private;
473   int fstimeo = -1;
474
475   /*
476    * Send an NFS ping to this node
477    */
478
479   if (ping_len == 0)
480     start_ping(fs->fs_version);
481
482   /*
483    * Queue the packet...
484    */
485   error = fwd_packet(MK_RPC_XID(RPC_XID_NFSPING, np->np_xid),
486                      (voidp) ping_buf,
487                      ping_len,
488                      fs->fs_ip,
489                      (struct sockaddr_in *) 0,
490                      (voidp) ((long) np->np_xid), /* for 64-bit archs */
491                      nfs_pinged);
492
493   /*
494    * See if a hard error occurred
495    */
496   switch (error) {
497   case ENETDOWN:
498   case ENETUNREACH:
499   case EHOSTDOWN:
500   case EHOSTUNREACH:
501     np->np_ping = MAX_ALLOWED_PINGS;    /* immediately down */
502     np->np_ttl = (time_t) 0;
503     /*
504      * This causes an immediate call to nfs_timed_out
505      * whenever the server was thought to be up.
506      * See +++ below.
507      */
508     fstimeo = 0;
509     break;
510
511   case 0:
512 #ifdef DEBUG
513     dlog("Sent NFS ping to %s", fs->fs_host);
514 #endif /* DEBUG */
515     break;
516   }
517
518   /*
519    * Back off the ping interval if we are not getting replies and
520    * the remote system is know to be down.
521    */
522   switch (fs->fs_flags & (FSF_DOWN | FSF_VALID)) {
523   case FSF_VALID:               /* Up */
524     if (fstimeo < 0)            /* +++ see above */
525       fstimeo = FAST_NFS_PING;
526     break;
527
528   case FSF_VALID | FSF_DOWN:    /* Down */
529     fstimeo = fs->fs_pinger;
530     break;
531
532   default:                      /* Unknown */
533     fstimeo = FAST_NFS_PING;
534     break;
535   }
536
537 #ifdef DEBUG
538   dlog("NFS timeout in %d seconds", fstimeo);
539 #endif /* DEBUG */
540
541   fs->fs_cid = timeout(fstimeo, nfs_timed_out, (voidp) fs);
542 }
543
544
545 int
546 nfs_srvr_port(fserver *fs, u_short * port, voidp wchan)
547 {
548   int error = -1;
549   if ((fs->fs_flags & FSF_VALID) == FSF_VALID) {
550     if ((fs->fs_flags & FSF_DOWN) == 0) {
551       nfs_private *np = (nfs_private *) fs->fs_private;
552       if (np->np_error == 0) {
553         *port = np->np_mountd;
554         error = 0;
555       } else {
556         error = np->np_error;
557       }
558       /*
559        * Now go get the port mapping again in case it changed.
560        * Note that it is used even if (np_mountd_inval)
561        * is True.  The flag is used simply as an
562        * indication that the mountd may be invalid, not
563        * that it is known to be invalid.
564        */
565       if (np->np_mountd_inval)
566         recompute_portmap(fs);
567       else
568         np->np_mountd_inval = TRUE;
569     } else {
570       error = EWOULDBLOCK;
571     }
572   }
573   if (error < 0 && wchan && !(fs->fs_flags & FSF_WANT)) {
574     /*
575      * If a wait channel is supplied, and no
576      * error has yet occurred, then arrange
577      * that a wakeup is done on the wait channel,
578      * whenever a wakeup is done on this fs node.
579      * Wakeup's are done on the fs node whenever
580      * it changes state - thus causing control to
581      * come back here and new, better things to happen.
582      */
583     fs->fs_flags |= FSF_WANT;
584     sched_task(wakeup_task, wchan, (voidp) fs);
585   }
586   return error;
587 }
588
589
590 static void
591 start_nfs_pings(fserver *fs, int pingval)
592 {
593   if (!(fs->fs_flags & FSF_PINGING)) {
594     fs->fs_flags |= FSF_PINGING;
595     if (fs->fs_cid)
596       untimeout(fs->fs_cid);
597     if (pingval < 0) {
598       srvrlog(fs, "wired up");
599       fs->fs_flags |= FSF_VALID;
600       fs->fs_flags &= ~FSF_DOWN;
601     } else {
602       nfs_keepalive(fs);
603     }
604   } else {
605 #ifdef DEBUG
606     dlog("Already running pings to %s", fs->fs_host);
607 #endif /* DEBUG */
608   }
609 }
610
611
612 /*
613  * Find an nfs server for a host.
614  */
615 fserver *
616 find_nfs_srvr(mntfs *mf)
617 {
618   char *host = mf->mf_fo->opt_rhost;
619   char *nfs_proto = NULL;
620   fserver *fs;
621   int pingval;
622   mntent_t mnt;
623   nfs_private *np;
624   struct hostent *hp = 0;
625   struct sockaddr_in *ip;
626   u_long nfs_version = 0;       /* default is no version specified */
627 #ifdef MNTTAB_OPT_PROTO
628   char *rfsname = mf->mf_fo->opt_rfs;
629 #endif /* MNTTAB_OPT_PROTO */
630
631   /*
632    * Get ping interval from mount options.
633    * Current only used to decide whether pings
634    * are required or not.  < 0 = no pings.
635    */
636   mnt.mnt_opts = mf->mf_mopts;
637   pingval = hasmntval(&mnt, "ping");
638
639   /*
640    * Get the NFS version from the mount options. This is used
641    * to decide the highest NFS version to try.
642    */
643 #ifdef MNTTAB_OPT_VERS
644   nfs_version = hasmntval(&mnt, MNTTAB_OPT_VERS);
645 #endif /* MNTTAB_OPT_VERS */
646
647 #ifdef MNTTAB_OPT_PROTO
648   {
649     char *proto_opt = hasmntopt(&mnt, MNTTAB_OPT_PROTO);
650     if (proto_opt) {
651       char **p;
652
653       proto_opt += sizeof(MNTTAB_OPT_PROTO) - 1; /* skip the "proto" */
654
655       for (p = protocols; *p; p ++)
656         if (proto_opt[0] == '=' &&
657             NSTREQ(&proto_opt[1], *p, strlen(*p))) {
658           nfs_proto = *p;
659           break;
660         }
661       if (*p == NULL)
662         plog(XLOG_WARNING, "ignoring unknown protocol option for %s:%s",
663              host, rfsname);
664     }
665   }
666 #endif /* MNTTAB_OPT_PROTO */
667
668 #ifdef HAVE_NFS_NFSV2_H
669   /* allow overriding if nfsv2 option is specified in mount options */
670   if (hasmntopt(&mnt, "nfsv2")) {
671     nfs_version = (u_long) 2;   /* nullify any ``vers=X'' statements */
672     nfs_proto = "udp";          /* nullify any ``proto=tcp'' statements */
673     plog(XLOG_WARNING, "found compatiblity option \"nfsv2\": set options vers=2, proto=udp for host %s", host);
674   }
675 #endif /* HAVE_NFS_NFSV2_H */
676
677   /*
678    * lookup host address and canonical name
679    */
680   hp = gethostbyname(host);
681
682   /*
683    * New code from Bob Harris <harris@basil-rathbone.mit.edu>
684    * Use canonical name to keep track of file server
685    * information.  This way aliases do not generate
686    * multiple NFS pingers.  (Except when we're normalizing
687    * hosts.)
688    */
689   if (hp && !(gopt.flags & CFM_NORMALIZE_HOSTNAMES))
690     host = (char *) hp->h_name;
691
692   if (hp) {
693     switch (hp->h_addrtype) {
694     case AF_INET:
695       ip = ALLOC(struct sockaddr_in);
696       memset((voidp) ip, 0, sizeof(*ip));
697       ip->sin_family = AF_INET;
698       memmove((voidp) &ip->sin_addr, (voidp) hp->h_addr, sizeof(ip->sin_addr));
699
700       ip->sin_port = htons(NFS_PORT);
701       break;
702
703     default:
704       ip = 0;
705       break;
706     }
707   } else {
708     plog(XLOG_USER, "Unknown host: %s", host);
709     ip = 0;
710   }
711
712   /*
713    * Get the NFS Version, and verify server is up. Probably no
714    * longer need to start server down below.
715    */
716   if (ip) {
717 #ifdef HAVE_FS_NFS3
718     /*
719      * Find the best combination of NFS version and protocol.
720      * When given a choice, use the highest available version,
721      * and use TCP over UDP if available.
722      */
723     if (nfs_proto)
724       nfs_version = get_nfs_version(host, ip, nfs_version, nfs_proto);
725     else {
726       int best_nfs_version = 0;
727       int proto_nfs_version;
728       char **p;
729
730       for (p = protocols; *p; p++) {
731         proto_nfs_version = get_nfs_version(host, ip, nfs_version, *p);
732
733         if (proto_nfs_version > best_nfs_version) {
734           best_nfs_version = proto_nfs_version;
735           nfs_proto = *p;
736         }
737       }
738       nfs_version = best_nfs_version;
739     }
740
741     if (!nfs_version) {
742       /*
743        * If the NFS server is down or does not support the portmapper call
744        * (such as certain Novell NFS servers) we mark it as version 2 and we
745        * let the nfs code deal with the case that is down.  If when the
746        * server comes back up, it can support NFS V.3 and/or TCP, it will
747        * use those.
748        */
749       nfs_version = NFS_VERSION;
750       nfs_proto = "udp";
751     }
752 #else /* not HAVE_FS_NFS3 */
753     nfs_version = NFS_VERSION;
754 #endif /* not HAVE_FS_NFS3 */
755   }
756
757   if (!nfs_proto)
758     nfs_proto = "udp";
759
760   plog(XLOG_INFO, "Using NFS version %d, protocol %s on host %s",
761        (int) nfs_version, nfs_proto, host);
762
763   /*
764    * Try to find an existing fs server structure for this host.
765    * Note that differing versions or protocols have their own structures.
766    * XXX: Need to fix the ping mechanism to actually use the NFS protocol
767    * chosen here (right now it always uses datagram sockets).
768    */
769   ITER(fs, fserver, &nfs_srvr_list) {
770     if (STREQ(host, fs->fs_host) &&
771         nfs_version == fs->fs_version &&
772         STREQ(nfs_proto, fs->fs_proto)) {
773       /*
774        * following if statement from Mike Mitchell
775        * <mcm@unx.sas.com>
776        * Initialize the ping data if we aren't pinging
777        * now.  The np_ttl and np_ping fields are
778        * especially important.
779        */
780       if (!(fs->fs_flags & FSF_PINGING)) {
781         np = (nfs_private *) fs->fs_private;
782         np->np_mountd_inval = TRUE;
783         np->np_xid = NPXID_ALLOC(struct );
784         np->np_error = -1;
785         np->np_ping = 0;
786         /*
787          * Initially the server will be deemed dead
788          * after MAX_ALLOWED_PINGS of the fast variety
789          * have failed.
790          */
791         np->np_ttl = MAX_ALLOWED_PINGS * FAST_NFS_PING + clocktime() - 1;
792       }
793       /*
794        * fill in the IP address -- this is only needed
795        * if there is a chance an IP address will change
796        * between mounts.
797        * Mike Mitchell, mcm@unx.sas.com, 09/08/93
798        */
799       if (hp && fs->fs_ip)
800         memmove((voidp) &fs->fs_ip->sin_addr, (voidp) hp->h_addr, sizeof(fs->fs_ip->sin_addr));
801
802       start_nfs_pings(fs, pingval);
803       fs->fs_refc++;
804       if (ip)
805         XFREE(ip);
806       return fs;
807     }
808   }
809
810   /*
811    * Get here if we can't find an entry
812    */
813
814   /*
815    * Allocate a new server
816    */
817   fs = ALLOC(struct fserver);
818   fs->fs_refc = 1;
819   fs->fs_host = strdup(hp ? hp->h_name : "unknown_hostname");
820   if (gopt.flags & CFM_NORMALIZE_HOSTNAMES)
821     host_normalize(&fs->fs_host);
822   fs->fs_ip = ip;
823   fs->fs_cid = 0;
824   if (ip) {
825     fs->fs_flags = FSF_DOWN;    /* Starts off down */
826   } else {
827     fs->fs_flags = FSF_ERROR | FSF_VALID;
828     mf->mf_flags |= MFF_ERROR;
829     mf->mf_error = ENOENT;
830   }
831   fs->fs_version = nfs_version;
832   fs->fs_proto = nfs_proto;
833   fs->fs_type = MNTTAB_TYPE_NFS;
834   fs->fs_pinger = AM_PINGER;
835   np = ALLOC(struct nfs_private);
836   memset((voidp) np, 0, sizeof(*np));
837   np->np_mountd_inval = TRUE;
838   np->np_xid = NPXID_ALLOC(struct );
839   np->np_error = -1;
840
841   /*
842    * Initially the server will be deemed dead after
843    * MAX_ALLOWED_PINGS of the fast variety have failed.
844    */
845   np->np_ttl = clocktime() + MAX_ALLOWED_PINGS * FAST_NFS_PING - 1;
846   fs->fs_private = (voidp) np;
847   fs->fs_prfree = (void (*)(voidp)) free;
848
849   if (!(fs->fs_flags & FSF_ERROR)) {
850     /*
851      * Start of keepalive timer
852      */
853     start_nfs_pings(fs, pingval);
854   }
855
856   /*
857    * Add to list of servers
858    */
859   ins_que(&fs->fs_q, &nfs_srvr_list);
860
861   return fs;
862 }