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