xref: /netbsd-src/external/bsd/am-utils/dist/amq/amq.c (revision 46f3b50fca8118b155a649350f8a0db6316173f6)
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 */
53 static int flush_flag;
54 static int getpid_flag;
55 static int getpwd_flag;
56 static int getvers_flag;
57 static int minfo_flag;
58 static int mapinfo_flag;
59 static int quiet_flag;
60 static int stats_flag;
61 static int unmount_flag;
62 static int use_tcp_flag;
63 static int use_udp_flag;
64 static u_long amd_program_number = AMQ_PROGRAM;
65 static char *debug_opts;
66 static char *amq_logfile;
67 static char *xlog_optstr;
68 static char localhost[] = "localhost";
69 static char *def_server = localhost;
70 
71 /* externals */
72 extern int optind;
73 extern char *optarg;
74 
75 /* structures */
76 enum show_opt {
77   Full, Stats, Calc, Short, ShowDone
78 };
79 
80 
81 static void
time_print(time_type tt)82 time_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  */
96 static void
show_mti(amq_mount_tree * mt,enum show_opt e,int * mwid,int * dwid,int * twid)97 show_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  */
175 static void
show_pwd(amq_mount_tree * mt,char * path,size_t l,int * flag)176 show_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  */
199 static void
show_mt(amq_mount_tree * mt,enum show_opt e,int * mwid,int * dwid,int * pwid)200 show_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 
210 static void
show_mi(amq_mount_info_list * ml,enum show_opt e,int * mwid,int * dwid,int * twid)211 show_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 
260 static void
show_mapinfo(amq_map_info_list * ml,enum show_opt e,int * nwid,int * wwid)261 show_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  */
308 static void
show_ms(amq_mount_stats * ms)309 show_ms(amq_mount_stats *ms)
310 {
311   printf("\
312 requests  stale     mount     mount     unmount\n\
313 deferred  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)
320 static char *
cluster_server(void)321 cluster_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 
340 static void
print_umnt_error(amq_sync_umnt * rv,const char * fs)341 print_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 
376 static int
amu_sync_umnt_to_retval(amq_sync_umnt * rv)377 amu_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 
397 static int
clnt_failed(CLIENT * clnt,char * server)398 clnt_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  */
409 int
main(int argc,char * argv[])410 main(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, "\
532 Usage: %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