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.
8 * This code is derived from software contributed to Berkeley by
9 * Jan-Simon Pendry at Imperial College, London.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
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.
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
36 * File: am-utils/amd/srvr_nfs.c
46 #endif /* HAVE_CONFIG_H */
51 * Number of pings allowed to fail before host is declared down
52 * - three-fifths of the allowed mount time...
54 #define MAX_ALLOWED_PINGS (3 + /* for luck ... */ 1)
57 * How often to ping when starting a new server
59 #define FAST_NFS_PING 3
61 #if (FAST_NFS_PING * MAX_ALLOWED_PINGS) >= ALLOWED_MOUNT_TIME
62 # error: sanity check failed in srvr_nfs.c
64 * you cannot do things this way...
65 * sufficient fast pings must be given the chance to fail
66 * within the allowed mount time
68 #endif /* (FAST_NFS_PING * MAX_ALLOWED_PINGS) >= ALLOWED_MOUNT_TIME */
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 */
82 qelem nfs_srvr_list = {&nfs_srvr_list, &nfs_srvr_list};
85 static int global_xid; /* For NFS pings */
86 #define XID_ALLOC() (++global_xid)
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];
98 #if defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3)
100 * Protocols we know about, in order of preference.
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
106 * Or we might simply make this is a platform-specific order. -Ion 09/13/2003
108 static char *protocols[] = { "tcp", "udp", NULL };
109 #endif /* defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3) */
111 /* forward definitions */
112 static void nfs_keepalive(voidp);
116 * Flush cached data for an fserver (or for all, if fs==NULL)
119 flush_srvr_nfs_cache(fserver *fs)
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';
136 * Startup the NFS ping for a particular version.
139 create_ping_payload(u_long nfs_version)
142 struct rpc_msg ping_msg;
145 * Non nfs mounts like /afs/glue.umd.edu have ended up here.
147 if (nfs_version == 0) {
148 nfs_version = NFS_VERSION;
149 plog(XLOG_WARNING, "%s: nfs_version = 0, changed to 2", __func__);
151 plog(XLOG_INFO, "%s: nfs_version: %d", __func__, (int) nfs_version);
153 rpc_msg_init(&ping_msg, NFS_PROGRAM, nfs_version, NFSPROC_NULL);
156 * Create an XDR endpoint
158 xdrmem_create(&ping_xdr, ping_buf[nfs_version - NFS_VERSION], sizeof(ping_buf[0]), XDR_ENCODE);
161 * Create the NFS ping message
163 if (!xdr_callmsg(&ping_xdr, &ping_msg)) {
164 plog(XLOG_ERROR, "Couldn't create ping RPC message");
169 * Find out how long it is
171 ping_len[nfs_version - NFS_VERSION] = xdr_getpos(&ping_xdr);
174 * Destroy the XDR endpoint - we don't need it anymore
176 xdr_destroy(&ping_xdr);
181 * Called when a portmap reply arrives
184 got_portmap(voidp pkt, int len, struct sockaddr_in *sa, struct sockaddr_in *ia, voidp idv, int done)
186 fserver *fs2 = (fserver *) idv;
190 * Find which fileserver we are talking about
192 ITER(fs, fserver, &nfs_srvr_list)
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;
201 if (!error && port) {
202 dlog("got port (%d) for mountd on %s", (int) port, fs->fs_host);
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
209 np->np_mountd = htons((u_short) port);
210 np->np_mountd_inval = 'N';
213 dlog("Error fetching port for mountd on %s", fs->fs_host);
214 dlog("\t error=%d, port=%d", error, (int) port);
216 * Almost certainly no mountd running on remote host
218 np->np_error = error ? error : ETIMEDOUT;
221 if (fs->fs_flags & FSF_WANT)
224 dlog("Got portmap for old port request");
226 dlog("portmap request timed out");
232 * Obtain portmap information
235 call_portmap(fserver *fs, AUTH *auth, u_long prog, u_long vers, u_long prot)
237 struct rpc_msg pmap_msg;
239 char iobuf[UDPMSGSIZE];
243 rpc_msg_init(&pmap_msg, PMAPPROG, PMAPVERS, PMAPPROC_NULL);
248 len = make_rpc_packet(iobuf,
253 (XDRPROC_T_TYPE) xdr_pmap,
256 struct sockaddr_in sin;
257 memset((voidp) &sin, 0, sizeof(sin));
259 sin.sin_port = htons(PMAPPORT);
260 error = fwd_packet(RPC_XID_PORTMAP, iobuf, len,
261 &sin, &sin, (voidp) fs, got_portmap);
271 recompute_portmap(fserver *fs)
277 * No portmap calls for pure WebNFS servers.
279 if (fs->fs_flags & FSF_WEBNFS)
285 error = make_nfs_auth();
288 nfs_private *np = (nfs_private *) fs->fs_private;
289 np->np_error = error;
293 if (fs->fs_version == 0)
294 plog(XLOG_WARNING, "%s: nfs_version = 0 fixed", __func__);
296 plog(XLOG_INFO, "%s: NFS version %d on %s", __func__,
297 (int) fs->fs_version, fs->fs_host);
299 if (fs->fs_version == NFS_VERSION3)
300 mnt_version = AM_MOUNTVERS3;
302 #endif /* HAVE_FS_NFS3 */
303 mnt_version = MOUNTVERS;
305 plog(XLOG_INFO, "Using MOUNT version: %d", (int) mnt_version);
306 call_portmap(fs, nfs_auth, MOUNTPROG, mnt_version, (u_long) IPPROTO_UDP);
311 get_mountd_port(fserver *fs, u_short *port, wchan_t wchan)
319 nfs_private *np = (nfs_private *) fs->fs_private;
320 if (np->np_error == 0) {
321 *port = np->np_mountd;
324 error = np->np_error;
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.
333 switch (np->np_mountd_inval) {
335 recompute_portmap(fs);
338 np->np_mountd_inval = 'Y';
346 if (error < 0 && wchan && !(fs->fs_flags & FSF_WANT)) {
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.
356 fs->fs_flags |= FSF_WANT;
357 sched_task(wakeup_task, wchan, (wchan_t) fs);
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.
369 nfs_keepalive_callback(voidp pkt, int len, struct sockaddr_in *sp, struct sockaddr_in *tsp, voidp idv, int done)
371 int xid = (long) idv; /* cast needed for 64-bit archs */
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)) {
385 * Reset the ping counter.
386 * Update the keepalive timer.
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");
397 srvrlog(fs, "starts up");
398 fs->fs_flags |= FSF_VALID;
403 if (fs->fs_flags & FSF_VALID) {
404 dlog("file server %s type nfs is still up", fs->fs_host);
408 fs->fs_flags |= FSF_VALID;
413 * Adjust ping interval
415 untimeout(fs->fs_cid);
416 fs->fs_cid = timeout(fs->fs_pinger, nfs_keepalive, (voidp) fs);
419 * Update ttl for this server
421 np->np_ttl = clocktime(NULL) +
422 (MAX_ALLOWED_PINGS - 1) * FAST_NFS_PING + fs->fs_pinger - 1;
427 np->np_xid = XID_ALLOC();
430 * Failed pings is zero...
435 * Recompute portmap information if not known
437 if (np->np_mountd_inval == 'Y')
438 recompute_portmap(fs);
446 dlog("Spurious ping packet");
451 check_fs_addr_change(fserver *fs)
453 struct hostent *hp = NULL;
455 char *old_ipaddr, *new_ipaddr;
457 hp = gethostbyname(fs->fs_host);
459 hp->h_addrtype != AF_INET ||
460 !STREQ((char *) hp->h_name, fs->fs_host) ||
461 memcmp((voidp) &fs->fs_ip->sin_addr,
463 sizeof(fs->fs_ip->sin_addr)) == 0)
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);
472 /* copy new IP addr */
473 memmove((voidp) &fs->fs_ip->sin_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;
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? */
491 * Called when no ping-reply received
494 nfs_keepalive_timeout(voidp v)
497 nfs_private *np = (nfs_private *) fs->fs_private;
500 * Another ping has failed
504 srvrlog(fs, "not responding");
507 * Not known to be up any longer
510 fs->fs_flags &= ~FSF_VALID;
513 * If ttl has expired then guess that it is dead
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) {
520 * Server was up, but is now down.
522 srvrlog(fs, "is down");
523 fs->fs_flags |= FSF_DOWN | FSF_VALID;
525 * Since the server is down, the portmap
526 * information may now be wrong, so it
527 * must be flushed from the local cache
529 flush_nfs_fhandle_cache(fs);
531 check_fs_addr_change(fs); /* check if IP addr of fserver changed */
536 if ((fs->fs_flags & FSF_VALID) == 0)
537 srvrlog(fs, "starts down");
538 fs->fs_flags |= FSF_VALID;
540 if (oflags != fs->fs_flags && (fs->fs_flags & FSF_WANT))
543 * Reset failed ping count
548 dlog("%d pings to %s failed - at most %d allowed", np->np_ping, fs->fs_host, MAX_ALLOWED_PINGS);
552 * New RPC xid, so any late responses to the previous ping
555 np->np_xid = XID_ALLOC();
558 * Run keepalive again
565 * Keep track of whether a server is alive
568 nfs_keepalive(voidp v)
572 nfs_private *np = (nfs_private *) fs->fs_private;
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;
578 * Send an NFS ping to this node
581 if (ping_len[fs_version - NFS_VERSION] == 0)
582 create_ping_payload(fs_version);
585 * Queue the packet...
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],
591 (struct sockaddr_in *) NULL,
592 (voidp) ((long) np->np_xid), /* cast needed for 64-bit archs */
593 nfs_keepalive_callback);
596 * See if a hard error occurred
603 np->np_ping = MAX_ALLOWED_PINGS; /* immediately down */
604 np->np_ttl = (time_t) 0;
606 * This causes an immediate call to nfs_keepalive_timeout
607 * whenever the server was thought to be up.
614 dlog("Sent NFS ping to %s", fs->fs_host);
619 * Back off the ping interval if we are not getting replies and
620 * the remote system is known to be down.
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;
628 case FSF_VALID | FSF_DOWN: /* Down */
629 fstimeo = fs->fs_pinger;
632 default: /* Unknown */
633 fstimeo = FAST_NFS_PING;
637 dlog("NFS timeout in %d seconds", fstimeo);
639 fs->fs_cid = timeout(fstimeo, nfs_keepalive_timeout, (voidp) fs);
644 start_nfs_pings(fserver *fs, int pingval)
646 if (pingval == 0) /* could be because ping mnt option not found */
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);
655 if ((fs->fs_flags & FSF_PINGING) && fs->fs_pinger == pingval) {
656 dlog("already running pings to %s", fs->fs_host);
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",
663 fs->fs_pinger, (fs->fs_pinger < 0 ? " (off)" : ""),
664 pingval, (pingval < 0 ? " (off)" : ""));
666 fs->fs_pinger = pingval;
669 untimeout(fs->fs_cid);
671 srvrlog(fs, "wired up (pings disabled)");
672 fs->fs_flags |= FSF_VALID;
673 fs->fs_flags &= ~FSF_DOWN;
675 fs->fs_flags |= FSF_PINGING;
682 * Find an nfs server for a host.
685 find_nfs_srvr(mntfs *mf)
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 */
698 int nfs_port_opt = 0;
699 int fserver_is_down = 0;
701 if (mf->mf_fo == NULL) {
702 plog(XLOG_ERROR, "%s: NULL mf_fo", __func__);
705 host = mf->mf_fo->opt_rhost;
707 * Get ping interval from mount options.
708 * Current only used to decide whether pings
709 * are required or not. < 0 = no pings.
711 mnt.mnt_opts = mf->mf_mopts;
712 pingval = hasmntval(&mnt, "ping");
714 if (mf->mf_flags & MFF_NFS_SCALEDOWN) {
716 * the server granted us a filehandle, but we were unable to mount it.
717 * therefore, scale down to NFSv2/UDP and try again.
719 nfs_version = NFS_VERSION;
721 plog(XLOG_WARNING, "%s: NFS mount failed, trying again with NFSv2/UDP",
723 mf->mf_flags &= ~MFF_NFS_SCALEDOWN;
726 * Get the NFS version from the mount options. This is used
727 * to decide the highest NFS version to try.
729 #ifdef MNTTAB_OPT_VERS
730 nfs_version = hasmntval(&mnt, MNTTAB_OPT_VERS);
731 #endif /* MNTTAB_OPT_VERS */
733 #ifdef MNTTAB_OPT_PROTO
735 char *proto_opt = hasmnteq(&mnt, MNTTAB_OPT_PROTO);
738 for (p = protocols; *p; p++)
739 if (NSTREQ(proto_opt, *p, strlen(*p))) {
744 plog(XLOG_WARNING, "ignoring unknown protocol option for %s:%s",
745 host, mf->mf_fo->opt_rfs);
748 #endif /* MNTTAB_OPT_PROTO */
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);
757 #endif /* HAVE_NFS_NFSV2_H */
759 /* check if we've globally overridden the NFS version/protocol */
761 nfs_version = gopt.nfs_vers;
762 plog(XLOG_INFO, "%s: force NFS version to %d", __func__,
765 if (gopt.nfs_proto) {
766 nfs_proto = gopt.nfs_proto;
767 plog(XLOG_INFO, "%s: force NFS protocol transport to %s", __func__,
773 * lookup host address and canonical name
775 hp = gethostbyname(host);
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
784 if (hp && !(gopt.flags & CFM_NORMALIZE_HOSTNAMES))
785 host = (char *) hp->h_name;
788 switch (hp->h_addrtype) {
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));
798 plog(XLOG_USER, "No IP address for host %s", host);
802 plog(XLOG_USER, "Unknown host: %s", host);
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.
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);
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.
827 nfs_version = NFS_VERSION;
829 plog(XLOG_INFO, "The client supports only NFS(2,udp)");
830 #endif /* not HAVE_FS_NFS3 */
833 if (amu_hasmntopt(&mnt, MNTTAB_OPT_PUBLIC)) {
835 * Use WebNFS to obtain file handles.
837 mf->mf_flags |= MFF_WEBNFS;
838 plog(XLOG_INFO, "%s option used, NOT contacting the portmapper on %s",
839 MNTTAB_OPT_PUBLIC, host);
841 * Prefer NFSv4/tcp if the client supports it (cf. RFC 2054, 7).
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",
855 #if defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3) || defined(HAVE_FS_NFS4)
857 #else /* not defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3) || defined(HAVE_FS_NFS4) */
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",
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.
869 if (check_pmap_up(host, ip)) {
871 best_nfs_version = get_nfs_version(host, ip, nfs_version, nfs_proto,
873 nfs_port = ip->sin_port;
875 #ifdef MNTTAB_OPT_PROTO
877 u_int proto_nfs_version;
880 for (p = protocols; *p; p++) {
881 proto_nfs_version = get_nfs_version(host, ip, nfs_version, *p,
883 if (proto_nfs_version > best_nfs_version) {
884 best_nfs_version = proto_nfs_version;
886 nfs_port = ip->sin_port;
890 #endif /* MNTTAB_OPT_PROTO */
892 plog(XLOG_INFO, "portmapper service not running on %s", host);
895 /* use the portmapper results only nfs_version is not set yet */
896 if (!best_nfs_version) {
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
904 if (nfs_version == 0) {
905 nfs_version = NFS_VERSION;
908 plog(XLOG_INFO, "NFS service not running on %s", host);
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);
919 * Determine the NFS port.
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).
925 nfs_port_opt = hasmntval(&mnt, MNTTAB_OPT_PORT);
926 if (nfs_port_opt > 0)
927 nfs_port = htons(nfs_port_opt);
929 nfs_port = htons(NFS_PORT);
931 dlog("%s: using port %d for nfs on %s", __func__,
932 (int) ntohs(nfs_port), host);
933 ip->sin_port = nfs_port;
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).
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)) {
947 * fill in the IP address -- this is only needed
948 * if there is a chance an IP address will change
950 * Mike Mitchell, mcm@unx.sas.com, 09/08/93
952 if (hp && fs->fs_ip &&
953 memcmp((voidp) &fs->fs_ip->sin_addr,
955 sizeof(fs->fs_ip->sin_addr)) != 0) {
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);
964 flush_nfs_fhandle_cache(fs);
965 memmove((voidp) &fs->fs_ip->sin_addr, (voidp) hp->h_addr, sizeof(fs->fs_ip->sin_addr));
969 * If the new file systems doesn't use WebNFS, the nfs pings may
970 * try to contact the portmapper.
972 if (!(mf->mf_flags & MFF_WEBNFS))
973 fs->fs_flags &= ~FSF_WEBNFS;
975 /* check if pingval needs to be updated/set/reset */
976 start_nfs_pings(fs, pingval);
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.
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();
991 * Initially the server will be deemed dead
992 * after MAX_ALLOWED_PINGS of the fast variety
995 np->np_ttl = MAX_ALLOWED_PINGS * FAST_NFS_PING + clocktime(NULL) - 1;
996 start_nfs_pings(fs, pingval);
998 fs->fs_flags |= FSF_VALID | FSF_DOWN;
1000 fs->fs_flags = FSF_VALID;
1012 * Get here if we can't find an entry
1016 * Allocate a new server
1018 fs = ALLOC(struct fserver);
1020 fs->fs_host = xstrdup(hp ? hp->h_name : "unknown_hostname");
1021 if (gopt.flags & CFM_NORMALIZE_HOSTNAMES)
1022 host_normalize(&fs->fs_host);
1026 fs->fs_flags = FSF_DOWN; /* Starts off down */
1028 fs->fs_flags = FSF_ERROR | FSF_VALID;
1029 mf->mf_flags |= MFF_ERROR;
1030 mf->mf_error = ENOENT;
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();
1047 plog(XLOG_INFO, "%s: using mountport: %d", __func__,
1048 (int) ntohs(np->np_mountd));
1049 np->np_mountd_inval = 'P';
1055 * Initially the server will be deemed dead after
1056 * MAX_ALLOWED_PINGS of the fast variety have failed.
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;
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;
1070 * Add to list of servers
1072 ins_que(&fs->fs_q, &nfs_srvr_list);