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 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 * 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 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 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 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 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 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 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 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 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 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