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