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 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <alloca.h> 29 #include <unistd.h> 30 #include <limits.h> 31 #include <strings.h> 32 #include <stdlib.h> 33 #include <stdarg.h> 34 #include <stdio.h> 35 #include <errno.h> 36 #include <time.h> 37 #include <ctype.h> 38 #include <regex.h> 39 40 #include <fmdump.h> 41 42 #define FMDUMP_EXIT_SUCCESS 0 43 #define FMDUMP_EXIT_FATAL 1 44 #define FMDUMP_EXIT_USAGE 2 45 #define FMDUMP_EXIT_ERROR 3 46 47 const char *g_pname; 48 ulong_t g_errs; 49 ulong_t g_recs; 50 char *g_root; 51 struct topo_hdl *g_thp; 52 53 /*PRINTFLIKE2*/ 54 void 55 fmdump_printf(FILE *fp, const char *format, ...) 56 { 57 va_list ap; 58 59 va_start(ap, format); 60 61 if (vfprintf(fp, format, ap) < 0) { 62 (void) fprintf(stderr, "%s: failed to print record: %s\n", 63 g_pname, strerror(errno)); 64 g_errs++; 65 } 66 67 va_end(ap); 68 } 69 70 void 71 fmdump_vwarn(const char *format, va_list ap) 72 { 73 int err = errno; 74 75 (void) fprintf(stderr, "%s: warning: ", g_pname); 76 (void) vfprintf(stderr, format, ap); 77 78 if (strchr(format, '\n') == NULL) 79 (void) fprintf(stderr, ": %s\n", strerror(err)); 80 81 g_errs++; 82 } 83 84 /*PRINTFLIKE1*/ 85 void 86 fmdump_warn(const char *format, ...) 87 { 88 va_list ap; 89 90 va_start(ap, format); 91 fmdump_vwarn(format, ap); 92 va_end(ap); 93 } 94 95 char * 96 fmdump_date(char *buf, size_t len, const fmd_log_record_t *rp) 97 { 98 if (rp->rec_sec > LONG_MAX) { 99 fmdump_warn("record time is too large for 32-bit utility\n"); 100 (void) snprintf(buf, len, "0x%llx", rp->rec_sec); 101 } else { 102 time_t tod = (time_t)rp->rec_sec; 103 time_t now = time(NULL); 104 if (tod > now+60 || 105 tod < now - 6L*30L*24L*60L*60L) { /* 6 months ago */ 106 (void) strftime(buf, len, "%b %d %Y %T", 107 localtime(&tod)); 108 } else { 109 size_t sz; 110 sz = strftime(buf, len, "%b %d %T", localtime(&tod)); 111 (void) snprintf(buf + sz, len - sz, ".%4.4llu", 112 rp->rec_nsec / (NANOSEC / 10000)); 113 } 114 } 115 116 return (buf); 117 } 118 119 char * 120 fmdump_year(char *buf, size_t len, const fmd_log_record_t *rp) 121 { 122 #ifdef _ILP32 123 if (rp->rec_sec > LONG_MAX) { 124 fmdump_warn("record time is too large for 32-bit utility\n"); 125 (void) snprintf(buf, len, "0x%llx", rp->rec_sec); 126 } else { 127 #endif 128 time_t tod = (time_t)rp->rec_sec; 129 (void) strftime(buf, len, "%b %d %Y %T", localtime(&tod)); 130 #ifdef _ILP32 131 } 132 #endif 133 return (buf); 134 } 135 136 static int 137 usage(FILE *fp) 138 { 139 (void) fprintf(fp, "Usage: %s [-efvV] [-c class] [-R root] [-t time] " 140 "[-T time] [-u uuid]\n\t\t[-n name[.name]*[=value]] [file]\n", 141 g_pname); 142 143 (void) fprintf(fp, 144 "\t-c select events that match the specified class\n" 145 "\t-e display error log content instead of fault log content\n" 146 "\t-f follow growth of log file by waiting for additional data\n" 147 "\t-R set root directory for pathname expansions\n" 148 "\t-t select events that occurred after the specified time\n" 149 "\t-T select events that occurred before the specified time\n" 150 "\t-u select events that match the specified uuid\n" 151 "\t-n select events containing named nvpair " 152 "(with matching value)\n" 153 "\t-v set verbose mode: display additional event detail\n" 154 "\t-V set very verbose mode: display complete event contents\n"); 155 156 return (FMDUMP_EXIT_USAGE); 157 } 158 159 /*ARGSUSED*/ 160 static int 161 error(fmd_log_t *lp, void *private) 162 { 163 fmdump_warn("skipping record: %s\n", 164 fmd_log_errmsg(lp, fmd_log_errno(lp))); 165 return (0); 166 } 167 168 /* 169 * Yet another disgusting argument parsing function (TM). We attempt to parse 170 * a time argument in a variety of strptime(3C) formats, in which case it is 171 * interpreted as a local time and is converted to a timeval using mktime(3C). 172 * If those formats fail, we look to see if the time is a decimal integer 173 * followed by one of our magic suffixes, in which case the time is interpreted 174 * as a time delta *before* the current time-of-day (i.e. "1h" = "1 hour ago"). 175 */ 176 static struct timeval * 177 gettimeopt(const char *arg) 178 { 179 const struct { 180 const char *name; 181 hrtime_t mul; 182 } suffix[] = { 183 { "ns", NANOSEC / NANOSEC }, 184 { "nsec", NANOSEC / NANOSEC }, 185 { "us", NANOSEC / MICROSEC }, 186 { "usec", NANOSEC / MICROSEC }, 187 { "ms", NANOSEC / MILLISEC }, 188 { "msec", NANOSEC / MILLISEC }, 189 { "s", NANOSEC / SEC }, 190 { "sec", NANOSEC / SEC }, 191 { "m", NANOSEC * (hrtime_t)60 }, 192 { "min", NANOSEC * (hrtime_t)60 }, 193 { "h", NANOSEC * (hrtime_t)(60 * 60) }, 194 { "hour", NANOSEC * (hrtime_t)(60 * 60) }, 195 { "d", NANOSEC * (hrtime_t)(24 * 60 * 60) }, 196 { "day", NANOSEC * (hrtime_t)(24 * 60 * 60) }, 197 { NULL } 198 }; 199 200 struct timeval *tvp = malloc(sizeof (struct timeval)); 201 struct timeval tod; 202 struct tm tm; 203 char *p; 204 205 if (tvp == NULL) { 206 (void) fprintf(stderr, "%s: failed to allocate memory: %s\n", 207 g_pname, strerror(errno)); 208 exit(FMDUMP_EXIT_FATAL); 209 } 210 211 if (gettimeofday(&tod, NULL) != 0) { 212 (void) fprintf(stderr, "%s: failed to get tod: %s\n", 213 g_pname, strerror(errno)); 214 exit(FMDUMP_EXIT_FATAL); 215 } 216 217 /* 218 * First try a variety of strptime() calls. If these all fail, we'll 219 * try parsing an integer followed by one of our suffix[] strings. 220 * NOTE: any form using %y must appear *before* the equivalent %Y form; 221 * otherwise %Y will accept the two year digits but infer century zero. 222 * Any form ending in %y must additionally check isdigit(*p) to ensure 223 * that it does not inadvertently match 2 digits of a 4-digit year. 224 * 225 * Beware: Any strptime() sequence containing consecutive %x sequences 226 * may fall victim to SCCS expanding it as a keyword! If this happens 227 * we use separate string constant that ANSI C will concatenate. 228 */ 229 if ((p = strptime(arg, "%m/%d/%y" "%t" "%H:%M:%S", &tm)) == NULL && 230 (p = strptime(arg, "%m/%d/%Y" "%t" "%H:%M:%S", &tm)) == NULL && 231 (p = strptime(arg, "%m/%d/%y" "%t" "%H:%M", &tm)) == NULL && 232 (p = strptime(arg, "%m/%d/%Y" "%t" "%H:%M", &tm)) == NULL && 233 ((p = strptime(arg, "%m/%d/%y", &tm)) == NULL || isdigit(*p)) && 234 (p = strptime(arg, "%m/%d/%Y", &tm)) == NULL && 235 (p = strptime(arg, "%y-%m-%dT%H:%M:%S", &tm)) == NULL && 236 (p = strptime(arg, "%Y-%m-%dT%H:%M:%S", &tm)) == NULL && 237 (p = strptime(arg, "%y-%m-%dT%H:%M", &tm)) == NULL && 238 (p = strptime(arg, "%Y-%m-%dT%H:%M", &tm)) == NULL && 239 (p = strptime(arg, "%y-%m-%d", &tm)) == NULL && 240 (p = strptime(arg, "%Y-%m-%d", &tm)) == NULL && 241 (p = strptime(arg, "%d%b%y" "%t" "%H:%M:%S", &tm)) == NULL && 242 (p = strptime(arg, "%d%b%Y" "%t" "%H:%M:%S", &tm)) == NULL && 243 (p = strptime(arg, "%d%b%y" "%t" "%H:%M", &tm)) == NULL && 244 (p = strptime(arg, "%d%b%Y" "%t" "%H:%M", &tm)) == NULL && 245 ((p = strptime(arg, "%d%b%y", &tm)) == NULL || isdigit(*p)) && 246 (p = strptime(arg, "%d%b%Y", &tm)) == NULL && 247 (p = strptime(arg, "%b%t%d" "%t" "%H:%M:%S", &tm)) == NULL && 248 (p = strptime(arg, "%b%t%d" "%t" "%H:%M:%S", &tm)) == NULL && 249 (p = strptime(arg, "%H:%M:%S", &tm)) == NULL && 250 (p = strptime(arg, "%H:%M", &tm)) == NULL) { 251 252 hrtime_t nsec; 253 int i; 254 255 errno = 0; 256 nsec = strtol(arg, (char **)&p, 10); 257 258 if (errno != 0 || nsec == 0 || p == arg || *p == '\0') { 259 (void) fprintf(stderr, "%s: illegal time " 260 "format -- %s\n", g_pname, arg); 261 exit(FMDUMP_EXIT_USAGE); 262 } 263 264 for (i = 0; suffix[i].name != NULL; i++) { 265 if (strcasecmp(suffix[i].name, p) == 0) { 266 nsec *= suffix[i].mul; 267 break; 268 } 269 } 270 271 if (suffix[i].name == NULL) { 272 (void) fprintf(stderr, "%s: illegal time " 273 "format -- %s\n", g_pname, arg); 274 exit(FMDUMP_EXIT_USAGE); 275 } 276 277 tvp->tv_sec = nsec / NANOSEC; 278 tvp->tv_usec = (nsec % NANOSEC) / (NANOSEC / MICROSEC); 279 280 if (tvp->tv_sec > tod.tv_sec) { 281 (void) fprintf(stderr, "%s: time delta precedes " 282 "UTC time origin -- %s\n", g_pname, arg); 283 exit(FMDUMP_EXIT_USAGE); 284 } 285 286 tvp->tv_sec = tod.tv_sec - tvp->tv_sec; 287 288 } else if (*p == '\0' || *p == '.') { 289 /* 290 * If tm_year is zero, we matched [%b %d] %H:%M[:%S]; use 291 * the result of localtime(&tod.tv_sec) to fill in the rest. 292 */ 293 if (tm.tm_year == 0) { 294 int h = tm.tm_hour; 295 int m = tm.tm_min; 296 int s = tm.tm_sec; 297 int b = tm.tm_mon; 298 int d = tm.tm_mday; 299 300 bcopy(localtime(&tod.tv_sec), &tm, sizeof (tm)); 301 tm.tm_isdst = 0; /* see strptime(3C) and below */ 302 303 if (d > 0) { 304 tm.tm_mon = b; 305 tm.tm_mday = d; 306 } 307 308 tm.tm_hour = h; 309 tm.tm_min = m; 310 tm.tm_sec = s; 311 } 312 313 errno = 0; 314 tvp->tv_sec = mktime(&tm); 315 tvp->tv_usec = 0; 316 317 if (tvp->tv_sec == -1L && errno != 0) { 318 (void) fprintf(stderr, "%s: failed to compose " 319 "time %s: %s\n", g_pname, arg, strerror(errno)); 320 exit(FMDUMP_EXIT_ERROR); 321 } 322 323 /* 324 * If our mktime() set tm_isdst, adjust the result for DST by 325 * subtracting the offset between the main and alternate zones. 326 */ 327 if (tm.tm_isdst) 328 tvp->tv_sec -= timezone - altzone; 329 330 if (p[0] == '.') { 331 arg = p; 332 errno = 0; 333 tvp->tv_usec = 334 (suseconds_t)(strtod(arg, &p) * (double)MICROSEC); 335 336 if (errno != 0 || p == arg || *p != '\0') { 337 (void) fprintf(stderr, "%s: illegal time " 338 "suffix -- .%s\n", g_pname, arg); 339 exit(FMDUMP_EXIT_USAGE); 340 } 341 } 342 343 } else { 344 (void) fprintf(stderr, "%s: unexpected suffix after " 345 "time %s -- %s\n", g_pname, arg, p); 346 exit(FMDUMP_EXIT_USAGE); 347 } 348 349 return (tvp); 350 } 351 352 /* 353 * If the -u option is specified in combination with the -e option, we iterate 354 * over each record in the fault log with a matching UUID finding xrefs to the 355 * error log, and then use this function to iterate over every xref'd record. 356 */ 357 int 358 xref_iter(fmd_log_t *lp, const fmd_log_record_t *rp, void *arg) 359 { 360 const fmd_log_record_t *xrp = rp->rec_xrefs; 361 fmdump_arg_t *dap = arg; 362 int i, rv = 0; 363 364 for (i = 0; rv == 0 && i < rp->rec_nrefs; i++, xrp++) { 365 if (fmd_log_filter(lp, dap->da_fc, dap->da_fv, xrp)) 366 rv = dap->da_fmt->do_func(lp, xrp, dap->da_fp); 367 } 368 369 return (rv); 370 } 371 372 int 373 xoff_iter(fmd_log_t *lp, const fmd_log_record_t *rp, void *arg) 374 { 375 fmdump_lyr_t *dyp = arg; 376 377 fmdump_printf(dyp->dy_fp, "%16llx ", (u_longlong_t)rp->rec_off); 378 return (dyp->dy_func(lp, rp, dyp->dy_arg)); 379 } 380 381 /* 382 * Initialize fmd_log_filter_nvarg_t from -n name=value argument string. 383 */ 384 static fmd_log_filter_nvarg_t * 385 setupnamevalue(char *namevalue) 386 { 387 fmd_log_filter_nvarg_t *argt; 388 char *value; 389 regex_t *value_regex = NULL; 390 char errstr[128]; 391 int rv; 392 393 if ((value = strchr(namevalue, '=')) == NULL) { 394 value_regex = NULL; 395 } else { 396 *value++ = '\0'; /* separate name and value string */ 397 398 /* 399 * Skip white space before value to facilitate direct 400 * cut/paste from previous fmdump output. 401 */ 402 while (isspace(*value)) 403 value++; 404 405 if ((value_regex = malloc(sizeof (regex_t))) == NULL) { 406 (void) fprintf(stderr, "%s: failed to allocate memory: " 407 "%s\n", g_pname, strerror(errno)); 408 exit(FMDUMP_EXIT_FATAL); 409 } 410 411 /* compile regular expression for possible string match */ 412 if ((rv = regcomp(value_regex, value, 413 REG_NOSUB|REG_NEWLINE)) != 0) { 414 (void) regerror(rv, value_regex, errstr, 415 sizeof (errstr)); 416 (void) fprintf(stderr, "unexpected regular expression " 417 "in %s: %s\n", value, errstr); 418 free(value_regex); 419 exit(FMDUMP_EXIT_USAGE); 420 } 421 } 422 423 if ((argt = malloc(sizeof (fmd_log_filter_nvarg_t))) == NULL) { 424 (void) fprintf(stderr, "%s: failed to allocate memory: %s\n", 425 g_pname, strerror(errno)); 426 exit(FMDUMP_EXIT_FATAL); 427 } 428 argt->nvarg_name = namevalue; /* now just name */ 429 argt->nvarg_value = value; 430 argt->nvarg_value_regex = value_regex; 431 return (argt); 432 } 433 434 /* 435 * If the -a option is not present, filter out fault records that correspond 436 * to events that the producer requested not be messaged for administrators. 437 */ 438 /*ARGSUSED*/ 439 int 440 log_filter_silent(fmd_log_t *lp, const fmd_log_record_t *rp, void *arg) 441 { 442 boolean_t msg; 443 444 return (nvlist_lookup_boolean_value(rp->rec_nvl, 445 FM_SUSPECT_MESSAGE, &msg) != 0 || msg != 0); 446 } 447 448 int 449 main(int argc, char *argv[]) 450 { 451 int opt_a = 0, opt_e = 0, opt_f = 0, opt_H = 0; 452 int opt_u = 0, opt_v = 0, opt_V = 0; 453 454 char ifile[PATH_MAX] = ""; 455 int iflags = 0; 456 457 fmdump_arg_t arg; 458 fmdump_lyr_t lyr; 459 const fmdump_ops_t *ops; 460 fmd_log_filter_t *filtv; 461 uint_t filtc; 462 463 fmd_log_filter_t *errfv, *fltfv, *allfv; 464 uint_t errfc = 0, fltfc = 0, allfc = 0; 465 466 fmd_log_header_t log; 467 fmd_log_rec_f *func; 468 void *farg; 469 fmd_log_t *lp; 470 int c, err; 471 off64_t off = 0; 472 473 g_pname = argv[0]; 474 475 errfv = alloca(sizeof (fmd_log_filter_t) * argc); 476 fltfv = alloca(sizeof (fmd_log_filter_t) * argc); 477 allfv = alloca(sizeof (fmd_log_filter_t) * argc); 478 479 while (optind < argc) { 480 while ((c = 481 getopt(argc, argv, "ac:efHn:O:R:t:T:u:vV")) != EOF) { 482 switch (c) { 483 case 'a': 484 opt_a++; 485 break; 486 case 'c': 487 errfv[errfc].filt_func = fmd_log_filter_class; 488 errfv[errfc].filt_arg = optarg; 489 allfv[allfc++] = errfv[errfc++]; 490 break; 491 case 'e': 492 opt_e++; 493 break; 494 case 'f': 495 opt_f++; 496 break; 497 case 'H': 498 opt_H++; 499 break; 500 case 'O': 501 off = strtoull(optarg, NULL, 16); 502 iflags |= FMD_LOG_XITER_OFFS; 503 break; 504 case 'R': 505 g_root = optarg; 506 break; 507 case 't': 508 errfv[errfc].filt_func = fmd_log_filter_after; 509 errfv[errfc].filt_arg = gettimeopt(optarg); 510 allfv[allfc++] = errfv[errfc++]; 511 break; 512 case 'T': 513 errfv[errfc].filt_func = fmd_log_filter_before; 514 errfv[errfc].filt_arg = gettimeopt(optarg); 515 allfv[allfc++] = errfv[errfc++]; 516 break; 517 case 'u': 518 fltfv[fltfc].filt_func = fmd_log_filter_uuid; 519 fltfv[fltfc].filt_arg = optarg; 520 allfv[allfc++] = fltfv[fltfc++]; 521 opt_u++; 522 opt_a++; /* -u implies -a */ 523 break; 524 case 'n': { 525 fltfv[fltfc].filt_func = fmd_log_filter_nv; 526 fltfv[fltfc].filt_arg = setupnamevalue(optarg); 527 allfv[allfc++] = fltfv[fltfc++]; 528 break; 529 } 530 case 'v': 531 opt_v++; 532 break; 533 case 'V': 534 opt_V++; 535 break; 536 default: 537 return (usage(stderr)); 538 } 539 } 540 541 if (optind < argc) { 542 if (*ifile != '\0') { 543 (void) fprintf(stderr, "%s: illegal " 544 "argument -- %s\n", g_pname, argv[optind]); 545 return (FMDUMP_EXIT_USAGE); 546 } else { 547 (void) strlcpy(ifile, 548 argv[optind++], sizeof (ifile)); 549 } 550 } 551 } 552 553 if (*ifile == '\0') { 554 (void) snprintf(ifile, sizeof (ifile), "%s/var/fm/fmd/%slog", 555 g_root ? g_root : "", opt_e && !opt_u ? "err" : "flt"); 556 } else if (g_root != NULL) { 557 (void) fprintf(stderr, "%s: -R option is not appropriate " 558 "when file operand is present\n", g_pname); 559 return (FMDUMP_EXIT_USAGE); 560 } 561 562 if ((lp = fmd_log_open(FMD_LOG_VERSION, ifile, &err)) == NULL) { 563 (void) fprintf(stderr, "%s: failed to open %s: %s\n", 564 g_pname, ifile, fmd_log_errmsg(NULL, err)); 565 return (FMDUMP_EXIT_FATAL); 566 } 567 568 if (opt_H) { 569 fmd_log_header(lp, &log); 570 571 (void) printf("EXD_CREATOR = %s\n", log.log_creator); 572 (void) printf("EXD_HOSTNAME = %s\n", log.log_hostname); 573 (void) printf("EXD_FMA_LABEL = %s\n", log.log_label); 574 (void) printf("EXD_FMA_VERSION = %s\n", log.log_version); 575 (void) printf("EXD_FMA_OSREL = %s\n", log.log_osrelease); 576 (void) printf("EXD_FMA_OSVER = %s\n", log.log_osversion); 577 (void) printf("EXD_FMA_PLAT = %s\n", log.log_platform); 578 (void) printf("EXD_FMA_UUID = %s\n", log.log_uuid); 579 580 return (FMDUMP_EXIT_SUCCESS); 581 } 582 583 if (off != 0 && fmd_log_seek(lp, off) != 0) { 584 (void) fprintf(stderr, "%s: failed to seek %s: %s\n", 585 g_pname, ifile, fmd_log_errmsg(lp, fmd_log_errno(lp))); 586 return (FMDUMP_EXIT_FATAL); 587 } 588 589 if (opt_e && opt_u) 590 ops = &fmdump_err_ops; 591 else if (strcmp(fmd_log_label(lp), fmdump_flt_ops.do_label) == 0) 592 ops = &fmdump_flt_ops; 593 else if (strcmp(fmd_log_label(lp), fmdump_asru_ops.do_label) == 0) 594 ops = &fmdump_asru_ops; 595 else 596 ops = &fmdump_err_ops; 597 598 if (!opt_a && ops == &fmdump_flt_ops) { 599 fltfv[fltfc].filt_func = log_filter_silent; 600 fltfv[fltfc].filt_arg = NULL; 601 allfv[allfc++] = fltfv[fltfc++]; 602 } 603 604 if (opt_V) { 605 arg.da_fmt = &ops->do_formats[FMDUMP_VERB2]; 606 iflags |= FMD_LOG_XITER_REFS; 607 } else if (opt_v) { 608 arg.da_fmt = &ops->do_formats[FMDUMP_VERB1]; 609 } else 610 arg.da_fmt = &ops->do_formats[FMDUMP_SHORT]; 611 612 arg.da_fv = errfv; 613 arg.da_fc = errfc; 614 arg.da_fp = stdout; 615 616 if (iflags & FMD_LOG_XITER_OFFS) 617 fmdump_printf(arg.da_fp, "%16s ", "OFFSET"); 618 619 if (arg.da_fmt->do_hdr) 620 fmdump_printf(arg.da_fp, "%s\n", arg.da_fmt->do_hdr); 621 622 if (opt_e && opt_u) { 623 iflags |= FMD_LOG_XITER_REFS; 624 func = xref_iter; 625 farg = &arg; 626 filtc = fltfc; 627 filtv = fltfv; 628 } else { 629 func = arg.da_fmt->do_func; 630 farg = arg.da_fp; 631 filtc = allfc; 632 filtv = allfv; 633 } 634 635 if (iflags & FMD_LOG_XITER_OFFS) { 636 lyr.dy_func = func; 637 lyr.dy_arg = farg; 638 lyr.dy_fp = arg.da_fp; 639 func = xoff_iter; 640 farg = &lyr; 641 } 642 643 do { 644 if (fmd_log_xiter(lp, iflags, filtc, filtv, 645 func, error, farg, &g_recs) != 0) { 646 (void) fprintf(stderr, 647 "%s: failed to dump %s: %s\n", g_pname, ifile, 648 fmd_log_errmsg(lp, fmd_log_errno(lp))); 649 g_errs++; 650 } 651 652 if (opt_f) 653 (void) sleep(1); 654 655 } while (opt_f); 656 657 if (!opt_f && g_recs == 0 && isatty(STDOUT_FILENO)) 658 (void) fprintf(stderr, "%s: %s is empty\n", g_pname, ifile); 659 660 fmd_log_close(lp); 661 return (g_errs ? FMDUMP_EXIT_ERROR : FMDUMP_EXIT_SUCCESS); 662 } 663