1 0 stevel /* 2 0 stevel * CDDL HEADER START 3 0 stevel * 4 0 stevel * The contents of this file are subject to the terms of the 5 5609 cy152378 * Common Development and Distribution License (the "License"). 6 5609 cy152378 * You may not use this file except in compliance with the License. 7 0 stevel * 8 0 stevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 0 stevel * or http://www.opensolaris.org/os/licensing. 10 0 stevel * See the License for the specific language governing permissions 11 0 stevel * and limitations under the License. 12 0 stevel * 13 0 stevel * When distributing Covered Code, include this CDDL HEADER in each 14 0 stevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 0 stevel * If applicable, add the following below this CDDL HEADER, with the 16 0 stevel * fields enclosed by brackets "[]" replaced with your own identifying 17 0 stevel * information: Portions Copyright [yyyy] [name of copyright owner] 18 0 stevel * 19 0 stevel * CDDL HEADER END 20 0 stevel */ 21 0 stevel /* 22 8740 Sean * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 0 stevel * Use is subject to license terms. 24 0 stevel */ 25 0 stevel 26 0 stevel #include <alloca.h> 27 0 stevel #include <unistd.h> 28 0 stevel #include <limits.h> 29 0 stevel #include <strings.h> 30 0 stevel #include <stdlib.h> 31 0 stevel #include <stdarg.h> 32 0 stevel #include <stdio.h> 33 0 stevel #include <errno.h> 34 0 stevel #include <time.h> 35 0 stevel #include <ctype.h> 36 6640 cth #include <regex.h> 37 8740 Sean #include <dirent.h> 38 0 stevel 39 0 stevel #include <fmdump.h> 40 0 stevel 41 0 stevel #define FMDUMP_EXIT_SUCCESS 0 42 0 stevel #define FMDUMP_EXIT_FATAL 1 43 0 stevel #define FMDUMP_EXIT_USAGE 2 44 0 stevel #define FMDUMP_EXIT_ERROR 3 45 0 stevel 46 0 stevel const char *g_pname; 47 0 stevel ulong_t g_errs; 48 0 stevel ulong_t g_recs; 49 0 stevel char *g_root; 50 9501 Robert 51 1414 cindi struct topo_hdl *g_thp; 52 9501 Robert fmd_msg_hdl_t *g_msg; 53 0 stevel 54 0 stevel /*PRINTFLIKE2*/ 55 0 stevel void 56 0 stevel fmdump_printf(FILE *fp, const char *format, ...) 57 0 stevel { 58 0 stevel va_list ap; 59 0 stevel 60 0 stevel va_start(ap, format); 61 0 stevel 62 0 stevel if (vfprintf(fp, format, ap) < 0) { 63 0 stevel (void) fprintf(stderr, "%s: failed to print record: %s\n", 64 0 stevel g_pname, strerror(errno)); 65 0 stevel g_errs++; 66 0 stevel } 67 0 stevel 68 0 stevel va_end(ap); 69 0 stevel } 70 0 stevel 71 0 stevel void 72 0 stevel fmdump_vwarn(const char *format, va_list ap) 73 0 stevel { 74 0 stevel int err = errno; 75 0 stevel 76 0 stevel (void) fprintf(stderr, "%s: warning: ", g_pname); 77 0 stevel (void) vfprintf(stderr, format, ap); 78 0 stevel 79 0 stevel if (strchr(format, '\n') == NULL) 80 0 stevel (void) fprintf(stderr, ": %s\n", strerror(err)); 81 0 stevel 82 0 stevel g_errs++; 83 0 stevel } 84 0 stevel 85 0 stevel /*PRINTFLIKE1*/ 86 0 stevel void 87 0 stevel fmdump_warn(const char *format, ...) 88 0 stevel { 89 0 stevel va_list ap; 90 0 stevel 91 0 stevel va_start(ap, format); 92 0 stevel fmdump_vwarn(format, ap); 93 0 stevel va_end(ap); 94 0 stevel } 95 0 stevel 96 0 stevel char * 97 0 stevel fmdump_date(char *buf, size_t len, const fmd_log_record_t *rp) 98 0 stevel { 99 0 stevel if (rp->rec_sec > LONG_MAX) { 100 0 stevel fmdump_warn("record time is too large for 32-bit utility\n"); 101 0 stevel (void) snprintf(buf, len, "0x%llx", rp->rec_sec); 102 0 stevel } else { 103 0 stevel time_t tod = (time_t)rp->rec_sec; 104 5609 cy152378 time_t now = time(NULL); 105 5609 cy152378 if (tod > now+60 || 106 5609 cy152378 tod < now - 6L*30L*24L*60L*60L) { /* 6 months ago */ 107 5609 cy152378 (void) strftime(buf, len, "%b %d %Y %T", 108 5609 cy152378 localtime(&tod)); 109 5609 cy152378 } else { 110 5609 cy152378 size_t sz; 111 5609 cy152378 sz = strftime(buf, len, "%b %d %T", localtime(&tod)); 112 5609 cy152378 (void) snprintf(buf + sz, len - sz, ".%4.4llu", 113 5609 cy152378 rp->rec_nsec / (NANOSEC / 10000)); 114 5609 cy152378 } 115 0 stevel } 116 0 stevel 117 0 stevel return (buf); 118 0 stevel } 119 0 stevel 120 0 stevel char * 121 0 stevel fmdump_year(char *buf, size_t len, const fmd_log_record_t *rp) 122 0 stevel { 123 0 stevel #ifdef _ILP32 124 0 stevel if (rp->rec_sec > LONG_MAX) { 125 0 stevel fmdump_warn("record time is too large for 32-bit utility\n"); 126 0 stevel (void) snprintf(buf, len, "0x%llx", rp->rec_sec); 127 0 stevel } else { 128 0 stevel #endif 129 0 stevel time_t tod = (time_t)rp->rec_sec; 130 0 stevel (void) strftime(buf, len, "%b %d %Y %T", localtime(&tod)); 131 0 stevel #ifdef _ILP32 132 0 stevel } 133 0 stevel #endif 134 0 stevel return (buf); 135 0 stevel } 136 0 stevel 137 0 stevel static int 138 0 stevel usage(FILE *fp) 139 0 stevel { 140 9501 Robert (void) fprintf(fp, "Usage: %s [-efmvV] [-c class] [-R root] [-t time] " 141 6640 cth "[-T time] [-u uuid]\n\t\t[-n name[.name]*[=value]] [file]\n", 142 6640 cth g_pname); 143 0 stevel 144 0 stevel (void) fprintf(fp, 145 0 stevel "\t-c select events that match the specified class\n" 146 0 stevel "\t-e display error log content instead of fault log content\n" 147 0 stevel "\t-f follow growth of log file by waiting for additional data\n" 148 9501 Robert "\t-m display human-readable messages for the fault log\n" 149 0 stevel "\t-R set root directory for pathname expansions\n" 150 0 stevel "\t-t select events that occurred after the specified time\n" 151 0 stevel "\t-T select events that occurred before the specified time\n" 152 0 stevel "\t-u select events that match the specified uuid\n" 153 6640 cth "\t-n select events containing named nvpair " 154 6640 cth "(with matching value)\n" 155 0 stevel "\t-v set verbose mode: display additional event detail\n" 156 0 stevel "\t-V set very verbose mode: display complete event contents\n"); 157 0 stevel 158 0 stevel return (FMDUMP_EXIT_USAGE); 159 0 stevel } 160 0 stevel 161 0 stevel /*ARGSUSED*/ 162 0 stevel static int 163 0 stevel error(fmd_log_t *lp, void *private) 164 0 stevel { 165 0 stevel fmdump_warn("skipping record: %s\n", 166 0 stevel fmd_log_errmsg(lp, fmd_log_errno(lp))); 167 0 stevel return (0); 168 0 stevel } 169 0 stevel 170 0 stevel /* 171 0 stevel * Yet another disgusting argument parsing function (TM). We attempt to parse 172 0 stevel * a time argument in a variety of strptime(3C) formats, in which case it is 173 0 stevel * interpreted as a local time and is converted to a timeval using mktime(3C). 174 0 stevel * If those formats fail, we look to see if the time is a decimal integer 175 0 stevel * followed by one of our magic suffixes, in which case the time is interpreted 176 0 stevel * as a time delta *before* the current time-of-day (i.e. "1h" = "1 hour ago"). 177 0 stevel */ 178 0 stevel static struct timeval * 179 0 stevel gettimeopt(const char *arg) 180 0 stevel { 181 0 stevel const struct { 182 0 stevel const char *name; 183 0 stevel hrtime_t mul; 184 0 stevel } suffix[] = { 185 6640 cth { "ns", NANOSEC / NANOSEC }, 186 0 stevel { "nsec", NANOSEC / NANOSEC }, 187 0 stevel { "us", NANOSEC / MICROSEC }, 188 0 stevel { "usec", NANOSEC / MICROSEC }, 189 0 stevel { "ms", NANOSEC / MILLISEC }, 190 0 stevel { "msec", NANOSEC / MILLISEC }, 191 0 stevel { "s", NANOSEC / SEC }, 192 0 stevel { "sec", NANOSEC / SEC }, 193 0 stevel { "m", NANOSEC * (hrtime_t)60 }, 194 0 stevel { "min", NANOSEC * (hrtime_t)60 }, 195 0 stevel { "h", NANOSEC * (hrtime_t)(60 * 60) }, 196 0 stevel { "hour", NANOSEC * (hrtime_t)(60 * 60) }, 197 0 stevel { "d", NANOSEC * (hrtime_t)(24 * 60 * 60) }, 198 0 stevel { "day", NANOSEC * (hrtime_t)(24 * 60 * 60) }, 199 0 stevel { NULL } 200 0 stevel }; 201 0 stevel 202 0 stevel struct timeval *tvp = malloc(sizeof (struct timeval)); 203 0 stevel struct timeval tod; 204 0 stevel struct tm tm; 205 0 stevel char *p; 206 0 stevel 207 0 stevel if (tvp == NULL) { 208 0 stevel (void) fprintf(stderr, "%s: failed to allocate memory: %s\n", 209 0 stevel g_pname, strerror(errno)); 210 0 stevel exit(FMDUMP_EXIT_FATAL); 211 0 stevel } 212 0 stevel 213 0 stevel if (gettimeofday(&tod, NULL) != 0) { 214 0 stevel (void) fprintf(stderr, "%s: failed to get tod: %s\n", 215 0 stevel g_pname, strerror(errno)); 216 0 stevel exit(FMDUMP_EXIT_FATAL); 217 0 stevel } 218 0 stevel 219 0 stevel /* 220 0 stevel * First try a variety of strptime() calls. If these all fail, we'll 221 0 stevel * try parsing an integer followed by one of our suffix[] strings. 222 0 stevel * NOTE: any form using %y must appear *before* the equivalent %Y form; 223 0 stevel * otherwise %Y will accept the two year digits but infer century zero. 224 0 stevel * Any form ending in %y must additionally check isdigit(*p) to ensure 225 0 stevel * that it does not inadvertently match 2 digits of a 4-digit year. 226 0 stevel * 227 0 stevel * Beware: Any strptime() sequence containing consecutive %x sequences 228 0 stevel * may fall victim to SCCS expanding it as a keyword! If this happens 229 0 stevel * we use separate string constant that ANSI C will concatenate. 230 0 stevel */ 231 0 stevel if ((p = strptime(arg, "%m/%d/%y" "%t" "%H:%M:%S", &tm)) == NULL && 232 0 stevel (p = strptime(arg, "%m/%d/%Y" "%t" "%H:%M:%S", &tm)) == NULL && 233 0 stevel (p = strptime(arg, "%m/%d/%y" "%t" "%H:%M", &tm)) == NULL && 234 0 stevel (p = strptime(arg, "%m/%d/%Y" "%t" "%H:%M", &tm)) == NULL && 235 0 stevel ((p = strptime(arg, "%m/%d/%y", &tm)) == NULL || isdigit(*p)) && 236 0 stevel (p = strptime(arg, "%m/%d/%Y", &tm)) == NULL && 237 0 stevel (p = strptime(arg, "%y-%m-%dT%H:%M:%S", &tm)) == NULL && 238 0 stevel (p = strptime(arg, "%Y-%m-%dT%H:%M:%S", &tm)) == NULL && 239 0 stevel (p = strptime(arg, "%y-%m-%dT%H:%M", &tm)) == NULL && 240 0 stevel (p = strptime(arg, "%Y-%m-%dT%H:%M", &tm)) == NULL && 241 0 stevel (p = strptime(arg, "%y-%m-%d", &tm)) == NULL && 242 0 stevel (p = strptime(arg, "%Y-%m-%d", &tm)) == NULL && 243 0 stevel (p = strptime(arg, "%d%b%y" "%t" "%H:%M:%S", &tm)) == NULL && 244 0 stevel (p = strptime(arg, "%d%b%Y" "%t" "%H:%M:%S", &tm)) == NULL && 245 0 stevel (p = strptime(arg, "%d%b%y" "%t" "%H:%M", &tm)) == NULL && 246 0 stevel (p = strptime(arg, "%d%b%Y" "%t" "%H:%M", &tm)) == NULL && 247 0 stevel ((p = strptime(arg, "%d%b%y", &tm)) == NULL || isdigit(*p)) && 248 0 stevel (p = strptime(arg, "%d%b%Y", &tm)) == NULL && 249 0 stevel (p = strptime(arg, "%b%t%d" "%t" "%H:%M:%S", &tm)) == NULL && 250 0 stevel (p = strptime(arg, "%b%t%d" "%t" "%H:%M:%S", &tm)) == NULL && 251 0 stevel (p = strptime(arg, "%H:%M:%S", &tm)) == NULL && 252 0 stevel (p = strptime(arg, "%H:%M", &tm)) == NULL) { 253 0 stevel 254 0 stevel hrtime_t nsec; 255 0 stevel int i; 256 0 stevel 257 0 stevel errno = 0; 258 0 stevel nsec = strtol(arg, (char **)&p, 10); 259 0 stevel 260 0 stevel if (errno != 0 || nsec == 0 || p == arg || *p == '\0') { 261 0 stevel (void) fprintf(stderr, "%s: illegal time " 262 0 stevel "format -- %s\n", g_pname, arg); 263 0 stevel exit(FMDUMP_EXIT_USAGE); 264 0 stevel } 265 0 stevel 266 0 stevel for (i = 0; suffix[i].name != NULL; i++) { 267 0 stevel if (strcasecmp(suffix[i].name, p) == 0) { 268 0 stevel nsec *= suffix[i].mul; 269 0 stevel break; 270 0 stevel } 271 0 stevel } 272 0 stevel 273 0 stevel if (suffix[i].name == NULL) { 274 0 stevel (void) fprintf(stderr, "%s: illegal time " 275 0 stevel "format -- %s\n", g_pname, arg); 276 0 stevel exit(FMDUMP_EXIT_USAGE); 277 0 stevel } 278 0 stevel 279 0 stevel tvp->tv_sec = nsec / NANOSEC; 280 0 stevel tvp->tv_usec = (nsec % NANOSEC) / (NANOSEC / MICROSEC); 281 0 stevel 282 0 stevel if (tvp->tv_sec > tod.tv_sec) { 283 0 stevel (void) fprintf(stderr, "%s: time delta precedes " 284 0 stevel "UTC time origin -- %s\n", g_pname, arg); 285 0 stevel exit(FMDUMP_EXIT_USAGE); 286 0 stevel } 287 0 stevel 288 0 stevel tvp->tv_sec = tod.tv_sec - tvp->tv_sec; 289 0 stevel 290 0 stevel } else if (*p == '\0' || *p == '.') { 291 0 stevel /* 292 0 stevel * If tm_year is zero, we matched [%b %d] %H:%M[:%S]; use 293 0 stevel * the result of localtime(&tod.tv_sec) to fill in the rest. 294 0 stevel */ 295 0 stevel if (tm.tm_year == 0) { 296 0 stevel int h = tm.tm_hour; 297 0 stevel int m = tm.tm_min; 298 0 stevel int s = tm.tm_sec; 299 0 stevel int b = tm.tm_mon; 300 0 stevel int d = tm.tm_mday; 301 0 stevel 302 0 stevel bcopy(localtime(&tod.tv_sec), &tm, sizeof (tm)); 303 0 stevel tm.tm_isdst = 0; /* see strptime(3C) and below */ 304 0 stevel 305 0 stevel if (d > 0) { 306 0 stevel tm.tm_mon = b; 307 0 stevel tm.tm_mday = d; 308 0 stevel } 309 0 stevel 310 0 stevel tm.tm_hour = h; 311 0 stevel tm.tm_min = m; 312 0 stevel tm.tm_sec = s; 313 0 stevel } 314 0 stevel 315 0 stevel errno = 0; 316 0 stevel tvp->tv_sec = mktime(&tm); 317 0 stevel tvp->tv_usec = 0; 318 0 stevel 319 0 stevel if (tvp->tv_sec == -1L && errno != 0) { 320 0 stevel (void) fprintf(stderr, "%s: failed to compose " 321 0 stevel "time %s: %s\n", g_pname, arg, strerror(errno)); 322 0 stevel exit(FMDUMP_EXIT_ERROR); 323 0 stevel } 324 0 stevel 325 0 stevel /* 326 0 stevel * If our mktime() set tm_isdst, adjust the result for DST by 327 0 stevel * subtracting the offset between the main and alternate zones. 328 0 stevel */ 329 0 stevel if (tm.tm_isdst) 330 0 stevel tvp->tv_sec -= timezone - altzone; 331 0 stevel 332 0 stevel if (p[0] == '.') { 333 0 stevel arg = p; 334 0 stevel errno = 0; 335 0 stevel tvp->tv_usec = 336 0 stevel (suseconds_t)(strtod(arg, &p) * (double)MICROSEC); 337 0 stevel 338 0 stevel if (errno != 0 || p == arg || *p != '\0') { 339 0 stevel (void) fprintf(stderr, "%s: illegal time " 340 0 stevel "suffix -- .%s\n", g_pname, arg); 341 0 stevel exit(FMDUMP_EXIT_USAGE); 342 0 stevel } 343 0 stevel } 344 0 stevel 345 0 stevel } else { 346 0 stevel (void) fprintf(stderr, "%s: unexpected suffix after " 347 0 stevel "time %s -- %s\n", g_pname, arg, p); 348 0 stevel exit(FMDUMP_EXIT_USAGE); 349 0 stevel } 350 0 stevel 351 0 stevel return (tvp); 352 0 stevel } 353 0 stevel 354 0 stevel /* 355 0 stevel * If the -u option is specified in combination with the -e option, we iterate 356 0 stevel * over each record in the fault log with a matching UUID finding xrefs to the 357 0 stevel * error log, and then use this function to iterate over every xref'd record. 358 0 stevel */ 359 0 stevel int 360 0 stevel xref_iter(fmd_log_t *lp, const fmd_log_record_t *rp, void *arg) 361 0 stevel { 362 0 stevel const fmd_log_record_t *xrp = rp->rec_xrefs; 363 0 stevel fmdump_arg_t *dap = arg; 364 0 stevel int i, rv = 0; 365 0 stevel 366 0 stevel for (i = 0; rv == 0 && i < rp->rec_nrefs; i++, xrp++) { 367 0 stevel if (fmd_log_filter(lp, dap->da_fc, dap->da_fv, xrp)) 368 0 stevel rv = dap->da_fmt->do_func(lp, xrp, dap->da_fp); 369 0 stevel } 370 0 stevel 371 0 stevel return (rv); 372 0 stevel } 373 0 stevel 374 0 stevel int 375 0 stevel xoff_iter(fmd_log_t *lp, const fmd_log_record_t *rp, void *arg) 376 0 stevel { 377 0 stevel fmdump_lyr_t *dyp = arg; 378 0 stevel 379 0 stevel fmdump_printf(dyp->dy_fp, "%16llx ", (u_longlong_t)rp->rec_off); 380 0 stevel return (dyp->dy_func(lp, rp, dyp->dy_arg)); 381 0 stevel } 382 0 stevel 383 0 stevel /* 384 6640 cth * Initialize fmd_log_filter_nvarg_t from -n name=value argument string. 385 6640 cth */ 386 6640 cth static fmd_log_filter_nvarg_t * 387 6640 cth setupnamevalue(char *namevalue) 388 6640 cth { 389 6640 cth fmd_log_filter_nvarg_t *argt; 390 6640 cth char *value; 391 6640 cth regex_t *value_regex = NULL; 392 6640 cth char errstr[128]; 393 6640 cth int rv; 394 6640 cth 395 6640 cth if ((value = strchr(namevalue, '=')) == NULL) { 396 6640 cth value_regex = NULL; 397 6640 cth } else { 398 6640 cth *value++ = '\0'; /* separate name and value string */ 399 6640 cth 400 6640 cth /* 401 6640 cth * Skip white space before value to facilitate direct 402 6640 cth * cut/paste from previous fmdump output. 403 6640 cth */ 404 6640 cth while (isspace(*value)) 405 6640 cth value++; 406 6640 cth 407 6640 cth if ((value_regex = malloc(sizeof (regex_t))) == NULL) { 408 6640 cth (void) fprintf(stderr, "%s: failed to allocate memory: " 409 6640 cth "%s\n", g_pname, strerror(errno)); 410 6640 cth exit(FMDUMP_EXIT_FATAL); 411 6640 cth } 412 6640 cth 413 6640 cth /* compile regular expression for possible string match */ 414 6640 cth if ((rv = regcomp(value_regex, value, 415 6640 cth REG_NOSUB|REG_NEWLINE)) != 0) { 416 6640 cth (void) regerror(rv, value_regex, errstr, 417 6640 cth sizeof (errstr)); 418 6640 cth (void) fprintf(stderr, "unexpected regular expression " 419 6640 cth "in %s: %s\n", value, errstr); 420 6640 cth free(value_regex); 421 6640 cth exit(FMDUMP_EXIT_USAGE); 422 6640 cth } 423 6640 cth } 424 6640 cth 425 6640 cth if ((argt = malloc(sizeof (fmd_log_filter_nvarg_t))) == NULL) { 426 6640 cth (void) fprintf(stderr, "%s: failed to allocate memory: %s\n", 427 6640 cth g_pname, strerror(errno)); 428 6640 cth exit(FMDUMP_EXIT_FATAL); 429 6640 cth } 430 6640 cth argt->nvarg_name = namevalue; /* now just name */ 431 6640 cth argt->nvarg_value = value; 432 6640 cth argt->nvarg_value_regex = value_regex; 433 6640 cth return (argt); 434 6640 cth } 435 6640 cth 436 6640 cth /* 437 0 stevel * If the -a option is not present, filter out fault records that correspond 438 0 stevel * to events that the producer requested not be messaged for administrators. 439 0 stevel */ 440 0 stevel /*ARGSUSED*/ 441 0 stevel int 442 0 stevel log_filter_silent(fmd_log_t *lp, const fmd_log_record_t *rp, void *arg) 443 0 stevel { 444 0 stevel boolean_t msg; 445 0 stevel 446 0 stevel return (nvlist_lookup_boolean_value(rp->rec_nvl, 447 0 stevel FM_SUSPECT_MESSAGE, &msg) != 0 || msg != 0); 448 0 stevel } 449 0 stevel 450 8740 Sean struct loglink { 451 8740 Sean char *path; 452 8740 Sean long suffix; 453 8740 Sean struct loglink *next; 454 8740 Sean }; 455 8740 Sean 456 8740 Sean static void 457 8740 Sean addlink(struct loglink **llp, char *dirname, char *logname, long suffix) 458 8740 Sean { 459 8740 Sean struct loglink *newp; 460 8740 Sean size_t len; 461 8740 Sean char *str; 462 8740 Sean 463 8740 Sean newp = malloc(sizeof (struct loglink)); 464 8740 Sean len = strlen(dirname) + strlen(logname) + 2; 465 8740 Sean str = malloc(len); 466 8740 Sean if (newp == NULL || str == NULL) { 467 8740 Sean (void) fprintf(stderr, "%s: failed to allocate memory: %s\n", 468 8740 Sean g_pname, strerror(errno)); 469 8740 Sean exit(FMDUMP_EXIT_FATAL); 470 8740 Sean } 471 8740 Sean 472 8740 Sean (void) snprintf(str, len, "%s/%s", dirname, logname); 473 8740 Sean newp->path = str; 474 8740 Sean newp->suffix = suffix; 475 8740 Sean 476 8740 Sean while (*llp != NULL && suffix < (*llp)->suffix) 477 8740 Sean llp = &(*llp)->next; 478 8740 Sean 479 8740 Sean newp->next = *llp; 480 8740 Sean *llp = newp; 481 8740 Sean } 482 8740 Sean 483 8740 Sean /* 484 8740 Sean * Find and return all the rotated logs. 485 8740 Sean */ 486 8740 Sean static struct loglink * 487 8740 Sean get_rotated_logs(char *logpath) 488 8740 Sean { 489 8740 Sean char dirname[PATH_MAX], *logname, *endptr; 490 8740 Sean DIR *dirp; 491 8740 Sean struct dirent *dp; 492 8740 Sean long len, suffix; 493 8740 Sean struct loglink *head = NULL; 494 8740 Sean 495 8740 Sean (void) strlcpy(dirname, logpath, sizeof (dirname)); 496 8740 Sean logname = strrchr(dirname, '/'); 497 8740 Sean *logname++ = '\0'; 498 8740 Sean len = strlen(logname); 499 8740 Sean 500 8740 Sean if ((dirp = opendir(dirname)) == NULL) { 501 8740 Sean (void) fprintf(stderr, "%s: failed to opendir `%s': %s\n", 502 8740 Sean g_pname, dirname, strerror(errno)); 503 8740 Sean return (NULL); 504 8740 Sean } 505 8740 Sean 506 8740 Sean while ((dp = readdir(dirp)) != NULL) { 507 8740 Sean /* 508 8740 Sean * Search the log directory for logs named "<logname>.0", 509 8740 Sean * "<logname>.1", etc and add to the link in the 510 8740 Sean * reverse numeric order. 511 8740 Sean */ 512 8740 Sean if (strlen(dp->d_name) < len + 2 || 513 8740 Sean strncmp(dp->d_name, logname, len) != 0 || 514 8740 Sean dp->d_name[len] != '.') 515 8740 Sean continue; 516 8740 Sean 517 8740 Sean /* 518 8740 Sean * "*.0-" file normally should not be seen. It may 519 8740 Sean * exist when user manually run 'fmadm rotate'. 520 8740 Sean * In such case, we put it at the end of the list so 521 8740 Sean * it'll be dumped after all the rotated logs, before 522 8740 Sean * the current one. 523 8740 Sean */ 524 8740 Sean if (strcmp(dp->d_name + len + 1, "0-") == 0) 525 8740 Sean addlink(&head, dirname, dp->d_name, -1); 526 8740 Sean else if ((suffix = strtol(dp->d_name + len + 1, 527 8740 Sean &endptr, 10)) >= 0 && *endptr == '\0') 528 8740 Sean addlink(&head, dirname, dp->d_name, suffix); 529 8740 Sean } 530 8740 Sean 531 8740 Sean (void) closedir(dirp); 532 8740 Sean 533 8740 Sean return (head); 534 8740 Sean } 535 8740 Sean 536 0 stevel int 537 0 stevel main(int argc, char *argv[]) 538 0 stevel { 539 9501 Robert int opt_a = 0, opt_e = 0, opt_f = 0, opt_H = 0, opt_m = 0; 540 0 stevel int opt_u = 0, opt_v = 0, opt_V = 0; 541 0 stevel 542 0 stevel char ifile[PATH_MAX] = ""; 543 0 stevel int iflags = 0; 544 0 stevel 545 0 stevel fmdump_arg_t arg; 546 0 stevel fmdump_lyr_t lyr; 547 0 stevel const fmdump_ops_t *ops; 548 0 stevel fmd_log_filter_t *filtv; 549 0 stevel uint_t filtc; 550 0 stevel 551 0 stevel fmd_log_filter_t *errfv, *fltfv, *allfv; 552 0 stevel uint_t errfc = 0, fltfc = 0, allfc = 0; 553 0 stevel 554 0 stevel fmd_log_header_t log; 555 0 stevel fmd_log_rec_f *func; 556 0 stevel void *farg; 557 0 stevel fmd_log_t *lp; 558 0 stevel int c, err; 559 0 stevel off64_t off = 0; 560 8740 Sean ulong_t recs; 561 8740 Sean struct loglink *rotated_logs = NULL, *llp; 562 0 stevel 563 0 stevel g_pname = argv[0]; 564 0 stevel 565 0 stevel errfv = alloca(sizeof (fmd_log_filter_t) * argc); 566 0 stevel fltfv = alloca(sizeof (fmd_log_filter_t) * argc); 567 0 stevel allfv = alloca(sizeof (fmd_log_filter_t) * argc); 568 0 stevel 569 0 stevel while (optind < argc) { 570 6640 cth while ((c = 571 9501 Robert getopt(argc, argv, "ac:efHmn:O:R:t:T:u:vV")) != EOF) { 572 0 stevel switch (c) { 573 0 stevel case 'a': 574 0 stevel opt_a++; 575 0 stevel break; 576 0 stevel case 'c': 577 0 stevel errfv[errfc].filt_func = fmd_log_filter_class; 578 0 stevel errfv[errfc].filt_arg = optarg; 579 0 stevel allfv[allfc++] = errfv[errfc++]; 580 0 stevel break; 581 0 stevel case 'e': 582 0 stevel opt_e++; 583 0 stevel break; 584 0 stevel case 'f': 585 0 stevel opt_f++; 586 0 stevel break; 587 0 stevel case 'H': 588 0 stevel opt_H++; 589 9501 Robert break; 590 9501 Robert case 'm': 591 9501 Robert opt_m++; 592 0 stevel break; 593 0 stevel case 'O': 594 0 stevel off = strtoull(optarg, NULL, 16); 595 0 stevel iflags |= FMD_LOG_XITER_OFFS; 596 0 stevel break; 597 0 stevel case 'R': 598 0 stevel g_root = optarg; 599 0 stevel break; 600 0 stevel case 't': 601 0 stevel errfv[errfc].filt_func = fmd_log_filter_after; 602 0 stevel errfv[errfc].filt_arg = gettimeopt(optarg); 603 0 stevel allfv[allfc++] = errfv[errfc++]; 604 0 stevel break; 605 0 stevel case 'T': 606 0 stevel errfv[errfc].filt_func = fmd_log_filter_before; 607 0 stevel errfv[errfc].filt_arg = gettimeopt(optarg); 608 0 stevel allfv[allfc++] = errfv[errfc++]; 609 0 stevel break; 610 0 stevel case 'u': 611 0 stevel fltfv[fltfc].filt_func = fmd_log_filter_uuid; 612 0 stevel fltfv[fltfc].filt_arg = optarg; 613 0 stevel allfv[allfc++] = fltfv[fltfc++]; 614 0 stevel opt_u++; 615 0 stevel opt_a++; /* -u implies -a */ 616 0 stevel break; 617 6640 cth case 'n': { 618 6640 cth fltfv[fltfc].filt_func = fmd_log_filter_nv; 619 6640 cth fltfv[fltfc].filt_arg = setupnamevalue(optarg); 620 6640 cth allfv[allfc++] = fltfv[fltfc++]; 621 6640 cth break; 622 6640 cth } 623 0 stevel case 'v': 624 0 stevel opt_v++; 625 0 stevel break; 626 0 stevel case 'V': 627 0 stevel opt_V++; 628 0 stevel break; 629 0 stevel default: 630 0 stevel return (usage(stderr)); 631 0 stevel } 632 0 stevel } 633 0 stevel 634 0 stevel if (optind < argc) { 635 0 stevel if (*ifile != '\0') { 636 0 stevel (void) fprintf(stderr, "%s: illegal " 637 0 stevel "argument -- %s\n", g_pname, argv[optind]); 638 0 stevel return (FMDUMP_EXIT_USAGE); 639 0 stevel } else { 640 0 stevel (void) strlcpy(ifile, 641 0 stevel argv[optind++], sizeof (ifile)); 642 0 stevel } 643 0 stevel } 644 0 stevel } 645 0 stevel 646 0 stevel if (*ifile == '\0') { 647 0 stevel (void) snprintf(ifile, sizeof (ifile), "%s/var/fm/fmd/%slog", 648 0 stevel g_root ? g_root : "", opt_e && !opt_u ? "err" : "flt"); 649 8740 Sean /* 650 8740 Sean * logadm may rotate the logs. When no input file is specified, 651 8740 Sean * we try to dump all the rotated logs as well in the right 652 8740 Sean * order. 653 8740 Sean */ 654 8740 Sean if (!opt_H && off == 0) 655 8740 Sean rotated_logs = get_rotated_logs(ifile); 656 0 stevel } else if (g_root != NULL) { 657 0 stevel (void) fprintf(stderr, "%s: -R option is not appropriate " 658 0 stevel "when file operand is present\n", g_pname); 659 0 stevel return (FMDUMP_EXIT_USAGE); 660 0 stevel } 661 0 stevel 662 9501 Robert if ((g_msg = fmd_msg_init(g_root, FMD_MSG_VERSION)) == NULL) { 663 9501 Robert (void) fprintf(stderr, "%s: failed to initialize " 664 9501 Robert "libfmd_msg: %s\n", g_pname, strerror(errno)); 665 9501 Robert return (FMDUMP_EXIT_FATAL); 666 9501 Robert } 667 9501 Robert 668 0 stevel if ((lp = fmd_log_open(FMD_LOG_VERSION, ifile, &err)) == NULL) { 669 0 stevel (void) fprintf(stderr, "%s: failed to open %s: %s\n", 670 0 stevel g_pname, ifile, fmd_log_errmsg(NULL, err)); 671 0 stevel return (FMDUMP_EXIT_FATAL); 672 0 stevel } 673 0 stevel 674 0 stevel if (opt_H) { 675 0 stevel fmd_log_header(lp, &log); 676 0 stevel 677 0 stevel (void) printf("EXD_CREATOR = %s\n", log.log_creator); 678 0 stevel (void) printf("EXD_HOSTNAME = %s\n", log.log_hostname); 679 0 stevel (void) printf("EXD_FMA_LABEL = %s\n", log.log_label); 680 0 stevel (void) printf("EXD_FMA_VERSION = %s\n", log.log_version); 681 0 stevel (void) printf("EXD_FMA_OSREL = %s\n", log.log_osrelease); 682 0 stevel (void) printf("EXD_FMA_OSVER = %s\n", log.log_osversion); 683 0 stevel (void) printf("EXD_FMA_PLAT = %s\n", log.log_platform); 684 1052 dilpreet (void) printf("EXD_FMA_UUID = %s\n", log.log_uuid); 685 0 stevel 686 0 stevel return (FMDUMP_EXIT_SUCCESS); 687 0 stevel } 688 0 stevel 689 0 stevel if (off != 0 && fmd_log_seek(lp, off) != 0) { 690 0 stevel (void) fprintf(stderr, "%s: failed to seek %s: %s\n", 691 0 stevel g_pname, ifile, fmd_log_errmsg(lp, fmd_log_errno(lp))); 692 0 stevel return (FMDUMP_EXIT_FATAL); 693 0 stevel } 694 0 stevel 695 0 stevel if (opt_e && opt_u) 696 0 stevel ops = &fmdump_err_ops; 697 0 stevel else if (strcmp(fmd_log_label(lp), fmdump_flt_ops.do_label) == 0) 698 0 stevel ops = &fmdump_flt_ops; 699 0 stevel else if (strcmp(fmd_log_label(lp), fmdump_asru_ops.do_label) == 0) 700 0 stevel ops = &fmdump_asru_ops; 701 0 stevel else 702 0 stevel ops = &fmdump_err_ops; 703 0 stevel 704 0 stevel if (!opt_a && ops == &fmdump_flt_ops) { 705 0 stevel fltfv[fltfc].filt_func = log_filter_silent; 706 0 stevel fltfv[fltfc].filt_arg = NULL; 707 0 stevel allfv[allfc++] = fltfv[fltfc++]; 708 0 stevel } 709 0 stevel 710 0 stevel if (opt_V) { 711 0 stevel arg.da_fmt = &ops->do_formats[FMDUMP_VERB2]; 712 0 stevel iflags |= FMD_LOG_XITER_REFS; 713 0 stevel } else if (opt_v) { 714 0 stevel arg.da_fmt = &ops->do_formats[FMDUMP_VERB1]; 715 9501 Robert } else if (opt_m) { 716 9501 Robert arg.da_fmt = &ops->do_formats[FMDUMP_MSG]; 717 0 stevel } else 718 0 stevel arg.da_fmt = &ops->do_formats[FMDUMP_SHORT]; 719 9501 Robert 720 9501 Robert if (opt_m && arg.da_fmt->do_func == NULL) { 721 9501 Robert (void) fprintf(stderr, "%s: -m mode is not supported for " 722 9501 Robert "log of type %s: %s\n", g_pname, fmd_log_label(lp), ifile); 723 9501 Robert return (FMDUMP_EXIT_USAGE); 724 9501 Robert } 725 0 stevel 726 0 stevel arg.da_fv = errfv; 727 0 stevel arg.da_fc = errfc; 728 0 stevel arg.da_fp = stdout; 729 0 stevel 730 0 stevel if (iflags & FMD_LOG_XITER_OFFS) 731 0 stevel fmdump_printf(arg.da_fp, "%16s ", "OFFSET"); 732 0 stevel 733 10928 Stephen if (arg.da_fmt->do_hdr && !(opt_V && ops == &fmdump_flt_ops)) 734 0 stevel fmdump_printf(arg.da_fp, "%s\n", arg.da_fmt->do_hdr); 735 0 stevel 736 0 stevel if (opt_e && opt_u) { 737 0 stevel iflags |= FMD_LOG_XITER_REFS; 738 0 stevel func = xref_iter; 739 0 stevel farg = &arg; 740 0 stevel filtc = fltfc; 741 0 stevel filtv = fltfv; 742 0 stevel } else { 743 0 stevel func = arg.da_fmt->do_func; 744 0 stevel farg = arg.da_fp; 745 0 stevel filtc = allfc; 746 0 stevel filtv = allfv; 747 0 stevel } 748 0 stevel 749 0 stevel if (iflags & FMD_LOG_XITER_OFFS) { 750 0 stevel lyr.dy_func = func; 751 0 stevel lyr.dy_arg = farg; 752 0 stevel lyr.dy_fp = arg.da_fp; 753 0 stevel func = xoff_iter; 754 0 stevel farg = &lyr; 755 0 stevel } 756 0 stevel 757 8740 Sean for (llp = rotated_logs; llp != NULL; llp = llp->next) { 758 8740 Sean fmd_log_t *rlp; 759 8740 Sean 760 8740 Sean if ((rlp = fmd_log_open(FMD_LOG_VERSION, llp->path, &err)) 761 8740 Sean == NULL) { 762 8740 Sean (void) fprintf(stderr, "%s: failed to open %s: %s\n", 763 8740 Sean g_pname, llp->path, fmd_log_errmsg(NULL, err)); 764 8740 Sean g_errs++; 765 8740 Sean continue; 766 8740 Sean } 767 8740 Sean 768 8740 Sean recs = 0; 769 8740 Sean if (fmd_log_xiter(rlp, iflags, filtc, filtv, 770 8740 Sean func, error, farg, &recs) != 0) { 771 8740 Sean (void) fprintf(stderr, 772 8740 Sean "%s: failed to dump %s: %s\n", g_pname, llp->path, 773 8740 Sean fmd_log_errmsg(rlp, fmd_log_errno(rlp))); 774 8740 Sean g_errs++; 775 8740 Sean } 776 8740 Sean g_recs += recs; 777 8740 Sean 778 8740 Sean fmd_log_close(rlp); 779 8740 Sean } 780 8740 Sean 781 0 stevel do { 782 8740 Sean recs = 0; 783 0 stevel if (fmd_log_xiter(lp, iflags, filtc, filtv, 784 8740 Sean func, error, farg, &recs) != 0) { 785 0 stevel (void) fprintf(stderr, 786 0 stevel "%s: failed to dump %s: %s\n", g_pname, ifile, 787 0 stevel fmd_log_errmsg(lp, fmd_log_errno(lp))); 788 0 stevel g_errs++; 789 0 stevel } 790 8740 Sean g_recs += recs; 791 0 stevel 792 0 stevel if (opt_f) 793 0 stevel (void) sleep(1); 794 0 stevel 795 0 stevel } while (opt_f); 796 0 stevel 797 0 stevel if (!opt_f && g_recs == 0 && isatty(STDOUT_FILENO)) 798 0 stevel (void) fprintf(stderr, "%s: %s is empty\n", g_pname, ifile); 799 0 stevel 800 9501 Robert if (g_thp != NULL) 801 9501 Robert topo_close(g_thp); 802 9501 Robert 803 0 stevel fmd_log_close(lp); 804 9501 Robert fmd_msg_fini(g_msg); 805 9501 Robert 806 0 stevel return (g_errs ? FMDUMP_EXIT_ERROR : FMDUMP_EXIT_SUCCESS); 807 0 stevel } 808