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