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