1 /* $NetBSD: amfs_host.c,v 1.1.1.3 2015/01/17 16:34:15 christos Exp $ */
2
3 /*
4 * Copyright (c) 1997-2014 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.
8 * All rights reserved.
9 *
10 * This code is derived from software contributed to Berkeley by
11 * Jan-Simon Pendry at Imperial College, London.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
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. Neither the name of the University nor the names of its contributors
22 * may be used to endorse or promote products derived from this software
23 * without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
36 *
37 *
38 * File: am-utils/amd/amfs_host.c
39 *
40 */
41
42 /*
43 * NFS host file system.
44 * Mounts all exported filesystems from a given host.
45 * This has now degenerated into a mess but will not
46 * be rewritten. Amd 6 will support the abstractions
47 * needed to make this work correctly.
48 */
49
50 #ifdef HAVE_CONFIG_H
51 # include <config.h>
52 #endif /* HAVE_CONFIG_H */
53 #include <am_defs.h>
54 #include <amd.h>
55
56 static char *amfs_host_match(am_opts *fo);
57 static int amfs_host_init(mntfs *mf);
58 static int amfs_host_mount(am_node *am, mntfs *mf);
59 static int amfs_host_umount(am_node *am, mntfs *mf);
60 static void amfs_host_umounted(mntfs *mf);
61
62 /*
63 * Ops structure
64 */
65 am_ops amfs_host_ops =
66 {
67 "host",
68 amfs_host_match,
69 amfs_host_init,
70 amfs_host_mount,
71 amfs_host_umount,
72 amfs_error_lookup_child,
73 amfs_error_mount_child,
74 amfs_error_readdir,
75 0, /* amfs_host_readlink */
76 0, /* amfs_host_mounted */
77 amfs_host_umounted,
78 find_nfs_srvr,
79 0, /* amfs_host_get_wchan */
80 FS_MKMNT | FS_BACKGROUND | FS_AMQINFO,
81 #ifdef HAVE_FS_AUTOFS
82 AUTOFS_HOST_FS_FLAGS,
83 #endif /* HAVE_FS_AUTOFS */
84 };
85
86
87 /*
88 * Determine the mount point:
89 *
90 * The next change we put in to better handle PCs. This is a bit
91 * disgusting, so you'd better sit down. We change the make_mntpt function
92 * to look for exported file systems without a leading '/'. If they don't
93 * have a leading '/', we add one. If the export is 'a:' through 'z:'
94 * (without a leading slash), we change it to 'a%' (or b% or z%). This
95 * allows the entire PC disk to be mounted.
96 */
97 static void
make_mntpt(char * mntpt,size_t l,const exports ex,const char * mf_mount)98 make_mntpt(char *mntpt, size_t l, const exports ex, const char *mf_mount)
99 {
100 if (ex->ex_dir[0] == '/') {
101 if (ex->ex_dir[1] == 0)
102 xstrlcpy(mntpt, mf_mount, l);
103 else
104 xsnprintf(mntpt, l, "%s%s", mf_mount, ex->ex_dir);
105 } else if (ex->ex_dir[0] >= 'a' &&
106 ex->ex_dir[0] <= 'z' &&
107 ex->ex_dir[1] == ':' &&
108 ex->ex_dir[2] == '/' &&
109 ex->ex_dir[3] == 0)
110 xsnprintf(mntpt, l, "%s/%c%%", mf_mount, ex->ex_dir[0]);
111 else
112 xsnprintf(mntpt, l, "%s/%s", mf_mount, ex->ex_dir);
113 }
114
115
116 /*
117 * Execute needs the same as NFS plus a helper command
118 */
119 static char *
amfs_host_match(am_opts * fo)120 amfs_host_match(am_opts *fo)
121 {
122 extern am_ops nfs_ops;
123
124 /*
125 * Make sure rfs is specified to keep nfs_match happy...
126 */
127 if (!fo->opt_rfs)
128 fo->opt_rfs = "/";
129
130 return (*nfs_ops.fs_match) (fo);
131 }
132
133
134 static int
amfs_host_init(mntfs * mf)135 amfs_host_init(mntfs *mf)
136 {
137 u_short mountd_port;
138
139 if (strchr(mf->mf_info, ':') == 0)
140 return ENOENT;
141
142 /*
143 * This is primarily to schedule a wakeup so that as soon
144 * as our fileserver is ready, we can continue setting up
145 * the host filesystem. If we don't do this, the standard
146 * amfs_auto code will set up a fileserver structure, but it will
147 * have to wait for another nfs request from the client to come
148 * in before finishing. Our way is faster since we don't have
149 * to wait for the client to resend its request (which could
150 * take a second or two).
151 */
152 /*
153 * First, we find the fileserver for this mntfs and then call
154 * get_mountd_port with our mntfs passed as the wait channel.
155 * get_mountd_port will check some things and then schedule
156 * it so that when the fileserver is ready, a wakeup is done
157 * on this mntfs. amfs_cont() is already sleeping on this mntfs
158 * so as soon as that wakeup happens amfs_cont() is called and
159 * this mount is retried.
160 */
161 if (mf->mf_server)
162 /*
163 * We don't really care if there's an error returned.
164 * Since this is just to help speed things along, the
165 * error will get handled properly elsewhere.
166 */
167 get_mountd_port(mf->mf_server, &mountd_port, get_mntfs_wchan(mf));
168
169 return 0;
170 }
171
172
173 static int
do_mount(am_nfs_handle_t * fhp,char * mntdir,char * fs_name,mntfs * mf)174 do_mount(am_nfs_handle_t *fhp, char *mntdir, char *fs_name, mntfs *mf)
175 {
176 struct stat stb;
177
178 dlog("amfs_host: mounting fs %s on %s\n", fs_name, mntdir);
179
180 (void) mkdirs(mntdir, 0555);
181 if (stat(mntdir, &stb) < 0 || (stb.st_mode & S_IFMT) != S_IFDIR) {
182 plog(XLOG_ERROR, "No mount point for %s - skipping", mntdir);
183 return ENOENT;
184 }
185
186 return mount_nfs_fh(fhp, mntdir, fs_name, mf);
187 }
188
189
190 static int
sortfun(const voidp x,const voidp y)191 sortfun(const voidp x, const voidp y)
192 {
193 exports *a = (exports *) x;
194 exports *b = (exports *) y;
195
196 return strcmp((*a)->ex_dir, (*b)->ex_dir);
197 }
198
199
200 /*
201 * Get filehandle
202 */
203 static int
fetch_fhandle(CLIENT * client,char * dir,am_nfs_handle_t * fhp,u_long nfs_version)204 fetch_fhandle(CLIENT *client, char *dir, am_nfs_handle_t *fhp, u_long nfs_version)
205 {
206 struct timeval tv;
207 enum clnt_stat clnt_stat;
208 struct fhstatus res;
209 #ifdef HAVE_FS_NFS3
210 struct am_mountres3 res3;
211 #endif /* HAVE_FS_NFS3 */
212
213 /*
214 * Pick a number, any number...
215 */
216 tv.tv_sec = 20;
217 tv.tv_usec = 0;
218
219 dlog("Fetching fhandle for %s", dir);
220
221 /*
222 * Call the mount daemon on the remote host to
223 * get the filehandle. Use NFS version specific call.
224 */
225
226 plog(XLOG_INFO, "fetch_fhandle: NFS version %d", (int) nfs_version);
227 #ifdef HAVE_FS_NFS3
228 if (nfs_version == NFS_VERSION3
229 #ifdef HAVE_FS_NFS4
230 #ifndef NO_FALLBACK
231 || nfs_version == NFS_VERSION4
232 #endif /* NO_FALLBACK */
233 #endif /* HAVE_FS_NFS4 */
234 ) {
235
236 memset((char *) &res3, 0, sizeof(res3));
237 clnt_stat = clnt_call(client,
238 MOUNTPROC_MNT,
239 (XDRPROC_T_TYPE) xdr_dirpath,
240 (SVC_IN_ARG_TYPE) &dir,
241 (XDRPROC_T_TYPE) xdr_am_mountres3,
242 (SVC_IN_ARG_TYPE) &res3,
243 tv);
244 if (clnt_stat != RPC_SUCCESS) {
245 plog(XLOG_ERROR, "mountd rpc failed: %s", clnt_sperrno(clnt_stat));
246 return EIO;
247 }
248 /* Check the status of the filehandle */
249 if ((errno = res3.fhs_status)) {
250 dlog("fhandle fetch for mount version 3 failed: %m");
251 return errno;
252 }
253 memset((voidp) &fhp->v3, 0, sizeof(am_nfs_fh3));
254 fhp->v3.am_fh3_length = res3.mountres3_u.mountinfo.fhandle.fhandle3_len;
255 memmove(fhp->v3.am_fh3_data,
256 res3.mountres3_u.mountinfo.fhandle.fhandle3_val,
257 fhp->v3.am_fh3_length);
258 } else { /* not NFS_VERSION3 mount */
259 #endif /* HAVE_FS_NFS3 */
260 clnt_stat = clnt_call(client,
261 MOUNTPROC_MNT,
262 (XDRPROC_T_TYPE) xdr_dirpath,
263 (SVC_IN_ARG_TYPE) &dir,
264 (XDRPROC_T_TYPE) xdr_fhstatus,
265 (SVC_IN_ARG_TYPE) &res,
266 tv);
267 if (clnt_stat != RPC_SUCCESS) {
268 plog(XLOG_ERROR, "mountd rpc failed: %s", clnt_sperrno(clnt_stat));
269 return EIO;
270 }
271 /* Check status of filehandle */
272 if (res.fhs_status) {
273 errno = res.fhs_status;
274 dlog("fhandle fetch for mount version 1 failed: %m");
275 return errno;
276 }
277 memmove(&fhp->v2, &res.fhs_fh, NFS_FHSIZE);
278 #ifdef HAVE_FS_NFS3
279 } /* end of "if (nfs_version == NFS_VERSION3)" statement */
280 #endif /* HAVE_FS_NFS3 */
281
282 /* all is well */
283 return 0;
284 }
285
286
287 /*
288 * Scan mount table to see if something already mounted
289 */
290 static int
already_mounted(mntlist * mlist,char * dir)291 already_mounted(mntlist *mlist, char *dir)
292 {
293 mntlist *ml;
294
295 for (ml = mlist; ml; ml = ml->mnext)
296 if (STREQ(ml->mnt->mnt_dir, dir))
297 return 1;
298 return 0;
299 }
300
301
302 static int
amfs_host_mount(am_node * am,mntfs * mf)303 amfs_host_mount(am_node *am, mntfs *mf)
304 {
305 struct timeval tv2;
306 CLIENT *client;
307 enum clnt_stat clnt_stat;
308 int n_export;
309 int j, k;
310 exports exlist = 0, ex;
311 exports *ep = NULL;
312 am_nfs_handle_t *fp = NULL;
313 char *host;
314 int error = 0;
315 struct sockaddr_in sin;
316 int sock = RPC_ANYSOCK;
317 int ok = FALSE;
318 mntlist *mlist;
319 char fs_name[MAXPATHLEN], *rfs_dir;
320 char mntpt[MAXPATHLEN];
321 struct timeval tv;
322 u_long mnt_version;
323
324 /*
325 * WebNFS servers don't necessarily run mountd.
326 */
327 if (mf->mf_flags & MFF_WEBNFS) {
328 plog(XLOG_ERROR, "amfs_host_mount: cannot support WebNFS");
329 return EIO;
330 }
331
332 /*
333 * Read the mount list
334 */
335 mlist = read_mtab(mf->mf_mount, mnttab_file_name);
336
337 #ifdef MOUNT_TABLE_ON_FILE
338 /*
339 * Unlock the mount list
340 */
341 unlock_mntlist();
342 #endif /* MOUNT_TABLE_ON_FILE */
343
344 /*
345 * Take a copy of the server hostname, address, and nfs version
346 * to mount version conversion.
347 */
348 host = mf->mf_server->fs_host;
349 sin = *mf->mf_server->fs_ip;
350 plog(XLOG_INFO, "amfs_host_mount: NFS version %d", (int) mf->mf_server->fs_version);
351 #ifdef HAVE_FS_NFS3
352 if (mf->mf_server->fs_version == NFS_VERSION3)
353 mnt_version = AM_MOUNTVERS3;
354 else
355 #endif /* HAVE_FS_NFS3 */
356 mnt_version = MOUNTVERS;
357
358 /*
359 * The original 10 second per try timeout is WAY too large, especially
360 * if we're only waiting 10 or 20 seconds max for the response.
361 * That would mean we'd try only once in 10 seconds, and we could
362 * lose the transmit or receive packet, and never try again.
363 * A 2-second per try timeout here is much more reasonable.
364 * 09/28/92 Mike Mitchell, mcm@unx.sas.com
365 */
366 tv.tv_sec = 2;
367 tv.tv_usec = 0;
368
369 /*
370 * Create a client attached to mountd
371 */
372 client = get_mount_client(host, &sin, &tv, &sock, mnt_version);
373 if (client == NULL) {
374 #ifdef HAVE_CLNT_SPCREATEERROR
375 plog(XLOG_ERROR, "get_mount_client failed for %s: %s",
376 host, clnt_spcreateerror(""));
377 #else /* not HAVE_CLNT_SPCREATEERROR */
378 plog(XLOG_ERROR, "get_mount_client failed for %s", host);
379 #endif /* not HAVE_CLNT_SPCREATEERROR */
380 error = EIO;
381 goto out;
382 }
383 if (!nfs_auth) {
384 error = make_nfs_auth();
385 if (error)
386 goto out;
387 }
388 client->cl_auth = nfs_auth;
389
390 dlog("Fetching export list from %s", host);
391
392 /*
393 * Fetch the export list
394 */
395 tv2.tv_sec = 10;
396 tv2.tv_usec = 0;
397 clnt_stat = clnt_call(client,
398 MOUNTPROC_EXPORT,
399 (XDRPROC_T_TYPE) xdr_void,
400 0,
401 (XDRPROC_T_TYPE) xdr_exports,
402 (SVC_IN_ARG_TYPE) & exlist,
403 tv2);
404 if (clnt_stat != RPC_SUCCESS) {
405 const char *msg = clnt_sperrno(clnt_stat);
406 plog(XLOG_ERROR, "host_mount rpc failed: %s", msg);
407 /* clnt_perror(client, "rpc"); */
408 error = EIO;
409 goto out;
410 }
411
412 /*
413 * Figure out how many exports were returned
414 */
415 for (n_export = 0, ex = exlist; ex; ex = ex->ex_next) {
416 n_export++;
417 }
418
419 /*
420 * Allocate an array of pointers into the list
421 * so that they can be sorted. If the filesystem
422 * is already mounted then ignore it.
423 */
424 ep = (exports *) xmalloc(n_export * sizeof(exports));
425 for (j = 0, ex = exlist; ex; ex = ex->ex_next) {
426 make_mntpt(mntpt, sizeof(mntpt), ex, mf->mf_mount);
427 if (already_mounted(mlist, mntpt))
428 /* we have at least one mounted f/s, so don't fail the mount */
429 ok = TRUE;
430 else
431 ep[j++] = ex;
432 }
433 n_export = j;
434
435 /*
436 * Sort into order.
437 * This way the mounts are done in order down the tree,
438 * instead of any random order returned by the mount
439 * daemon (the protocol doesn't specify...).
440 */
441 qsort(ep, n_export, sizeof(exports), sortfun);
442
443 /*
444 * Allocate an array of filehandles
445 */
446 fp = (am_nfs_handle_t *) xmalloc(n_export * sizeof(am_nfs_handle_t));
447
448 /*
449 * Try to obtain filehandles for each directory.
450 * If a fetch fails then just zero out the array
451 * reference but discard the error.
452 */
453 for (j = k = 0; j < n_export; j++) {
454 /* Check and avoid a duplicated export entry */
455 if (j > k && ep[k] && STREQ(ep[j]->ex_dir, ep[k]->ex_dir)) {
456 dlog("avoiding dup fhandle requested for %s", ep[j]->ex_dir);
457 ep[j] = NULL;
458 } else {
459 k = j;
460 error = fetch_fhandle(client, ep[j]->ex_dir, &fp[j],
461 mf->mf_server->fs_version);
462 if (error)
463 ep[j] = NULL;
464 }
465 }
466
467 /*
468 * Mount each filesystem for which we have a filehandle.
469 * If any of the mounts succeed then mark "ok" and return
470 * error code 0 at the end. If they all fail then return
471 * the last error code.
472 */
473 xstrlcpy(fs_name, mf->mf_info, sizeof(fs_name));
474 if ((rfs_dir = strchr(fs_name, ':')) == (char *) NULL) {
475 plog(XLOG_FATAL, "amfs_host_mount: mf_info has no colon");
476 error = EINVAL;
477 goto out;
478 }
479 ++rfs_dir;
480 for (j = 0; j < n_export; j++) {
481 ex = ep[j];
482 if (ex) {
483 /*
484 * Note: the sizeof space left in rfs_dir is what's left in fs_name
485 * after strchr() above returned a pointer _inside_ fs_name. The
486 * calculation below also takes into account that rfs_dir was
487 * incremented by the ++ above.
488 */
489 xstrlcpy(rfs_dir, ex->ex_dir, sizeof(fs_name) - (rfs_dir - fs_name));
490 make_mntpt(mntpt, sizeof(mntpt), ex, mf->mf_mount);
491 if (do_mount(&fp[j], mntpt, fs_name, mf) == 0)
492 ok = TRUE;
493 }
494 }
495
496 /*
497 * Clean up and exit
498 */
499 out:
500 discard_mntlist(mlist);
501 XFREE(ep);
502 XFREE(fp);
503 if (sock != RPC_ANYSOCK)
504 (void) amu_close(sock);
505 if (client)
506 clnt_destroy(client);
507 if (exlist)
508 xdr_pri_free((XDRPROC_T_TYPE) xdr_exports, (caddr_t) &exlist);
509 if (ok)
510 return 0;
511 return error;
512 }
513
514
515 /*
516 * Return true if pref is a directory prefix of dir.
517 *
518 * XXX TODO:
519 * Does not work if pref is "/".
520 */
521 static int
directory_prefix(char * pref,char * dir)522 directory_prefix(char *pref, char *dir)
523 {
524 int len = strlen(pref);
525
526 if (!NSTREQ(pref, dir, len))
527 return FALSE;
528 if (dir[len] == '/' || dir[len] == '\0')
529 return TRUE;
530 return FALSE;
531 }
532
533
534 /*
535 * Unmount a mount tree
536 */
537 static int
amfs_host_umount(am_node * am,mntfs * mf)538 amfs_host_umount(am_node *am, mntfs *mf)
539 {
540 mntlist *ml, *mprev;
541 int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0;
542 int xerror = 0;
543
544 /*
545 * Read the mount list
546 */
547 mntlist *mlist = read_mtab(mf->mf_mount, mnttab_file_name);
548
549 #ifdef MOUNT_TABLE_ON_FILE
550 /*
551 * Unlock the mount list
552 */
553 unlock_mntlist();
554 #endif /* MOUNT_TABLE_ON_FILE */
555
556 /*
557 * Reverse list...
558 */
559 ml = mlist;
560 mprev = NULL;
561 while (ml) {
562 mntlist *ml2 = ml->mnext;
563 ml->mnext = mprev;
564 mprev = ml;
565 ml = ml2;
566 }
567 mlist = mprev;
568
569 /*
570 * Unmount all filesystems...
571 */
572 for (ml = mlist; ml && !xerror; ml = ml->mnext) {
573 char *dir = ml->mnt->mnt_dir;
574 if (directory_prefix(mf->mf_mount, dir)) {
575 int error;
576 dlog("amfs_host: unmounts %s", dir);
577 /*
578 * Unmount "dir"
579 */
580 error = UMOUNT_FS(dir, mnttab_file_name, unmount_flags);
581 /*
582 * Keep track of errors
583 */
584 if (error) {
585 /*
586 * If we have not already set xerror and error is not ENOENT,
587 * then set xerror equal to error and log it.
588 * 'xerror' is the return value for this function.
589 *
590 * We do not want to pass ENOENT as an error because if the
591 * directory does not exists our work is done anyway.
592 */
593 if (!xerror && error != ENOENT)
594 xerror = error;
595 if (error != EBUSY) {
596 errno = error;
597 plog(XLOG_ERROR, "Tree unmount of %s failed: %m", ml->mnt->mnt_dir);
598 }
599 } else {
600 (void) rmdirs(dir);
601 }
602 }
603 }
604
605 /*
606 * Throw away mount list
607 */
608 discard_mntlist(mlist);
609
610 /*
611 * Try to remount, except when we are shutting down.
612 */
613 if (xerror && amd_state != Finishing) {
614 xerror = amfs_host_mount(am, mf);
615 if (!xerror) {
616 /*
617 * Don't log this - it's usually too verbose
618 plog(XLOG_INFO, "Remounted host %s", mf->mf_info);
619 */
620 xerror = EBUSY;
621 }
622 }
623 return xerror;
624 }
625
626
627 /*
628 * Tell mountd we're done.
629 * This is not quite right, because we may still
630 * have other filesystems mounted, but the existing
631 * mountd protocol is badly broken anyway.
632 */
633 static void
amfs_host_umounted(mntfs * mf)634 amfs_host_umounted(mntfs *mf)
635 {
636 char *host;
637 CLIENT *client;
638 enum clnt_stat clnt_stat;
639 struct sockaddr_in sin;
640 int sock = RPC_ANYSOCK;
641 struct timeval tv;
642 u_long mnt_version;
643
644 if (mf->mf_error || mf->mf_refc > 1 || !mf->mf_server)
645 return;
646
647 /*
648 * WebNFS servers shouldn't ever get here.
649 */
650 if (mf->mf_flags & MFF_WEBNFS) {
651 plog(XLOG_ERROR, "amfs_host_umounted: cannot support WebNFS");
652 return;
653 }
654
655 /*
656 * Take a copy of the server hostname, address, and NFS version
657 * to mount version conversion.
658 */
659 host = mf->mf_server->fs_host;
660 sin = *mf->mf_server->fs_ip;
661 plog(XLOG_INFO, "amfs_host_umounted: NFS version %d", (int) mf->mf_server->fs_version);
662 #ifdef HAVE_FS_NFS3
663 if (mf->mf_server->fs_version == NFS_VERSION3)
664 mnt_version = AM_MOUNTVERS3;
665 else
666 #endif /* HAVE_FS_NFS3 */
667 mnt_version = MOUNTVERS;
668
669 /*
670 * Create a client attached to mountd
671 */
672 tv.tv_sec = 10;
673 tv.tv_usec = 0;
674 client = get_mount_client(host, &sin, &tv, &sock, mnt_version);
675 if (client == NULL) {
676 #ifdef HAVE_CLNT_SPCREATEERROR
677 plog(XLOG_ERROR, "get_mount_client failed for %s: %s",
678 host, clnt_spcreateerror(""));
679 #else /* not HAVE_CLNT_SPCREATEERROR */
680 plog(XLOG_ERROR, "get_mount_client failed for %s", host);
681 #endif /* not HAVE_CLNT_SPCREATEERROR */
682 goto out;
683 }
684
685 if (!nfs_auth) {
686 if (make_nfs_auth())
687 goto out;
688 }
689 client->cl_auth = nfs_auth;
690
691 dlog("Unmounting all from %s", host);
692
693 clnt_stat = clnt_call(client,
694 MOUNTPROC_UMNTALL,
695 (XDRPROC_T_TYPE) xdr_void,
696 0,
697 (XDRPROC_T_TYPE) xdr_void,
698 0,
699 tv);
700 if (clnt_stat != RPC_SUCCESS && clnt_stat != RPC_SYSTEMERROR) {
701 /* RPC_SYSTEMERROR seems to be returned for no good reason ... */
702 const char *msg = clnt_sperrno(clnt_stat);
703 plog(XLOG_ERROR, "unmount all from %s rpc failed: %s", host, msg);
704 goto out;
705 }
706
707 out:
708 if (sock != RPC_ANYSOCK)
709 (void) amu_close(sock);
710 if (client)
711 clnt_destroy(client);
712 }
713