1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27/*
28 * Copyright (c) 2018, Joyent, Inc.
29 */
30
31#include <fm/fmd_adm.h>
32
33#include <strings.h>
34#include <limits.h>
35#include <stdlib.h>
36#include <stdarg.h>
37#include <stdio.h>
38#include <errno.h>
39#include <poll.h>
40#include <locale.h>
41
42#include "statcommon.h"
43
44#define	FMSTAT_EXIT_SUCCESS	0
45#define	FMSTAT_EXIT_ERROR	1
46#define	FMSTAT_EXIT_USAGE	2
47
48static const struct stats {
49	fmd_stat_t module;
50	fmd_stat_t authority;
51	fmd_stat_t state;
52	fmd_stat_t loadtime;
53	fmd_stat_t snaptime;
54	fmd_stat_t received;
55	fmd_stat_t discarded;
56	fmd_stat_t retried;
57	fmd_stat_t replayed;
58	fmd_stat_t lost;
59	fmd_stat_t dispatched;
60	fmd_stat_t dequeued;
61	fmd_stat_t prdequeued;
62	fmd_stat_t accepted;
63	fmd_stat_t memtotal;
64	fmd_stat_t buftotal;
65	fmd_stat_t caseopen;
66	fmd_stat_t casesolved;
67	fmd_stat_t wcnt;
68	fmd_stat_t wtime;
69	fmd_stat_t wlentime;
70	fmd_stat_t wlastupdate;
71	fmd_stat_t dtime;
72	fmd_stat_t dlastupdate;
73} stats_template = {
74	{ "module", FMD_TYPE_STRING },
75	{ "authority", FMD_TYPE_STRING },
76	{ "state", FMD_TYPE_STRING },
77	{ "loadtime", FMD_TYPE_TIME },
78	{ "snaptime", FMD_TYPE_TIME },
79	{ "received", FMD_TYPE_UINT64 },
80	{ "discarded", FMD_TYPE_UINT64 },
81	{ "retried", FMD_TYPE_UINT64 },
82	{ "replayed", FMD_TYPE_UINT64 },
83	{ "lost", FMD_TYPE_UINT64 },
84	{ "dispatched", FMD_TYPE_UINT64 },
85	{ "dequeued", FMD_TYPE_UINT64 },
86	{ "prdequeued", FMD_TYPE_UINT64 },
87	{ "accepted", FMD_TYPE_UINT64 },
88	{ "memtotal", FMD_TYPE_SIZE },
89	{ "buftotal", FMD_TYPE_SIZE },
90	{ "caseopen", FMD_TYPE_UINT64 },
91	{ "casesolved", FMD_TYPE_UINT64 },
92	{ "wcnt", FMD_TYPE_UINT32 },
93	{ "wtime", FMD_TYPE_TIME },
94	{ "wlentime", FMD_TYPE_TIME },
95	{ "wlastupdate", FMD_TYPE_TIME },
96	{ "dtime", FMD_TYPE_TIME },
97	{ "dlastupdate", FMD_TYPE_TIME },
98};
99
100static const char *g_pname;
101static fmd_adm_t *g_adm;
102
103static struct modstats {
104	char *m_name;
105	struct modstats *m_next;
106	struct stats m_stbuf[2];
107	int m_stidx;
108	int m_id;
109	struct stats *m_old;
110	struct stats *m_new;
111	double m_wait;
112	double m_svc;
113	double m_pct_b;
114	double m_pct_w;
115} *g_mods;
116
117static uint_t timestamp_fmt = NODATE;
118
119#if !defined(TEXT_DOMAIN)		/* Should be defined by cc -D */
120#define	TEXT_DOMAIN "SYS_TEST"		/* Use this only if it isn't */
121#endif
122
123static void
124vwarn(const char *format, va_list ap)
125{
126	int err = errno;
127
128	(void) fprintf(stderr, "%s: ", g_pname);
129
130	if (format != NULL)
131		(void) vfprintf(stderr, format, ap);
132
133	errno = err; /* restore errno for fmd_adm_errmsg() */
134
135	if (format == NULL)
136		(void) fprintf(stderr, "%s\n", fmd_adm_errmsg(g_adm));
137	else if (strchr(format, '\n') == NULL)
138		(void) fprintf(stderr, ": %s\n", fmd_adm_errmsg(g_adm));
139}
140
141/*PRINTFLIKE1*/
142void
143warn(const char *format, ...)
144{
145	va_list ap;
146
147	va_start(ap, format);
148	vwarn(format, ap);
149	va_end(ap);
150}
151
152/*PRINTFLIKE1*/
153void
154die(const char *format, ...)
155{
156	va_list ap;
157
158	va_start(ap, format);
159	vwarn(format, ap);
160	va_end(ap);
161
162	fmd_adm_close(g_adm);
163	exit(FMSTAT_EXIT_ERROR);
164}
165
166static char *
167time2str(char *buf, size_t len, uint64_t time)
168{
169	static const struct unit {
170		const char *u_name;
171		hrtime_t u_mul;
172	} units[] = {
173		{ "d",	NANOSEC * (hrtime_t)(24 * 60 * 60) },
174		{ "h",	NANOSEC * (hrtime_t)(60 * 60) },
175		{ "m",	NANOSEC * (hrtime_t)60 },
176		{ "s",	NANOSEC / SEC },
177		{ "ms",	NANOSEC / MILLISEC },
178		{ "us",	NANOSEC / MICROSEC },
179		{ "ns",	NANOSEC / NANOSEC },
180	};
181
182	const struct unit *up;
183
184	for (up = units; time % up->u_mul != 0; up++)
185		continue; /* find largest unit of which 'time' is a multiple */
186
187	(void) snprintf(buf, len, "%llu%s", time / up->u_mul, up->u_name);
188	return (buf);
189}
190
191static char *
192size2str(char *buf, size_t len, uint64_t size)
193{
194	static const char units[] = "bKMGTPE";
195	const uint64_t scale = 1024;
196	const char *up = units;
197	uint64_t osize = 0;
198
199	/*
200	 * Convert the input size to a round number of the appropriately
201	 * scaled units (saved in 'size') and a remainder (saved in 'osize').
202	 */
203	while (size >= scale && up < (units + sizeof (units) - 2)) {
204		up++;
205		osize = size;
206		size = (size + (scale / 2)) / scale;
207	}
208
209	/*
210	 * Format the result using at most one decimal place and the unit
211	 * depending upon the amount of remainder (same as df -h algorithm).
212	 */
213	if (osize != 0 && (osize / scale) < 10)
214		(void) snprintf(buf, len, "%.1f%c", (float)osize / scale, *up);
215	else if (size != 0)
216		(void) snprintf(buf, len, "%llu%c", size, *up);
217	else
218		(void) snprintf(buf, len, "0");
219
220	return (buf);
221}
222
223static uint64_t
224u64delta(uint64_t old, uint64_t new)
225{
226	return (new >= old ? (new - old) : ((UINT64_MAX - old) + new + 1));
227}
228
229static struct modstats *
230modstat_create(const char *name, id_t id)
231{
232	struct modstats *mp = malloc(sizeof (struct modstats));
233
234	if (mp == NULL)
235		return (NULL);
236
237	bzero(mp, sizeof (struct modstats));
238
239	if (name != NULL && (mp->m_name = strdup(name)) == NULL) {
240		free(mp);
241		return (NULL);
242	}
243
244	mp->m_id = id;
245	mp->m_next = g_mods;
246	g_mods = mp;
247	return (mp);
248}
249
250/*
251 * Given a statistics buffer containing event queue statistics, compute the
252 * common queue statistics for the given module and store the results in 'mp'.
253 * We set m_new and m_old for the caller, and store the compute values of
254 * m_svc, m_wait, m_pct_w, and m_pct_b there as well.  The caller must not free
255 * 'ams' until after using the results as m_new may contain pointers to it.
256 */
257static void
258modstat_compute(struct modstats *mp, fmd_adm_stats_t *ams)
259{
260	static fmd_stat_t *t_beg = (fmd_stat_t *)(&stats_template + 0);
261	static fmd_stat_t *t_end = (fmd_stat_t *)(&stats_template + 1);
262
263	struct stats *old, *new;
264	fmd_stat_t *tsp, *nsp, *sp;
265	double elapsed, avg_w, avg_d;
266	uint64_t delta;
267
268	old = mp->m_old = &mp->m_stbuf[mp->m_stidx];
269	mp->m_stidx = 1 - mp->m_stidx;
270	new = mp->m_new = &mp->m_stbuf[mp->m_stidx];
271
272	/*
273	 * The statistics can come in any order; we compare each one to the
274	 * template of statistics of interest, find the matching ones, and copy
275	 * their values into the appropriate slot of the 'new' stats.
276	 */
277	for (nsp = ams->ams_buf; nsp < ams->ams_buf + ams->ams_len; nsp++) {
278		for (tsp = t_beg; tsp < t_end; tsp++) {
279			const char *p = strrchr(nsp->fmds_name, '.');
280
281			/*
282			 * The fmd queue stats can either be named fmd.<name>
283			 * or fmd.xprt.%u.<name> depending on whether we're
284			 * looking at the module queue or the transport queue.
285			 * So we match using the patterns fmd.* and *.<name>
286			 * and store only the value of <name> in stats_template.
287			 */
288			if (p == NULL || strcmp(p + 1, tsp->fmds_name) != 0 ||
289			    strncmp(nsp->fmds_name, "fmd.", 4) != 0)
290				continue; /* continue until we match the stat */
291
292			if (tsp->fmds_type != nsp->fmds_type) {
293				warn("%s has unexpected type (%u != %u)\n",
294				    nsp->fmds_name, tsp->fmds_type,
295				    nsp->fmds_type);
296			} else {
297				sp = (fmd_stat_t *)new + (tsp - t_beg);
298				sp->fmds_value = nsp->fmds_value;
299			}
300		}
301	}
302
303	/*
304	 * Compute the elapsed time by taking the delta between 'snaptime', or
305	 * or between snaptime and loadtime if there is no previous snapshot.
306	 * If delta is zero, set it to 1sec so we don't divide by zero later.
307	 */
308	delta = u64delta(old->snaptime.fmds_value.ui64 ?
309	    old->snaptime.fmds_value.ui64 : old->loadtime.fmds_value.ui64,
310	    new->snaptime.fmds_value.ui64);
311
312	elapsed = delta ? (double)delta : (double)NANOSEC;
313
314	/*
315	 * Compute average wait queue len by taking the delta in the wait queue
316	 * len * time products (wlentime stat) and dividing by the elapsed time.
317	 */
318	delta = u64delta(old->wlentime.fmds_value.ui64,
319	    new->wlentime.fmds_value.ui64);
320
321	if (delta != 0)
322		mp->m_wait = (double)delta / elapsed;
323	else
324		mp->m_wait = 0.0;
325
326	/*
327	 * Compute average wait time by taking the delta in the wait queue time
328	 * (wtime) and dividing by the delta in the number of dispatches.
329	 */
330	delta = u64delta(old->dispatched.fmds_value.ui64,
331	    new->dispatched.fmds_value.ui64);
332
333	if (delta != 0) {
334		avg_w = (double)u64delta(old->wtime.fmds_value.ui64,
335		    new->wtime.fmds_value.ui64) / (double)delta;
336	} else
337		avg_w = 0.0;
338
339	/*
340	 * Compute average dispatch time by taking the delta in the dispatch
341	 * time (dtime) and dividing by the delta in the number of dequeues.
342	 */
343	delta = u64delta(old->dequeued.fmds_value.ui64,
344	    new->dequeued.fmds_value.ui64);
345
346	if (delta != 0) {
347		avg_d = (double)u64delta(old->dtime.fmds_value.ui64,
348		    new->dtime.fmds_value.ui64) / (double)delta;
349	} else
350		avg_d = 0.0;
351
352	/*
353	 * Finally compute the average overall service time by adding together
354	 * the average wait and dispatch times and converting to milliseconds.
355	 */
356	mp->m_svc = ((avg_w + avg_d) * (double)MILLISEC) / (double)NANOSEC;
357
358	/*
359	 * Compute the %wait and %busy times by taking the delta in wait and
360	 * busy times, dividing by the elapsed time, and multiplying by 100.
361	 */
362	delta = u64delta(old->wtime.fmds_value.ui64,
363	    new->wtime.fmds_value.ui64);
364
365	if (delta != 0)
366		mp->m_pct_w = ((double)delta / elapsed) * 100.0;
367	else
368		mp->m_pct_w = 0.0;
369
370	delta = u64delta(old->dtime.fmds_value.ui64,
371	    new->dtime.fmds_value.ui64);
372
373	if (delta != 0)
374		mp->m_pct_b = ((double)delta / elapsed) * 100.0;
375	else
376		mp->m_pct_b = 0.0;
377}
378
379/*ARGSUSED*/
380static void
381stat_one_xprt(id_t id, void *ignored)
382{
383	fmd_adm_stats_t ams;
384	struct modstats *mp;
385
386	if (fmd_adm_xprt_stats(g_adm, id, &ams) != 0) {
387		warn("failed to retrieve statistics for transport %d", (int)id);
388		return;
389	}
390
391	for (mp = g_mods; mp != NULL; mp = mp->m_next) {
392		if (mp->m_id == id)
393			break;
394	}
395
396	if (mp == NULL && (mp = modstat_create(NULL, id)) == NULL) {
397		warn("failed to allocate memory for transport %d", (int)id);
398		(void) fmd_adm_stats_free(g_adm, &ams);
399		return;
400	}
401
402	modstat_compute(mp, &ams);
403
404	(void) printf("%3d %5s %7llu %7llu %7llu %7llu "
405	    "%4.1f %6.1f %3.0f %3.0f %s\n", (int)id,
406	    mp->m_new->state.fmds_value.str,
407	    u64delta(mp->m_old->prdequeued.fmds_value.ui64,
408	    mp->m_new->prdequeued.fmds_value.ui64),
409	    u64delta(mp->m_old->received.fmds_value.ui64,
410	    mp->m_new->received.fmds_value.ui64),
411	    u64delta(mp->m_old->discarded.fmds_value.ui64,
412	    mp->m_new->discarded.fmds_value.ui64),
413	    u64delta(mp->m_old->lost.fmds_value.ui64,
414	    mp->m_new->lost.fmds_value.ui64),
415	    mp->m_wait, mp->m_svc, mp->m_pct_w, mp->m_pct_b,
416	    mp->m_new->module.fmds_value.str);
417
418	(void) fmd_adm_stats_free(g_adm, &ams);
419}
420
421static void
422stat_xprt(void)
423{
424	(void) printf("%3s %5s %7s %7s %7s %7s %4s %6s %3s %3s %s\n",
425	    "id", "state", "ev_send", "ev_recv", "ev_drop", "ev_lost",
426	    "wait", "svc_t", "%w", "%b", "module");
427
428	if (fmd_adm_xprt_iter(g_adm, stat_one_xprt, NULL) != 0)
429		die("failed to retrieve list of transports");
430}
431
432static void
433stat_one_xprt_auth(id_t id, void *arg)
434{
435	const char *module = arg;
436	fmd_adm_stats_t ams;
437	struct modstats *mp;
438
439	if (fmd_adm_xprt_stats(g_adm, id, &ams) != 0) {
440		warn("failed to retrieve statistics for transport %d", (int)id);
441		return;
442	}
443
444	for (mp = g_mods; mp != NULL; mp = mp->m_next) {
445		if (mp->m_id == id)
446			break;
447	}
448
449	if (mp == NULL && (mp = modstat_create(NULL, id)) == NULL) {
450		warn("failed to allocate memory for transport %d", (int)id);
451		(void) fmd_adm_stats_free(g_adm, &ams);
452		return;
453	}
454
455	modstat_compute(mp, &ams);
456
457	if (module == NULL ||
458	    strcmp(module, mp->m_new->module.fmds_value.str) == 0) {
459		(void) printf("%3d %5s %-18s  %s\n", (int)id,
460		    mp->m_new->state.fmds_value.str,
461		    mp->m_new->module.fmds_value.str,
462		    mp->m_new->authority.fmds_value.str ?
463		    mp->m_new->authority.fmds_value.str : "-");
464	}
465
466	(void) fmd_adm_stats_free(g_adm, &ams);
467}
468
469static void
470stat_xprt_auth(const char *module)
471{
472	(void) printf("%3s %5s %-18s  %s\n",
473	    "id", "state", "module", "authority");
474
475	if (fmd_adm_xprt_iter(g_adm, stat_one_xprt_auth, (void *)module) != 0)
476		die("failed to retrieve list of transports");
477}
478
479/*ARGSUSED*/
480static int
481stat_one_fmd(const fmd_adm_modinfo_t *ami, void *ignored)
482{
483	char memsz[8], bufsz[8];
484	fmd_adm_stats_t ams;
485	struct modstats *mp;
486
487	if (fmd_adm_module_stats(g_adm, ami->ami_name, &ams) != 0) {
488		warn("failed to retrieve statistics for %s", ami->ami_name);
489		return (0); /* continue on to the next module */
490	}
491
492	for (mp = g_mods; mp != NULL; mp = mp->m_next) {
493		if (strcmp(mp->m_name, ami->ami_name) == 0)
494			break;
495	}
496
497	if (mp == NULL && (mp = modstat_create(ami->ami_name, 0)) == NULL) {
498		warn("failed to allocate memory for %s", ami->ami_name);
499		(void) fmd_adm_stats_free(g_adm, &ams);
500		return (0);
501	}
502
503	modstat_compute(mp, &ams);
504
505	(void) printf("%-18s %7llu %7llu %4.1f %6.1f %3.0f %3.0f "
506	    "%5llu %5llu %6s %6s\n", ami->ami_name,
507	    u64delta(mp->m_old->prdequeued.fmds_value.ui64,
508	    mp->m_new->prdequeued.fmds_value.ui64),
509	    u64delta(mp->m_old->accepted.fmds_value.ui64,
510	    mp->m_new->accepted.fmds_value.ui64),
511	    mp->m_wait, mp->m_svc, mp->m_pct_w, mp->m_pct_b,
512	    mp->m_new->caseopen.fmds_value.ui64,
513	    mp->m_new->casesolved.fmds_value.ui64,
514	    size2str(memsz, sizeof (memsz),
515	    mp->m_new->memtotal.fmds_value.ui64),
516	    size2str(bufsz, sizeof (bufsz),
517	    mp->m_new->buftotal.fmds_value.ui64));
518
519	(void) fmd_adm_stats_free(g_adm, &ams);
520	return (0);
521}
522
523static void
524stat_fmd(void)
525{
526	(void) printf("%-18s %7s %7s %4s %6s %3s %3s %5s %5s %6s %6s\n",
527	    "module", "ev_recv", "ev_acpt", "wait", "svc_t", "%w", "%b",
528	    "open", "solve", "memsz", "bufsz");
529
530	if (fmd_adm_module_iter(g_adm, stat_one_fmd, NULL) != 0)
531		die("failed to retrieve list of modules");
532}
533
534static void
535stat_mod(const char *name, int aflag, int zflag)
536{
537	fmd_adm_stats_t ams;
538	fmd_stat_t *sp;
539	char buf[64];
540
541	if (fmd_adm_stats_read(g_adm, name, &ams) != 0) {
542		die("failed to retrieve statistics for %s",
543		    name ? name : "fmd(1M)");
544	}
545
546	(void) printf("%20s %-16s %s\n", "NAME", "VALUE", "DESCRIPTION");
547
548	for (sp = ams.ams_buf; sp < ams.ams_buf + ams.ams_len; sp++) {
549		if (aflag == 0 && strncmp(sp->fmds_name, "fmd.", 4) == 0)
550			continue; /* skip fmd-internal stats unless -a used */
551
552		if (zflag) {
553			switch (sp->fmds_type) {
554			case FMD_TYPE_INT32:
555			case FMD_TYPE_UINT32:
556				if (sp->fmds_value.ui32 == 0)
557					continue;
558				break;
559			case FMD_TYPE_INT64:
560			case FMD_TYPE_UINT64:
561			case FMD_TYPE_TIME:
562			case FMD_TYPE_SIZE:
563				if (sp->fmds_value.ui64 == 0)
564					continue;
565				break;
566			case FMD_TYPE_STRING:
567				if (sp->fmds_value.str == NULL ||
568				    sp->fmds_value.str[0] == '\0')
569					continue;
570				break;
571			}
572		}
573
574		(void) printf("%20s ", sp->fmds_name);
575
576		switch (sp->fmds_type) {
577		case FMD_TYPE_BOOL:
578			(void) printf("%-16s",
579			    sp->fmds_value.bool ? "true" : "false");
580			break;
581		case FMD_TYPE_INT32:
582			(void) printf("%-16d", sp->fmds_value.i32);
583			break;
584		case FMD_TYPE_UINT32:
585			(void) printf("%-16u", sp->fmds_value.ui32);
586			break;
587		case FMD_TYPE_INT64:
588			(void) printf("%-16lld", sp->fmds_value.i64);
589			break;
590		case FMD_TYPE_UINT64:
591			(void) printf("%-16llu", sp->fmds_value.ui64);
592			break;
593		case FMD_TYPE_STRING:
594			(void) printf("%-16s", sp->fmds_value.str ?
595			    sp->fmds_value.str : "<<null>>");
596			break;
597		case FMD_TYPE_TIME:
598			(void) printf("%-16s",
599			    time2str(buf, sizeof (buf), sp->fmds_value.ui64));
600			break;
601		case FMD_TYPE_SIZE:
602			(void) printf("%-16s",
603			    size2str(buf, sizeof (buf), sp->fmds_value.ui64));
604			break;
605		default:
606			(void) snprintf(buf, sizeof (buf),
607			    "<<type=%u>>\n", sp->fmds_type);
608			(void) printf("%-16s", buf);
609		}
610
611		(void) printf(" %s\n", sp->fmds_desc);
612	}
613
614	(void) fmd_adm_stats_free(g_adm, &ams);
615}
616
617/*ARGSUSED*/
618static int
619stat_one_serd(const fmd_adm_serdinfo_t *asi, void *ignored)
620{
621	char buf1[32], buf2[32], n[32];
622
623	(void) snprintf(n, sizeof (n), ">%llu", asi->asi_n);
624
625	(void) printf("%-36s %3s %5s %3u %24s %s\n",
626	    asi->asi_name, n, time2str(buf1, sizeof (buf1), asi->asi_t),
627	    asi->asi_count, time2str(buf2, sizeof (buf2), asi->asi_delta),
628	    (asi->asi_flags & FMD_ADM_SERD_FIRED) ? "fire" : "pend");
629
630	return (0);
631}
632
633static void
634stat_mod_serd(const char *name)
635{
636	(void) printf("%-36s %3s %5s %3s %24s %4s\n",
637	    "NAME", ">N", "T", "CNT", "DELTA", "STAT");
638
639	if (fmd_adm_serd_iter(g_adm, name, stat_one_serd, NULL) != 0)
640		die("failed to retrieve serd engines for %s", name);
641}
642
643static int
644getint(const char *name, const char *s)
645{
646	long val;
647	char *p;
648
649	errno = 0;
650	val = strtol(s, &p, 10);
651
652	if (errno != 0 || p == s || *p != '\0' || val < 0 || val > INT_MAX) {
653		(void) fprintf(stderr, "%s: invalid %s argument -- %s\n",
654		    g_pname, name, s);
655		exit(FMSTAT_EXIT_USAGE);
656	}
657
658	return ((int)val);
659}
660
661static uint32_t
662getu32(const char *name, const char *s)
663{
664	u_longlong_t val;
665	char *p;
666
667	errno = 0;
668	val = strtoull(s, &p, 0);
669
670	if (errno != 0 || p == s || *p != '\0' || val > UINT32_MAX) {
671		(void) fprintf(stderr, "%s: invalid %s argument -- %s\n",
672		    g_pname, name, s);
673		exit(FMSTAT_EXIT_USAGE);
674	}
675
676	return ((uint32_t)val);
677}
678
679static int
680usage(FILE *fp)
681{
682	(void) fprintf(fp, "Usage: %s [-astTz] [-m module] "
683	    "[-P prog] [-d d|u] [interval [count]]\n\n", g_pname);
684
685	(void) fprintf(fp,
686	    "\t-a show all statistics, including those kept by fmd\n"
687	    "\t-d display a timestamp in date (d) or unix time_t (u)\n"
688	    "\t-m show module-specific statistics\n"
689	    "\t-P connect to alternate fmd program\n"
690	    "\t-s show module-specific serd engines\n"
691	    "\t-t show transport-specific statistics\n"
692	    "\t-T show transport modules and authorities\n"
693	    "\t-z suppress zero-valued statistics\n");
694
695	return (FMSTAT_EXIT_USAGE);
696}
697
698int
699main(int argc, char *argv[])
700{
701	int opt_a = 0, opt_s = 0, opt_t = 0, opt_T = 0, opt_z = 0;
702	const char *opt_m = NULL;
703	int msec = 0, iter = 1;
704
705	uint32_t program;
706	char *p;
707	int c;
708
709	if ((p = strrchr(argv[0], '/')) == NULL)
710		g_pname = argv[0];
711	else
712		g_pname = p + 1;
713
714	if ((p = getenv("FMD_PROGRAM")) != NULL)
715		program = getu32("$FMD_PROGRAM", p);
716	else
717		program = FMD_ADM_PROGRAM;
718
719	(void) setlocale(LC_ALL, "");
720	(void) textdomain(TEXT_DOMAIN);
721
722	while ((c = getopt(argc, argv, "ad:m:P:stTz")) != EOF) {
723		switch (c) {
724		case 'a':
725			opt_a++;
726			break;
727		case 'd':
728			if (optarg) {
729				if (*optarg == 'u')
730					timestamp_fmt = UDATE;
731				else if (*optarg == 'd')
732					timestamp_fmt = DDATE;
733				else
734					return (usage(stderr));
735			} else {
736				return (usage(stderr));
737			}
738			break;
739		case 'm':
740			opt_m = optarg;
741			break;
742		case 'P':
743			program = getu32("program", optarg);
744			break;
745		case 's':
746			opt_s++;
747			break;
748		case 't':
749			opt_t++;
750			break;
751		case 'T':
752			opt_T++;
753			break;
754		case 'z':
755			opt_z++;
756			break;
757		default:
758			return (usage(stderr));
759		}
760	}
761
762	if (optind < argc) {
763		msec = getint("interval", argv[optind++]) * MILLISEC;
764		iter = -1;
765	}
766
767	if (optind < argc)
768		iter = getint("count", argv[optind++]);
769
770	if (optind < argc)
771		return (usage(stderr));
772
773	if (opt_t != 0 && (opt_m != NULL || opt_s != 0)) {
774		(void) fprintf(stderr,
775		    "%s: -t cannot be used with -m or -s\n", g_pname);
776		return (FMSTAT_EXIT_USAGE);
777	}
778
779	if (opt_t != 0 && opt_T != 0) {
780		(void) fprintf(stderr,
781		    "%s: -t and -T are mutually exclusive options\n", g_pname);
782		return (FMSTAT_EXIT_USAGE);
783	}
784
785	if (opt_m == NULL && opt_s != 0) {
786		(void) fprintf(stderr,
787		    "%s: -s requires -m <module>\n", g_pname);
788		return (FMSTAT_EXIT_USAGE);
789	}
790
791	if ((g_adm = fmd_adm_open(NULL, program, FMD_ADM_VERSION)) == NULL)
792		die(NULL); /* fmd_adm_errmsg() has enough info */
793
794	while (iter < 0 || iter-- > 0) {
795		if (timestamp_fmt != NODATE)
796			print_timestamp(timestamp_fmt);
797		if (opt_s)
798			stat_mod_serd(opt_m);
799		else if (opt_T)
800			stat_xprt_auth(opt_m);
801		else if (opt_a || opt_m)
802			stat_mod(opt_m, opt_a, opt_z);
803		else if (opt_t)
804			stat_xprt();
805		else
806			stat_fmd();
807
808		if (iter != 0) {
809			(void) poll(NULL, 0, msec);
810			(void) putchar('\n');
811		}
812	}
813
814	fmd_adm_close(g_adm);
815	return (FMSTAT_EXIT_SUCCESS);
816}
817