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 }