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