1/*	$NetBSD: amq.c,v 1.4 2022/08/23 07:42:28 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/amq/amq.c
39 *
40 */
41
42/*
43 * Automounter query tool
44 */
45
46#ifdef HAVE_CONFIG_H
47# include <config.h>
48#endif /* HAVE_CONFIG_H */
49#include <am_defs.h>
50#include <amq.h>
51
52/* locals */
53static int flush_flag;
54static int getpid_flag;
55static int getpwd_flag;
56static int getvers_flag;
57static int minfo_flag;
58static int mapinfo_flag;
59static int quiet_flag;
60static int stats_flag;
61static int unmount_flag;
62static int use_tcp_flag;
63static int use_udp_flag;
64static u_long amd_program_number = AMQ_PROGRAM;
65static char *debug_opts;
66static char *amq_logfile;
67static char *xlog_optstr;
68static char localhost[] = "localhost";
69static char *def_server = localhost;
70
71/* externals */
72extern int optind;
73extern char *optarg;
74
75/* structures */
76enum show_opt {
77  Full, Stats, Calc, Short, ShowDone
78};
79
80
81static void
82time_print(time_type tt)
83{
84  time_t t = (time_t)tt;
85  struct tm *tp = localtime(&t);
86  printf("%02d/%02d/%04d %02d:%02d:%02d",
87	 tp->tm_mon + 1, tp->tm_mday,
88	 tp->tm_year < 1900 ? tp->tm_year + 1900 : tp->tm_year,
89	 tp->tm_hour, tp->tm_min, tp->tm_sec);
90}
91
92/*
93 * If (e) is Calc then just calculate the sizes
94 * Otherwise display the mount node on stdout
95 */
96static void
97show_mti(amq_mount_tree *mt, enum show_opt e, int *mwid, int *dwid, int *twid)
98{
99  switch (e) {
100  case Calc:
101    {
102      int mw = strlen(mt->mt_mountinfo);
103      int dw = strlen(mt->mt_directory);
104      int tw = strlen(mt->mt_type);
105      if (mw > *mwid)
106	*mwid = mw;
107      if (dw > *dwid)
108	*dwid = dw;
109      if (tw > *twid)
110	*twid = tw;
111    }
112  break;
113
114  case Full:
115    {
116      printf("%-*.*s %-*.*s %-*.*s %s\n\t%-5d %-7d %-6d %-7d %-7d %-6d",
117	     *dwid, *dwid,
118	     *mt->mt_directory ? mt->mt_directory : "/",	/* XXX */
119	     *twid, *twid,
120	     mt->mt_type,
121	     *mwid, *mwid,
122	     mt->mt_mountinfo,
123	     mt->mt_mountpoint,
124
125	     mt->mt_mountuid,
126	     mt->mt_getattr,
127	     mt->mt_lookup,
128	     mt->mt_readdir,
129	     mt->mt_readlink,
130	     mt->mt_statfs);
131      time_print(mt->mt_mounttime);
132      printf("\n");
133    }
134  break;
135
136  case Stats:
137    {
138      printf("%-*.*s %-5d %-7d %-6d %-7d %-7d %-6d ",
139	     *dwid, *dwid,
140	     *mt->mt_directory ? mt->mt_directory : "/",	/* XXX */
141
142	     mt->mt_mountuid,
143	     mt->mt_getattr,
144	     mt->mt_lookup,
145	     mt->mt_readdir,
146	     mt->mt_readlink,
147	     mt->mt_statfs);
148      time_print(mt->mt_mounttime);
149      printf("\n");
150    }
151  break;
152
153  case Short:
154    {
155      printf("%-*.*s %-*.*s %-*.*s %s\n",
156	     *dwid, *dwid,
157	     *mt->mt_directory ? mt->mt_directory : "/",
158	     *twid, *twid,
159	     mt->mt_type,
160	     *mwid, *mwid,
161	     mt->mt_mountinfo,
162	     mt->mt_mountpoint);
163    }
164  break;
165
166  default:
167    break;
168  }
169}
170
171
172/*
173 * Display a pwd data
174 */
175static void
176show_pwd(amq_mount_tree *mt, char *path, size_t l, int *flag)
177{
178  int len;
179
180  while (mt) {
181    len = strlen(mt->mt_mountpoint);
182    if (NSTREQ(path, mt->mt_mountpoint, len) &&
183	!STREQ(mt->mt_directory, mt->mt_mountpoint)) {
184      char buf[MAXPATHLEN+1];	/* must be same size as 'path' */
185      xstrlcpy(buf, mt->mt_directory, sizeof(buf));
186      xstrlcat(buf, &path[len], sizeof(buf));
187      xstrlcpy(path, buf, l);
188      *flag = 1;
189    }
190    show_pwd(mt->mt_next, path, l, flag);
191    mt = mt->mt_child;
192  }
193}
194
195
196/*
197 * Display a mount tree.
198 */
199static void
200show_mt(amq_mount_tree *mt, enum show_opt e, int *mwid, int *dwid, int *pwid)
201{
202  while (mt) {
203    show_mti(mt, e, mwid, dwid, pwid);
204    show_mt(mt->mt_next, e, mwid, dwid, pwid);
205    mt = mt->mt_child;
206  }
207}
208
209
210static void
211show_mi(amq_mount_info_list *ml, enum show_opt e, int *mwid, int *dwid, int *twid)
212{
213  u_int i;
214
215  switch (e) {
216
217  case Calc:
218    {
219      for (i = 0; i < ml->amq_mount_info_list_len; i++) {
220	amq_mount_info *mi = &ml->amq_mount_info_list_val[i];
221	int mw = strlen(mi->mi_mountinfo);
222	int dw = strlen(mi->mi_mountpt);
223	int tw = strlen(mi->mi_type);
224	if (mw > *mwid)
225	  *mwid = mw;
226	if (dw > *dwid)
227	  *dwid = dw;
228	if (tw > *twid)
229	  *twid = tw;
230      }
231    }
232  break;
233
234  case Full:
235    {
236      for (i = 0; i < ml->amq_mount_info_list_len; i++) {
237	amq_mount_info *mi = &ml->amq_mount_info_list_val[i];
238	printf("%-*.*s %-*.*s %-*.*s %-3d %s is %s ",
239	       *mwid, *mwid, mi->mi_mountinfo,
240	       *dwid, *dwid, mi->mi_mountpt,
241	       *twid, *twid, mi->mi_type,
242	       mi->mi_refc, mi->mi_fserver,
243	       mi->mi_up > 0 ? "up" :
244	       mi->mi_up < 0 ? "starting" : "down");
245	if (mi->mi_error > 0) {
246	  printf(" (%s)", strerror(mi->mi_error));
247	} else if (mi->mi_error < 0) {
248	  fputs(" (in progress)", stdout);
249	}
250	fputc('\n', stdout);
251      }
252    }
253  break;
254
255  default:
256    break;
257  }
258}
259
260static void
261show_mapinfo(amq_map_info_list *ml, enum show_opt e, int *nwid, int *wwid)
262{
263  u_int i;
264
265  switch (e) {
266
267  case Calc:
268    {
269      for (i = 0; i < ml->amq_map_info_list_len; i++) {
270	amq_map_info *mi = &ml->amq_map_info_list_val[i];
271	int nw = strlen(mi->mi_name);
272	int ww = strlen(mi->mi_wildcard ? mi->mi_wildcard : "(null");
273	if (nw > *nwid)
274	  *nwid = nw;
275	if (ww > *wwid)
276	  *wwid = ww;
277      }
278    }
279  break;
280
281  case Full:
282    {
283      printf("%-*.*s %-*.*s %-8.8s %-7.7s %-7.7s %-7.7s %-s Modified\n",
284	*nwid, *nwid, "Name",
285	*wwid, *wwid, "Wild",
286	"Flags", "Refcnt", "Entries", "Reloads", "Stat");
287      for (i = 0; i < ml->amq_map_info_list_len; i++) {
288	amq_map_info *mi = &ml->amq_map_info_list_val[i];
289        printf("%-*.*s %*.*s %-8x %-7d %-7d %-7d %s ",
290	       *nwid, *nwid, mi->mi_name,
291	       *wwid, *wwid, mi->mi_wildcard,
292	       mi->mi_flags, mi->mi_refc, mi->mi_nentries, mi->mi_reloads,
293	       mi->mi_up == -1 ? "root" : (mi->mi_up ? "  up" : "down"));
294        time_print(mi->mi_modify);
295	fputc('\n', stdout);
296      }
297    }
298  break;
299
300  default:
301    break;
302  }
303}
304
305/*
306 * Display general mount statistics
307 */
308static void
309show_ms(amq_mount_stats *ms)
310{
311  printf("\
312requests  stale     mount     mount     unmount\n\
313deferred  fhandles  ok        failed    failed\n\
314%-9d %-9d %-9d %-9d %-9d\n",
315	 ms->as_drops, ms->as_stale, ms->as_mok, ms->as_merr, ms->as_uerr);
316}
317
318
319#if defined(HAVE_CLUSTER_H) && defined(HAVE_CNODEID) && defined(HAVE_GETCCENT)
320static char *
321cluster_server(void)
322{
323  struct cct_entry *cp;
324
325  if (cnodeid() == 0) {
326    /*
327     * Not clustered
328     */
329    return def_server;
330  }
331  while (cp = getccent())
332    if (cp->cnode_type == 'r')
333      return cp->cnode_name;
334
335  return def_server;
336}
337#endif /* defined(HAVE_CLUSTER_H) && defined(HAVE_CNODEID) && defined(HAVE_GETCCENT) */
338
339
340static void
341print_umnt_error(amq_sync_umnt *rv, const char *fs)
342{
343
344  switch (rv->au_etype) {
345  case AMQ_UMNT_OK:
346    break;
347  case AMQ_UMNT_FAILED:
348    printf("unmount failed: %s\n", strerror(rv->au_errno));
349    break;
350  case AMQ_UMNT_FORK:
351    if (rv->au_errno == 0)
352      printf("%s is not mounted\n", fs);
353    else
354      printf("falling back to asynchronous unmount: %s\n",
355	  strerror(rv->au_errno));
356    break;
357  case AMQ_UMNT_READ:
358    printf("pipe read error: %s\n", strerror(rv->au_errno));
359    break;
360  case AMQ_UMNT_SERVER:
361    printf("amd server down\n");
362    break;
363  case AMQ_UMNT_SIGNAL:
364    printf("got signal: %d\n", rv->au_signal);
365    break;
366  /*
367   * Omit default so the compiler can check for missing cases.
368   *
369  default:
370    break;
371   */
372  }
373}
374
375
376static int
377amu_sync_umnt_to_retval(amq_sync_umnt *rv)
378{
379  switch (rv->au_etype) {
380  case AMQ_UMNT_FORK:
381    if (rv->au_errno == 0) {
382      /*
383       * We allow this error so that things like:
384       *   amq -uu /l/cd0d && eject cd0
385       * will work when /l/cd0d is not mounted.
386       * XXX - We still print an error message.
387       */
388      return 0;
389    }
390    /*FALLTHROUGH*/
391  default:
392    return rv->au_etype;
393  }
394}
395
396
397static int
398clnt_failed(CLIENT *clnt, char *server)
399{
400  fprintf(stderr, "%s: ", am_get_progname());
401  clnt_perror(clnt, server);
402  return 1;
403}
404
405
406/*
407 * MAIN
408 */
409int
410main(int argc, char *argv[])
411{
412  int opt_ch;
413  int errs = 0;
414  char *server;
415  struct sockaddr_in server_addr;
416  CLIENT *clnt = NULL;
417  struct hostent *hp;
418  int nodefault = 0;
419  struct timeval tv;
420  char *progname = NULL;
421
422  /*
423   * Compute program name
424   */
425  if (argv[0]) {
426    progname = strrchr(argv[0], '/');
427    if (progname && progname[1])
428      progname++;
429    else
430      progname = argv[0];
431  }
432  if (!progname)
433    progname = "amq";
434  am_set_progname(progname);
435
436  /*
437   * Parse arguments
438   */
439  while ((opt_ch = getopt(argc, argv, "Hfh:il:mqsuvx:D:pP:TUw")) != -1)
440    switch (opt_ch) {
441    case 'H':
442      goto show_usage;
443      break;
444
445    case 'f':
446      flush_flag = 1;
447      nodefault = 1;
448      break;
449
450    case 'h':
451      def_server = optarg;
452      break;
453
454    case 'i':
455      mapinfo_flag = 1;
456      nodefault = 1;
457      break;
458
459    case 'l':
460      amq_logfile = optarg;
461      nodefault = 1;
462      break;
463
464    case 'm':
465      minfo_flag = 1;
466      nodefault = 1;
467      break;
468
469    case 'p':
470      getpid_flag = 1;
471      nodefault = 1;
472      break;
473
474    case 'q':
475      quiet_flag = 1;
476      nodefault = 1;
477      break;
478
479    case 's':
480      stats_flag = 1;
481      nodefault = 1;
482      break;
483
484    case 'u':
485      unmount_flag++;
486      nodefault = 1;
487      break;
488
489    case 'v':
490      getvers_flag = 1;
491      nodefault = 1;
492      break;
493
494    case 'x':
495      xlog_optstr = optarg;
496      nodefault = 1;
497      break;
498
499    case 'D':
500      debug_opts = optarg;
501      nodefault = 1;
502      break;
503
504    case 'P':
505      amd_program_number = atoi(optarg);
506      break;
507
508    case 'T':
509      use_tcp_flag = 1;
510      break;
511
512    case 'U':
513      use_udp_flag = 1;
514      break;
515
516    case 'w':
517      getpwd_flag = 1;
518      break;
519
520    default:
521      errs = 1;
522      break;
523    }
524
525  if (optind == argc) {
526    if (unmount_flag)
527      errs = 1;
528  }
529  if (errs) {
530  show_usage:
531    fprintf(stderr, "\
532Usage: %s [-fimpqsvwHTU] [-h hostname] [-l log_file|\"syslog\"]\n\
533\t[-x log_options] [-D debug_options]\n\
534\t[-P program_number] [[-u[u]] directory ...]\n",
535	    am_get_progname()
536    );
537    exit(1);
538  }
539
540
541  /* set use_udp and use_tcp flags both to on if none are defined */
542  if (!use_tcp_flag && !use_udp_flag)
543    use_tcp_flag = use_udp_flag = 1;
544
545#if defined(HAVE_CLUSTER_H) && defined(HAVE_CNODEID) && defined(HAVE_GETCCENT)
546  /*
547   * Figure out root server of cluster
548   */
549  if (def_server == localhost)
550    server = cluster_server();
551  else
552#endif /* defined(HAVE_CLUSTER_H) && defined(HAVE_CNODEID) && defined(HAVE_GETCCENT) */
553    server = def_server;
554
555  /*
556   * Get address of server
557   */
558  if ((hp = gethostbyname(server)) == 0 && !STREQ(server, localhost)) {
559    fprintf(stderr, "%s: Can't get address of %s\n",
560	    am_get_progname(), server);
561    exit(1);
562  }
563  memset(&server_addr, 0, sizeof(server_addr));
564  /* as per POSIX, sin_len need not be set (used internally by kernel) */
565  server_addr.sin_family = AF_INET;
566  if (hp) {
567    memmove((voidp) &server_addr.sin_addr, (voidp) hp->h_addr,
568	    sizeof(server_addr.sin_addr));
569  } else {
570    /* fake "localhost" */
571    server_addr.sin_addr.s_addr = htonl(0x7f000001);
572  }
573
574  /*
575   * Create RPC endpoint
576   */
577  tv.tv_sec = 5;		/* 5 seconds for timeout or per retry */
578  tv.tv_usec = 0;
579
580  if (use_tcp_flag)	/* try tcp first */
581    clnt = clnt_create(server, amd_program_number, AMQ_VERSION, "tcp");
582  if (!clnt && use_udp_flag) {	/* try udp next */
583    clnt = clnt_create(server, amd_program_number, AMQ_VERSION, "udp");
584    /* if ok, set timeout (valid for connectionless transports only) */
585    if (clnt)
586      clnt_control(clnt, CLSET_RETRY_TIMEOUT, (char *) &tv);
587  }
588  if (!clnt) {
589    fprintf(stderr, "%s: ", am_get_progname());
590    clnt_pcreateerror(server);
591    exit(1);
592  }
593
594  /*
595   * Control debugging
596   */
597  if (debug_opts) {
598    int *rc;
599    amq_setopt opt;
600    opt.as_opt = AMOPT_DEBUG;
601    opt.as_str = debug_opts;
602    rc = amqproc_setopt_1(&opt, clnt);
603    if (rc && *rc < 0) {
604      fprintf(stderr, "%s: daemon not compiled for debug\n",
605	      am_get_progname());
606      errs = 1;
607    } else if (!rc || *rc > 0) {
608      fprintf(stderr, "%s: debug setting for \"%s\" failed\n",
609	      am_get_progname(), debug_opts);
610      errs = 1;
611    }
612  }
613
614  /*
615   * Control logging
616   */
617  if (xlog_optstr) {
618    int *rc;
619    amq_setopt opt;
620    opt.as_opt = AMOPT_XLOG;
621    opt.as_str = xlog_optstr;
622    rc = amqproc_setopt_1(&opt, clnt);
623    if (!rc || *rc) {
624      fprintf(stderr, "%s: setting log level to \"%s\" failed\n",
625	      am_get_progname(), xlog_optstr);
626      errs = 1;
627    }
628  }
629
630  /*
631   * Control log file
632   */
633  if (amq_logfile) {
634    int *rc;
635    amq_setopt opt;
636    opt.as_opt = AMOPT_LOGFILE;
637    opt.as_str = amq_logfile;
638    rc = amqproc_setopt_1(&opt, clnt);
639    if (!rc || *rc) {
640      fprintf(stderr, "%s: setting logfile to \"%s\" failed\n",
641	      am_get_progname(), amq_logfile);
642      errs = 1;
643    }
644  }
645
646  /*
647   * Flush map cache
648   */
649  if (flush_flag) {
650    int *rc;
651    amq_setopt opt;
652    opt.as_opt = AMOPT_FLUSHMAPC;
653    opt.as_str = "";
654    rc = amqproc_setopt_1(&opt, clnt);
655    if (!rc || *rc) {
656      fprintf(stderr, "%s: amd on %s cannot flush the map cache\n",
657	      am_get_progname(), server);
658      errs = 1;
659    }
660  }
661
662  /*
663   * getpwd info
664   */
665  if (getpwd_flag) {
666    char path[MAXPATHLEN+1];
667    char *wd;
668    amq_mount_tree_list *mlp;
669    amq_mount_tree_p mt;
670    u_int i;
671    int flag;
672
673    wd = getcwd(path, MAXPATHLEN+1);
674    if (!wd) {
675      fprintf(stderr, "%s: getcwd failed (%s)", am_get_progname(),
676	  strerror(errno));
677      exit(1);
678    }
679    mlp = amqproc_export_1((voidp) 0, clnt);
680    for (i = 0; mlp && i < mlp->amq_mount_tree_list_len; i++) {
681      mt = mlp->amq_mount_tree_list_val[i];
682      while (1) {
683	flag = 0;
684	show_pwd(mt, path, sizeof(path), &flag);
685	if (!flag) {
686	  printf("%s\n", path);
687	  break;
688	}
689      }
690    }
691    exit(0);
692  }
693
694  /*
695   * Mount info
696   */
697  if (minfo_flag) {
698    int dummy;
699    amq_mount_info_list *ml = amqproc_getmntfs_1(&dummy, clnt);
700    if (ml) {
701      int mwid = 0, dwid = 0, twid = 0;
702      show_mi(ml, Calc, &mwid, &dwid, &twid);
703      mwid++;
704      dwid++;
705      twid++;
706      show_mi(ml, Full, &mwid, &dwid, &twid);
707
708    } else {
709      fprintf(stderr, "%s: amd on %s cannot provide mount info\n",
710	      am_get_progname(), server);
711    }
712  }
713
714
715  /*
716   * Map
717   */
718  if (mapinfo_flag) {
719    int dummy;
720    amq_map_info_list *ml = amqproc_getmapinfo_1(&dummy, clnt);
721    if (ml) {
722      int mwid = 0, wwid = 0;
723      show_mapinfo(ml, Calc, &mwid, &wwid);
724      mwid++;
725      if (wwid)
726	 wwid++;
727      show_mapinfo(ml, Full, &mwid, &wwid);
728    } else {
729      fprintf(stderr, "%s: amd on %s cannot provide map info\n",
730	      am_get_progname(), server);
731    }
732  }
733
734  /*
735   * Get Version
736   */
737  if (getvers_flag) {
738    amq_string *spp = amqproc_getvers_1((voidp) 0, clnt);
739    if (spp && *spp) {
740      fputs(*spp, stdout);
741      XFREE(*spp);
742    } else {
743      fprintf(stderr, "%s: failed to get version information\n",
744	      am_get_progname());
745      errs = 1;
746    }
747  }
748
749  /*
750   * Get PID of amd
751   */
752  if (getpid_flag) {
753    int *ip = amqproc_getpid_1((voidp) 0, clnt);
754    if (ip && *ip) {
755      printf("%d\n", *ip);
756    } else {
757      fprintf(stderr, "%s: failed to get PID of amd\n", am_get_progname());
758      errs = 1;
759    }
760  }
761
762  /*
763   * Apply required operation to all remaining arguments
764   */
765  if (optind < argc) {
766    do {
767      char *fs = argv[optind++];
768      if (unmount_flag > 1) {
769	amq_sync_umnt *sup;
770	/*
771	 * Synchronous unmount request
772	 */
773        sup = amqproc_sync_umnt_1(&fs, clnt);
774	if (sup) {
775	  if (quiet_flag == 0)
776	    print_umnt_error(sup, fs);
777	  errs = amu_sync_umnt_to_retval(sup);
778	} else {
779	  errs = clnt_failed(clnt, server);
780	}
781      }	else if (unmount_flag) {
782	/*
783	 * Unmount request
784	 */
785	amqproc_umnt_1(&fs, clnt);
786      } else {
787	/*
788	 * Stats request
789	 */
790	amq_mount_tree_p *mtp = amqproc_mnttree_1(&fs, clnt);
791	if (mtp) {
792	  amq_mount_tree *mt = *mtp;
793	  if (mt) {
794	    int mwid = 0, dwid = 0, twid = 0;
795	    show_mt(mt, Calc, &mwid, &dwid, &twid);
796	    mwid++;
797	    dwid++, twid++;
798	    printf("%-*.*s Uid   Getattr Lookup RdDir   RdLnk   Statfs Mounted@\n",
799		   dwid, dwid, "What");
800	    show_mt(mt, Stats, &mwid, &dwid, &twid);
801	  } else {
802	    fprintf(stderr, "%s: %s not automounted\n", am_get_progname(), fs);
803	  }
804	  xdr_pri_free((XDRPROC_T_TYPE) xdr_amq_mount_tree_p, (caddr_t) mtp);
805	} else {
806	  errs = clnt_failed(clnt, server);
807	}
808      }
809    } while (optind < argc);
810
811  } else if (unmount_flag) {
812    goto show_usage;
813
814  } else if (stats_flag) {
815    amq_mount_stats *ms = amqproc_stats_1((voidp) 0, clnt);
816    if (ms) {
817      show_ms(ms);
818    } else {
819      errs = clnt_failed(clnt, server);
820    }
821
822  } else if (!nodefault) {
823    amq_mount_tree_list *mlp = amqproc_export_1((voidp) 0, clnt);
824    if (mlp) {
825      enum show_opt e = Calc;
826      int mwid = 0, dwid = 0, pwid = 0;
827
828      while (e != ShowDone) {
829	u_int i;
830	for (i = 0; i < mlp->amq_mount_tree_list_len; i++) {
831	  show_mt(mlp->amq_mount_tree_list_val[i],
832		  e, &mwid, &dwid, &pwid);
833	}
834	mwid++;
835	dwid++, pwid++;
836	if (e == Calc)
837	  e = Short;
838	else if (e == Short)
839	  e = ShowDone;
840      }
841
842    } else {
843      errs = clnt_failed(clnt, server);
844    }
845  }
846  exit(errs);
847  return errs; /* should never reach here */
848}
849