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 (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  24  * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
  25  * Copyright (c) 2013, Joyent, Inc. All rights reserved.
  26  */
  27 
  28 #include <alloca.h>
  29 #include <unistd.h>
  30 #include <limits.h>
  31 #include <strings.h>
  32 #include <stdlib.h>
  33 #include <stdarg.h>
  34 #include <stdio.h>
  35 #include <errno.h>
  36 #include <time.h>
  37 #include <ctype.h>
  38 #include <regex.h>
  39 #include <dirent.h>
  40 #include <pthread.h>
  41 
  42 #include <fmdump.h>
  43 
  44 #define FMDUMP_EXIT_SUCCESS     0
  45 #define FMDUMP_EXIT_FATAL       1
  46 #define FMDUMP_EXIT_USAGE       2
  47 #define FMDUMP_EXIT_ERROR       3
  48 
  49 const char *g_pname;
  50 ulong_t g_errs;
  51 ulong_t g_recs;
  52 char *g_root;
  53 
  54 struct topo_hdl *g_thp;
  55 fmd_msg_hdl_t *g_msg;
  56 
  57 /*PRINTFLIKE2*/
  58 void
  59 fmdump_printf(FILE *fp, const char *format, ...)
  60 {
  61         va_list ap;
  62 
  63         va_start(ap, format);
  64 
  65         if (vfprintf(fp, format, ap) < 0) {
  66                 (void) fprintf(stderr, "%s: failed to print record: %s\n",
  67                     g_pname, strerror(errno));
  68                 g_errs++;
  69         }
  70 
  71         va_end(ap);
  72 }
  73 
  74 void
  75 fmdump_vwarn(const char *format, va_list ap)
  76 {
  77         int err = errno;
  78 
  79         (void) fprintf(stderr, "%s: warning: ", g_pname);
  80         (void) vfprintf(stderr, format, ap);
  81 
  82         if (strchr(format, '\n') == NULL)
  83                 (void) fprintf(stderr, ": %s\n", strerror(err));
  84 
  85         g_errs++;
  86 }
  87 
  88 /*PRINTFLIKE1*/
  89 void
  90 fmdump_warn(const char *format, ...)
  91 {
  92         va_list ap;
  93 
  94         va_start(ap, format);
  95         fmdump_vwarn(format, ap);
  96         va_end(ap);
  97 }
  98 
  99 static void
 100 fmdump_exit(int err, int exitcode, const char *format, va_list ap)
 101 {
 102         (void) fprintf(stderr, "%s: ", g_pname);
 103 
 104         (void) vfprintf(stderr, format, ap);
 105 
 106         if (strchr(format, '\n') == NULL)
 107                 (void) fprintf(stderr, ": %s\n", strerror(err));
 108 
 109         exit(exitcode);
 110 }
 111 
 112 /*PRINTFLIKE1*/
 113 static void
 114 fmdump_fatal(const char *format, ...)
 115 {
 116         int err = errno;
 117 
 118         va_list ap;
 119 
 120         va_start(ap, format);
 121         fmdump_exit(err, FMDUMP_EXIT_FATAL, format, ap);
 122         va_end(ap);
 123 }
 124 
 125 /*PRINTFLIKE1*/
 126 static void
 127 fmdump_usage(const char *format, ...)
 128 {
 129 
 130         int err = errno;
 131 
 132         va_list ap;
 133 
 134         va_start(ap, format);
 135         fmdump_exit(err, FMDUMP_EXIT_USAGE, format, ap);
 136         va_end(ap);
 137 }
 138 
 139 char *
 140 fmdump_date(char *buf, size_t len, const fmd_log_record_t *rp)
 141 {
 142         if (rp->rec_sec > LONG_MAX) {
 143                 fmdump_warn("record time is too large for 32-bit utility\n");
 144                 (void) snprintf(buf, len, "0x%llx", rp->rec_sec);
 145         } else {
 146                 time_t tod = (time_t)rp->rec_sec;
 147                 time_t now = time(NULL);
 148                 if (tod > now+60 ||
 149                     tod < now - 6L*30L*24L*60L*60L) { /* 6 months ago */
 150                         (void) strftime(buf, len, "%b %d %Y %T",
 151                             localtime(&tod));
 152                 } else {
 153                         size_t sz;
 154                         sz = strftime(buf, len, "%b %d %T", localtime(&tod));
 155                         (void) snprintf(buf + sz, len - sz, ".%4.4llu",
 156                             rp->rec_nsec / (NANOSEC / 10000));
 157                 }
 158         }
 159 
 160         return (buf);
 161 }
 162 
 163 char *
 164 fmdump_year(char *buf, size_t len, const fmd_log_record_t *rp)
 165 {
 166 #ifdef _ILP32
 167         if (rp->rec_sec > LONG_MAX) {
 168                 fmdump_warn("record time is too large for 32-bit utility\n");
 169                 (void) snprintf(buf, len, "0x%llx", rp->rec_sec);
 170         } else {
 171 #endif
 172                 time_t tod = (time_t)rp->rec_sec;
 173                 (void) strftime(buf, len, "%b %d %Y %T", localtime(&tod));
 174 #ifdef _ILP32
 175         }
 176 #endif
 177         return (buf);
 178 }
 179 
 180 /* BEGIN CSTYLED */
 181 static const char *synopsis =
 182 "Usage: %s [[-e | -i | -I] | -A ] [-f] [-mvVp] [-c class] [-R root]\n"
 183         "\t      [-t time ][-T time] [-u uuid] [-n name[.name]*[=value]] "
 184                                                         "[file]...\n    "
 185     "Log selection: [-e | -i | -I] or one [file]; default is the fault log\n"
 186         "\t-e  display error log content\n"
 187         "\t-i  display infolog content\n"
 188         "\t-I  display the high-value-infolog content\n"
 189         "\t-R  set root directory for pathname expansions\n    "
 190     "Command behaviour:\n"
 191         "\t-A  Aggregate specified [file]s or, if no [file], all known logs\n"
 192         "\t-f  follow growth of log file by waiting for additional data\n    "
 193     "Output options:\n"
 194         "\t-m  display human-readable messages (only for fault logs)\n"
 195         "\t-v  set verbose mode: display additional event detail\n"
 196         "\t-V  set very verbose mode: display complete event contents\n"
 197         "\t-p  Used with -V: apply some output prettification\n"
 198         "\t-j  Used with -V: emit JSON-formatted output\n    "
 199     "Selection filters:\n"
 200         "\t-c  select events that match the specified class\n"
 201         "\t-t  select events that occurred after the specified time\n"
 202         "\t-T  select events that occurred before the specified time\n"
 203         "\t-u  select events that match the specified diagnosis uuid\n"
 204         "\t-n  select events containing named nvpair (with matching value)\n";
 205 /* END CSTYLED */
 206 
 207 static int
 208 usage(FILE *fp)
 209 {
 210         (void) fprintf(fp, synopsis, g_pname);
 211         return (FMDUMP_EXIT_USAGE);
 212 }
 213 
 214 /*ARGSUSED*/
 215 static int
 216 error(fmd_log_t *lp, void *private)
 217 {
 218         fmdump_warn("skipping record: %s\n",
 219             fmd_log_errmsg(lp, fmd_log_errno(lp)));
 220         return (0);
 221 }
 222 
 223 /*
 224  * Yet another disgusting argument parsing function (TM).  We attempt to parse
 225  * a time argument in a variety of strptime(3C) formats, in which case it is
 226  * interpreted as a local time and is converted to a timeval using mktime(3C).
 227  * If those formats fail, we look to see if the time is a decimal integer
 228  * followed by one of our magic suffixes, in which case the time is interpreted
 229  * as a time delta *before* the current time-of-day (i.e. "1h" = "1 hour ago").
 230  */
 231 static struct timeval *
 232 gettimeopt(const char *arg)
 233 {
 234         const struct {
 235                 const char *name;
 236                 hrtime_t mul;
 237         } suffix[] = {
 238                 { "ns",         NANOSEC / NANOSEC },
 239                 { "nsec",       NANOSEC / NANOSEC },
 240                 { "us",         NANOSEC / MICROSEC },
 241                 { "usec",       NANOSEC / MICROSEC },
 242                 { "ms",         NANOSEC / MILLISEC },
 243                 { "msec",       NANOSEC / MILLISEC },
 244                 { "s",          NANOSEC / SEC },
 245                 { "sec",        NANOSEC / SEC },
 246                 { "m",          NANOSEC * (hrtime_t)60 },
 247                 { "min",        NANOSEC * (hrtime_t)60 },
 248                 { "h",          NANOSEC * (hrtime_t)(60 * 60) },
 249                 { "hour",       NANOSEC * (hrtime_t)(60 * 60) },
 250                 { "d",          NANOSEC * (hrtime_t)(24 * 60 * 60) },
 251                 { "day",        NANOSEC * (hrtime_t)(24 * 60 * 60) },
 252                 { NULL }
 253         };
 254 
 255         struct timeval *tvp = malloc(sizeof (struct timeval));
 256         struct timeval tod;
 257         struct tm tm;
 258         char *p;
 259 
 260         if (tvp == NULL)
 261                 fmdump_fatal("failed to allocate memory");
 262 
 263         if (gettimeofday(&tod, NULL) != 0)
 264                 fmdump_fatal("failed to get tod");
 265 
 266         /*
 267          * First try a variety of strptime() calls.  If these all fail, we'll
 268          * try parsing an integer followed by one of our suffix[] strings.
 269          */
 270         if ((p = strptime(arg, "%m/%d/%Y %H:%M:%S", &tm)) == NULL &&
 271             (p = strptime(arg, "%m/%d/%y %H:%M:%S", &tm)) == NULL &&
 272             (p = strptime(arg, "%m/%d/%Y %H:%M", &tm)) == NULL &&
 273             (p = strptime(arg, "%m/%d/%y %H:%M", &tm)) == NULL &&
 274             (p = strptime(arg, "%m/%d/%Y", &tm)) == NULL &&
 275             (p = strptime(arg, "%m/%d/%y", &tm)) == NULL &&
 276             (p = strptime(arg, "%Y-%m-%dT%H:%M:%S", &tm)) == NULL &&
 277             (p = strptime(arg, "%y-%m-%dT%H:%M:%S", &tm)) == NULL &&
 278             (p = strptime(arg, "%Y-%m-%dT%H:%M", &tm)) == NULL &&
 279             (p = strptime(arg, "%y-%m-%dT%H:%M", &tm)) == NULL &&
 280             (p = strptime(arg, "%Y-%m-%d", &tm)) == NULL &&
 281             (p = strptime(arg, "%y-%m-%d", &tm)) == NULL &&
 282             (p = strptime(arg, "%d%b%Y %H:%M:%S", &tm)) == NULL &&
 283             (p = strptime(arg, "%d%b%y %H:%M:%S", &tm)) == NULL &&
 284             (p = strptime(arg, "%d%b%Y %H:%M", &tm)) == NULL &&
 285             (p = strptime(arg, "%d%b%y %H:%M", &tm)) == NULL &&
 286             (p = strptime(arg, "%d%b%Y", &tm)) == NULL &&
 287             (p = strptime(arg, "%d%b%y", &tm)) == NULL &&
 288             (p = strptime(arg, "%b %d %H:%M:%S", &tm)) == NULL &&
 289             (p = strptime(arg, "%b %d %H:%M:%S", &tm)) == NULL &&
 290             (p = strptime(arg, "%H:%M:%S", &tm)) == NULL &&
 291             (p = strptime(arg, "%H:%M", &tm)) == NULL) {
 292 
 293                 hrtime_t nsec;
 294                 int i;
 295 
 296                 errno = 0;
 297                 nsec = strtol(arg, (char **)&p, 10);
 298 
 299                 if (errno != 0 || nsec == 0 || p == arg || *p == '\0')
 300                         fmdump_usage("illegal time format -- %s\n", arg);
 301 
 302                 for (i = 0; suffix[i].name != NULL; i++) {
 303                         if (strcasecmp(suffix[i].name, p) == 0) {
 304                                 nsec *= suffix[i].mul;
 305                                 break;
 306                         }
 307                 }
 308 
 309                 if (suffix[i].name == NULL)
 310                         fmdump_usage("illegal time format -- %s\n", arg);
 311 
 312                 tvp->tv_sec = nsec / NANOSEC;
 313                 tvp->tv_usec = (nsec % NANOSEC) / (NANOSEC / MICROSEC);
 314 
 315                 if (tvp->tv_sec > tod.tv_sec)
 316                         fmdump_usage("time delta precedes UTC time origin "
 317                             "-- %s\n", arg);
 318 
 319                 tvp->tv_sec = tod.tv_sec - tvp->tv_sec;
 320 
 321         } else if (*p == '\0' || *p == '.') {
 322                 /*
 323                  * If tm_year is zero, we matched [%b %d] %H:%M[:%S]; use
 324                  * the result of localtime(&tod.tv_sec) to fill in the rest.
 325                  */
 326                 if (tm.tm_year == 0) {
 327                         int h = tm.tm_hour;
 328                         int m = tm.tm_min;
 329                         int s = tm.tm_sec;
 330                         int b = tm.tm_mon;
 331                         int d = tm.tm_mday;
 332 
 333                         bcopy(localtime(&tod.tv_sec), &tm, sizeof (tm));
 334                         tm.tm_isdst = 0; /* see strptime(3C) and below */
 335 
 336                         if (d > 0) {
 337                                 tm.tm_mon = b;
 338                                 tm.tm_mday = d;
 339                         }
 340 
 341                         tm.tm_hour = h;
 342                         tm.tm_min = m;
 343                         tm.tm_sec = s;
 344                 }
 345 
 346                 errno = 0;
 347                 tvp->tv_sec = mktime(&tm);
 348                 tvp->tv_usec = 0;
 349 
 350                 if (tvp->tv_sec == -1L && errno != 0)
 351                         fmdump_fatal("failed to compose time %s", arg);
 352 
 353                 /*
 354                  * If our mktime() set tm_isdst, adjust the result for DST by
 355                  * subtracting the offset between the main and alternate zones.
 356                  */
 357                 if (tm.tm_isdst)
 358                         tvp->tv_sec -= timezone - altzone;
 359 
 360                 if (p[0] == '.') {
 361                         arg = p;
 362                         errno = 0;
 363                         tvp->tv_usec =
 364                             (suseconds_t)(strtod(arg, &p) * (double)MICROSEC);
 365 
 366                         if (errno != 0 || p == arg || *p != '\0')
 367                                 fmdump_usage("illegal time suffix -- .%s\n",
 368                                     arg);
 369                 }
 370 
 371         } else {
 372                 fmdump_usage("unexpected suffix after time %s -- %s\n", arg, p);
 373         }
 374 
 375         return (tvp);
 376 }
 377 
 378 /*
 379  * If the -u option is specified in combination with the -e option, we iterate
 380  * over each record in the fault log with a matching UUID finding xrefs to the
 381  * error log, and then use this function to iterate over every xref'd record.
 382  */
 383 int
 384 xref_iter(fmd_log_t *lp, const fmd_log_record_t *rp, void *arg)
 385 {
 386         const fmd_log_record_t *xrp = rp->rec_xrefs;
 387         fmdump_arg_t *dap = arg;
 388         int i, rv = 0;
 389 
 390         for (i = 0; rv == 0 && i < rp->rec_nrefs; i++, xrp++) {
 391                 if (fmd_log_filter(lp, dap->da_fc, dap->da_fv, xrp))
 392                         rv = dap->da_fmt->do_func(lp, xrp, dap->da_fp);
 393         }
 394 
 395         return (rv);
 396 }
 397 
 398 int
 399 xoff_iter(fmd_log_t *lp, const fmd_log_record_t *rp, void *arg)
 400 {
 401         fmdump_lyr_t *dyp = arg;
 402 
 403         fmdump_printf(dyp->dy_fp, "%16llx ", (u_longlong_t)rp->rec_off);
 404         return (dyp->dy_func(lp, rp, dyp->dy_arg));
 405 }
 406 
 407 /*
 408  * Initialize fmd_log_filter_nvarg_t from -n name=value argument string.
 409  */
 410 static fmd_log_filter_nvarg_t *
 411 setupnamevalue(char *namevalue)
 412 {
 413         fmd_log_filter_nvarg_t  *argt;
 414         char                    *value;
 415         regex_t                 *value_regex = NULL;
 416         char                    errstr[128];
 417         int                     rv;
 418 
 419         if ((value = strchr(namevalue, '=')) == NULL) {
 420                 value_regex = NULL;
 421         } else {
 422                 *value++ = '\0';        /* separate name and value string */
 423 
 424                 /*
 425                  * Skip white space before value to facilitate direct
 426                  * cut/paste from previous fmdump output.
 427                  */
 428                 while (isspace(*value))
 429                         value++;
 430 
 431                 if ((value_regex = malloc(sizeof (regex_t))) == NULL)
 432                         fmdump_fatal("failed to allocate memory");
 433 
 434                 /* compile regular expression for possible string match */
 435                 if ((rv = regcomp(value_regex, value,
 436                     REG_NOSUB|REG_NEWLINE)) != 0) {
 437                         (void) regerror(rv, value_regex, errstr,
 438                             sizeof (errstr));
 439                         free(value_regex);
 440                         fmdump_usage("unexpected regular expression in "
 441                             "%s: %s\n", value, errstr);
 442                 }
 443         }
 444 
 445         if ((argt = malloc(sizeof (fmd_log_filter_nvarg_t))) == NULL)
 446                 fmdump_fatal("failed to allocate memory");
 447 
 448         argt->nvarg_name = namevalue;                /* now just name */
 449         argt->nvarg_value = value;
 450         argt->nvarg_value_regex = value_regex;
 451         return (argt);
 452 }
 453 
 454 /*
 455  * If the -a option is not present, filter out fault records that correspond
 456  * to events that the producer requested not be messaged for administrators.
 457  */
 458 /*ARGSUSED*/
 459 int
 460 log_filter_silent(fmd_log_t *lp, const fmd_log_record_t *rp, void *arg)
 461 {
 462         int opt_A = (arg != NULL);
 463         boolean_t msg;
 464         char *class;
 465 
 466         /*
 467          * If -A was used then apply this filter only to events of list class
 468          */
 469         if (opt_A) {
 470                 if (nvlist_lookup_string(rp->rec_nvl, FM_CLASS, &class) != 0 ||
 471                     strncmp(class, FM_LIST_EVENT ".",
 472                     sizeof (FM_LIST_EVENT)) != 0)
 473                         return (1);
 474         }
 475 
 476         return (nvlist_lookup_boolean_value(rp->rec_nvl,
 477             FM_SUSPECT_MESSAGE, &msg) != 0 || msg != 0);
 478 }
 479 
 480 struct loglink {
 481         char            *path;
 482         long            suffix;
 483         struct loglink  *next;
 484 };
 485 
 486 static void
 487 addlink(struct loglink **llp, char *dirname, char *logname, long suffix)
 488 {
 489         struct loglink *newp;
 490         size_t len;
 491         char *str;
 492 
 493         newp = malloc(sizeof (struct loglink));
 494         len = strlen(dirname) + strlen(logname) + 2;
 495         str = malloc(len);
 496         if (newp == NULL || str == NULL)
 497                 fmdump_fatal("failed to allocate memory");
 498 
 499         (void) snprintf(str, len, "%s/%s", dirname, logname);
 500         newp->path = str;
 501         newp->suffix = suffix;
 502 
 503         while (*llp != NULL && suffix < (*llp)->suffix)
 504                 llp = &(*llp)->next;
 505 
 506         newp->next = *llp;
 507         *llp = newp;
 508 }
 509 
 510 /*
 511  * Find and return all the rotated logs.
 512  */
 513 static struct loglink *
 514 get_rotated_logs(char *logpath)
 515 {
 516         char dirname[PATH_MAX], *logname, *endptr;
 517         DIR *dirp;
 518         struct dirent *dp;
 519         long len, suffix;
 520         struct loglink *head = NULL;
 521 
 522         (void) strlcpy(dirname, logpath, sizeof (dirname));
 523         logname = strrchr(dirname, '/');
 524         *logname++ = '\0';
 525         len = strlen(logname);
 526 
 527         if ((dirp = opendir(dirname)) == NULL) {
 528                 fmdump_warn("failed to opendir `%s'", dirname);
 529                 g_errs++;
 530                 return (NULL);
 531         }
 532 
 533         while ((dp = readdir(dirp)) != NULL) {
 534                 /*
 535                  * Search the log directory for logs named "<logname>.0",
 536                  * "<logname>.1", etc and add to the link in the
 537                  * reverse numeric order.
 538                  */
 539                 if (strlen(dp->d_name) < len + 2 ||
 540                     strncmp(dp->d_name, logname, len) != 0 ||
 541                     dp->d_name[len] != '.')
 542                         continue;
 543 
 544                 /*
 545                  * "*.0-" file normally should not be seen.  It may
 546                  * exist when user manually run 'fmadm rotate'.
 547                  * In such case, we put it at the end of the list so
 548                  * it'll be dumped after all the rotated logs, before
 549                  * the current one.
 550                  */
 551                 if (strcmp(dp->d_name + len + 1, "0-") == 0)
 552                         addlink(&head, dirname, dp->d_name, -1);
 553                 else if ((suffix = strtol(dp->d_name + len + 1,
 554                     &endptr, 10)) >= 0 && *endptr == '\0')
 555                         addlink(&head, dirname, dp->d_name, suffix);
 556         }
 557 
 558         (void) closedir(dirp);
 559 
 560         return (head);
 561 }
 562 
 563 /*
 564  * Aggregate log files.  If ifiles is not NULL then one or more files
 565  * were listed on the command line, and we will merge just those files.
 566  * Otherwise we will merge all known log file types, and include the
 567  * rotated logs for each type (you can suppress the inclusion of
 568  * some logtypes through use of FMDUMP_AGGREGATE_IGNORE in the process
 569  * environment, setting it to a comma-separated list of log labels and/or
 570  * log filenames to ignore).
 571  *
 572  * We will not attempt to perform a chronological sort across all log records
 573  * of all files.  Indeed, we won't even sort individual log files -
 574  * we will not re-order events differently to how they appeared in their
 575  * original log file.  This is because log files are already inherently
 576  * ordered by the order in which fmd receives and processes events.
 577  * So we determine the output order by comparing the "next" record
 578  * off the top of each log file.
 579  *
 580  * We will construct a number of log record source "pipelines".  As above,
 581  * the next record to render in the overall output is that from the
 582  * pipeline with the oldest event.
 583  *
 584  * For the case that input logfiles were listed on the command line, each
 585  * pipeline will process exactly one of those logfiles.  Distinct pipelines
 586  * may process logfiles of the same "type" - eg if two "error" logs and
 587  * one "fault" logs are specified then there'll be two pipelines producing
 588  * events from "error" logs.
 589  *
 590  * If we are merging all known log types then we will construct exactly
 591  * one pipeline for each known log type - one for error, one for fault, etc.
 592  * Each pipeline will process first the rotated logs of that type and then
 593  * move on to the current log of that type.
 594  *
 595  * The output from all pipelines flows into a serializer which selects
 596  * the next record once all pipelines have asserted their output state.
 597  * The output state of a pipeline is one of:
 598  *
 599  *      - record available: the next record from this pipeline is available
 600  *        for comparison and consumption
 601  *
 602  *      - done: this pipeline will produce no more records
 603  *
 604  *      - polling: this pipeline is polling for new records and will
 605  *        make them available as output if/when any are observed
 606  *
 607  *      - processing: output state will be updated shortly
 608  *
 609  * A pipeline iterates over each file queued to it using fmd_log_xiter.
 610  * We do this in a separate thread for each pipeline.  The callback on
 611  * each iteration must update the serializer to let it know that
 612  * a new record is available.  In the serializer thread we decide whether
 613  * we have all records expected have arrived and it is time to choose
 614  * the next output record.
 615  */
 616 
 617 /*
 618  * A pipeline descriptor.  The pl_cv condition variable is used together
 619  * with pl_lock for initial synchronisation, and thereafter with the
 620  * lock for the serializer for pausing and continuing this pipeline.
 621  */
 622 struct fmdump_pipeline {
 623         pthread_mutex_t pl_lock;        /* used only in pipeline startup */
 624         int pl_started;                 /* sync with main thread on startup */
 625         pthread_t pl_thr;               /* our processing thread */
 626         pthread_cond_t pl_cv;           /* see above */
 627         struct loglink *pl_rotated;     /* rotated logs to process first */
 628         char *pl_logpath;               /* target path to process */
 629         char *pl_processing;            /* path currently being processed */
 630         struct fmdump_srlzer *pl_srlzer;        /* link to serializer */
 631         int pl_srlzeridx;               /* serializer index for this pipeline */
 632         const fmdump_ops_t *pl_ops;     /* ops for the log type we're given */
 633         int pl_fmt;                     /* FMDUMP_{SHORT,VERB1,VERB2,PRETTY} */
 634         boolean_t pl_follow;            /* go into poll mode at log end */
 635         fmdump_arg_t pl_arg;            /* arguments */
 636 };
 637 
 638 enum fmdump_pipestate {
 639         FMDUMP_PIPE_PROCESSING = 0x1000,
 640         FMDUMP_PIPE_RECORDAVAIL,
 641         FMDUMP_PIPE_POLLING,
 642         FMDUMP_PIPE_DONE
 643 };
 644 
 645 /*
 646  * Each pipeline has an associated output slot in the serializer.  This
 647  * must be updated with the serializer locked.  After update evaluate
 648  * whether there are enough slots decided that we should select a
 649  * record to output.
 650  */
 651 struct fmdump_srlzer_slot {
 652         enum fmdump_pipestate ss_state;
 653         uint64_t ss_sec;
 654         uint64_t ss_nsec;
 655 };
 656 
 657 /*
 658  * All pipelines are linked to a single serializer.  The serializer
 659  * structure must be updated under the ds_lock; this mutex is also
 660  * paired with the pl_cv of individual pipelines (one mutex, many condvars)
 661  * in pausing and continuing individual pipelines.
 662  */
 663 struct fmdump_srlzer {
 664         struct fmdump_pipeline *ds_pipearr;     /* pipeline array */
 665         pthread_mutex_t ds_lock;                /* see above */
 666         uint32_t ds_pipecnt;                    /* number of pipelines */
 667         uint32_t ds_pollcnt;                    /* pipelines in poll mode */
 668         uint32_t ds_nrecordavail;               /* pipelines with a record */
 669         uint32_t ds_ndone;                      /* completed pipelines */
 670         struct fmdump_srlzer_slot *ds_slot;     /* slot array */
 671 };
 672 
 673 /*
 674  * All known log types.  When aggregation is requested an no file list
 675  * is provided we will process the logs identified here (if lt_enabled
 676  * is true and not over-ridden by environment settings).  We also
 677  * use this in determining the appropriate ops structure for each distinct
 678  * label.
 679  */
 680 static struct fmdump_logtype {
 681         const char *lt_label;           /* label from log header */
 682         boolean_t lt_enabled;           /* include in merge? */
 683         const char *lt_logname;         /* var/fm/fmd/%s */
 684         const fmdump_ops_t *lt_ops;
 685 } logtypes[] = {
 686         {
 687                 "error",
 688                 B_TRUE,
 689                 "errlog",
 690                 &fmdump_err_ops
 691         },
 692         {
 693                 "fault",
 694                 B_TRUE,
 695                 "fltlog",
 696                 &fmdump_flt_ops
 697         },
 698         {
 699                 "info",
 700                 B_TRUE,
 701                 "infolog",
 702                 &fmdump_info_ops
 703         },
 704         {
 705                 "info",
 706                 B_TRUE,
 707                 "infolog_hival",
 708                 &fmdump_info_ops
 709         },
 710         {
 711                 "asru",
 712                 B_FALSE,                /* not included unless in file list */
 713                 NULL,
 714                 &fmdump_asru_ops    /* but we need ops when it is */
 715         }
 716 };
 717 
 718 /*
 719  * Disable logtypes per environment setting.  Does not apply when a list
 720  * of logs is provided on the command line.
 721  */
 722 static void
 723 do_disables(void)
 724 {
 725         char *env = getenv("FMDUMP_AGGREGATE_IGNORE");
 726         char *dup, *start, *tofree;
 727         int i;
 728 
 729         if (env == NULL)
 730                 return;
 731 
 732         tofree = dup = strdup(env);
 733 
 734         while (dup != NULL) {
 735                 start = strsep(&dup, ",");
 736                 for (i = 0; i < sizeof (logtypes) / sizeof (logtypes[0]); i++) {
 737                         if (logtypes[i].lt_logname == NULL)
 738                                 continue;
 739 
 740                         if (strcmp(start, logtypes[i].lt_label) == 0 ||
 741                             strcmp(start, logtypes[i].lt_logname) == 0) {
 742                                 logtypes[i].lt_enabled = B_FALSE;
 743                         }
 744                 }
 745         }
 746 
 747         free(tofree);
 748 }
 749 
 750 static void
 751 srlzer_enter(struct fmdump_pipeline *pl)
 752 {
 753         struct fmdump_srlzer *srlzer = pl->pl_srlzer;
 754 
 755         (void) pthread_mutex_lock(&srlzer->ds_lock);
 756 }
 757 
 758 static void
 759 srlzer_exit(struct fmdump_pipeline *pl)
 760 {
 761         struct fmdump_srlzer *srlzer = pl->pl_srlzer;
 762 
 763         ASSERT(MUTEX_HELD(&srlzer->ds_lock));
 764         (void) pthread_mutex_unlock(&srlzer->ds_lock);
 765 }
 766 
 767 static struct fmdump_pipeline *
 768 srlzer_choose(struct fmdump_srlzer *srlzer)
 769 {
 770         struct fmdump_srlzer_slot *slot, *oldest;
 771         int oldestidx = -1;
 772         int first = 1;
 773         int i;
 774 
 775         ASSERT(MUTEX_HELD(&srlzer->ds_lock));
 776 
 777         for (i = 0, slot = &srlzer->ds_slot[0]; i < srlzer->ds_pipecnt;
 778             i++, slot++) {
 779                 if (slot->ss_state != FMDUMP_PIPE_RECORDAVAIL)
 780                         continue;
 781 
 782                 if (first) {
 783                         oldest = slot;
 784                         oldestidx = i;
 785                         first = 0;
 786                         continue;
 787                 }
 788 
 789                 if (slot->ss_sec < oldest->ss_sec ||
 790                     slot->ss_sec == oldest->ss_sec &&
 791                     slot->ss_nsec < oldest->ss_nsec) {
 792                         oldest = slot;
 793                         oldestidx = i;
 794                 }
 795         }
 796 
 797         return (oldestidx >= 0 ? &srlzer->ds_pipearr[oldestidx] : NULL);
 798 }
 799 
 800 static void
 801 pipeline_stall(struct fmdump_pipeline *pl)
 802 {
 803         struct fmdump_srlzer *srlzer = pl->pl_srlzer;
 804 
 805         ASSERT(MUTEX_HELD(&srlzer->ds_lock));
 806         (void) pthread_cond_wait(&pl->pl_cv, &srlzer->ds_lock);
 807 }
 808 
 809 static void
 810 pipeline_continue(struct fmdump_pipeline *pl)
 811 {
 812         struct fmdump_srlzer *srlzer = pl->pl_srlzer;
 813 
 814         ASSERT(MUTEX_HELD(&srlzer->ds_lock));
 815         (void) pthread_cond_signal(&srlzer->ds_pipearr[pl->pl_srlzeridx].pl_cv);
 816 }
 817 
 818 /*
 819  * Called on each pipeline record iteration to make a new record
 820  * available for input to the serializer.  Returns 0 to indicate that
 821  * the caller must stall the pipeline, or 1 to indicate that the
 822  * caller should go ahead and render their record.  If this record
 823  * addition fills the serializer then choose a pipeline that must
 824  * render output.
 825  */
 826 static int
 827 pipeline_output(struct fmdump_pipeline *pl, const fmd_log_record_t *rp)
 828 {
 829         struct fmdump_srlzer *srlzer = pl->pl_srlzer;
 830         struct fmdump_srlzer_slot *slot;
 831         struct fmdump_pipeline *wpl;
 832         int thisidx = pl->pl_srlzeridx;
 833 
 834         ASSERT(MUTEX_HELD(&srlzer->ds_lock));
 835 
 836         slot = &srlzer->ds_slot[thisidx];
 837         slot->ss_state = FMDUMP_PIPE_RECORDAVAIL;
 838         slot->ss_sec = rp->rec_sec;
 839         slot->ss_nsec = rp->rec_nsec;
 840         srlzer->ds_nrecordavail++;
 841 
 842         /*
 843          * Once all pipelines are polling we just render in arrival order.
 844          */
 845         if (srlzer->ds_pollcnt == srlzer->ds_pipecnt)
 846                 return (1);
 847 
 848         /*
 849          * If not all pipelines have asserted an output yet then the
 850          * caller must block.
 851          */
 852         if (srlzer->ds_nrecordavail + srlzer->ds_ndone +
 853             srlzer->ds_pollcnt < srlzer->ds_pipecnt)
 854                 return (0);
 855 
 856         /*
 857          * Right so it's time to turn the crank by choosing which of the
 858          * filled line of slots should produce output.  If it is the slot
 859          * for our caller then return their index to them, otherwise return
 860          * -1 to the caller to make them block and cv_signal the winner.
 861          */
 862         wpl = srlzer_choose(srlzer);
 863         ASSERT(wpl != NULL);
 864 
 865         if (wpl == pl)
 866                 return (1);
 867 
 868         /* Wake the oldest, and return 0 to put the caller to sleep */
 869         pipeline_continue(wpl);
 870 
 871         return (0);
 872 }
 873 
 874 static void
 875 pipeline_mark_consumed(struct fmdump_pipeline *pl)
 876 {
 877         struct fmdump_srlzer *srlzer = pl->pl_srlzer;
 878 
 879         ASSERT(MUTEX_HELD(&srlzer->ds_lock));
 880         srlzer->ds_slot[pl->pl_srlzeridx].ss_state = FMDUMP_PIPE_PROCESSING;
 881         srlzer->ds_nrecordavail--;
 882 }
 883 
 884 static void
 885 pipeline_done(struct fmdump_pipeline *pl)
 886 {
 887         struct fmdump_srlzer *srlzer = pl->pl_srlzer;
 888         struct fmdump_pipeline *wpl;
 889 
 890         srlzer_enter(pl);
 891 
 892         srlzer->ds_slot[pl->pl_srlzeridx].ss_state = FMDUMP_PIPE_DONE;
 893         srlzer->ds_ndone++;
 894         wpl = srlzer_choose(srlzer);
 895         if (wpl != NULL)
 896                 pipeline_continue(wpl);
 897 
 898         srlzer_exit(pl);
 899 }
 900 
 901 static void
 902 pipeline_pollmode(struct fmdump_pipeline *pl)
 903 {
 904         struct fmdump_srlzer *srlzer = pl->pl_srlzer;
 905         struct fmdump_pipeline *wpl;
 906 
 907         if (srlzer->ds_slot[pl->pl_srlzeridx].ss_state == FMDUMP_PIPE_POLLING)
 908                 return;
 909 
 910         srlzer_enter(pl);
 911 
 912         srlzer->ds_slot[pl->pl_srlzeridx].ss_state = FMDUMP_PIPE_POLLING;
 913         if (++srlzer->ds_pollcnt + srlzer->ds_nrecordavail ==
 914             srlzer->ds_pipecnt && (wpl = srlzer_choose(srlzer)) != NULL)
 915                 pipeline_continue(wpl);
 916 
 917         srlzer_exit(pl);
 918 }
 919 
 920 static int
 921 pipeline_err(fmd_log_t *lp, void *arg)
 922 {
 923         struct fmdump_pipeline *pl = (struct fmdump_pipeline *)arg;
 924 
 925         fmdump_warn("skipping record in %s: %s\n", pl->pl_processing,
 926             fmd_log_errmsg(lp, fmd_log_errno(lp)));
 927         g_errs++;
 928 
 929         return (0);
 930 }
 931 
 932 static int
 933 pipeline_cb(fmd_log_t *lp, const fmd_log_record_t *rp, void *arg)
 934 {
 935         struct fmdump_pipeline *pl = (struct fmdump_pipeline *)arg;
 936         int rc;
 937 
 938         fmd_log_rec_f *func = pl->pl_arg.da_fmt->do_func;
 939 
 940         srlzer_enter(pl);
 941 
 942         if (!pipeline_output(pl, rp))
 943                 pipeline_stall(pl);
 944 
 945         rc = func(lp, rp, pl->pl_arg.da_fp);
 946         pipeline_mark_consumed(pl);
 947 
 948         srlzer_exit(pl);
 949 
 950         return (rc);
 951 }
 952 
 953 static void
 954 pipeline_process(struct fmdump_pipeline *pl, char *logpath, boolean_t follow)
 955 {
 956         fmd_log_header_t log;
 957         fmd_log_t *lp;
 958         int err;
 959         int i;
 960 
 961         pl->pl_processing = logpath;
 962 
 963         if ((lp = fmd_log_open(FMD_LOG_VERSION, logpath, &err)) == NULL) {
 964                 fmdump_warn("failed to open %s: %s\n",
 965                     logpath, fmd_log_errmsg(NULL, err));
 966                 g_errs++;
 967                 return;
 968         }
 969 
 970         fmd_log_header(lp, &log);
 971         for (i = 0; i < sizeof (logtypes) / sizeof (logtypes[0]); i++) {
 972                 if (strcmp(log.log_label, logtypes[i].lt_label) == 0) {
 973                         pl->pl_ops = logtypes[i].lt_ops;
 974                         pl->pl_arg.da_fmt =
 975                             &pl->pl_ops->do_formats[pl->pl_fmt];
 976                         break;
 977                 }
 978         }
 979 
 980         if (pl->pl_ops == NULL) {
 981                 fmdump_warn("unknown log type %s for %s\n",
 982                     log.log_label, logpath);
 983                 g_errs++;
 984                 return;
 985         }
 986 
 987         do {
 988                 if (fmd_log_xiter(lp, FMD_LOG_XITER_REFS, pl->pl_arg.da_fc,
 989                     pl->pl_arg.da_fv, pipeline_cb, pipeline_err, (void *)pl,
 990                     NULL) != 0) {
 991                         fmdump_warn("failed to dump %s: %s\n",
 992                             logpath, fmd_log_errmsg(lp, fmd_log_errno(lp)));
 993                         g_errs++;
 994                         fmd_log_close(lp);
 995                         return;
 996                 }
 997 
 998                 if (follow) {
 999                         pipeline_pollmode(pl);
1000                         (void) sleep(1);
1001                 }
1002 
1003         } while (follow);
1004 
1005         fmd_log_close(lp);
1006 }
1007 
1008 static void *
1009 pipeline_thr(void *arg)
1010 {
1011         struct fmdump_pipeline *pl = (struct fmdump_pipeline *)arg;
1012         struct loglink *ll;
1013 
1014         (void) pthread_mutex_lock(&pl->pl_lock);
1015         pl->pl_started = 1;
1016         (void) pthread_mutex_unlock(&pl->pl_lock);
1017         (void) pthread_cond_signal(&pl->pl_cv);
1018 
1019         for (ll = pl->pl_rotated; ll != NULL; ll = ll->next)
1020                 pipeline_process(pl, ll->path, B_FALSE);
1021 
1022         pipeline_process(pl, pl->pl_logpath, pl->pl_follow);
1023         pipeline_done(pl);
1024 
1025         return (NULL);
1026 }
1027 
1028 
1029 static int
1030 aggregate(char **ifiles, int n_ifiles, int opt_f,
1031     fmd_log_filter_t *fv, uint_t fc,
1032     int opt_v, int opt_V, int opt_p, int opt_j)
1033 {
1034         struct fmdump_pipeline *pipeline, *pl;
1035         struct fmdump_srlzer srlzer;
1036         uint32_t npipe;
1037         int fmt;
1038         int i;
1039 
1040         if (ifiles != NULL) {
1041                 npipe = n_ifiles;
1042                 pipeline = calloc(npipe, sizeof (struct fmdump_pipeline));
1043                 if (!pipeline)
1044                         fmdump_fatal("failed to allocate memory");
1045 
1046                 for (i = 0; i < n_ifiles; i++)
1047                         pipeline[i].pl_logpath = ifiles[i];
1048         } else {
1049                 pipeline = calloc(sizeof (logtypes) / sizeof (logtypes[0]),
1050                     sizeof (struct fmdump_pipeline));
1051                 if (!pipeline)
1052                         fmdump_fatal("failed to allocate memory");
1053 
1054                 do_disables();
1055 
1056                 npipe = 0;
1057                 for (i = 0; i < sizeof (logtypes) / sizeof (logtypes[0]); i++) {
1058                         struct fmdump_logtype *ltp = &logtypes[i];
1059                         char *logpath;
1060 
1061                         if (ltp->lt_enabled == B_FALSE)
1062                                 continue;
1063 
1064                         if ((logpath = malloc(PATH_MAX)) == NULL)
1065                                 fmdump_fatal("failed to allocate memory");
1066 
1067                         (void) snprintf(logpath, PATH_MAX,
1068                             "%s/var/fm/fmd/%s",
1069                             g_root ? g_root : "", ltp->lt_logname);
1070 
1071                         pipeline[npipe].pl_rotated =
1072                             get_rotated_logs(logpath);
1073 
1074                         pipeline[npipe++].pl_logpath = logpath;
1075                 }
1076         }
1077 
1078         if (opt_V)
1079                 fmt = opt_p ? FMDUMP_PRETTY : opt_j ? FMDUMP_JSON :
1080                     FMDUMP_VERB2;
1081         else if (opt_v)
1082                 fmt = FMDUMP_VERB1;
1083         else
1084                 fmt = FMDUMP_SHORT;
1085 
1086         bzero(&srlzer, sizeof (srlzer));
1087         srlzer.ds_pipearr = pipeline;
1088         srlzer.ds_pipecnt = npipe;
1089         srlzer.ds_slot = calloc(npipe, sizeof (struct fmdump_srlzer_slot));
1090         if (!srlzer.ds_slot)
1091                 fmdump_fatal("failed to allocate memory");
1092         (void) pthread_mutex_init(&srlzer.ds_lock, NULL);
1093 
1094         for (i = 0, pl = &pipeline[0]; i < npipe; i++, pl++) {
1095                 (void) pthread_mutex_init(&pl->pl_lock, NULL);
1096                 (void) pthread_cond_init(&pl->pl_cv, NULL);
1097                 srlzer.ds_slot[i].ss_state = FMDUMP_PIPE_PROCESSING;
1098                 pl->pl_srlzer = &srlzer;
1099                 pl->pl_srlzeridx = i;
1100                 pl->pl_follow = opt_f ? B_TRUE : B_FALSE;
1101                 pl->pl_fmt = fmt;
1102                 pl->pl_arg.da_fv = fv;
1103                 pl->pl_arg.da_fc = fc;
1104                 pl->pl_arg.da_fp = stdout;
1105 
1106                 (void) pthread_mutex_lock(&pl->pl_lock);
1107 
1108                 if (pthread_create(&pl->pl_thr, NULL,
1109                     pipeline_thr, (void *)pl) != 0)
1110                         fmdump_fatal("pthread_create for pipeline %d failed",
1111                             i);
1112         }
1113 
1114         for (i = 0, pl = &pipeline[0]; i < npipe; i++, pl++) {
1115                 while (!pl->pl_started)
1116                         (void) pthread_cond_wait(&pl->pl_cv, &pl->pl_lock);
1117 
1118                 (void) pthread_mutex_unlock(&pl->pl_lock);
1119         }
1120 
1121         for (i = 0, pl = &pipeline[0]; i < npipe; i++, pl++)
1122                 (void) pthread_join(pl->pl_thr, NULL);
1123 
1124         if (ifiles == NULL) {
1125                 for (i = 0; i < npipe; i++)
1126                         free(pipeline[i].pl_logpath);
1127         }
1128 
1129         free(srlzer.ds_slot);
1130 
1131         free(pipeline);
1132 
1133         return (FMDUMP_EXIT_SUCCESS);
1134 }
1135 
1136 static void
1137 cleanup(char **ifiles, int n_ifiles)
1138 {
1139         int i;
1140 
1141         if (ifiles == NULL)
1142                 return;
1143 
1144         for (i = 0; i < n_ifiles; i++) {
1145                 if (ifiles[i] != NULL) {
1146                         free(ifiles[i]);
1147                         ifiles[i] = NULL;
1148                 }
1149         }
1150 
1151         free(ifiles);
1152 }
1153 
1154 int
1155 main(int argc, char *argv[])
1156 {
1157         int opt_a = 0, opt_e = 0, opt_f = 0, opt_H = 0, opt_m = 0, opt_p = 0;
1158         int opt_u = 0, opt_v = 0, opt_V = 0, opt_j = 0;
1159         int opt_i = 0, opt_I = 0;
1160         int opt_A = 0;
1161         char **ifiles = NULL;
1162         char *ifile = NULL;
1163         int n_ifiles;
1164         int ifileidx = 0;
1165         int iflags = 0;
1166 
1167         fmdump_arg_t arg;
1168         fmdump_lyr_t lyr;
1169         const fmdump_ops_t *ops;
1170         fmd_log_filter_t *filtv;
1171         uint_t filtc;
1172 
1173         fmd_log_filter_t *errfv, *fltfv, *allfv;
1174         uint_t errfc = 0, fltfc = 0, allfc = 0;
1175 
1176         fmd_log_header_t log;
1177         fmd_log_rec_f *func;
1178         void *farg;
1179         fmd_log_t *lp;
1180         int c, err;
1181         off64_t off = 0;
1182         ulong_t recs;
1183         struct loglink *rotated_logs = NULL, *llp;
1184 
1185         g_pname = argv[0];
1186 
1187         errfv = alloca(sizeof (fmd_log_filter_t) * argc);
1188         fltfv = alloca(sizeof (fmd_log_filter_t) * argc);
1189         allfv = alloca(sizeof (fmd_log_filter_t) * argc);
1190 
1191         while (optind < argc) {
1192                 while ((c =
1193                     getopt(argc, argv, "Aac:efHiIjmn:O:pR:t:T:u:vV")) != EOF) {
1194                         switch (c) {
1195                         case 'A':
1196                                 opt_A++;
1197                                 break;
1198                         case 'a':
1199                                 opt_a++;
1200                                 break;
1201                         case 'c':
1202                                 errfv[errfc].filt_func = fmd_log_filter_class;
1203                                 errfv[errfc].filt_arg = optarg;
1204                                 allfv[allfc++] = errfv[errfc++];
1205                                 break;
1206                         case 'e':
1207                                 if (opt_i)
1208                                         return (usage(stderr));
1209                                 opt_e++;
1210                                 break;
1211                         case 'f':
1212                                 opt_f++;
1213                                 break;
1214                         case 'H':
1215                                 opt_H++;
1216                                 break;
1217                         case 'i':
1218                                 if (opt_e || opt_I)
1219                                         return (usage(stderr));
1220                                 opt_i++;
1221                                 break;
1222                         case 'I':
1223                                 if (opt_e || opt_i)
1224                                         return (usage(stderr));
1225                                 opt_I++;
1226                                 break;
1227                         case 'j':
1228                                 if (opt_p)
1229                                         return (usage(stderr));
1230                                 opt_j++;
1231                                 break;
1232                         case 'm':
1233                                 opt_m++;
1234                                 break;
1235                         case 'O':
1236                                 off = strtoull(optarg, NULL, 16);
1237                                 iflags |= FMD_LOG_XITER_OFFS;
1238                                 break;
1239                         case 'p':
1240                                 if (opt_j)
1241                                         return (usage(stderr));
1242                                 opt_p++;
1243                                 break;
1244                         case 'R':
1245                                 g_root = optarg;
1246                                 break;
1247                         case 't':
1248                                 errfv[errfc].filt_func = fmd_log_filter_after;
1249                                 errfv[errfc].filt_arg = gettimeopt(optarg);
1250                                 allfv[allfc++] = errfv[errfc++];
1251                                 break;
1252                         case 'T':
1253                                 errfv[errfc].filt_func = fmd_log_filter_before;
1254                                 errfv[errfc].filt_arg = gettimeopt(optarg);
1255                                 allfv[allfc++] = errfv[errfc++];
1256                                 break;
1257                         case 'u':
1258                                 fltfv[fltfc].filt_func = fmd_log_filter_uuid;
1259                                 fltfv[fltfc].filt_arg = optarg;
1260                                 allfv[allfc++] = fltfv[fltfc++];
1261                                 opt_u++;
1262                                 opt_a++; /* -u implies -a */
1263                                 break;
1264                         case 'n': {
1265                                 fltfv[fltfc].filt_func = fmd_log_filter_nv;
1266                                 fltfv[fltfc].filt_arg = setupnamevalue(optarg);
1267                                 allfv[allfc++] = fltfv[fltfc++];
1268                                 break;
1269                         }
1270                         case 'v':
1271                                 opt_v++;
1272                                 break;
1273                         case 'V':
1274                                 opt_V++;
1275                                 break;
1276                         default:
1277                                 return (usage(stderr));
1278                         }
1279                 }
1280 
1281                 if (opt_A && (opt_e || opt_i || opt_I || opt_m || opt_u))
1282                         fmdump_usage("-A excludes all of "
1283                             "-e, -i, -I, -m and -u\n");
1284 
1285                 if (optind < argc) {
1286                         char *dest;
1287 
1288                         if (ifiles == NULL) {
1289                                 n_ifiles = argc - optind;
1290                                 ifiles = calloc(n_ifiles, sizeof (char *));
1291                                 if (ifiles == NULL) {
1292                                         fmdump_fatal(
1293                                             "failed to allocate memory for "
1294                                             "%d input file%s", n_ifiles,
1295                                             n_ifiles > 1 ? "s" : "");
1296                                 }
1297                         }
1298 
1299                         if (ifileidx > 0 && !opt_A)
1300                                 fmdump_usage("illegal argument -- %s\n",
1301                                     argv[optind]);
1302 
1303                         if ((dest = malloc(PATH_MAX)) == NULL)
1304                                 fmdump_fatal("failed to allocate memory");
1305 
1306                         (void) strlcpy(dest, argv[optind++], PATH_MAX);
1307                         ifiles[ifileidx++] = dest;
1308                 }
1309         }
1310 
1311         if (opt_A) {
1312                 int rc;
1313 
1314                 if (!opt_a) {
1315                         fltfv[fltfc].filt_func = log_filter_silent;
1316                         fltfv[fltfc].filt_arg = (void *)1;
1317                         allfv[allfc++] = fltfv[fltfc++];
1318                 }
1319 
1320                 rc = aggregate(ifiles, n_ifiles, opt_f,
1321                     allfv, allfc,
1322                     opt_v, opt_V, opt_p, opt_j);
1323 
1324                 cleanup(ifiles, n_ifiles);
1325                 return (rc);
1326         } else {
1327                 if (ifiles == NULL) {
1328                         if ((ifile = calloc(1, PATH_MAX)) == NULL)
1329                                 fmdump_fatal("failed to allocate memory");
1330                 } else {
1331                         ifile = ifiles[0];
1332                 }
1333         }
1334 
1335 
1336         if (*ifile == '\0') {
1337                 const char *pfx, *sfx;
1338 
1339                 if (opt_u || (!opt_e && !opt_i && !opt_I)) {
1340                         pfx = "flt";
1341                         sfx = "";
1342                 } else {
1343                         if (opt_e) {
1344                                 pfx = "err";
1345                                 sfx = "";
1346                         } else {
1347                                 pfx = "info";
1348                                 sfx = opt_I ? "_hival" : "";
1349                         }
1350                 }
1351 
1352                 (void) snprintf(ifile, PATH_MAX, "%s/var/fm/fmd/%slog%s",
1353                     g_root ? g_root : "", pfx, sfx);
1354                 /*
1355                  * logadm may rotate the logs.  When no input file is specified,
1356                  * we try to dump all the rotated logs as well in the right
1357                  * order.
1358                  */
1359                 if (!opt_H && off == 0)
1360                         rotated_logs = get_rotated_logs(ifile);
1361         } else if (g_root != NULL) {
1362                 fmdump_usage("-R option is not appropriate "
1363                     "when file operand is present\n");
1364         }
1365 
1366         if ((g_msg = fmd_msg_init(g_root, FMD_MSG_VERSION)) == NULL)
1367                 fmdump_fatal("failed to initialize libfmd_msg");
1368 
1369         if ((lp = fmd_log_open(FMD_LOG_VERSION, ifile, &err)) == NULL) {
1370                 fmdump_fatal("failed to open %s: %s\n", ifile,
1371                     fmd_log_errmsg(NULL, err));
1372         }
1373 
1374         if (opt_H) {
1375                 fmd_log_header(lp, &log);
1376 
1377                 (void) printf("EXD_CREATOR = %s\n", log.log_creator);
1378                 (void) printf("EXD_HOSTNAME = %s\n", log.log_hostname);
1379                 (void) printf("EXD_FMA_LABEL = %s\n", log.log_label);
1380                 (void) printf("EXD_FMA_VERSION = %s\n", log.log_version);
1381                 (void) printf("EXD_FMA_OSREL = %s\n", log.log_osrelease);
1382                 (void) printf("EXD_FMA_OSVER = %s\n", log.log_osversion);
1383                 (void) printf("EXD_FMA_PLAT = %s\n", log.log_platform);
1384                 (void) printf("EXD_FMA_UUID = %s\n", log.log_uuid);
1385 
1386                 return (FMDUMP_EXIT_SUCCESS);
1387         }
1388 
1389         if (off != 0 && fmd_log_seek(lp, off) != 0) {
1390                 fmdump_fatal("failed to seek %s: %s\n", ifile,
1391                     fmd_log_errmsg(lp, fmd_log_errno(lp)));
1392         }
1393 
1394         if (opt_e && opt_u)
1395                 ops = &fmdump_err_ops;
1396         else if (strcmp(fmd_log_label(lp), fmdump_flt_ops.do_label) == 0)
1397                 ops = &fmdump_flt_ops;
1398         else if (strcmp(fmd_log_label(lp), fmdump_asru_ops.do_label) == 0)
1399                 ops = &fmdump_asru_ops;
1400         else if (strcmp(fmd_log_label(lp), fmdump_info_ops.do_label) == 0)
1401                 ops = &fmdump_info_ops;
1402         else
1403                 ops = &fmdump_err_ops;
1404 
1405         if (!opt_a && ops == &fmdump_flt_ops) {
1406                 fltfv[fltfc].filt_func = log_filter_silent;
1407                 fltfv[fltfc].filt_arg = NULL;
1408                 allfv[allfc++] = fltfv[fltfc++];
1409         }
1410 
1411         if (opt_V) {
1412                 arg.da_fmt =
1413                     &ops->do_formats[opt_p ? FMDUMP_PRETTY :
1414                     opt_j ? FMDUMP_JSON : FMDUMP_VERB2];
1415                 iflags |= FMD_LOG_XITER_REFS;
1416         } else if (opt_v) {
1417                 arg.da_fmt = &ops->do_formats[FMDUMP_VERB1];
1418         } else if (opt_m) {
1419                 arg.da_fmt = &ops->do_formats[FMDUMP_MSG];
1420         } else
1421                 arg.da_fmt = &ops->do_formats[FMDUMP_SHORT];
1422 
1423         if (opt_m && arg.da_fmt->do_func == NULL) {
1424                 fmdump_usage("-m mode is not supported for "
1425                     "log of type %s: %s\n", fmd_log_label(lp), ifile);
1426         }
1427 
1428         arg.da_fv = errfv;
1429         arg.da_fc = errfc;
1430         arg.da_fp = stdout;
1431 
1432         if (iflags & FMD_LOG_XITER_OFFS)
1433                 fmdump_printf(arg.da_fp, "%16s ", "OFFSET");
1434 
1435         if (arg.da_fmt->do_hdr && !(opt_V && ops == &fmdump_flt_ops))
1436                 fmdump_printf(arg.da_fp, "%s\n", arg.da_fmt->do_hdr);
1437 
1438         if (opt_e && opt_u) {
1439                 iflags |= FMD_LOG_XITER_REFS;
1440                 func = xref_iter;
1441                 farg = &arg;
1442                 filtc = fltfc;
1443                 filtv = fltfv;
1444         } else {
1445                 func = arg.da_fmt->do_func;
1446                 farg = arg.da_fp;
1447                 filtc = allfc;
1448                 filtv = allfv;
1449         }
1450 
1451         if (iflags & FMD_LOG_XITER_OFFS) {
1452                 lyr.dy_func = func;
1453                 lyr.dy_arg = farg;
1454                 lyr.dy_fp = arg.da_fp;
1455                 func = xoff_iter;
1456                 farg = &lyr;
1457         }
1458 
1459         for (llp = rotated_logs; llp != NULL; llp = llp->next) {
1460                 fmd_log_t *rlp;
1461 
1462                 if ((rlp = fmd_log_open(FMD_LOG_VERSION, llp->path, &err))
1463                     == NULL) {
1464                         fmdump_warn("failed to open %s: %s\n",
1465                             llp->path, fmd_log_errmsg(NULL, err));
1466                         g_errs++;
1467                         continue;
1468                 }
1469 
1470                 recs = 0;
1471                 if (fmd_log_xiter(rlp, iflags, filtc, filtv,
1472                     func, error, farg, &recs) != 0) {
1473                         fmdump_warn("failed to dump %s: %s\n", llp->path,
1474                             fmd_log_errmsg(rlp, fmd_log_errno(rlp)));
1475                         g_errs++;
1476                 }
1477                 g_recs += recs;
1478 
1479                 fmd_log_close(rlp);
1480         }
1481 
1482         do {
1483                 recs = 0;
1484                 if (fmd_log_xiter(lp, iflags, filtc, filtv,
1485                     func, error, farg, &recs) != 0) {
1486                         fmdump_warn("failed to dump %s: %s\n", ifile,
1487                             fmd_log_errmsg(lp, fmd_log_errno(lp)));
1488                         g_errs++;
1489                 }
1490                 g_recs += recs;
1491 
1492                 if (opt_f)
1493                         (void) sleep(1);
1494 
1495         } while (opt_f);
1496 
1497         if (!opt_f && g_recs == 0 && isatty(STDOUT_FILENO))
1498                 fmdump_warn("%s is empty\n", ifile);
1499 
1500         if (g_thp != NULL)
1501                 topo_close(g_thp);
1502 
1503         fmd_log_close(lp);
1504         fmd_msg_fini(g_msg);
1505 
1506         if (ifiles == NULL)
1507                 free(ifile);
1508         else
1509                 cleanup(ifiles, n_ifiles);
1510 
1511         return (g_errs ? FMDUMP_EXIT_ERROR : FMDUMP_EXIT_SUCCESS);
1512 }