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