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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/fm/protocol.h> 30 #include <sys/types.h> 31 #include <sys/mkdev.h> 32 33 #include <alloca.h> 34 #include <unistd.h> 35 #include <limits.h> 36 #include <strings.h> 37 #include <stdlib.h> 38 #include <fcntl.h> 39 #include <errno.h> 40 #include <libgen.h> 41 #include <dirent.h> 42 #include <fmd_log_impl.h> 43 #include <fmd_log.h> 44 45 #define CAT_FMA_RGROUP (EXT_GROUP | EXC_DEFAULT | EXD_GROUP_RFMA) 46 #define CAT_FMA_GROUP (EXT_GROUP | EXC_DEFAULT | EXD_GROUP_FMA) 47 48 #define CAT_FMA_LABEL (EXT_STRING | EXC_DEFAULT | EXD_FMA_LABEL) 49 #define CAT_FMA_VERSION (EXT_STRING | EXC_DEFAULT | EXD_FMA_VERSION) 50 #define CAT_FMA_OSREL (EXT_STRING | EXC_DEFAULT | EXD_FMA_OSREL) 51 #define CAT_FMA_OSVER (EXT_STRING | EXC_DEFAULT | EXD_FMA_OSVER) 52 #define CAT_FMA_PLAT (EXT_STRING | EXC_DEFAULT | EXD_FMA_PLAT) 53 #define CAT_FMA_TODSEC (EXT_UINT64 | EXC_DEFAULT | EXD_FMA_TODSEC) 54 #define CAT_FMA_TODNSEC (EXT_UINT64 | EXC_DEFAULT | EXD_FMA_TODNSEC) 55 #define CAT_FMA_NVLIST (EXT_RAW | EXC_DEFAULT | EXD_FMA_NVLIST) 56 #define CAT_FMA_MAJOR (EXT_UINT32 | EXC_DEFAULT | EXD_FMA_MAJOR) 57 #define CAT_FMA_MINOR (EXT_UINT32 | EXC_DEFAULT | EXD_FMA_MINOR) 58 #define CAT_FMA_INODE (EXT_UINT64 | EXC_DEFAULT | EXD_FMA_INODE) 59 #define CAT_FMA_OFFSET (EXT_UINT64 | EXC_DEFAULT | EXD_FMA_OFFSET) 60 61 static int fmd_log_load_record(fmd_log_t *, uint_t, fmd_log_record_t *); 62 static void fmd_log_free_record(fmd_log_record_t *); 63 static int fmd_log_load_xrefs(fmd_log_t *, uint_t, fmd_log_record_t *); 64 65 static const char FMD_CREATOR[] = "fmd"; 66 67 /* 68 * fmd_log_set_errno is used as a utility function throughout the library. It 69 * sets both lp->log_errno and errno to the specified value. If the current 70 * error is EFDL_EXACCT, we store it internally as that value plus ea_error(). 71 * If no ea_error() is present, we assume EFDL_BADTAG (catalog tag mismatch). 72 */ 73 static int 74 fmd_log_set_errno(fmd_log_t *lp, int err) 75 { 76 if (err == EFDL_EXACCT && ea_error() != EXR_OK) 77 lp->log_errno = EFDL_EXACCT + ea_error(); 78 else if (err == EFDL_EXACCT) 79 lp->log_errno = EFDL_BADTAG; 80 else 81 lp->log_errno = err; 82 83 errno = lp->log_errno; 84 return (-1); 85 } 86 87 /*PRINTFLIKE2*/ 88 static void 89 fmd_log_dprintf(fmd_log_t *lp, const char *format, ...) 90 { 91 va_list ap; 92 93 if (lp->log_flags & FMD_LF_DEBUG) { 94 (void) fputs("fmd_log DEBUG: ", stderr); 95 va_start(ap, format); 96 (void) vfprintf(stderr, format, ap); 97 va_end(ap); 98 } 99 } 100 101 /* 102 * fmd_log_load_record() is used to load the exacct object at the current file 103 * location into the specified fmd_log_record structure. Once the caller has 104 * made use of this information, it can clean up using fmd_log_free_record(). 105 */ 106 static int 107 fmd_log_load_record(fmd_log_t *lp, uint_t iflags, fmd_log_record_t *rp) 108 { 109 ea_object_t *grp, *obj; 110 off64_t off; 111 int err; 112 113 if (iflags & FMD_LOG_XITER_OFFS) { 114 ea_clear(&lp->log_ea); 115 off = lseek64(lp->log_fd, 0, SEEK_CUR); 116 } 117 118 if ((grp = ea_get_object_tree(&lp->log_ea, 1)) == NULL) 119 return (fmd_log_set_errno(lp, EFDL_EXACCT)); 120 121 if (grp->eo_catalog != CAT_FMA_RGROUP && 122 grp->eo_catalog != CAT_FMA_GROUP) { 123 fmd_log_dprintf(lp, "bad catalog tag 0x%x\n", grp->eo_catalog); 124 ea_free_object(grp, EUP_ALLOC); 125 return (fmd_log_set_errno(lp, EFDL_EXACCT)); 126 } 127 128 bzero(rp, sizeof (fmd_log_record_t)); 129 rp->rec_grp = grp; 130 131 if (iflags & FMD_LOG_XITER_OFFS) 132 rp->rec_off = off; 133 134 for (obj = grp->eo_group.eg_objs; obj != NULL; obj = obj->eo_next) { 135 switch (obj->eo_catalog) { 136 case CAT_FMA_NVLIST: 137 if ((err = nvlist_unpack(obj->eo_item.ei_raw, 138 obj->eo_item.ei_size, &rp->rec_nvl, 0)) != 0) { 139 fmd_log_free_record(rp); 140 return (fmd_log_set_errno(lp, err)); 141 } 142 break; 143 144 case CAT_FMA_TODSEC: 145 rp->rec_sec = obj->eo_item.ei_uint64; 146 break; 147 148 case CAT_FMA_TODNSEC: 149 rp->rec_nsec = obj->eo_item.ei_uint64; 150 break; 151 152 case CAT_FMA_GROUP: 153 rp->rec_nrefs += obj->eo_group.eg_nobjs; 154 break; 155 } 156 } 157 158 if (rp->rec_nvl == NULL || nvlist_lookup_string(rp->rec_nvl, 159 FM_CLASS, (char **)&rp->rec_class) != 0) { 160 fmd_log_free_record(rp); 161 return (fmd_log_set_errno(lp, EFDL_NOCLASS)); 162 } 163 164 if (rp->rec_nrefs != 0 && fmd_log_load_xrefs(lp, iflags, rp) != 0) { 165 err = errno; /* errno is set for us */ 166 fmd_log_free_record(rp); 167 return (fmd_log_set_errno(lp, err)); 168 } 169 170 return (0); 171 } 172 173 /* 174 * fmd_log_free_record frees memory associated with the specified record. If 175 * cross-references are contained in this record, we proceed recursively. 176 */ 177 static void 178 fmd_log_free_record(fmd_log_record_t *rp) 179 { 180 uint_t i; 181 182 if (rp->rec_xrefs != NULL) { 183 for (i = 0; i < rp->rec_nrefs; i++) 184 fmd_log_free_record(&rp->rec_xrefs[i]); 185 free(rp->rec_xrefs); 186 } 187 188 nvlist_free(rp->rec_nvl); 189 ea_free_object(rp->rec_grp, EUP_ALLOC); 190 } 191 192 /* 193 * fmd_log_load_xref loads the cross-reference represented by the specified 194 * exacct group 'grp' into the next empty slot in rp->rec_xrefs. This function 195 * is called repeatedly by fmd_log_load_xrefs() for each embedded reference. 196 */ 197 static int 198 fmd_log_load_xref(fmd_log_t *lp, uint_t iflags, 199 fmd_log_record_t *rp, ea_object_t *grp) 200 { 201 ea_object_t *obj; 202 fmd_log_t *xlp; 203 dev_t dev; 204 205 off64_t off = (off64_t)-1L; 206 major_t maj = (major_t)-1L; 207 minor_t min = (minor_t)-1L; 208 ino64_t ino = (ino64_t)-1L; 209 210 for (obj = grp->eo_group.eg_objs; obj != NULL; obj = obj->eo_next) { 211 switch (obj->eo_catalog) { 212 case CAT_FMA_MAJOR: 213 maj = obj->eo_item.ei_uint32; 214 break; 215 case CAT_FMA_MINOR: 216 min = obj->eo_item.ei_uint32; 217 break; 218 case CAT_FMA_INODE: 219 ino = obj->eo_item.ei_uint64; 220 break; 221 case CAT_FMA_OFFSET: 222 off = obj->eo_item.ei_uint64; 223 break; 224 } 225 } 226 227 if (off == (off64_t)-1L || ino == (ino64_t)-1L || 228 maj == (major_t)-1L || min == (minor_t)-1L) 229 return (fmd_log_set_errno(lp, EFDL_BADREF)); 230 231 if ((dev = makedev(maj, min)) == NODEV) 232 return (fmd_log_set_errno(lp, EFDL_BADDEV)); 233 234 /* 235 * Search our xref list for matching (dev_t, ino64_t). If we can't 236 * find one, return silently without doing anything. We expect log 237 * xrefs to be broken whenever log files are trimmed or removed; their 238 * only purpose is to help us debug diagnosis engine algorithms. 239 */ 240 for (xlp = lp->log_xrefs; xlp != NULL; xlp = xlp->log_xnext) { 241 if (xlp->log_stat.st_ino == ino && xlp->log_stat.st_dev == dev) 242 break; 243 } 244 245 if (xlp == NULL) { 246 fmd_log_dprintf(lp, "broken xref dev=%lx ino=%llx\n", 247 (ulong_t)dev, (u_longlong_t)ino); 248 return (0); 249 } 250 251 xlp->log_flags &= ~FMD_LF_START; 252 ea_clear(&xlp->log_ea); 253 (void) lseek64(xlp->log_fd, off, SEEK_SET); 254 255 return (fmd_log_load_record(xlp, 256 iflags, &rp->rec_xrefs[rp->rec_nrefs++])); 257 } 258 259 /* 260 * fmd_log_load_xrdir is called by fmd_log_load_xrefs when the FMD_LF_XREFS bit 261 * is not yet set, indicating we haven't looked for cross-referenced files. We 262 * open the directory associated with the specified log file and attempt to 263 * perform an fmd_log_open() on every file found there (i.e. /var/fm/fmd). If 264 * we are successful, the files are chained on to lp->log_xrefs, where the 265 * fmd_log_load_xref() function can find them by comparing dev/ino to log_stat. 266 */ 267 static void 268 fmd_log_load_xrdir(fmd_log_t *lp) 269 { 270 fmd_log_t *xlp; 271 char dirbuf[PATH_MAX], path[PATH_MAX], *dirpath; 272 struct dirent *dp; 273 DIR *dirp; 274 275 lp->log_flags |= FMD_LF_XREFS; 276 (void) strlcpy(dirbuf, lp->log_path, sizeof (dirbuf)); 277 dirpath = dirname(dirbuf); 278 279 if ((dirp = opendir(dirpath)) == NULL) 280 return; /* failed to open directory; just skip it */ 281 282 while ((dp = readdir(dirp)) != NULL) { 283 if (dp->d_name[0] == '.') 284 continue; /* skip "." and ".." and hidden files */ 285 286 (void) snprintf(path, sizeof (path), 287 "%s/%s", dirpath, dp->d_name); 288 289 if (strcmp(path, lp->log_path) != 0 && 290 (xlp = fmd_log_open(lp->log_abi, path, NULL)) != NULL) { 291 fmd_log_dprintf(lp, "%s loaded %s for xrefs\n", 292 lp->log_path, xlp->log_path); 293 xlp->log_xnext = lp->log_xrefs; 294 lp->log_xrefs = xlp; 295 } 296 } 297 } 298 299 /* 300 * fmd_log_load_xrefs iterates again over the record's exacct group and for 301 * each cross-reference (embedded CAT_FMA_GROUP), attempts to fill in the 302 * corresponding xref. rp->rec_nrefs is reset to the number of valid items 303 * in the finished rp->rec_xrefs array; see fmd_log_load_xref() for more info. 304 */ 305 static int 306 fmd_log_load_xrefs(fmd_log_t *lp, uint_t iflags, fmd_log_record_t *rp) 307 { 308 size_t size = sizeof (fmd_log_record_t) * rp->rec_nrefs; 309 ea_object_t *rgrp = rp->rec_grp; 310 ea_object_t *grp, *obj; 311 312 if (!(iflags & FMD_LOG_XITER_REFS)) 313 return (0); /* do not load any xrefs */ 314 315 if (!(lp->log_flags & FMD_LF_XREFS)) 316 fmd_log_load_xrdir(lp); 317 318 if ((rp->rec_xrefs = malloc(size)) == NULL) 319 return (fmd_log_set_errno(lp, EFDL_NOMEM)); 320 321 bzero(rp->rec_xrefs, size); 322 rp->rec_nrefs = 0; 323 324 /* 325 * Make a second pass through the record group to locate and process 326 * each cross-reference sub-group. The structure of the groups is 327 * as follows (left-hand-side symbols named after the variables used): 328 * 329 * rgrp := CAT_FMA_TODSEC CAT_FMA_TODNSEC CAT_FMA_NVLIST grp* 330 * grp := obj* (i.e. zero or more groups of xref items) 331 * obj := CAT_FMA_MAJOR CAT_FMA_MINOR CAT_FMA_INODE CAT_FMA_OFFSET 332 * 333 * For each xref 'obj', we call fmd_log_load_xref() to parse the four 334 * xref members and then load the specified record into rp->rec_xrefs. 335 */ 336 for (grp = rgrp->eo_group.eg_objs; grp != NULL; grp = grp->eo_next) { 337 if (grp->eo_catalog != CAT_FMA_GROUP) 338 continue; /* ignore anything that isn't a group */ 339 340 for (obj = grp->eo_group.eg_objs; 341 obj != NULL; obj = obj->eo_next) { 342 if (fmd_log_load_xref(lp, iflags, rp, obj) != 0) 343 return (-1); /* errno is set for us */ 344 } 345 } 346 347 return (0); 348 } 349 350 static fmd_log_t * 351 fmd_log_open_err(fmd_log_t *lp, int *errp, int err) 352 { 353 if (errp != NULL) 354 *errp = err == EFDL_EXACCT ? EFDL_EXACCT + ea_error() : err; 355 356 if (lp != NULL) 357 fmd_log_close(lp); 358 359 return (NULL); 360 } 361 362 fmd_log_t * 363 fmd_log_open(int abi, const char *name, int *errp) 364 { 365 ea_object_t *grp, *obj; 366 fmd_log_t *lp; 367 int fd; 368 369 if (abi != FMD_LOG_VERSION) 370 return (fmd_log_open_err(NULL, errp, EFDL_VERSION)); 371 372 if ((lp = malloc(sizeof (fmd_log_t))) == NULL) 373 return (fmd_log_open_err(NULL, errp, EFDL_NOMEM)); 374 375 bzero(lp, sizeof (fmd_log_t)); 376 377 if ((lp->log_path = strdup(name)) == NULL) 378 return (fmd_log_open_err(lp, errp, EFDL_NOMEM)); 379 380 if ((lp->log_fd = open64(name, O_RDONLY)) == -1 || 381 fstat64(lp->log_fd, &lp->log_stat) == -1 || 382 (fd = dup(lp->log_fd)) == -1) 383 return (fmd_log_open_err(lp, errp, errno)); 384 385 if (ea_fdopen(&lp->log_ea, fd, FMD_CREATOR, 386 EO_VALID_HDR | EO_HEAD, O_RDONLY) == -1) { 387 (void) close(fd); 388 return (fmd_log_open_err(lp, errp, EFDL_EXACCT)); 389 } 390 391 lp->log_abi = abi; 392 lp->log_flags |= FMD_LF_EAOPEN; 393 if (getenv("FMD_LOG_DEBUG") != NULL) 394 lp->log_flags |= FMD_LF_DEBUG; 395 396 /* 397 * Read the first group of log meta-data: the write-once read-only 398 * file header. We read all records in this group, ignoring all but 399 * the VERSION and LABEL, which are required and must be verified. 400 */ 401 if ((grp = ea_get_object_tree(&lp->log_ea, 1)) == NULL) 402 return (fmd_log_open_err(lp, errp, EFDL_EXACCT)); 403 404 if (grp->eo_catalog != CAT_FMA_GROUP) { 405 ea_free_object(grp, EUP_ALLOC); 406 return (fmd_log_open_err(lp, errp, EFDL_EXACCT)); 407 } 408 409 for (obj = grp->eo_group.eg_objs; obj != NULL; obj = obj->eo_next) { 410 switch (obj->eo_catalog) { 411 case CAT_FMA_VERSION: 412 lp->log_version = strdup(obj->eo_item.ei_string); 413 if (lp->log_version == NULL) { 414 ea_free_object(grp, EUP_ALLOC); 415 return (fmd_log_open_err(lp, errp, EFDL_NOMEM)); 416 } 417 break; 418 case CAT_FMA_LABEL: 419 lp->log_label = strdup(obj->eo_item.ei_string); 420 if (lp->log_label == NULL) { 421 ea_free_object(grp, EUP_ALLOC); 422 return (fmd_log_open_err(lp, errp, EFDL_NOMEM)); 423 } 424 break; 425 case CAT_FMA_OSREL: 426 lp->log_osrelease = strdup(obj->eo_item.ei_string); 427 if (lp->log_osrelease == NULL) { 428 ea_free_object(grp, EUP_ALLOC); 429 return (fmd_log_open_err(lp, errp, EFDL_NOMEM)); 430 } 431 break; 432 case CAT_FMA_OSVER: 433 lp->log_osversion = strdup(obj->eo_item.ei_string); 434 if (lp->log_osversion == NULL) { 435 ea_free_object(grp, EUP_ALLOC); 436 return (fmd_log_open_err(lp, errp, EFDL_NOMEM)); 437 } 438 break; 439 case CAT_FMA_PLAT: 440 lp->log_platform = strdup(obj->eo_item.ei_string); 441 if (lp->log_platform == NULL) { 442 ea_free_object(grp, EUP_ALLOC); 443 return (fmd_log_open_err(lp, errp, EFDL_NOMEM)); 444 } 445 break; 446 } 447 } 448 449 ea_free_object(grp, EUP_ALLOC); 450 451 if (lp->log_version == NULL || lp->log_label == NULL) 452 return (fmd_log_open_err(lp, errp, EFDL_BADHDR)); 453 454 /* 455 * Read the second group of log meta-data: the table of contents. At 456 * present there are no records libfmd_log needs in here, so we just 457 * skip over this entire group so that fmd_log_xiter() starts after it. 458 */ 459 if ((grp = ea_get_object_tree(&lp->log_ea, 1)) == NULL) 460 return (fmd_log_open_err(lp, errp, EFDL_EXACCT)); 461 462 if (grp->eo_catalog != CAT_FMA_GROUP) { 463 ea_free_object(grp, EUP_ALLOC); 464 return (fmd_log_open_err(lp, errp, EFDL_EXACCT)); 465 } 466 467 ea_free_object(grp, EUP_ALLOC); 468 lp->log_flags |= FMD_LF_START; 469 470 fmd_log_dprintf(lp, "open log %s dev=%lx ino=%llx\n", lp->log_path, 471 (ulong_t)lp->log_stat.st_dev, (u_longlong_t)lp->log_stat.st_ino); 472 473 return (lp); 474 } 475 476 void 477 fmd_log_close(fmd_log_t *lp) 478 { 479 fmd_log_t *xlp, *nlp; 480 481 if (lp == NULL) 482 return; /* permit null lp to simply caller code */ 483 484 for (xlp = lp->log_xrefs; xlp != NULL; xlp = nlp) { 485 nlp = xlp->log_xnext; 486 fmd_log_close(xlp); 487 } 488 489 if (lp->log_flags & FMD_LF_EAOPEN) 490 (void) ea_close(&lp->log_ea); 491 492 if (lp->log_fd >= 0) 493 (void) close(lp->log_fd); 494 495 free(lp->log_path); 496 free(lp->log_version); 497 free(lp->log_label); 498 free(lp->log_osrelease); 499 free(lp->log_osversion); 500 free(lp->log_platform); 501 502 free(lp); 503 } 504 505 const char * 506 fmd_log_label(fmd_log_t *lp) 507 { 508 return (lp->log_label); 509 } 510 511 void 512 fmd_log_header(fmd_log_t *lp, fmd_log_header_t *hp) 513 { 514 const char *creator = ea_get_creator(&lp->log_ea); 515 const char *hostname = ea_get_hostname(&lp->log_ea); 516 517 hp->log_creator = creator ? creator : ""; 518 hp->log_hostname = hostname ? hostname : ""; 519 hp->log_label = lp->log_label ? lp->log_label : ""; 520 hp->log_version = lp->log_version ? lp->log_version : ""; 521 hp->log_osrelease = lp->log_osrelease ? lp->log_osrelease : ""; 522 hp->log_osversion = lp->log_osversion ? lp->log_osversion : ""; 523 hp->log_platform = lp->log_platform ? lp->log_platform : ""; 524 } 525 526 /* 527 * Note: this will be verrrry slow for big files. If this function becomes 528 * important, we'll need to add a function to libexacct to let us rewind. 529 * Currently libexacct has no notion of seeking other than record-at-a-time. 530 */ 531 int 532 fmd_log_rewind(fmd_log_t *lp) 533 { 534 ea_object_t obj, *grp; 535 536 if (!(lp->log_flags & FMD_LF_START)) { 537 while (ea_previous_object(&lp->log_ea, &obj) != EO_ERROR) 538 continue; /* rewind until beginning of file */ 539 540 if ((grp = ea_get_object_tree(&lp->log_ea, 1)) == NULL) 541 return (fmd_log_set_errno(lp, EFDL_EXACCT)); 542 else 543 ea_free_object(grp, EUP_ALLOC); /* hdr group */ 544 545 if ((grp = ea_get_object_tree(&lp->log_ea, 1)) == NULL) 546 return (fmd_log_set_errno(lp, EFDL_EXACCT)); 547 else 548 ea_free_object(grp, EUP_ALLOC); /* toc group */ 549 550 lp->log_flags |= FMD_LF_START; 551 } 552 553 return (0); 554 } 555 556 static int 557 fmd_log_xiter_filter(fmd_log_t *lp, const fmd_log_record_t *rp, 558 uint_t fac, const fmd_log_filtvec_t *fav) 559 { 560 uint_t i, j; 561 562 for (i = 0; i < fac; i++) { 563 for (j = 0; j < fav[i].filt_argc; j++) { 564 if (fav[i].filt_argv[j].filt_func(lp, rp, 565 fav[i].filt_argv[j].filt_arg) != 0) 566 break; /* logical OR of this class is true */ 567 } 568 569 if (j == fav[i].filt_argc) 570 return (0); /* logical AND of filter is false */ 571 } 572 573 return (1); /* logical AND of filter is true */ 574 } 575 576 static int 577 fmd_log_xiter_filtcmp(const void *lp, const void *rp) 578 { 579 return ((intptr_t)((fmd_log_filter_t *)lp)->filt_func - 580 (intptr_t)((fmd_log_filter_t *)rp)->filt_func); 581 } 582 583 int 584 fmd_log_filter(fmd_log_t *lp, uint_t fc, fmd_log_filter_t *fv, 585 const fmd_log_record_t *rp) 586 { 587 fmd_log_filtvec_t *fav = alloca(fc * sizeof (fmd_log_filtvec_t)); 588 uint_t i, fac = 0; 589 590 /* 591 * If a filter array was provided, create an array of filtvec structs 592 * to perform logical AND/OR processing. See fmd_log_xiter(), below. 593 */ 594 bzero(fav, fc * sizeof (fmd_log_filtvec_t)); 595 qsort(fv, fc, sizeof (fmd_log_filter_t), fmd_log_xiter_filtcmp); 596 597 for (i = 0; i < fc; i++) { 598 if (i == 0 || fv[i].filt_func != fv[i - 1].filt_func) 599 fav[fac++].filt_argv = &fv[i]; 600 fav[fac - 1].filt_argc++; 601 } 602 603 return (fmd_log_xiter_filter(lp, rp, fac, fav)); 604 } 605 606 int 607 fmd_log_xiter(fmd_log_t *lp, uint_t flag, uint_t fc, fmd_log_filter_t *fv, 608 fmd_log_rec_f *rfunc, fmd_log_err_f *efunc, void *private, ulong_t *rcntp) 609 { 610 fmd_log_record_t rec; 611 fmd_log_filtvec_t *fav = NULL; 612 uint_t i, fac = 0; 613 ulong_t rcnt = 0; 614 int rv = 0; 615 616 if (flag & ~FMD_LOG_XITER_MASK) 617 return (fmd_log_set_errno(lp, EINVAL)); 618 619 /* 620 * If a filter array was provided, create an array of filtvec structs 621 * where each filtvec holds a pointer to an equivalent list of filters, 622 * as determined by their filt_func. We sort the input array by func, 623 * and then fill in the filtvec struct array. We can then compute the 624 * logical OR of equivalent filters by iterating over filt_argv, and 625 * we can compute the logical AND of 'fv' by iterating over filt_argc. 626 */ 627 if (fc != 0) { 628 if ((fav = calloc(fc, sizeof (fmd_log_filtvec_t))) == NULL) 629 return (fmd_log_set_errno(lp, EFDL_NOMEM)); 630 631 qsort(fv, fc, sizeof (fmd_log_filter_t), fmd_log_xiter_filtcmp); 632 633 for (i = 0; i < fc; i++) { 634 if (i == 0 || fv[i].filt_func != fv[i - 1].filt_func) 635 fav[fac++].filt_argv = &fv[i]; 636 fav[fac - 1].filt_argc++; 637 } 638 } 639 640 lp->log_flags &= ~FMD_LF_START; 641 ea_clear(&lp->log_ea); 642 643 do { 644 if (fmd_log_load_record(lp, flag, &rec) != 0) { 645 if (lp->log_errno == EFDL_EXACCT + EXR_EOF) 646 break; /* end-of-file reached */ 647 rv = efunc ? efunc(lp, private) : -1; 648 rcnt++; 649 } else { 650 if (fc == 0 || fmd_log_xiter_filter(lp, &rec, fac, fav)) 651 rv = rfunc(lp, &rec, private); 652 653 fmd_log_free_record(&rec); 654 rcnt++; 655 } 656 } while (rv == 0); 657 658 if (fac != 0) 659 free(fav); 660 661 if (rcntp != NULL) 662 *rcntp = rcnt; 663 664 return (rv); 665 } 666 667 int 668 fmd_log_iter(fmd_log_t *lp, fmd_log_rec_f *rfunc, void *private) 669 { 670 return (fmd_log_xiter(lp, 0, 0, NULL, rfunc, NULL, private, NULL)); 671 } 672 673 int 674 fmd_log_seek(fmd_log_t *lp, off64_t off) 675 { 676 lp->log_flags &= ~FMD_LF_START; 677 ea_clear(&lp->log_ea); 678 679 if (lseek64(lp->log_fd, off, SEEK_SET) != off) 680 return (fmd_log_set_errno(lp, errno)); 681 682 return (0); 683 } 684 685 static const char *const _fmd_errs[] = { 686 "client requires newer version of libfmd_log", /* EFDL_VERSION */ 687 "required memory allocation failed", /* EFDL_NOMEM */ 688 "log header did not contain required field", /* EFDL_BADHDR */ 689 "log record did not contain protocol class", /* EFDL_NOCLASS */ 690 "log record has invalid catalog tag", /* EFDL_BADTAG */ 691 "log record has invalid cross-reference group", /* EFDL_BADREF */ 692 "log record has invalid cross-reference dev_t", /* EFDL_BADDEV */ 693 "log record was not of expected type", /* EFDL_EXACCT + OK */ 694 "log access system call failed", /* EXR_SYSCALL_FAIL */ 695 "log file corruption detected", /* EXR_CORRUPT_FILE */ 696 "end-of-file reached", /* EXR_EOF */ 697 "log file does not have appropriate creator", /* EXR_NO_CREATOR */ 698 "invalid unpack buffer specified", /* EXR_INVALID_BUF */ 699 "invalid exacct operation for log file", /* EXR_NOTSUPP */ 700 "log file requires newer version of libexacct", /* EXR_UNKN_VERSION */ 701 "invalid object buffer specified", /* EXR_INVALID_OBJ */ 702 }; 703 704 static const int _fmd_nerr = sizeof (_fmd_errs) / sizeof (_fmd_errs[0]); 705 706 /*ARGSUSED*/ 707 const char * 708 fmd_log_errmsg(fmd_log_t *lp, int err) 709 { 710 const char *msg; 711 712 if (err > EFDL_BASE && err - EFDL_BASE < _fmd_nerr) 713 msg = _fmd_errs[err - EFDL_BASE]; 714 else 715 msg = strerror(err); 716 717 return (msg ? msg : "unknown error"); 718 } 719 720 int 721 fmd_log_errno(fmd_log_t *lp) 722 { 723 return (lp->log_errno); 724 } 725