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