amq.c revision 82801
1/*
2 * Copyright (c) 1997-2001 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 *      %W% (Berkeley) %G%
40 *
41 * $Id: amq.c,v 1.7.2.5 2001/01/12 22:43:43 ro Exp $
42 * $FreeBSD: head/contrib/amd/amq/amq.c 82801 2001-09-02 18:22:46Z obrien $
43 *
44 */
45
46/*
47 * Automounter query tool
48 */
49
50#ifndef lint
51char copyright[] = "\
52@(#)Copyright (c) 1997-2001 Erez Zadok\n\
53@(#)Copyright (c) 1990 Jan-Simon Pendry\n\
54@(#)Copyright (c) 1990 Imperial College of Science, Technology & Medicine\n\
55@(#)Copyright (c) 1990 The Regents of the University of California.\n\
56@(#)All rights reserved.\n";
57#if __GNUC__ < 2
58static char rcsid[] = "$Id: amq.c,v 1.7.2.5 2001/01/12 22:43:43 ro Exp $";
59static char sccsid[] = "%W% (Berkeley) %G%";
60#endif /* __GNUC__ < 2 */
61#endif /* not lint */
62
63#ifdef HAVE_CONFIG_H
64# include <config.h>
65#endif /* HAVE_CONFIG_H */
66#include <am_defs.h>
67#include <amq.h>
68
69/* locals */
70static int flush_flag;
71static int minfo_flag;
72static int getpid_flag;
73static int unmount_flag;
74static int stats_flag;
75static int getvers_flag;
76static int amd_program_number = AMQ_PROGRAM;
77static int use_tcp_flag, use_udp_flag;
78static int getpwd_flag;
79static char *debug_opts;
80static char *amq_logfile;
81static char *xlog_optstr;
82static char localhost[] = "localhost";
83static char *def_server = localhost;
84
85/* externals */
86extern int optind;
87extern char *optarg;
88
89/* structures */
90enum show_opt {
91  Full, Stats, Calc, Short, ShowDone
92};
93
94
95/*
96 * If (e) is Calc then just calculate the sizes
97 * Otherwise display the mount node on stdout
98 */
99static void
100show_mti(amq_mount_tree *mt, enum show_opt e, int *mwid, int *dwid, int *twid)
101{
102  switch (e) {
103  case Calc:
104    {
105      int mw = strlen(mt->mt_mountinfo);
106      int dw = strlen(mt->mt_directory);
107      int tw = strlen(mt->mt_type);
108      if (mw > *mwid)
109	*mwid = mw;
110      if (dw > *dwid)
111	*dwid = dw;
112      if (tw > *twid)
113	*twid = tw;
114    }
115  break;
116
117  case Full:
118    {
119      struct tm *tp = localtime((time_t *) &mt->mt_mounttime);
120      printf("%-*.*s %-*.*s %-*.*s %s\n\t%-5d %-7d %-6d %-7d %-7d %-6d %02d/%02d/%02d %02d:%02d:%02d\n",
121	     *dwid, *dwid,
122	     *mt->mt_directory ? mt->mt_directory : "/",	/* XXX */
123	     *twid, *twid,
124	     mt->mt_type,
125	     *mwid, *mwid,
126	     mt->mt_mountinfo,
127	     mt->mt_mountpoint,
128
129	     mt->mt_mountuid,
130	     mt->mt_getattr,
131	     mt->mt_lookup,
132	     mt->mt_readdir,
133	     mt->mt_readlink,
134	     mt->mt_statfs,
135
136	     tp->tm_year > 99 ? tp->tm_year - 100 : tp->tm_year,
137	     tp->tm_mon + 1, tp->tm_mday,
138	     tp->tm_hour, tp->tm_min, tp->tm_sec);
139    }
140  break;
141
142  case Stats:
143    {
144      struct tm *tp = localtime((time_t *) &mt->mt_mounttime);
145      printf("%-*.*s %-5d %-7d %-6d %-7d %-7d %-6d %02d/%02d/%02d %02d:%02d:%02d\n",
146	     *dwid, *dwid,
147	     *mt->mt_directory ? mt->mt_directory : "/",	/* XXX */
148
149	     mt->mt_mountuid,
150	     mt->mt_getattr,
151	     mt->mt_lookup,
152	     mt->mt_readdir,
153	     mt->mt_readlink,
154	     mt->mt_statfs,
155
156	     tp->tm_year > 99 ? tp->tm_year - 100 : tp->tm_year,
157	     tp->tm_mon + 1, tp->tm_mday,
158	     tp->tm_hour, tp->tm_min, tp->tm_sec);
159    }
160  break;
161
162  case Short:
163    {
164      printf("%-*.*s %-*.*s %-*.*s %s\n",
165	     *dwid, *dwid,
166	     *mt->mt_directory ? mt->mt_directory : "/",
167	     *twid, *twid,
168	     mt->mt_type,
169	     *mwid, *mwid,
170	     mt->mt_mountinfo,
171	     mt->mt_mountpoint);
172    }
173  break;
174
175  default:
176    break;
177  }
178}
179
180
181/*
182 * Display a pwd data
183 */
184static void
185show_pwd(amq_mount_tree *mt, char *path, int *flag)
186{
187  int len;
188
189  while (mt) {
190    len = strlen(mt->mt_mountpoint);
191    if (NSTREQ(path, mt->mt_mountpoint, len) &&
192	!STREQ(mt->mt_directory, mt->mt_mountpoint)) {
193      char buf[MAXPATHLEN+1];
194      strcpy(buf, mt->mt_directory);
195      strcat(buf, &path[len]);
196      strcpy(path, buf);
197      *flag = 1;
198    }
199    show_pwd(mt->mt_next, path, flag);
200    mt = mt->mt_child;
201  }
202}
203
204
205/*
206 * Display a mount tree.
207 */
208static void
209show_mt(amq_mount_tree *mt, enum show_opt e, int *mwid, int *dwid, int *pwid)
210{
211  while (mt) {
212    show_mti(mt, e, mwid, dwid, pwid);
213    show_mt(mt->mt_next, e, mwid, dwid, pwid);
214    mt = mt->mt_child;
215  }
216}
217
218
219static void
220show_mi(amq_mount_info_list *ml, enum show_opt e, int *mwid, int *dwid, int *twid)
221{
222  int i;
223
224  switch (e) {
225
226  case Calc:
227    {
228      for (i = 0; i < ml->amq_mount_info_list_len; i++) {
229	amq_mount_info *mi = &ml->amq_mount_info_list_val[i];
230	int mw = strlen(mi->mi_mountinfo);
231	int dw = strlen(mi->mi_mountpt);
232	int tw = strlen(mi->mi_type);
233	if (mw > *mwid)
234	  *mwid = mw;
235	if (dw > *dwid)
236	  *dwid = dw;
237	if (tw > *twid)
238	  *twid = tw;
239      }
240    }
241  break;
242
243  case Full:
244    {
245      for (i = 0; i < ml->amq_mount_info_list_len; i++) {
246	amq_mount_info *mi = &ml->amq_mount_info_list_val[i];
247	printf("%-*.*s %-*.*s %-*.*s %-3d %s is %s",
248	       *mwid, *mwid, mi->mi_mountinfo,
249	       *dwid, *dwid, mi->mi_mountpt,
250	       *twid, *twid, mi->mi_type,
251	       mi->mi_refc, mi->mi_fserver,
252	       mi->mi_up > 0 ? "up" :
253	       mi->mi_up < 0 ? "starting" : "down");
254	if (mi->mi_error > 0) {
255	  extern int sys_nerr;
256	  if (mi->mi_error < sys_nerr)
257#ifdef HAVE_STRERROR
258	    printf(" (%s)", strerror(mi->mi_error));
259#else /* not HAVE_STRERROR */
260	    printf(" (%s)", sys_errlist[mi->mi_error]);
261#endif /* not HAVE_STRERROR */
262	  else
263	    printf(" (Error %d)", mi->mi_error);
264	} else if (mi->mi_error < 0) {
265	  fputs(" (in progress)", stdout);
266	}
267	fputc('\n', stdout);
268      }
269    }
270  break;
271
272  default:
273    break;
274  }
275}
276
277
278/*
279 * Display general mount statistics
280 */
281static void
282show_ms(amq_mount_stats *ms)
283{
284  printf("\
285requests  stale     mount     mount     unmount\n\
286deferred  fhandles  ok        failed    failed\n\
287%-9d %-9d %-9d %-9d %-9d\n",
288	 ms->as_drops, ms->as_stale, ms->as_mok, ms->as_merr, ms->as_uerr);
289}
290
291
292#if defined(HAVE_CLUSTER_H) && defined(HAVE_CNODEID) && defined(HAVE_GETCCENT)
293static char *
294cluster_server(void)
295{
296  struct cct_entry *cp;
297
298  if (cnodeid() == 0) {
299    /*
300     * Not clustered
301     */
302    return def_server;
303  }
304  while (cp = getccent())
305    if (cp->cnode_type == 'r')
306      return cp->cnode_name;
307
308  return def_server;
309}
310#endif /* defined(HAVE_CLUSTER_H) && defined(HAVE_CNODEID) && defined(HAVE_GETCCENT) */
311
312
313/*
314 * MAIN
315 */
316int
317main(int argc, char *argv[])
318{
319  int opt_ch;
320  int errs = 0;
321  char *server;
322  struct sockaddr_in server_addr;
323  CLIENT *clnt = NULL;
324  struct hostent *hp;
325  int nodefault = 0;
326  struct timeval tv;
327  char *progname = NULL;
328
329  /*
330   * Compute program name
331   */
332  if (argv[0]) {
333    progname = strrchr(argv[0], '/');
334    if (progname && progname[1])
335      progname++;
336    else
337      progname = argv[0];
338  }
339  if (!progname)
340    progname = "amq";
341  am_set_progname(progname);
342
343  /*
344   * Parse arguments
345   */
346  while ((opt_ch = getopt(argc, argv, "Hfh:l:msuvx:D:pP:TUw")) != -1)
347    switch (opt_ch) {
348    case 'H':
349      goto show_usage;
350      break;
351
352    case 'f':
353      flush_flag = 1;
354      nodefault = 1;
355      break;
356
357    case 'h':
358      def_server = optarg;
359      break;
360
361    case 'l':
362      amq_logfile = optarg;
363      nodefault = 1;
364      break;
365
366    case 'm':
367      minfo_flag = 1;
368      nodefault = 1;
369      break;
370
371    case 'p':
372      getpid_flag = 1;
373      nodefault = 1;
374      break;
375
376    case 's':
377      stats_flag = 1;
378      nodefault = 1;
379      break;
380
381    case 'u':
382      unmount_flag = 1;
383      nodefault = 1;
384      break;
385
386    case 'v':
387      getvers_flag = 1;
388      nodefault = 1;
389      break;
390
391    case 'x':
392      xlog_optstr = optarg;
393      nodefault = 1;
394      break;
395
396    case 'D':
397      debug_opts = optarg;
398      nodefault = 1;
399      break;
400
401    case 'P':
402      amd_program_number = atoi(optarg);
403      break;
404
405    case 'T':
406      use_tcp_flag = 1;
407      break;
408
409    case 'U':
410      use_udp_flag = 1;
411      break;
412
413    case 'w':
414      getpwd_flag = 1;
415      break;
416
417    default:
418      errs = 1;
419      break;
420    }
421
422  if (optind == argc) {
423    if (unmount_flag)
424      errs = 1;
425  }
426  if (errs) {
427  show_usage:
428    fprintf(stderr, "\
429Usage: %s [-fmpsvwHTU] [-h hostname] [-l log_file|\"syslog\"]\n\
430\t[-x log_options] [-D debug_options]\n\
431\t[-P program_number] [[-u] directory ...]\n",
432	    am_get_progname()
433    );
434    exit(1);
435  }
436
437
438  /* set use_udp and use_tcp flags both to on if none are defined */
439  if (!use_tcp_flag && !use_udp_flag)
440    use_tcp_flag = use_udp_flag = 1;
441
442#if defined(HAVE_CLUSTER_H) && defined(HAVE_CNODEID) && defined(HAVE_GETCCENT)
443  /*
444   * Figure out root server of cluster
445   */
446  if (def_server == localhost)
447    server = cluster_server();
448  else
449#endif /* defined(HAVE_CLUSTER_H) && defined(HAVE_CNODEID) && defined(HAVE_GETCCENT) */
450    server = def_server;
451
452  /*
453   * Get address of server
454   */
455  if ((hp = gethostbyname(server)) == 0 && !STREQ(server, localhost)) {
456    fprintf(stderr, "%s: Can't get address of %s\n",
457	    am_get_progname(), server);
458    exit(1);
459  }
460  memset(&server_addr, 0, sizeof server_addr);
461  server_addr.sin_family = AF_INET;
462  if (hp) {
463    memmove((voidp) &server_addr.sin_addr, (voidp) hp->h_addr,
464	    sizeof(server_addr.sin_addr));
465  } else {
466    /* fake "localhost" */
467    server_addr.sin_addr.s_addr = htonl(0x7f000001);
468  }
469
470  /*
471   * Create RPC endpoint
472   */
473  tv.tv_sec = 5;		/* 5 seconds for timeout or per retry */
474  tv.tv_usec = 0;
475
476  if (use_tcp_flag)	/* try tcp first */
477    clnt = clnt_create(server, amd_program_number, AMQ_VERSION, "tcp");
478  if (!clnt && use_udp_flag) {	/* try udp next */
479    clnt = clnt_create(server, amd_program_number, AMQ_VERSION, "udp");
480    /* if ok, set timeout (valid for connectionless transports only) */
481    if (clnt)
482      clnt_control(clnt, CLSET_RETRY_TIMEOUT, (char *) &tv);
483  }
484  if (!clnt) {
485    fprintf(stderr, "%s: ", am_get_progname());
486    clnt_pcreateerror(server);
487    exit(1);
488  }
489
490  /*
491   * Control debugging
492   */
493  if (debug_opts) {
494    int *rc;
495    amq_setopt opt;
496    opt.as_opt = AMOPT_DEBUG;
497    opt.as_str = debug_opts;
498    rc = amqproc_setopt_1(&opt, clnt);
499    if (rc && *rc < 0) {
500      fprintf(stderr, "%s: daemon not compiled for debug\n",
501	      am_get_progname());
502      errs = 1;
503    } else if (!rc || *rc > 0) {
504      fprintf(stderr, "%s: debug setting for \"%s\" failed\n",
505	      am_get_progname(), debug_opts);
506      errs = 1;
507    }
508  }
509
510  /*
511   * Control logging
512   */
513  if (xlog_optstr) {
514    int *rc;
515    amq_setopt opt;
516    opt.as_opt = AMOPT_XLOG;
517    opt.as_str = xlog_optstr;
518    rc = amqproc_setopt_1(&opt, clnt);
519    if (!rc || *rc) {
520      fprintf(stderr, "%s: setting log level to \"%s\" failed\n",
521	      am_get_progname(), xlog_optstr);
522      errs = 1;
523    }
524  }
525
526  /*
527   * Control log file
528   */
529  if (amq_logfile) {
530    int *rc;
531    amq_setopt opt;
532    opt.as_opt = AMOPT_LOGFILE;
533    opt.as_str = amq_logfile;
534    rc = amqproc_setopt_1(&opt, clnt);
535    if (!rc || *rc) {
536      fprintf(stderr, "%s: setting logfile to \"%s\" failed\n",
537	      am_get_progname(), amq_logfile);
538      errs = 1;
539    }
540  }
541
542  /*
543   * Flush map cache
544   */
545  if (flush_flag) {
546    int *rc;
547    amq_setopt opt;
548    opt.as_opt = AMOPT_FLUSHMAPC;
549    opt.as_str = "";
550    rc = amqproc_setopt_1(&opt, clnt);
551    if (!rc || *rc) {
552      fprintf(stderr, "%s: amd on %s cannot flush the map cache\n",
553	      am_get_progname(), server);
554      errs = 1;
555    }
556  }
557
558  /*
559   * getpwd info
560   */
561  if (getpwd_flag) {
562    char path[MAXPATHLEN+1];
563    char *wd = getcwd(path, MAXPATHLEN+1);
564    amq_mount_tree_list *mlp = amqproc_export_1((voidp) 0, clnt);
565    amq_mount_tree_p mt;
566    int i, flag;
567
568    if (!wd) {
569      perror("getcwd");
570      exit(1);
571    }
572    for (i = 0; mlp && i < mlp->amq_mount_tree_list_len; i++) {
573      mt = mlp->amq_mount_tree_list_val[i];
574      while (1) {
575	flag = 0;
576	show_pwd(mt, path, &flag);
577	if (!flag) {
578	  printf("%s\n", path);
579	  break;
580	}
581      }
582    }
583    exit(0);
584  }
585
586  /*
587   * Mount info
588   */
589  if (minfo_flag) {
590    int dummy;
591    amq_mount_info_list *ml = amqproc_getmntfs_1(&dummy, clnt);
592    if (ml) {
593      int mwid = 0, dwid = 0, twid = 0;
594      show_mi(ml, Calc, &mwid, &dwid, &twid);
595      mwid++;
596      dwid++;
597      twid++;
598      show_mi(ml, Full, &mwid, &dwid, &twid);
599
600    } else {
601      fprintf(stderr, "%s: amd on %s cannot provide mount info\n",
602	      am_get_progname(), server);
603    }
604  }
605
606  /*
607   * Get Version
608   */
609  if (getvers_flag) {
610    amq_string *spp = amqproc_getvers_1((voidp) 0, clnt);
611    if (spp && *spp) {
612      fputs(*spp, stdout);
613      XFREE(*spp);
614    } else {
615      fprintf(stderr, "%s: failed to get version information\n",
616	      am_get_progname());
617      errs = 1;
618    }
619  }
620
621  /*
622   * Get PID of amd
623   */
624  if (getpid_flag) {
625    int *ip = amqproc_getpid_1((voidp) 0, clnt);
626    if (ip && *ip) {
627      printf("%d\n", *ip);
628    } else {
629      fprintf(stderr, "%s: failed to get PID of amd\n", am_get_progname());
630      errs = 1;
631    }
632  }
633
634  /*
635   * Apply required operation to all remaining arguments
636   */
637  if (optind < argc) {
638    do {
639      char *fs = argv[optind++];
640      if (unmount_flag) {
641	/*
642	 * Unmount request
643	 */
644	amqproc_umnt_1(&fs, clnt);
645      } else {
646	/*
647	 * Stats request
648	 */
649	amq_mount_tree_p *mtp = amqproc_mnttree_1(&fs, clnt);
650	if (mtp) {
651	  amq_mount_tree *mt = *mtp;
652	  if (mt) {
653	    int mwid = 0, dwid = 0, twid = 0;
654	    show_mt(mt, Calc, &mwid, &dwid, &twid);
655	    mwid++;
656	    dwid++, twid++;
657	    printf("%-*.*s Uid   Getattr Lookup RdDir   RdLnk   Statfs Mounted@\n",
658		   dwid, dwid, "What");
659	    show_mt(mt, Stats, &mwid, &dwid, &twid);
660	  } else {
661	    fprintf(stderr, "%s: %s not automounted\n", am_get_progname(), fs);
662	  }
663	  xdr_pri_free((XDRPROC_T_TYPE) xdr_amq_mount_tree_p, (caddr_t) mtp);
664	} else {
665	  fprintf(stderr, "%s: ", am_get_progname());
666	  clnt_perror(clnt, server);
667	  errs = 1;
668	}
669      }
670    } while (optind < argc);
671
672  } else if (unmount_flag) {
673    goto show_usage;
674
675  } else if (stats_flag) {
676    amq_mount_stats *ms = amqproc_stats_1((voidp) 0, clnt);
677    if (ms) {
678      show_ms(ms);
679    } else {
680      fprintf(stderr, "%s: ", am_get_progname());
681      clnt_perror(clnt, server);
682      errs = 1;
683    }
684
685  } else if (!nodefault) {
686    amq_mount_tree_list *mlp = amqproc_export_1((voidp) 0, clnt);
687    if (mlp) {
688      enum show_opt e = Calc;
689      int mwid = 0, dwid = 0, pwid = 0;
690      while (e != ShowDone) {
691	int i;
692	for (i = 0; i < mlp->amq_mount_tree_list_len; i++) {
693	  show_mt(mlp->amq_mount_tree_list_val[i],
694		  e, &mwid, &dwid, &pwid);
695	}
696	mwid++;
697	dwid++, pwid++;
698	if (e == Calc)
699	  e = Short;
700	else if (e == Short)
701	  e = ShowDone;
702      }
703
704    } else {
705      fprintf(stderr, "%s: ", am_get_progname());
706      clnt_perror(clnt, server);
707      errs = 1;
708    }
709  }
710  exit(errs);
711  return errs; /* should never reach here */
712}
713