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