4 * Copyright (c) 1997-2009 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.
10 * This code is derived from software contributed to Berkeley by
11 * Jan-Simon Pendry at Imperial College, London.
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
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. All advertising materials mentioning features or use of this software
22 * must display the following acknowledgment:
23 * This product includes software developed by the University of
24 * California, Berkeley and its contributors.
25 * 4. Neither the name of the University nor the names of its contributors
26 * may be used to endorse or promote products derived from this software
27 * without specific prior written permission.
29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
42 * File: am-utils/amd/srvr_nfs.c
52 #endif /* HAVE_CONFIG_H */
57 * Number of pings allowed to fail before host is declared down
58 * - three-fifths of the allowed mount time...
60 #define MAX_ALLOWED_PINGS (3 + /* for luck ... */ 1)
63 * How often to ping when starting a new server
65 #define FAST_NFS_PING 3
67 #if (FAST_NFS_PING * MAX_ALLOWED_PINGS) >= ALLOWED_MOUNT_TIME
68 # error: sanity check failed in srvr_nfs.c
70 * you cannot do things this way...
71 * sufficient fast pings must be given the chance to fail
72 * within the allowed mount time
74 #endif /* (FAST_NFS_PING * MAX_ALLOWED_PINGS) >= ALLOWED_MOUNT_TIME */
76 /* structures and typedefs */
77 typedef struct nfs_private
{
78 u_short np_mountd
; /* Mount daemon port number */
79 char np_mountd_inval
; /* Port *may* be invalid */
80 int np_ping
; /* Number of failed ping attempts */
81 time_t np_ttl
; /* Time when server is thought dead */
82 int np_xid
; /* RPC transaction id for pings */
83 int np_error
; /* Error during portmap request */
87 qelem nfs_srvr_list
= {&nfs_srvr_list
, &nfs_srvr_list
};
90 static int global_xid
; /* For NFS pings */
91 #define XID_ALLOC() (++global_xid)
94 # define NUM_NFS_VERS 2
95 #else /* not HAVE_FS_NFS3 */
96 # define NUM_NFS_VERS 1
97 #endif /* not HAVE_FS_NFS3 */
98 static int ping_len
[NUM_NFS_VERS
];
99 static char ping_buf
[NUM_NFS_VERS
][sizeof(struct rpc_msg
) + 32];
101 #if defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3)
103 * Protocols we know about, in order of preference.
105 * Note that Solaris 8 and newer NetBSD systems are switching to UDP first,
106 * so this order may have to be adjusted for Amd in the future once more
107 * vendors make that change. -Erez 11/24/2000
109 * Or we might simply make this is a platform-specific order. -Ion 09/13/2003
111 static char *protocols
[] = { "tcp", "udp", NULL
};
112 #endif /* defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3) */
114 /* forward definitions */
115 static void nfs_keepalive(voidp
);
119 * Flush cached data for an fserver (or for all, if fs==NULL)
122 flush_srvr_nfs_cache(fserver
*fs
)
126 ITER(fs2
, fserver
, &nfs_srvr_list
) {
127 if (fs
== NULL
|| fs
== fs2
) {
128 nfs_private
*np
= (nfs_private
*) fs2
->fs_private
;
130 np
->np_mountd_inval
= TRUE
;
139 * Startup the NFS ping for a particular version.
142 create_ping_payload(u_long nfs_version
)
145 struct rpc_msg ping_msg
;
148 * Non nfs mounts like /afs/glue.umd.edu have ended up here.
150 if (nfs_version
== 0) {
151 nfs_version
= NFS_VERSION
;
152 plog(XLOG_WARNING
, "create_ping_payload: nfs_version = 0, changed to 2");
154 plog(XLOG_INFO
, "create_ping_payload: nfs_version: %d", (int) nfs_version
);
156 rpc_msg_init(&ping_msg
, NFS_PROGRAM
, nfs_version
, NFSPROC_NULL
);
159 * Create an XDR endpoint
161 xdrmem_create(&ping_xdr
, ping_buf
[nfs_version
- NFS_VERSION
], sizeof(ping_buf
[0]), XDR_ENCODE
);
164 * Create the NFS ping message
166 if (!xdr_callmsg(&ping_xdr
, &ping_msg
)) {
167 plog(XLOG_ERROR
, "Couldn't create ping RPC message");
171 * Find out how long it is
173 ping_len
[nfs_version
- NFS_VERSION
] = xdr_getpos(&ping_xdr
);
176 * Destroy the XDR endpoint - we don't need it anymore
178 xdr_destroy(&ping_xdr
);
183 * Called when a portmap reply arrives
186 got_portmap(voidp pkt
, int len
, struct sockaddr_in
*sa
, struct sockaddr_in
*ia
, voidp idv
, int done
)
188 fserver
*fs2
= (fserver
*) idv
;
192 * Find which fileserver we are talking about
194 ITER(fs
, fserver
, &nfs_srvr_list
)
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
;
203 if (!error
&& port
) {
204 dlog("got port (%d) for mountd on %s", (int) port
, fs
->fs_host
);
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
211 np
->np_mountd
= htons((u_short
) port
);
212 np
->np_mountd_inval
= FALSE
;
215 dlog("Error fetching port for mountd on %s", fs
->fs_host
);
216 dlog("\t error=%d, port=%d", error
, (int) port
);
218 * Almost certainly no mountd running on remote host
220 np
->np_error
= error
? error
: ETIMEDOUT
;
223 if (fs
->fs_flags
& FSF_WANT
)
226 dlog("Got portmap for old port request");
228 dlog("portmap request timed out");
234 * Obtain portmap information
237 call_portmap(fserver
*fs
, AUTH
*auth
, u_long prog
, u_long vers
, u_long prot
)
239 struct rpc_msg pmap_msg
;
241 char iobuf
[UDPMSGSIZE
];
245 rpc_msg_init(&pmap_msg
, PMAPPROG
, PMAPVERS
, PMAPPROC_NULL
);
250 len
= make_rpc_packet(iobuf
,
255 (XDRPROC_T_TYPE
) xdr_pmap
,
258 struct sockaddr_in sin
;
259 memset((voidp
) &sin
, 0, sizeof(sin
));
261 sin
.sin_port
= htons(PMAPPORT
);
262 error
= fwd_packet(RPC_XID_PORTMAP
, iobuf
, len
,
263 &sin
, &sin
, (voidp
) fs
, got_portmap
);
273 recompute_portmap(fserver
*fs
)
279 * No portmap calls for pure WebNFS servers.
281 if (fs
->fs_flags
& FSF_WEBNFS
)
287 error
= make_nfs_auth();
290 nfs_private
*np
= (nfs_private
*) fs
->fs_private
;
291 np
->np_error
= error
;
295 if (fs
->fs_version
== 0)
296 plog(XLOG_WARNING
, "recompute_portmap: nfs_version = 0 fixed");
298 plog(XLOG_INFO
, "recompute_portmap: NFS version %d on %s",
299 (int) fs
->fs_version
, fs
->fs_host
);
301 if (fs
->fs_version
== NFS_VERSION3
)
302 mnt_version
= AM_MOUNTVERS3
;
304 #endif /* HAVE_FS_NFS3 */
305 mnt_version
= MOUNTVERS
;
307 plog(XLOG_INFO
, "Using MOUNT version: %d", (int) mnt_version
);
308 call_portmap(fs
, nfs_auth
, MOUNTPROG
, mnt_version
, (u_long
) IPPROTO_UDP
);
313 get_mountd_port(fserver
*fs
, u_short
*port
, wchan_t wchan
)
320 nfs_private
*np
= (nfs_private
*) fs
->fs_private
;
321 if (np
->np_error
== 0) {
322 *port
= np
->np_mountd
;
325 error
= np
->np_error
;
328 * Now go get the port mapping again in case it changed.
329 * Note that it is used even if (np_mountd_inval)
330 * is True. The flag is used simply as an
331 * indication that the mountd may be invalid, not
332 * that it is known to be invalid.
334 if (np
->np_mountd_inval
)
335 recompute_portmap(fs
);
337 np
->np_mountd_inval
= TRUE
;
339 if (error
< 0 && wchan
&& !(fs
->fs_flags
& FSF_WANT
)) {
341 * If a wait channel is supplied, and no
342 * error has yet occurred, then arrange
343 * that a wakeup is done on the wait channel,
344 * whenever a wakeup is done on this fs node.
345 * Wakeup's are done on the fs node whenever
346 * it changes state - thus causing control to
347 * come back here and new, better things to happen.
349 fs
->fs_flags
|= FSF_WANT
;
350 sched_task(wakeup_task
, wchan
, (wchan_t
) fs
);
357 * This is called when we get a reply to an RPC ping.
358 * The value of id was taken from the nfs_private
359 * structure when the ping was transmitted.
362 nfs_keepalive_callback(voidp pkt
, int len
, struct sockaddr_in
*sp
, struct sockaddr_in
*tsp
, voidp idv
, int done
)
364 int xid
= (long) idv
; /* cast needed for 64-bit archs */
374 ITER(fs
, fserver
, &nfs_srvr_list
) {
375 nfs_private
*np
= (nfs_private
*) fs
->fs_private
;
376 if (np
->np_xid
== xid
&& (fs
->fs_flags
& FSF_PINGING
)) {
378 * Reset the ping counter.
379 * Update the keepalive timer.
382 if (fs
->fs_flags
& FSF_DOWN
) {
383 fs
->fs_flags
&= ~FSF_DOWN
;
384 if (fs
->fs_flags
& FSF_VALID
) {
385 srvrlog(fs
, "is up");
390 srvrlog(fs
, "starts up");
391 fs
->fs_flags
|= FSF_VALID
;
396 if (fs
->fs_flags
& FSF_VALID
) {
397 dlog("file server %s type nfs is still up", fs
->fs_host
);
401 fs
->fs_flags
|= FSF_VALID
;
406 * Adjust ping interval
408 untimeout(fs
->fs_cid
);
409 fs
->fs_cid
= timeout(fs
->fs_pinger
, nfs_keepalive
, (voidp
) fs
);
412 * Update ttl for this server
414 np
->np_ttl
= clocktime(NULL
) +
415 (MAX_ALLOWED_PINGS
- 1) * FAST_NFS_PING
+ fs
->fs_pinger
- 1;
420 np
->np_xid
= XID_ALLOC();
423 * Failed pings is zero...
428 * Recompute portmap information if not known
430 if (np
->np_mountd_inval
)
431 recompute_portmap(fs
);
439 dlog("Spurious ping packet");
444 check_fs_addr_change(fserver
*fs
)
446 struct hostent
*hp
= NULL
;
448 char *old_ipaddr
, *new_ipaddr
;
450 hp
= gethostbyname(fs
->fs_host
);
452 hp
->h_addrtype
!= AF_INET
||
453 !STREQ((char *) hp
->h_name
, fs
->fs_host
) ||
454 memcmp((voidp
) &fs
->fs_ip
->sin_addr
,
456 sizeof(fs
->fs_ip
->sin_addr
)) == 0)
458 /* if got here: downed server changed IP address */
459 old_ipaddr
= strdup(inet_ntoa(fs
->fs_ip
->sin_addr
));
460 memmove((voidp
) &ia
, (voidp
) hp
->h_addr
, sizeof(struct in_addr
));
461 new_ipaddr
= inet_ntoa(ia
); /* ntoa uses static buf */
462 plog(XLOG_WARNING
, "EZK: down fileserver %s changed ip: %s -> %s",
463 fs
->fs_host
, old_ipaddr
, new_ipaddr
);
465 /* copy new IP addr */
466 memmove((voidp
) &fs
->fs_ip
->sin_addr
,
468 sizeof(fs
->fs_ip
->sin_addr
));
469 /* XXX: do we need to un/set these flags? */
470 fs
->fs_flags
&= ~FSF_DOWN
;
471 fs
->fs_flags
|= FSF_VALID
| FSF_WANT
;
472 map_flush_srvr(fs
); /* XXX: a race with flush_srvr_nfs_cache? */
473 flush_srvr_nfs_cache(fs
);
474 fs
->fs_flags
|= FSF_FORCE_UNMOUNT
;
477 flush_nfs_fhandle_cache(fs
); /* done in caller: nfs_keepalive_timeout */
478 /* XXX: need to purge nfs_private so that somehow it will get re-initialized? */
484 * Called when no ping-reply received
487 nfs_keepalive_timeout(voidp v
)
490 nfs_private
*np
= (nfs_private
*) fs
->fs_private
;
493 * Another ping has failed
497 srvrlog(fs
, "not responding");
500 * Not known to be up any longer
503 fs
->fs_flags
&= ~FSF_VALID
;
506 * If ttl has expired then guess that it is dead
508 if (np
->np_ttl
< clocktime(NULL
)) {
509 int oflags
= fs
->fs_flags
;
510 dlog("ttl has expired");
511 if ((fs
->fs_flags
& FSF_DOWN
) == 0) {
513 * Server was up, but is now down.
515 srvrlog(fs
, "is down");
516 fs
->fs_flags
|= FSF_DOWN
| FSF_VALID
;
518 * Since the server is down, the portmap
519 * information may now be wrong, so it
520 * must be flushed from the local cache
522 flush_nfs_fhandle_cache(fs
);
524 check_fs_addr_change(fs
); /* check if IP addr of fserver changed */
529 if ((fs
->fs_flags
& FSF_VALID
) == 0)
530 srvrlog(fs
, "starts down");
531 fs
->fs_flags
|= FSF_VALID
;
533 if (oflags
!= fs
->fs_flags
&& (fs
->fs_flags
& FSF_WANT
))
536 * Reset failed ping count
541 dlog("%d pings to %s failed - at most %d allowed", np
->np_ping
, fs
->fs_host
, MAX_ALLOWED_PINGS
);
545 * New RPC xid, so any late responses to the previous ping
548 np
->np_xid
= XID_ALLOC();
551 * Run keepalive again
558 * Keep track of whether a server is alive
561 nfs_keepalive(voidp v
)
565 nfs_private
*np
= (nfs_private
*) fs
->fs_private
;
569 * Send an NFS ping to this node
572 if (ping_len
[fs
->fs_version
- NFS_VERSION
] == 0)
573 create_ping_payload(fs
->fs_version
);
576 * Queue the packet...
578 error
= fwd_packet(MK_RPC_XID(RPC_XID_NFSPING
, np
->np_xid
),
579 ping_buf
[fs
->fs_version
- NFS_VERSION
],
580 ping_len
[fs
->fs_version
- NFS_VERSION
],
582 (struct sockaddr_in
*) NULL
,
583 (voidp
) ((long) np
->np_xid
), /* cast needed for 64-bit archs */
584 nfs_keepalive_callback
);
587 * See if a hard error occurred
594 np
->np_ping
= MAX_ALLOWED_PINGS
; /* immediately down */
595 np
->np_ttl
= (time_t) 0;
597 * This causes an immediate call to nfs_keepalive_timeout
598 * whenever the server was thought to be up.
605 dlog("Sent NFS ping to %s", fs
->fs_host
);
610 * Back off the ping interval if we are not getting replies and
611 * the remote system is known to be down.
613 switch (fs
->fs_flags
& (FSF_DOWN
| FSF_VALID
)) {
614 case FSF_VALID
: /* Up */
615 if (fstimeo
< 0) /* +++ see above */
616 fstimeo
= FAST_NFS_PING
;
619 case FSF_VALID
| FSF_DOWN
: /* Down */
620 fstimeo
= fs
->fs_pinger
;
623 default: /* Unknown */
624 fstimeo
= FAST_NFS_PING
;
628 dlog("NFS timeout in %d seconds", fstimeo
);
630 fs
->fs_cid
= timeout(fstimeo
, nfs_keepalive_timeout
, (voidp
) fs
);
635 start_nfs_pings(fserver
*fs
, int pingval
)
637 if (pingval
== 0) /* could be because ping mnt option not found */
639 /* if pings haven't been initalized, then init them for first time */
640 if (fs
->fs_flags
& FSF_PING_UNINIT
) {
641 fs
->fs_flags
&= ~FSF_PING_UNINIT
;
642 plog(XLOG_INFO
, "initializing %s's pinger to %d sec", fs
->fs_host
, pingval
);
646 if ((fs
->fs_flags
& FSF_PINGING
) && fs
->fs_pinger
== pingval
) {
647 dlog("already running pings to %s", fs
->fs_host
);
651 /* if got here, then we need to update the ping value */
652 plog(XLOG_INFO
, "changing %s's ping value from %d%s to %d%s",
654 fs
->fs_pinger
, (fs
->fs_pinger
< 0 ? " (off)" : ""),
655 pingval
, (pingval
< 0 ? " (off)" : ""));
657 fs
->fs_pinger
= pingval
;
660 untimeout(fs
->fs_cid
);
662 srvrlog(fs
, "wired up (pings disabled)");
663 fs
->fs_flags
|= FSF_VALID
;
664 fs
->fs_flags
&= ~FSF_DOWN
;
666 fs
->fs_flags
|= FSF_PINGING
;
673 * Find an nfs server for a host.
676 find_nfs_srvr(mntfs
*mf
)
678 char *host
= mf
->mf_fo
->opt_rhost
;
683 struct hostent
*hp
= NULL
;
684 struct sockaddr_in
*ip
= NULL
;
685 u_long nfs_version
= 0; /* default is no version specified */
686 u_long best_nfs_version
= 0;
687 char *nfs_proto
= NULL
; /* no IP protocol either */
689 int nfs_port_opt
= 0;
690 int fserver_is_down
= 0;
693 * Get ping interval from mount options.
694 * Current only used to decide whether pings
695 * are required or not. < 0 = no pings.
697 mnt
.mnt_opts
= mf
->mf_mopts
;
698 pingval
= hasmntval(&mnt
, "ping");
700 if (mf
->mf_flags
& MFF_NFS_SCALEDOWN
) {
702 * the server granted us a filehandle, but we were unable to mount it.
703 * therefore, scale down to NFSv2/UDP and try again.
705 nfs_version
= NFS_VERSION
;
707 plog(XLOG_WARNING
, "find_nfs_srvr: NFS mount failed, trying again with NFSv2/UDP");
708 mf
->mf_flags
&= ~MFF_NFS_SCALEDOWN
;
711 * Get the NFS version from the mount options. This is used
712 * to decide the highest NFS version to try.
714 #ifdef MNTTAB_OPT_VERS
715 nfs_version
= hasmntval(&mnt
, MNTTAB_OPT_VERS
);
716 #endif /* MNTTAB_OPT_VERS */
718 #ifdef MNTTAB_OPT_PROTO
720 char *proto_opt
= hasmnteq(&mnt
, MNTTAB_OPT_PROTO
);
723 for (p
= protocols
; *p
; p
++)
724 if (NSTREQ(proto_opt
, *p
, strlen(*p
))) {
729 plog(XLOG_WARNING
, "ignoring unknown protocol option for %s:%s",
730 host
, mf
->mf_fo
->opt_rfs
);
733 #endif /* MNTTAB_OPT_PROTO */
735 #ifdef HAVE_NFS_NFSV2_H
736 /* allow overriding if nfsv2 option is specified in mount options */
737 if (amu_hasmntopt(&mnt
, "nfsv2")) {
738 nfs_version
= NFS_VERSION
;/* nullify any ``vers=X'' statements */
739 nfs_proto
= "udp"; /* nullify any ``proto=tcp'' statements */
740 plog(XLOG_WARNING
, "found compatibility option \"nfsv2\": set options vers=2,proto=udp for host %s", host
);
742 #endif /* HAVE_NFS_NFSV2_H */
744 /* check if we've globally overridden the NFS version/protocol */
746 nfs_version
= gopt
.nfs_vers
;
747 plog(XLOG_INFO
, "find_nfs_srvr: force NFS version to %d",
750 if (gopt
.nfs_proto
) {
751 nfs_proto
= gopt
.nfs_proto
;
752 plog(XLOG_INFO
, "find_nfs_srvr: force NFS protocol transport to %s", nfs_proto
);
757 * lookup host address and canonical name
759 hp
= gethostbyname(host
);
762 * New code from Bob Harris <harris@basil-rathbone.mit.edu>
763 * Use canonical name to keep track of file server
764 * information. This way aliases do not generate
765 * multiple NFS pingers. (Except when we're normalizing
768 if (hp
&& !(gopt
.flags
& CFM_NORMALIZE_HOSTNAMES
))
769 host
= (char *) hp
->h_name
;
772 switch (hp
->h_addrtype
) {
774 ip
= ALLOC(struct sockaddr_in
);
775 memset((voidp
) ip
, 0, sizeof(*ip
));
776 /* as per POSIX, sin_len need not be set (used internally by kernel) */
777 ip
->sin_family
= AF_INET
;
778 memmove((voidp
) &ip
->sin_addr
, (voidp
) hp
->h_addr
, sizeof(ip
->sin_addr
));
782 plog(XLOG_USER
, "No IP address for host %s", host
);
786 plog(XLOG_USER
, "Unknown host: %s", host
);
791 * This may not be the best way to do things, but it really doesn't make
792 * sense to query a file server which is marked as 'down' for any
793 * version/proto combination.
795 ITER(fs
, fserver
, &nfs_srvr_list
) {
796 if (FSRV_ISDOWN(fs
) &&
797 STREQ(host
, fs
->fs_host
)) {
798 plog(XLOG_WARNING
, "fileserver %s is already hung - not running NFS proto/version discovery", host
);
807 * Get the NFS Version, and verify server is up.
808 * If the client only supports NFSv2, hardcode it but still try to
809 * contact the remote portmapper to see if the service is running.
812 nfs_version
= NFS_VERSION
;
814 plog(XLOG_INFO
, "The client supports only NFS(2,udp)");
815 #endif /* not HAVE_FS_NFS3 */
818 if (amu_hasmntopt(&mnt
, MNTTAB_OPT_PUBLIC
)) {
820 * Use WebNFS to obtain file handles.
822 mf
->mf_flags
|= MFF_WEBNFS
;
823 plog(XLOG_INFO
, "%s option used, NOT contacting the portmapper on %s",
824 MNTTAB_OPT_PUBLIC
, host
);
826 * Prefer NFSv3/tcp if the client supports it (cf. RFC 2054, 7).
830 nfs_version
= NFS_VERSION3
;
831 #else /* not HAVE_FS_NFS3 */
832 nfs_version
= NFS_VERSION
;
833 #endif /* not HAVE_FS_NFS3 */
834 plog(XLOG_INFO
, "No NFS version specified, will use NFSv%d",
838 #if defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3)
840 #else /* not defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3) */
842 #endif /* not defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3) */
843 plog(XLOG_INFO
, "No NFS protocol transport specified, will use %s",
848 * Find the best combination of NFS version and protocol.
849 * When given a choice, use the highest available version,
850 * and use TCP over UDP if available.
852 if (check_pmap_up(host
, ip
)) {
854 best_nfs_version
= get_nfs_version(host
, ip
, nfs_version
, nfs_proto
);
855 nfs_port
= ip
->sin_port
;
857 #ifdef MNTTAB_OPT_PROTO
859 u_int proto_nfs_version
;
862 for (p
= protocols
; *p
; p
++) {
863 proto_nfs_version
= get_nfs_version(host
, ip
, nfs_version
, *p
);
865 if (proto_nfs_version
> best_nfs_version
) {
866 best_nfs_version
= proto_nfs_version
;
868 nfs_port
= ip
->sin_port
;
872 #endif /* MNTTAB_OPT_PROTO */
874 plog(XLOG_INFO
, "portmapper service not running on %s", host
);
877 /* use the portmapper results only nfs_version is not set yet */
878 if (!best_nfs_version
) {
880 * If the NFS server is down or does not support the portmapper call
881 * (such as certain Novell NFS servers) we mark it as version 2 and we
882 * let the nfs code deal with the case when it is down. If/when the
883 * server comes back up and it can support NFSv3 and/or TCP, it will
886 if (nfs_version
== 0) {
887 nfs_version
= NFS_VERSION
;
890 plog(XLOG_INFO
, "NFS service not running on %s", host
);
893 if (nfs_version
== 0)
894 nfs_version
= best_nfs_version
;
895 plog(XLOG_INFO
, "Using NFS version %d, protocol %s on host %s",
896 (int) nfs_version
, nfs_proto
, host
);
901 * Determine the NFS port.
903 * A valid "port" mount option overrides anything else.
904 * If the port has been determined from the portmapper, use that.
905 * Default to NFS_PORT otherwise (cf. RFC 2054, 3).
907 nfs_port_opt
= hasmntval(&mnt
, MNTTAB_OPT_PORT
);
908 if (nfs_port_opt
> 0)
909 nfs_port
= htons(nfs_port_opt
);
911 nfs_port
= htons(NFS_PORT
);
913 dlog("find_nfs_srvr: using port %d for nfs on %s",
914 (int) ntohs(nfs_port
), host
);
915 ip
->sin_port
= nfs_port
;
919 * Try to find an existing fs server structure for this host.
920 * Note that differing versions or protocols have their own structures.
921 * XXX: Need to fix the ping mechanism to actually use the NFS protocol
922 * chosen here (right now it always uses datagram sockets).
924 ITER(fs
, fserver
, &nfs_srvr_list
) {
925 if (STREQ(host
, fs
->fs_host
) &&
926 nfs_version
== fs
->fs_version
&&
927 STREQ(nfs_proto
, fs
->fs_proto
)) {
929 * fill in the IP address -- this is only needed
930 * if there is a chance an IP address will change
932 * Mike Mitchell, mcm@unx.sas.com, 09/08/93
934 if (hp
&& fs
->fs_ip
&&
935 memcmp((voidp
) &fs
->fs_ip
->sin_addr
,
937 sizeof(fs
->fs_ip
->sin_addr
)) != 0) {
939 char *old_ipaddr
, *new_ipaddr
;
940 old_ipaddr
= strdup(inet_ntoa(fs
->fs_ip
->sin_addr
));
941 memmove((voidp
) &ia
, (voidp
) hp
->h_addr
, sizeof(struct in_addr
));
942 new_ipaddr
= inet_ntoa(ia
); /* ntoa uses static buf */
943 plog(XLOG_WARNING
, "fileserver %s changed ip: %s -> %s",
944 fs
->fs_host
, old_ipaddr
, new_ipaddr
);
946 flush_nfs_fhandle_cache(fs
);
947 memmove((voidp
) &fs
->fs_ip
->sin_addr
, (voidp
) hp
->h_addr
, sizeof(fs
->fs_ip
->sin_addr
));
951 * If the new file systems doesn't use WebNFS, the nfs pings may
952 * try to contact the portmapper.
954 if (!(mf
->mf_flags
& MFF_WEBNFS
))
955 fs
->fs_flags
&= ~FSF_WEBNFS
;
957 /* check if pingval needs to be updated/set/reset */
958 start_nfs_pings(fs
, pingval
);
961 * Following if statement from Mike Mitchell <mcm@unx.sas.com>
962 * Initialize the ping data if we aren't pinging now. The np_ttl and
963 * np_ping fields are especially important.
965 if (!(fs
->fs_flags
& FSF_PINGING
)) {
966 np
= (nfs_private
*) fs
->fs_private
;
967 np
->np_mountd_inval
= TRUE
;
968 np
->np_xid
= XID_ALLOC();
972 * Initially the server will be deemed dead
973 * after MAX_ALLOWED_PINGS of the fast variety
976 np
->np_ttl
= MAX_ALLOWED_PINGS
* FAST_NFS_PING
+ clocktime(NULL
) - 1;
977 start_nfs_pings(fs
, pingval
);
979 fs
->fs_flags
|= FSF_VALID
| FSF_DOWN
;
990 * Get here if we can't find an entry
994 * Allocate a new server
996 fs
= ALLOC(struct fserver
);
998 fs
->fs_host
= strdup(hp
? hp
->h_name
: "unknown_hostname");
999 if (gopt
.flags
& CFM_NORMALIZE_HOSTNAMES
)
1000 host_normalize(&fs
->fs_host
);
1004 fs
->fs_flags
= FSF_DOWN
; /* Starts off down */
1006 fs
->fs_flags
= FSF_ERROR
| FSF_VALID
;
1007 mf
->mf_flags
|= MFF_ERROR
;
1008 mf
->mf_error
= ENOENT
;
1010 if (mf
->mf_flags
& MFF_WEBNFS
)
1011 fs
->fs_flags
|= FSF_WEBNFS
;
1012 fs
->fs_version
= nfs_version
;
1013 fs
->fs_proto
= nfs_proto
;
1014 fs
->fs_type
= MNTTAB_TYPE_NFS
;
1015 fs
->fs_pinger
= AM_PINGER
;
1016 fs
->fs_flags
|= FSF_PING_UNINIT
; /* pinger hasn't been initialized */
1017 np
= ALLOC(struct nfs_private
);
1018 memset((voidp
) np
, 0, sizeof(*np
));
1019 np
->np_mountd_inval
= TRUE
;
1020 np
->np_xid
= XID_ALLOC();
1024 * Initially the server will be deemed dead after
1025 * MAX_ALLOWED_PINGS of the fast variety have failed.
1027 np
->np_ttl
= clocktime(NULL
) + MAX_ALLOWED_PINGS
* FAST_NFS_PING
- 1;
1028 fs
->fs_private
= (voidp
) np
;
1029 fs
->fs_prfree
= (void (*)(voidp
)) free
;
1031 if (!FSRV_ERROR(fs
)) {
1032 /* start of keepalive timer, first updating pingval */
1033 start_nfs_pings(fs
, pingval
);
1034 if (fserver_is_down
)
1035 fs
->fs_flags
|= FSF_VALID
| FSF_DOWN
;
1039 * Add to list of servers
1041 ins_que(&fs
->fs_q
, &nfs_srvr_list
);