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