xref: /netbsd-src/external/bsd/am-utils/dist/amd/amfs_host.c (revision 274254cdae52594c1aa480a736aef78313d15c9c)
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