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 }