xref: /freebsd-12.1/contrib/amd/amd/amfs_host.c (revision ad8bcc14)
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