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