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