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_UUID (EXT_STRING | EXC_DEFAULT | EXD_FMA_UUID) 54 #define CAT_FMA_TODSEC (EXT_UINT64 | EXC_DEFAULT | EXD_FMA_TODSEC) 55 #define CAT_FMA_TODNSEC (EXT_UINT64 | EXC_DEFAULT | EXD_FMA_TODNSEC) 56 #define CAT_FMA_NVLIST (EXT_RAW | EXC_DEFAULT | EXD_FMA_NVLIST) 57 #define CAT_FMA_MAJOR (EXT_UINT32 | EXC_DEFAULT | EXD_FMA_MAJOR) 58 #define CAT_FMA_MINOR (EXT_UINT32 | EXC_DEFAULT | EXD_FMA_MINOR) 59 #define CAT_FMA_INODE (EXT_UINT64 | EXC_DEFAULT | EXD_FMA_INODE) 60 #define CAT_FMA_OFFSET (EXT_UINT64 | EXC_DEFAULT | EXD_FMA_OFFSET) 61 62 static int fmd_log_load_record(fmd_log_t *, uint_t, fmd_log_record_t *); 63 static void fmd_log_free_record(fmd_log_record_t *); 64 static int fmd_log_load_xrefs(fmd_log_t *, uint_t, fmd_log_record_t *); 65 66 static const char FMD_CREATOR[] = "fmd"; 67 68 /* 69 * fmd_log_set_errno is used as a utility function throughout the library. It 70 * sets both lp->log_errno and errno to the specified value. If the current 71 * error is EFDL_EXACCT, we store it internally as that value plus ea_error(). 72 * If no ea_error() is present, we assume EFDL_BADTAG (catalog tag mismatch). 73 */ 74 static int 75 fmd_log_set_errno(fmd_log_t *lp, int err) 76 { 77 if (err == EFDL_EXACCT && ea_error() != EXR_OK) 78 lp->log_errno = EFDL_EXACCT + ea_error(); 79 else if (err == EFDL_EXACCT) 80 lp->log_errno = EFDL_BADTAG; 81 else 82 lp->log_errno = err; 83 84 errno = lp->log_errno; 85 return (-1); 86 } 87 88 /*PRINTFLIKE2*/ 89 static void 90 fmd_log_dprintf(fmd_log_t *lp, const char *format, ...) 91 { 92 va_list ap; 93 94 if (lp->log_flags & FMD_LF_DEBUG) { 95 (void) fputs("fmd_log DEBUG: ", stderr); 96 va_start(ap, format); 97 (void) vfprintf(stderr, format, ap); 98 va_end(ap); 99 } 100 } 101 102 /* 103 * fmd_log_load_record() is used to load the exacct object at the current file 104 * location into the specified fmd_log_record structure. Once the caller has 105 * made use of this information, it can clean up using fmd_log_free_record(). 106 */ 107 static int 108 fmd_log_load_record(fmd_log_t *lp, uint_t iflags, fmd_log_record_t *rp) 109 { 110 ea_object_t *grp, *obj; 111 off64_t off; 112 int err; 113 114 if (iflags & FMD_LOG_XITER_OFFS) { 115 ea_clear(&lp->log_ea); 116 off = lseek64(lp->log_fd, 0, SEEK_CUR); 117 } 118 119 if ((grp = ea_get_object_tree(&lp->log_ea, 1)) == NULL) 120 return (fmd_log_set_errno(lp, EFDL_EXACCT)); 121 122 if (grp->eo_catalog != CAT_FMA_RGROUP && 123 grp->eo_catalog != CAT_FMA_GROUP) { 124 fmd_log_dprintf(lp, "bad catalog tag 0x%x\n", grp->eo_catalog); 125 ea_free_object(grp, EUP_ALLOC); 126 return (fmd_log_set_errno(lp, EFDL_EXACCT)); 127 } 128 129 bzero(rp, sizeof (fmd_log_record_t)); 130 rp->rec_grp = grp; 131 132 if (iflags & FMD_LOG_XITER_OFFS) 133 rp->rec_off = off; 134 135 for (obj = grp->eo_group.eg_objs; obj != NULL; obj = obj->eo_next) { 136 switch (obj->eo_catalog) { 137 case CAT_FMA_NVLIST: 138 if ((err = nvlist_unpack(obj->eo_item.ei_raw, 139 obj->eo_item.ei_size, &rp->rec_nvl, 0)) != 0) { 140 fmd_log_free_record(rp); 141 return (fmd_log_set_errno(lp, err)); 142 } 143 break; 144 145 case CAT_FMA_TODSEC: 146 rp->rec_sec = obj->eo_item.ei_uint64; 147 break; 148 149 case CAT_FMA_TODNSEC: 150 rp->rec_nsec = obj->eo_item.ei_uint64; 151 break; 152 153 case CAT_FMA_GROUP: 154 rp->rec_nrefs += obj->eo_group.eg_nobjs; 155 break; 156 } 157 } 158 159 if (rp->rec_nvl == NULL || nvlist_lookup_string(rp->rec_nvl, 160 FM_CLASS, (char **)&rp->rec_class) != 0) { 161 fmd_log_free_record(rp); 162 return (fmd_log_set_errno(lp, EFDL_NOCLASS)); 163 } 164 165 if (rp->rec_nrefs != 0 && fmd_log_load_xrefs(lp, iflags, rp) != 0) { 166 err = errno; /* errno is set for us */ 167 fmd_log_free_record(rp); 168 return (fmd_log_set_errno(lp, err)); 169 } 170 171 return (0); 172 } 173 174 /* 175 * fmd_log_free_record frees memory associated with the specified record. If 176 * cross-references are contained in this record, we proceed recursively. 177 */ 178 static void 179 fmd_log_free_record(fmd_log_record_t *rp) 180 { 181 uint_t i; 182 183 if (rp->rec_xrefs != NULL) { 184 for (i = 0; i < rp->rec_nrefs; i++) 185 fmd_log_free_record(&rp->rec_xrefs[i]); 186 free(rp->rec_xrefs); 187 } 188 189 nvlist_free(rp->rec_nvl); 190 ea_free_object(rp->rec_grp, EUP_ALLOC); 191 } 192 193 /* 194 * fmd_log_load_xref loads the cross-reference represented by the specified 195 * exacct group 'grp' into the next empty slot in rp->rec_xrefs. This function 196 * is called repeatedly by fmd_log_load_xrefs() for each embedded reference. 197 */ 198 static int 199 fmd_log_load_xref(fmd_log_t *lp, uint_t iflags, 200 fmd_log_record_t *rp, ea_object_t *grp) 201 { 202 ea_object_t *obj; 203 fmd_log_t *xlp; 204 dev_t dev; 205 206 off64_t off = (off64_t)-1L; 207 major_t maj = (major_t)-1L; 208 minor_t min = (minor_t)-1L; 209 ino64_t ino = (ino64_t)-1L; 210 char *uuid = NULL; 211 212 for (obj = grp->eo_group.eg_objs; obj != NULL; obj = obj->eo_next) { 213 switch (obj->eo_catalog) { 214 case CAT_FMA_MAJOR: 215 maj = obj->eo_item.ei_uint32; 216 break; 217 case CAT_FMA_MINOR: 218 min = obj->eo_item.ei_uint32; 219 break; 220 case CAT_FMA_INODE: 221 ino = obj->eo_item.ei_uint64; 222 break; 223 case CAT_FMA_OFFSET: 224 off = obj->eo_item.ei_uint64; 225 break; 226 case CAT_FMA_UUID: 227 uuid = obj->eo_item.ei_string; 228 break; 229 } 230 } 231 232 if (off == (off64_t)-1L || (uuid == NULL && (ino == (ino64_t)-1L || 233 maj == (major_t)-1L || min == (minor_t)-1L))) 234 return (fmd_log_set_errno(lp, EFDL_BADREF)); 235 236 if (uuid == NULL && (dev = makedev(maj, min)) == NODEV) 237 return (fmd_log_set_errno(lp, EFDL_BADDEV)); 238 239 /* 240 * Search our xref list for matching (dev_t, ino64_t) or (uuid). 241 * If we can't find one, return silently without 242 * doing anything. We expect log xrefs to be broken whenever log 243 * files are trimmed or removed; their only purpose is to help us 244 * debug diagnosis engine algorithms. 245 */ 246 for (xlp = lp->log_xrefs; xlp != NULL; xlp = xlp->log_xnext) { 247 if (uuid == NULL) { 248 if (xlp->log_stat.st_ino == ino && 249 xlp->log_stat.st_dev == dev) 250 break; 251 } else if (xlp->log_uuid != NULL && 252 strcmp(xlp->log_uuid, uuid) == 0) 253 break; 254 } 255 256 if (xlp == NULL) { 257 if (uuid == NULL) 258 fmd_log_dprintf(lp, "broken xref dev=%lx ino=%llx\n", 259 (ulong_t)dev, (u_longlong_t)ino); 260 else 261 fmd_log_dprintf(lp, "broken xref uuid=%s\n", uuid); 262 263 return (0); 264 } 265 266 xlp->log_flags &= ~FMD_LF_START; 267 ea_clear(&xlp->log_ea); 268 (void) lseek64(xlp->log_fd, off, SEEK_SET); 269 270 return (fmd_log_load_record(xlp, 271 iflags, &rp->rec_xrefs[rp->rec_nrefs++])); 272 } 273 274 /* 275 * fmd_log_load_xrdir is called by fmd_log_load_xrefs when the FMD_LF_XREFS bit 276 * is not yet set, indicating we haven't looked for cross-referenced files. We 277 * open the directory associated with the specified log file and attempt to 278 * perform an fmd_log_open() on every file found there (i.e. /var/fm/fmd). If 279 * we are successful, the files are chained on to lp->log_xrefs, where the 280 * fmd_log_load_xref() function can find them by comparing dev/ino to log_stat. 281 */ 282 static void 283 fmd_log_load_xrdir(fmd_log_t *lp) 284 { 285 fmd_log_t *xlp; 286 char dirbuf[PATH_MAX], path[PATH_MAX], *dirpath; 287 struct dirent *dp; 288 DIR *dirp; 289 290 lp->log_flags |= FMD_LF_XREFS; 291 (void) strlcpy(dirbuf, lp->log_path, sizeof (dirbuf)); 292 dirpath = dirname(dirbuf); 293 294 if ((dirp = opendir(dirpath)) == NULL) 295 return; /* failed to open directory; just skip it */ 296 297 while ((dp = readdir(dirp)) != NULL) { 298 if (dp->d_name[0] == '.') 299 continue; /* skip "." and ".." and hidden files */ 300 301 (void) snprintf(path, sizeof (path), 302 "%s/%s", dirpath, dp->d_name); 303 304 if (strcmp(path, lp->log_path) != 0 && 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