Home | History | Annotate | Download | only in common
      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