aboutsummaryrefslogtreecommitdiff
path: root/contrib/amd/amd/amfs_host.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/amd/amd/amfs_host.c')
-rw-r--r--contrib/amd/amd/amfs_host.c709
1 files changed, 709 insertions, 0 deletions
diff --git a/contrib/amd/amd/amfs_host.c b/contrib/amd/amd/amfs_host.c
new file mode 100644
index 000000000000..55423c46ea33
--- /dev/null
+++ b/contrib/amd/amd/amfs_host.c
@@ -0,0 +1,709 @@
+/*
+ * Copyright (c) 1997-2006 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgment:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * File: am-utils/amd/amfs_host.c
+ *
+ */
+
+/*
+ * NFS host file system.
+ * Mounts all exported filesystems from a given host.
+ * This has now degenerated into a mess but will not
+ * be rewritten. Amd 6 will support the abstractions
+ * needed to make this work correctly.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+static char *amfs_host_match(am_opts *fo);
+static int amfs_host_init(mntfs *mf);
+static int amfs_host_mount(am_node *am, mntfs *mf);
+static int amfs_host_umount(am_node *am, mntfs *mf);
+static void amfs_host_umounted(mntfs *mf);
+
+/*
+ * Ops structure
+ */
+am_ops amfs_host_ops =
+{
+ "host",
+ amfs_host_match,
+ amfs_host_init,
+ amfs_host_mount,
+ amfs_host_umount,
+ amfs_error_lookup_child,
+ amfs_error_mount_child,
+ amfs_error_readdir,
+ 0, /* amfs_host_readlink */
+ 0, /* amfs_host_mounted */
+ amfs_host_umounted,
+ find_nfs_srvr,
+ 0, /* amfs_host_get_wchan */
+ FS_MKMNT | FS_BACKGROUND | FS_AMQINFO,
+#ifdef HAVE_FS_AUTOFS
+ AUTOFS_HOST_FS_FLAGS,
+#endif /* HAVE_FS_AUTOFS */
+};
+
+
+/*
+ * Determine the mount point:
+ *
+ * The next change we put in to better handle PCs. This is a bit
+ * disgusting, so you'd better sit down. We change the make_mntpt function
+ * to look for exported file systems without a leading '/'. If they don't
+ * have a leading '/', we add one. If the export is 'a:' through 'z:'
+ * (without a leading slash), we change it to 'a%' (or b% or z%). This
+ * allows the entire PC disk to be mounted.
+ */
+static void
+make_mntpt(char *mntpt, size_t l, const exports ex, const char *mf_mount)
+{
+ if (ex->ex_dir[0] == '/') {
+ if (ex->ex_dir[1] == 0)
+ xstrlcpy(mntpt, mf_mount, l);
+ else
+ xsnprintf(mntpt, l, "%s%s", mf_mount, ex->ex_dir);
+ } else if (ex->ex_dir[0] >= 'a' &&
+ ex->ex_dir[0] <= 'z' &&
+ ex->ex_dir[1] == ':' &&
+ ex->ex_dir[2] == '/' &&
+ ex->ex_dir[3] == 0)
+ xsnprintf(mntpt, l, "%s/%c%%", mf_mount, ex->ex_dir[0]);
+ else
+ xsnprintf(mntpt, l, "%s/%s", mf_mount, ex->ex_dir);
+}
+
+
+/*
+ * Execute needs the same as NFS plus a helper command
+ */
+static char *
+amfs_host_match(am_opts *fo)
+{
+ extern am_ops nfs_ops;
+
+ /*
+ * Make sure rfs is specified to keep nfs_match happy...
+ */
+ if (!fo->opt_rfs)
+ fo->opt_rfs = "/";
+
+ return (*nfs_ops.fs_match) (fo);
+}
+
+
+static int
+amfs_host_init(mntfs *mf)
+{
+ u_short mountd_port;
+
+ if (strchr(mf->mf_info, ':') == 0)
+ return ENOENT;
+
+ /*
+ * This is primarily to schedule a wakeup so that as soon
+ * as our fileserver is ready, we can continue setting up
+ * the host filesystem. If we don't do this, the standard
+ * amfs_auto code will set up a fileserver structure, but it will
+ * have to wait for another nfs request from the client to come
+ * in before finishing. Our way is faster since we don't have
+ * to wait for the client to resend its request (which could
+ * take a second or two).
+ */
+ /*
+ * First, we find the fileserver for this mntfs and then call
+ * get_mountd_port with our mntfs passed as the wait channel.
+ * get_mountd_port will check some things and then schedule
+ * it so that when the fileserver is ready, a wakeup is done
+ * on this mntfs. amfs_cont() is already sleeping on this mntfs
+ * so as soon as that wakeup happens amfs_cont() is called and
+ * this mount is retried.
+ */
+ if (mf->mf_server)
+ /*
+ * We don't really care if there's an error returned.
+ * Since this is just to help speed things along, the
+ * error will get handled properly elsewhere.
+ */
+ get_mountd_port(mf->mf_server, &mountd_port, get_mntfs_wchan(mf));
+
+ return 0;
+}
+
+
+static int
+do_mount(am_nfs_handle_t *fhp, char *mntdir, char *fs_name, mntfs *mf)
+{
+ struct stat stb;
+
+ dlog("amfs_host: mounting fs %s on %s\n", fs_name, mntdir);
+
+ (void) mkdirs(mntdir, 0555);
+ if (stat(mntdir, &stb) < 0 || (stb.st_mode & S_IFMT) != S_IFDIR) {
+ plog(XLOG_ERROR, "No mount point for %s - skipping", mntdir);
+ return ENOENT;
+ }
+
+ return mount_nfs_fh(fhp, mntdir, fs_name, mf);
+}
+
+
+static int
+sortfun(const voidp x, const voidp y)
+{
+ exports *a = (exports *) x;
+ exports *b = (exports *) y;
+
+ return strcmp((*a)->ex_dir, (*b)->ex_dir);
+}
+
+
+/*
+ * Get filehandle
+ */
+static int
+fetch_fhandle(CLIENT *client, char *dir, am_nfs_handle_t *fhp, u_long nfs_version)
+{
+ struct timeval tv;
+ enum clnt_stat clnt_stat;
+ struct fhstatus res;
+#ifdef HAVE_FS_NFS3
+ struct am_mountres3 res3;
+#endif /* HAVE_FS_NFS3 */
+
+ /*
+ * Pick a number, any number...
+ */
+ tv.tv_sec = 20;
+ tv.tv_usec = 0;
+
+ dlog("Fetching fhandle for %s", dir);
+
+ /*
+ * Call the mount daemon on the remote host to
+ * get the filehandle. Use NFS version specific call.
+ */
+
+ plog(XLOG_INFO, "fetch_fhandle: NFS version %d", (int) nfs_version);
+#ifdef HAVE_FS_NFS3
+ if (nfs_version == NFS_VERSION3) {
+ memset((char *) &res3, 0, sizeof(res3));
+ clnt_stat = clnt_call(client,
+ MOUNTPROC_MNT,
+ (XDRPROC_T_TYPE) xdr_dirpath,
+ (SVC_IN_ARG_TYPE) &dir,
+ (XDRPROC_T_TYPE) xdr_am_mountres3,
+ (SVC_IN_ARG_TYPE) &res3,
+ tv);
+ if (clnt_stat != RPC_SUCCESS) {
+ plog(XLOG_ERROR, "mountd rpc failed: %s", clnt_sperrno(clnt_stat));
+ return EIO;
+ }
+ /* Check the status of the filehandle */
+ if ((errno = res3.fhs_status)) {
+ dlog("fhandle fetch for mount version 3 failed: %m");
+ return errno;
+ }
+ memset((voidp) &fhp->v3, 0, sizeof(am_nfs_fh3));
+ fhp->v3.am_fh3_length = res3.mountres3_u.mountinfo.fhandle.fhandle3_len;
+ memmove(fhp->v3.am_fh3_data,
+ res3.mountres3_u.mountinfo.fhandle.fhandle3_val,
+ fhp->v3.am_fh3_length);
+ } else { /* not NFS_VERSION3 mount */
+#endif /* HAVE_FS_NFS3 */
+ clnt_stat = clnt_call(client,
+ MOUNTPROC_MNT,
+ (XDRPROC_T_TYPE) xdr_dirpath,
+ (SVC_IN_ARG_TYPE) &dir,
+ (XDRPROC_T_TYPE) xdr_fhstatus,
+ (SVC_IN_ARG_TYPE) &res,
+ tv);
+ if (clnt_stat != RPC_SUCCESS) {
+ plog(XLOG_ERROR, "mountd rpc failed: %s", clnt_sperrno(clnt_stat));
+ return EIO;
+ }
+ /* Check status of filehandle */
+ if (res.fhs_status) {
+ errno = res.fhs_status;
+ dlog("fhandle fetch for mount version 1 failed: %m");
+ return errno;
+ }
+ memmove(&fhp->v2, &res.fhs_fh, NFS_FHSIZE);
+#ifdef HAVE_FS_NFS3
+ } /* end of "if (nfs_version == NFS_VERSION3)" statement */
+#endif /* HAVE_FS_NFS3 */
+
+ /* all is well */
+ return 0;
+}
+
+
+/*
+ * Scan mount table to see if something already mounted
+ */
+static int
+already_mounted(mntlist *mlist, char *dir)
+{
+ mntlist *ml;
+
+ for (ml = mlist; ml; ml = ml->mnext)
+ if (STREQ(ml->mnt->mnt_dir, dir))
+ return 1;
+ return 0;
+}
+
+
+static int
+amfs_host_mount(am_node *am, mntfs *mf)
+{
+ struct timeval tv2;
+ CLIENT *client;
+ enum clnt_stat clnt_stat;
+ int n_export;
+ int j, k;
+ exports exlist = 0, ex;
+ exports *ep = 0;
+ am_nfs_handle_t *fp = 0;
+ char *host;
+ int error = 0;
+ struct sockaddr_in sin;
+ int sock = RPC_ANYSOCK;
+ int ok = FALSE;
+ mntlist *mlist;
+ char fs_name[MAXPATHLEN], *rfs_dir;
+ char mntpt[MAXPATHLEN];
+ struct timeval tv;
+ u_long mnt_version;
+
+ /*
+ * WebNFS servers don't necessarily run mountd.
+ */
+ if (mf->mf_flags & MFF_WEBNFS) {
+ plog(XLOG_ERROR, "amfs_host_mount: cannot support WebNFS");
+ return EIO;
+ }
+
+ /*
+ * Read the mount list
+ */
+ mlist = read_mtab(mf->mf_mount, mnttab_file_name);
+
+#ifdef MOUNT_TABLE_ON_FILE
+ /*
+ * Unlock the mount list
+ */
+ unlock_mntlist();
+#endif /* MOUNT_TABLE_ON_FILE */
+
+ /*
+ * Take a copy of the server hostname, address, and nfs version
+ * to mount version conversion.
+ */
+ host = mf->mf_server->fs_host;
+ sin = *mf->mf_server->fs_ip;
+ plog(XLOG_INFO, "amfs_host_mount: NFS version %d", (int) mf->mf_server->fs_version);
+#ifdef HAVE_FS_NFS3
+ if (mf->mf_server->fs_version == NFS_VERSION3)
+ mnt_version = AM_MOUNTVERS3;
+ else
+#endif /* HAVE_FS_NFS3 */
+ mnt_version = MOUNTVERS;
+
+ /*
+ * The original 10 second per try timeout is WAY too large, especially
+ * if we're only waiting 10 or 20 seconds max for the response.
+ * That would mean we'd try only once in 10 seconds, and we could
+ * lose the transmit or receive packet, and never try again.
+ * A 2-second per try timeout here is much more reasonable.
+ * 09/28/92 Mike Mitchell, mcm@unx.sas.com
+ */
+ tv.tv_sec = 2;
+ tv.tv_usec = 0;
+
+ /*
+ * Create a client attached to mountd
+ */
+ client = get_mount_client(host, &sin, &tv, &sock, mnt_version);
+ if (client == NULL) {
+#ifdef HAVE_CLNT_SPCREATEERROR
+ plog(XLOG_ERROR, "get_mount_client failed for %s: %s",
+ host, clnt_spcreateerror(""));
+#else /* not HAVE_CLNT_SPCREATEERROR */
+ plog(XLOG_ERROR, "get_mount_client failed for %s", host);
+#endif /* not HAVE_CLNT_SPCREATEERROR */
+ error = EIO;
+ goto out;
+ }
+ if (!nfs_auth) {
+ error = make_nfs_auth();
+ if (error)
+ goto out;
+ }
+ client->cl_auth = nfs_auth;
+
+ dlog("Fetching export list from %s", host);
+
+ /*
+ * Fetch the export list
+ */
+ tv2.tv_sec = 10;
+ tv2.tv_usec = 0;
+ clnt_stat = clnt_call(client,
+ MOUNTPROC_EXPORT,
+ (XDRPROC_T_TYPE) xdr_void,
+ 0,
+ (XDRPROC_T_TYPE) xdr_exports,
+ (SVC_IN_ARG_TYPE) & exlist,
+ tv2);
+ if (clnt_stat != RPC_SUCCESS) {
+ const char *msg = clnt_sperrno(clnt_stat);
+ plog(XLOG_ERROR, "host_mount rpc failed: %s", msg);
+ /* clnt_perror(client, "rpc"); */
+ error = EIO;
+ goto out;
+ }
+
+ /*
+ * Figure out how many exports were returned
+ */
+ for (n_export = 0, ex = exlist; ex; ex = ex->ex_next) {
+ n_export++;
+ }
+
+ /*
+ * Allocate an array of pointers into the list
+ * so that they can be sorted. If the filesystem
+ * is already mounted then ignore it.
+ */
+ ep = (exports *) xmalloc(n_export * sizeof(exports));
+ for (j = 0, ex = exlist; ex; ex = ex->ex_next) {
+ make_mntpt(mntpt, sizeof(mntpt), ex, mf->mf_mount);
+ if (already_mounted(mlist, mntpt))
+ /* we have at least one mounted f/s, so don't fail the mount */
+ ok = TRUE;
+ else
+ ep[j++] = ex;
+ }
+ n_export = j;
+
+ /*
+ * Sort into order.
+ * This way the mounts are done in order down the tree,
+ * instead of any random order returned by the mount
+ * daemon (the protocol doesn't specify...).
+ */
+ qsort(ep, n_export, sizeof(exports), sortfun);
+
+ /*
+ * Allocate an array of filehandles
+ */
+ fp = (am_nfs_handle_t *) xmalloc(n_export * sizeof(am_nfs_handle_t));
+
+ /*
+ * Try to obtain filehandles for each directory.
+ * If a fetch fails then just zero out the array
+ * reference but discard the error.
+ */
+ for (j = k = 0; j < n_export; j++) {
+ /* Check and avoid a duplicated export entry */
+ if (j > k && ep[k] && STREQ(ep[j]->ex_dir, ep[k]->ex_dir)) {
+ dlog("avoiding dup fhandle requested for %s", ep[j]->ex_dir);
+ ep[j] = 0;
+ } else {
+ k = j;
+ error = fetch_fhandle(client, ep[j]->ex_dir, &fp[j],
+ mf->mf_server->fs_version);
+ if (error)
+ ep[j] = 0;
+ }
+ }
+
+ /*
+ * Mount each filesystem for which we have a filehandle.
+ * If any of the mounts succeed then mark "ok" and return
+ * error code 0 at the end. If they all fail then return
+ * the last error code.
+ */
+ xstrlcpy(fs_name, mf->mf_info, MAXPATHLEN);
+ if ((rfs_dir = strchr(fs_name, ':')) == (char *) 0) {
+ plog(XLOG_FATAL, "amfs_host_mount: mf_info has no colon");
+ error = EINVAL;
+ goto out;
+ }
+ ++rfs_dir;
+ for (j = 0; j < n_export; j++) {
+ ex = ep[j];
+ if (ex) {
+ /*
+ * Note: the sizeof space left in rfs_dir is what's left in fs_name
+ * after strchr() above returned a pointer _inside_ fs_name. The
+ * calculation below also takes into account that rfs_dir was
+ * incremented by the ++ above.
+ */
+ xstrlcpy(rfs_dir, ex->ex_dir, sizeof(fs_name) - (rfs_dir - fs_name));
+ make_mntpt(mntpt, sizeof(mntpt), ex, mf->mf_mount);
+ if (do_mount(&fp[j], mntpt, fs_name, mf) == 0)
+ ok = TRUE;
+ }
+ }
+
+ /*
+ * Clean up and exit
+ */
+out:
+ discard_mntlist(mlist);
+ if (ep)
+ XFREE(ep);
+ if (fp)
+ XFREE(fp);
+ if (sock != RPC_ANYSOCK)
+ (void) amu_close(sock);
+ if (client)
+ clnt_destroy(client);
+ if (exlist)
+ xdr_pri_free((XDRPROC_T_TYPE) xdr_exports, (caddr_t) &exlist);
+ if (ok)
+ return 0;
+ return error;
+}
+
+
+/*
+ * Return true if pref is a directory prefix of dir.
+ *
+ * XXX TODO:
+ * Does not work if pref is "/".
+ */
+static int
+directory_prefix(char *pref, char *dir)
+{
+ int len = strlen(pref);
+
+ if (!NSTREQ(pref, dir, len))
+ return FALSE;
+ if (dir[len] == '/' || dir[len] == '\0')
+ return TRUE;
+ return FALSE;
+}
+
+
+/*
+ * Unmount a mount tree
+ */
+static int
+amfs_host_umount(am_node *am, mntfs *mf)
+{
+ mntlist *ml, *mprev;
+ int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0;
+ int xerror = 0;
+
+ /*
+ * Read the mount list
+ */
+ mntlist *mlist = read_mtab(mf->mf_mount, mnttab_file_name);
+
+#ifdef MOUNT_TABLE_ON_FILE
+ /*
+ * Unlock the mount list
+ */
+ unlock_mntlist();
+#endif /* MOUNT_TABLE_ON_FILE */
+
+ /*
+ * Reverse list...
+ */
+ ml = mlist;
+ mprev = 0;
+ while (ml) {
+ mntlist *ml2 = ml->mnext;
+ ml->mnext = mprev;
+ mprev = ml;
+ ml = ml2;
+ }
+ mlist = mprev;
+
+ /*
+ * Unmount all filesystems...
+ */
+ for (ml = mlist; ml && !xerror; ml = ml->mnext) {
+ char *dir = ml->mnt->mnt_dir;
+ if (directory_prefix(mf->mf_mount, dir)) {
+ int error;
+ dlog("amfs_host: unmounts %s", dir);
+ /*
+ * Unmount "dir"
+ */
+ error = UMOUNT_FS(dir, mnttab_file_name, unmount_flags);
+ /*
+ * Keep track of errors
+ */
+ if (error) {
+ /*
+ * If we have not already set xerror and error is not ENOENT,
+ * then set xerror equal to error and log it.
+ * 'xerror' is the return value for this function.
+ *
+ * We do not want to pass ENOENT as an error because if the
+ * directory does not exists our work is done anyway.
+ */
+ if (!xerror && error != ENOENT)
+ xerror = error;
+ if (error != EBUSY) {
+ errno = error;
+ plog(XLOG_ERROR, "Tree unmount of %s failed: %m", ml->mnt->mnt_dir);
+ }
+ } else {
+ (void) rmdirs(dir);
+ }
+ }
+ }
+
+ /*
+ * Throw away mount list
+ */
+ discard_mntlist(mlist);
+
+ /*
+ * Try to remount, except when we are shutting down.
+ */
+ if (xerror && amd_state != Finishing) {
+ xerror = amfs_host_mount(am, mf);
+ if (!xerror) {
+ /*
+ * Don't log this - it's usually too verbose
+ plog(XLOG_INFO, "Remounted host %s", mf->mf_info);
+ */
+ xerror = EBUSY;
+ }
+ }
+ return xerror;
+}
+
+
+/*
+ * Tell mountd we're done.
+ * This is not quite right, because we may still
+ * have other filesystems mounted, but the existing
+ * mountd protocol is badly broken anyway.
+ */
+static void
+amfs_host_umounted(mntfs *mf)
+{
+ char *host;
+ CLIENT *client;
+ enum clnt_stat clnt_stat;
+ struct sockaddr_in sin;
+ int sock = RPC_ANYSOCK;
+ struct timeval tv;
+ u_long mnt_version;
+
+ if (mf->mf_error || mf->mf_refc > 1 || !mf->mf_server)
+ return;
+
+ /*
+ * WebNFS servers shouldn't ever get here.
+ */
+ if (mf->mf_flags & MFF_WEBNFS) {
+ plog(XLOG_ERROR, "amfs_host_umounted: cannot support WebNFS");
+ return;
+ }
+
+ /*
+ * Take a copy of the server hostname, address, and NFS version
+ * to mount version conversion.
+ */
+ host = mf->mf_server->fs_host;
+ sin = *mf->mf_server->fs_ip;
+ plog(XLOG_INFO, "amfs_host_umounted: NFS version %d", (int) mf->mf_server->fs_version);
+#ifdef HAVE_FS_NFS3
+ if (mf->mf_server->fs_version == NFS_VERSION3)
+ mnt_version = AM_MOUNTVERS3;
+ else
+#endif /* HAVE_FS_NFS3 */
+ mnt_version = MOUNTVERS;
+
+ /*
+ * Create a client attached to mountd
+ */
+ tv.tv_sec = 10;
+ tv.tv_usec = 0;
+ client = get_mount_client(host, &sin, &tv, &sock, mnt_version);
+ if (client == NULL) {
+#ifdef HAVE_CLNT_SPCREATEERROR
+ plog(XLOG_ERROR, "get_mount_client failed for %s: %s",
+ host, clnt_spcreateerror(""));
+#else /* not HAVE_CLNT_SPCREATEERROR */
+ plog(XLOG_ERROR, "get_mount_client failed for %s", host);
+#endif /* not HAVE_CLNT_SPCREATEERROR */
+ goto out;
+ }
+
+ if (!nfs_auth) {
+ if (make_nfs_auth())
+ goto out;
+ }
+ client->cl_auth = nfs_auth;
+
+ dlog("Unmounting all from %s", host);
+
+ clnt_stat = clnt_call(client,
+ MOUNTPROC_UMNTALL,
+ (XDRPROC_T_TYPE) xdr_void,
+ 0,
+ (XDRPROC_T_TYPE) xdr_void,
+ 0,
+ tv);
+ if (clnt_stat != RPC_SUCCESS && clnt_stat != RPC_SYSTEMERROR) {
+ /* RPC_SYSTEMERROR seems to be returned for no good reason ... */
+ const char *msg = clnt_sperrno(clnt_stat);
+ plog(XLOG_ERROR, "unmount all from %s rpc failed: %s", host, msg);
+ goto out;
+ }
+
+out:
+ if (sock != RPC_ANYSOCK)
+ (void) amu_close(sock);
+ if (client)
+ clnt_destroy(client);
+}