xref: /netbsd-src/external/bsd/am-utils/dist/amd/amfs_host.c (revision 8bae5d409deb915cf7c8f0539fae22ff2cb8a313)
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
make_mntpt(char * mntpt,size_t l,const exports ex,const char * mf_mount)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 *
amfs_host_match(am_opts * fo)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
amfs_host_init(mntfs * mf)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
do_mount(am_nfs_handle_t * fhp,char * mntdir,char * fs_name,mntfs * mf)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
sortfun(const voidp x,const voidp y)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
fetch_fhandle(CLIENT * client,char * dir,am_nfs_handle_t * fhp,u_long nfs_version)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
already_mounted(mntlist * mlist,char * dir)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
amfs_host_mount(am_node * am,mntfs * mf)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
directory_prefix(char * pref,char * dir)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
amfs_host_umount(am_node * am,mntfs * mf)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
amfs_host_umounted(mntfs * mf)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