2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A */
2N/A
2N/A#include <sys/fm/protocol.h>
2N/A#include <sys/types.h>
2N/A#include <sys/mkdev.h>
2N/A
2N/A#include <alloca.h>
2N/A#include <unistd.h>
2N/A#include <limits.h>
2N/A#include <strings.h>
2N/A#include <stdlib.h>
2N/A#include <fcntl.h>
2N/A#include <errno.h>
2N/A#include <libgen.h>
2N/A#include <dirent.h>
2N/A#include <fmd_log_impl.h>
2N/A#include <fmd_log.h>
2N/A
2N/A#define CAT_FMA_RGROUP (EXT_GROUP | EXC_DEFAULT | EXD_GROUP_RFMA)
2N/A#define CAT_FMA_GROUP (EXT_GROUP | EXC_DEFAULT | EXD_GROUP_FMA)
2N/A
2N/A#define CAT_FMA_LABEL (EXT_STRING | EXC_DEFAULT | EXD_FMA_LABEL)
2N/A#define CAT_FMA_VERSION (EXT_STRING | EXC_DEFAULT | EXD_FMA_VERSION)
2N/A#define CAT_FMA_OSREL (EXT_STRING | EXC_DEFAULT | EXD_FMA_OSREL)
2N/A#define CAT_FMA_OSVER (EXT_STRING | EXC_DEFAULT | EXD_FMA_OSVER)
2N/A#define CAT_FMA_PLAT (EXT_STRING | EXC_DEFAULT | EXD_FMA_PLAT)
2N/A#define CAT_FMA_UUID (EXT_STRING | EXC_DEFAULT | EXD_FMA_UUID)
2N/A#define CAT_FMA_TODSEC (EXT_UINT64 | EXC_DEFAULT | EXD_FMA_TODSEC)
2N/A#define CAT_FMA_TODNSEC (EXT_UINT64 | EXC_DEFAULT | EXD_FMA_TODNSEC)
2N/A#define CAT_FMA_NVLIST (EXT_RAW | EXC_DEFAULT | EXD_FMA_NVLIST)
2N/A#define CAT_FMA_MAJOR (EXT_UINT32 | EXC_DEFAULT | EXD_FMA_MAJOR)
2N/A#define CAT_FMA_MINOR (EXT_UINT32 | EXC_DEFAULT | EXD_FMA_MINOR)
2N/A#define CAT_FMA_INODE (EXT_UINT64 | EXC_DEFAULT | EXD_FMA_INODE)
2N/A#define CAT_FMA_OFFSET (EXT_UINT64 | EXC_DEFAULT | EXD_FMA_OFFSET)
2N/A
2N/Astatic int fmd_log_load_record(fmd_log_t *, uint_t, fmd_log_record_t *);
2N/Astatic void fmd_log_free_record(fmd_log_record_t *);
2N/Astatic int fmd_log_load_xrefs(fmd_log_t *, uint_t, fmd_log_record_t *);
2N/A
2N/Astatic const char FMD_CREATOR[] = "fmd";
2N/A
2N/A/*
2N/A * fmd_log_set_errno is used as a utility function throughout the library. It
2N/A * sets both lp->log_errno and errno to the specified value. If the current
2N/A * error is EFDL_EXACCT, we store it internally as that value plus ea_error().
2N/A * If no ea_error() is present, we assume EFDL_BADTAG (catalog tag mismatch).
2N/A */
2N/Astatic int
2N/Afmd_log_set_errno(fmd_log_t *lp, int err)
2N/A{
2N/A if (err == EFDL_EXACCT && ea_error() != EXR_OK)
2N/A lp->log_errno = EFDL_EXACCT + ea_error();
2N/A else if (err == EFDL_EXACCT)
2N/A lp->log_errno = EFDL_BADTAG;
2N/A else
2N/A lp->log_errno = err;
2N/A
2N/A errno = lp->log_errno;
2N/A return (-1);
2N/A}
2N/A
2N/A/*PRINTFLIKE2*/
2N/Astatic void
2N/Afmd_log_dprintf(fmd_log_t *lp, const char *format, ...)
2N/A{
2N/A va_list ap;
2N/A
2N/A if (lp->log_flags & FMD_LF_DEBUG) {
2N/A (void) fputs("fmd_log DEBUG: ", stderr);
2N/A va_start(ap, format);
2N/A (void) vfprintf(stderr, format, ap);
2N/A va_end(ap);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * fmd_log_load_record() is used to load the exacct object at the current file
2N/A * location into the specified fmd_log_record structure. Once the caller has
2N/A * made use of this information, it can clean up using fmd_log_free_record().
2N/A */
2N/Astatic int
2N/Afmd_log_load_record(fmd_log_t *lp, uint_t iflags, fmd_log_record_t *rp)
2N/A{
2N/A ea_object_t *grp, *obj;
2N/A off64_t off;
2N/A int err;
2N/A
2N/A if (iflags & FMD_LOG_XITER_OFFS) {
2N/A ea_clear(&lp->log_ea);
2N/A off = lseek64(lp->log_fd, 0, SEEK_CUR);
2N/A }
2N/A
2N/A if ((grp = ea_get_object_tree(&lp->log_ea, 1)) == NULL)
2N/A return (fmd_log_set_errno(lp, EFDL_EXACCT));
2N/A
2N/A if (grp->eo_catalog != CAT_FMA_RGROUP &&
2N/A grp->eo_catalog != CAT_FMA_GROUP) {
2N/A fmd_log_dprintf(lp, "bad catalog tag 0x%x\n", grp->eo_catalog);
2N/A ea_free_object(grp, EUP_ALLOC);
2N/A return (fmd_log_set_errno(lp, EFDL_EXACCT));
2N/A }
2N/A
2N/A bzero(rp, sizeof (fmd_log_record_t));
2N/A rp->rec_grp = grp;
2N/A
2N/A if (iflags & FMD_LOG_XITER_OFFS)
2N/A rp->rec_off = off;
2N/A
2N/A for (obj = grp->eo_group.eg_objs; obj != NULL; obj = obj->eo_next) {
2N/A switch (obj->eo_catalog) {
2N/A case CAT_FMA_NVLIST:
2N/A if ((err = nvlist_unpack(obj->eo_item.ei_raw,
2N/A obj->eo_item.ei_size, &rp->rec_nvl, 0)) != 0) {
2N/A fmd_log_free_record(rp);
2N/A return (fmd_log_set_errno(lp, err));
2N/A }
2N/A break;
2N/A
2N/A case CAT_FMA_TODSEC:
2N/A rp->rec_sec = obj->eo_item.ei_uint64;
2N/A break;
2N/A
2N/A case CAT_FMA_TODNSEC:
2N/A rp->rec_nsec = obj->eo_item.ei_uint64;
2N/A break;
2N/A
2N/A case CAT_FMA_GROUP:
2N/A rp->rec_nrefs += obj->eo_group.eg_nobjs;
2N/A break;
2N/A }
2N/A }
2N/A
2N/A if (rp->rec_nvl == NULL || nvlist_lookup_string(rp->rec_nvl,
2N/A FM_CLASS, (char **)&rp->rec_class) != 0) {
2N/A fmd_log_free_record(rp);
2N/A return (fmd_log_set_errno(lp, EFDL_NOCLASS));
2N/A }
2N/A
2N/A if (rp->rec_nrefs != 0 && fmd_log_load_xrefs(lp, iflags, rp) != 0) {
2N/A err = errno; /* errno is set for us */
2N/A fmd_log_free_record(rp);
2N/A return (fmd_log_set_errno(lp, err));
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * fmd_log_free_record frees memory associated with the specified record. If
2N/A * cross-references are contained in this record, we proceed recursively.
2N/A */
2N/Astatic void
2N/Afmd_log_free_record(fmd_log_record_t *rp)
2N/A{
2N/A uint_t i;
2N/A
2N/A if (rp->rec_xrefs != NULL) {
2N/A for (i = 0; i < rp->rec_nrefs; i++)
2N/A fmd_log_free_record(&rp->rec_xrefs[i]);
2N/A free(rp->rec_xrefs);
2N/A }
2N/A
2N/A nvlist_free(rp->rec_nvl);
2N/A ea_free_object(rp->rec_grp, EUP_ALLOC);
2N/A}
2N/A
2N/A/*
2N/A * fmd_log_load_xref loads the cross-reference represented by the specified
2N/A * exacct group 'grp' into the next empty slot in rp->rec_xrefs. This function
2N/A * is called repeatedly by fmd_log_load_xrefs() for each embedded reference.
2N/A */
2N/Astatic int
2N/Afmd_log_load_xref(fmd_log_t *lp, uint_t iflags,
2N/A fmd_log_record_t *rp, ea_object_t *grp)
2N/A{
2N/A ea_object_t *obj;
2N/A fmd_log_t *xlp;
2N/A dev_t dev;
2N/A
2N/A off64_t off = (off64_t)-1L;
2N/A major_t maj = (major_t)-1L;
2N/A minor_t min = (minor_t)-1L;
2N/A ino64_t ino = (ino64_t)-1L;
2N/A char *uuid = NULL;
2N/A
2N/A for (obj = grp->eo_group.eg_objs; obj != NULL; obj = obj->eo_next) {
2N/A switch (obj->eo_catalog) {
2N/A case CAT_FMA_MAJOR:
2N/A maj = obj->eo_item.ei_uint32;
2N/A break;
2N/A case CAT_FMA_MINOR:
2N/A min = obj->eo_item.ei_uint32;
2N/A break;
2N/A case CAT_FMA_INODE:
2N/A ino = obj->eo_item.ei_uint64;
2N/A break;
2N/A case CAT_FMA_OFFSET:
2N/A off = obj->eo_item.ei_uint64;
2N/A break;
2N/A case CAT_FMA_UUID:
2N/A uuid = obj->eo_item.ei_string;
2N/A break;
2N/A }
2N/A }
2N/A
2N/A if (off == (off64_t)-1L || (uuid == NULL && (ino == (ino64_t)-1L ||
2N/A maj == (major_t)-1L || min == (minor_t)-1L)))
2N/A return (fmd_log_set_errno(lp, EFDL_BADREF));
2N/A
2N/A if (uuid == NULL && (dev = makedev(maj, min)) == NODEV)
2N/A return (fmd_log_set_errno(lp, EFDL_BADDEV));
2N/A
2N/A /*
2N/A * Search our xref list for matching (dev_t, ino64_t) or (uuid).
2N/A * If we can't find one, return silently without
2N/A * doing anything. We expect log xrefs to be broken whenever log
2N/A * files are trimmed or removed; their only purpose is to help us
2N/A * debug diagnosis engine algorithms.
2N/A */
2N/A for (xlp = lp->log_xrefs; xlp != NULL; xlp = xlp->log_xnext) {
2N/A if (uuid == NULL) {
2N/A if (xlp->log_stat.st_ino == ino &&
2N/A xlp->log_stat.st_dev == dev)
2N/A break;
2N/A } else if (xlp->log_uuid != NULL &&
2N/A strcmp(xlp->log_uuid, uuid) == 0)
2N/A break;
2N/A }
2N/A
2N/A if (xlp == NULL) {
2N/A if (uuid == NULL)
2N/A fmd_log_dprintf(lp, "broken xref dev=%lx ino=%llx\n",
2N/A (ulong_t)dev, (u_longlong_t)ino);
2N/A else
2N/A fmd_log_dprintf(lp, "broken xref uuid=%s\n", uuid);
2N/A
2N/A return (0);
2N/A }
2N/A
2N/A xlp->log_flags &= ~FMD_LF_START;
2N/A ea_clear(&xlp->log_ea);
2N/A (void) lseek64(xlp->log_fd, off, SEEK_SET);
2N/A
2N/A return (fmd_log_load_record(xlp,
2N/A iflags, &rp->rec_xrefs[rp->rec_nrefs++]));
2N/A}
2N/A
2N/A/*
2N/A * fmd_log_load_xrdir is called by fmd_log_load_xrefs when the FMD_LF_XREFS bit
2N/A * is not yet set, indicating we haven't looked for cross-referenced files. We
2N/A * open the directory associated with the specified log file and attempt to
2N/A * perform an fmd_log_open() on every file found there (i.e. /var/fm/fmd). If
2N/A * we are successful, the files are chained on to lp->log_xrefs, where the
2N/A * fmd_log_load_xref() function can find them by comparing dev/ino to log_stat.
2N/A */
2N/Astatic void
2N/Afmd_log_load_xrdir(fmd_log_t *lp)
2N/A{
2N/A fmd_log_t *xlp;
2N/A char dirbuf[PATH_MAX], path[PATH_MAX], *dirpath;
2N/A struct dirent *dp;
2N/A DIR *dirp;
2N/A struct stat statbuf;
2N/A
2N/A lp->log_flags |= FMD_LF_XREFS;
2N/A (void) strlcpy(dirbuf, lp->log_path, sizeof (dirbuf));
2N/A dirpath = dirname(dirbuf);
2N/A
2N/A if ((dirp = opendir(dirpath)) == NULL)
2N/A return; /* failed to open directory; just skip it */
2N/A
2N/A while ((dp = readdir(dirp)) != NULL) {
2N/A if (dp->d_name[0] == '.')
2N/A continue; /* skip "." and ".." and hidden files */
2N/A
2N/A (void) snprintf(path, sizeof (path),
2N/A "%s/%s", dirpath, dp->d_name);
2N/A
2N/A if (strcmp(path, lp->log_path) != 0 &&
2N/A stat(path, &statbuf) != -1 &&
2N/A (statbuf.st_mode & S_IFMT) == S_IFREG &&
2N/A (xlp = fmd_log_open(lp->log_abi, path, NULL)) != NULL) {
2N/A fmd_log_dprintf(lp, "%s loaded %s for xrefs\n",
2N/A lp->log_path, xlp->log_path);
2N/A xlp->log_xnext = lp->log_xrefs;
2N/A lp->log_xrefs = xlp;
2N/A }
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * fmd_log_load_xrefs iterates again over the record's exacct group and for
2N/A * each cross-reference (embedded CAT_FMA_GROUP), attempts to fill in the
2N/A * corresponding xref. rp->rec_nrefs is reset to the number of valid items
2N/A * in the finished rp->rec_xrefs array; see fmd_log_load_xref() for more info.
2N/A */
2N/Astatic int
2N/Afmd_log_load_xrefs(fmd_log_t *lp, uint_t iflags, fmd_log_record_t *rp)
2N/A{
2N/A size_t size = sizeof (fmd_log_record_t) * rp->rec_nrefs;
2N/A ea_object_t *rgrp = rp->rec_grp;
2N/A ea_object_t *grp, *obj;
2N/A
2N/A if (!(iflags & FMD_LOG_XITER_REFS))
2N/A return (0); /* do not load any xrefs */
2N/A
2N/A if (!(lp->log_flags & FMD_LF_XREFS))
2N/A fmd_log_load_xrdir(lp);
2N/A
2N/A if ((rp->rec_xrefs = malloc(size)) == NULL)
2N/A return (fmd_log_set_errno(lp, EFDL_NOMEM));
2N/A
2N/A bzero(rp->rec_xrefs, size);
2N/A rp->rec_nrefs = 0;
2N/A
2N/A /*
2N/A * Make a second pass through the record group to locate and process
2N/A * each cross-reference sub-group. The structure of the groups is
2N/A * as follows (left-hand-side symbols named after the variables used):
2N/A *
2N/A * rgrp := CAT_FMA_TODSEC CAT_FMA_TODNSEC CAT_FMA_NVLIST grp*
2N/A * grp := obj* (i.e. zero or more groups of xref items)
2N/A * obj := CAT_FMA_MAJOR CAT_FMA_MINOR CAT_FMA_INODE CAT_FMA_OFFSET
2N/A *
2N/A * For each xref 'obj', we call fmd_log_load_xref() to parse the four
2N/A * xref members and then load the specified record into rp->rec_xrefs.
2N/A */
2N/A for (grp = rgrp->eo_group.eg_objs; grp != NULL; grp = grp->eo_next) {
2N/A if (grp->eo_catalog != CAT_FMA_GROUP)
2N/A continue; /* ignore anything that isn't a group */
2N/A
2N/A for (obj = grp->eo_group.eg_objs;
2N/A obj != NULL; obj = obj->eo_next) {
2N/A if (fmd_log_load_xref(lp, iflags, rp, obj) != 0)
2N/A return (-1); /* errno is set for us */
2N/A }
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Astatic fmd_log_t *
2N/Afmd_log_open_err(fmd_log_t *lp, int *errp, int err)
2N/A{
2N/A if (errp != NULL)
2N/A *errp = err == EFDL_EXACCT ? EFDL_EXACCT + ea_error() : err;
2N/A
2N/A if (lp != NULL)
2N/A fmd_log_close(lp);
2N/A
2N/A return (NULL);
2N/A}
2N/A
2N/Afmd_log_t *
2N/Afmd_log_open(int abi, const char *name, int *errp)
2N/A{
2N/A ea_object_t *grp, *obj;
2N/A fmd_log_t *lp;
2N/A int fd;
2N/A
2N/A if (abi > FMD_LOG_VERSION)
2N/A return (fmd_log_open_err(NULL, errp, EFDL_VERSION));
2N/A
2N/A if ((lp = malloc(sizeof (fmd_log_t))) == NULL)
2N/A return (fmd_log_open_err(NULL, errp, EFDL_NOMEM));
2N/A
2N/A bzero(lp, sizeof (fmd_log_t));
2N/A
2N/A if ((lp->log_path = strdup(name)) == NULL)
2N/A return (fmd_log_open_err(lp, errp, EFDL_NOMEM));
2N/A
2N/A if ((lp->log_fd = open64(name, O_RDONLY)) == -1 ||
2N/A fstat64(lp->log_fd, &lp->log_stat) == -1 ||
2N/A (fd = dup(lp->log_fd)) == -1)
2N/A return (fmd_log_open_err(lp, errp, errno));
2N/A
2N/A if (ea_fdopen(&lp->log_ea, fd, FMD_CREATOR,
2N/A EO_VALID_HDR | EO_HEAD, O_RDONLY) == -1) {
2N/A (void) close(fd);
2N/A return (fmd_log_open_err(lp, errp, EFDL_EXACCT));
2N/A }
2N/A
2N/A lp->log_abi = abi;
2N/A lp->log_flags |= FMD_LF_EAOPEN;
2N/A if (getenv("FMD_LOG_DEBUG") != NULL)
2N/A lp->log_flags |= FMD_LF_DEBUG;
2N/A
2N/A /*
2N/A * Read the first group of log meta-data: the write-once read-only
2N/A * file header. We read all records in this group, ignoring all but
2N/A * the VERSION and LABEL, which are required and must be verified.
2N/A */
2N/A if ((grp = ea_get_object_tree(&lp->log_ea, 1)) == NULL)
2N/A return (fmd_log_open_err(lp, errp, EFDL_EXACCT));
2N/A
2N/A if (grp->eo_catalog != CAT_FMA_GROUP) {
2N/A ea_free_object(grp, EUP_ALLOC);
2N/A return (fmd_log_open_err(lp, errp, EFDL_EXACCT));
2N/A }
2N/A
2N/A for (obj = grp->eo_group.eg_objs; obj != NULL; obj = obj->eo_next) {
2N/A switch (obj->eo_catalog) {
2N/A case CAT_FMA_VERSION:
2N/A lp->log_version = strdup(obj->eo_item.ei_string);
2N/A if (lp->log_version == NULL) {
2N/A ea_free_object(grp, EUP_ALLOC);
2N/A return (fmd_log_open_err(lp, errp, EFDL_NOMEM));
2N/A }
2N/A break;
2N/A case CAT_FMA_LABEL:
2N/A lp->log_label = strdup(obj->eo_item.ei_string);
2N/A if (lp->log_label == NULL) {
2N/A ea_free_object(grp, EUP_ALLOC);
2N/A return (fmd_log_open_err(lp, errp, EFDL_NOMEM));
2N/A }
2N/A break;
2N/A case CAT_FMA_OSREL:
2N/A lp->log_osrelease = strdup(obj->eo_item.ei_string);
2N/A if (lp->log_osrelease == NULL) {
2N/A ea_free_object(grp, EUP_ALLOC);
2N/A return (fmd_log_open_err(lp, errp, EFDL_NOMEM));
2N/A }
2N/A break;
2N/A case CAT_FMA_OSVER:
2N/A lp->log_osversion = strdup(obj->eo_item.ei_string);
2N/A if (lp->log_osversion == NULL) {
2N/A ea_free_object(grp, EUP_ALLOC);
2N/A return (fmd_log_open_err(lp, errp, EFDL_NOMEM));
2N/A }
2N/A break;
2N/A case CAT_FMA_PLAT:
2N/A lp->log_platform = strdup(obj->eo_item.ei_string);
2N/A if (lp->log_platform == NULL) {
2N/A ea_free_object(grp, EUP_ALLOC);
2N/A return (fmd_log_open_err(lp, errp, EFDL_NOMEM));
2N/A }
2N/A break;
2N/A case CAT_FMA_UUID:
2N/A lp->log_uuid = strdup(obj->eo_item.ei_string);
2N/A if (lp->log_uuid == NULL) {
2N/A ea_free_object(grp, EUP_ALLOC);
2N/A return (fmd_log_open_err(lp, errp, EFDL_NOMEM));
2N/A }
2N/A break;
2N/A }
2N/A }
2N/A
2N/A ea_free_object(grp, EUP_ALLOC);
2N/A
2N/A if (lp->log_version == NULL || lp->log_label == NULL)
2N/A return (fmd_log_open_err(lp, errp, EFDL_BADHDR));
2N/A
2N/A /*
2N/A * Read the second group of log meta-data: the table of contents. At
2N/A * present there are no records libfmd_log needs in here, so we just
2N/A * skip over this entire group so that fmd_log_xiter() starts after it.
2N/A */
2N/A if ((grp = ea_get_object_tree(&lp->log_ea, 1)) == NULL)
2N/A return (fmd_log_open_err(lp, errp, EFDL_EXACCT));
2N/A
2N/A if (grp->eo_catalog != CAT_FMA_GROUP) {
2N/A ea_free_object(grp, EUP_ALLOC);
2N/A return (fmd_log_open_err(lp, errp, EFDL_EXACCT));
2N/A }
2N/A
2N/A ea_free_object(grp, EUP_ALLOC);
2N/A lp->log_flags |= FMD_LF_START;
2N/A
2N/A fmd_log_dprintf(lp, "open log %s dev=%lx ino=%llx\n", lp->log_path,
2N/A (ulong_t)lp->log_stat.st_dev, (u_longlong_t)lp->log_stat.st_ino);
2N/A
2N/A return (lp);
2N/A}
2N/A
2N/Avoid
2N/Afmd_log_close(fmd_log_t *lp)
2N/A{
2N/A fmd_log_t *xlp, *nlp;
2N/A
2N/A if (lp == NULL)
2N/A return; /* permit null lp to simply caller code */
2N/A
2N/A for (xlp = lp->log_xrefs; xlp != NULL; xlp = nlp) {
2N/A nlp = xlp->log_xnext;
2N/A fmd_log_close(xlp);
2N/A }
2N/A
2N/A if (lp->log_flags & FMD_LF_EAOPEN)
2N/A (void) ea_close(&lp->log_ea);
2N/A
2N/A if (lp->log_fd >= 0)
2N/A (void) close(lp->log_fd);
2N/A
2N/A free(lp->log_path);
2N/A free(lp->log_version);
2N/A free(lp->log_label);
2N/A free(lp->log_osrelease);
2N/A free(lp->log_osversion);
2N/A free(lp->log_platform);
2N/A free(lp->log_uuid);
2N/A
2N/A free(lp);
2N/A}
2N/A
2N/Aconst char *
2N/Afmd_log_label(fmd_log_t *lp)
2N/A{
2N/A return (lp->log_label);
2N/A}
2N/A
2N/Avoid
2N/Afmd_log_header(fmd_log_t *lp, fmd_log_header_t *hp)
2N/A{
2N/A const char *creator = ea_get_creator(&lp->log_ea);
2N/A const char *hostname = ea_get_hostname(&lp->log_ea);
2N/A
2N/A hp->log_creator = creator ? creator : "";
2N/A hp->log_hostname = hostname ? hostname : "";
2N/A hp->log_label = lp->log_label ? lp->log_label : "";
2N/A hp->log_version = lp->log_version ? lp->log_version : "";
2N/A hp->log_osrelease = lp->log_osrelease ? lp->log_osrelease : "";
2N/A hp->log_osversion = lp->log_osversion ? lp->log_osversion : "";
2N/A hp->log_platform = lp->log_platform ? lp->log_platform : "";
2N/A if (lp->log_abi > 1)
2N/A hp->log_uuid = lp->log_uuid ? lp->log_uuid : "";
2N/A}
2N/A
2N/A/*
2N/A * Note: this will be verrrry slow for big files. If this function becomes
2N/A * important, we'll need to add a function to libexacct to let us rewind.
2N/A * Currently libexacct has no notion of seeking other than record-at-a-time.
2N/A */
2N/Aint
2N/Afmd_log_rewind(fmd_log_t *lp)
2N/A{
2N/A ea_object_t obj, *grp;
2N/A
2N/A if (!(lp->log_flags & FMD_LF_START)) {
2N/A while (ea_previous_object(&lp->log_ea, &obj) != EO_ERROR)
2N/A continue; /* rewind until beginning of file */
2N/A
2N/A if ((grp = ea_get_object_tree(&lp->log_ea, 1)) == NULL)
2N/A return (fmd_log_set_errno(lp, EFDL_EXACCT));
2N/A else
2N/A ea_free_object(grp, EUP_ALLOC); /* hdr group */
2N/A
2N/A if ((grp = ea_get_object_tree(&lp->log_ea, 1)) == NULL)
2N/A return (fmd_log_set_errno(lp, EFDL_EXACCT));
2N/A else
2N/A ea_free_object(grp, EUP_ALLOC); /* toc group */
2N/A
2N/A lp->log_flags |= FMD_LF_START;
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Astatic int
2N/Afmd_log_xiter_filter(fmd_log_t *lp, const fmd_log_record_t *rp,
2N/A uint_t fac, const fmd_log_filtvec_t *fav)
2N/A{
2N/A uint_t i, j;
2N/A
2N/A for (i = 0; i < fac; i++) {
2N/A for (j = 0; j < fav[i].filt_argc; j++) {
2N/A if (fav[i].filt_argv[j].filt_func(lp, rp,
2N/A fav[i].filt_argv[j].filt_arg) != 0)
2N/A break; /* logical OR of this class is true */
2N/A }
2N/A
2N/A if (j == fav[i].filt_argc)
2N/A return (0); /* logical AND of filter is false */
2N/A }
2N/A
2N/A return (1); /* logical AND of filter is true */
2N/A}
2N/A
2N/Astatic int
2N/Afmd_log_xiter_filtcmp(const void *lp, const void *rp)
2N/A{
2N/A return ((intptr_t)((fmd_log_filter_t *)lp)->filt_func -
2N/A (intptr_t)((fmd_log_filter_t *)rp)->filt_func);
2N/A}
2N/A
2N/Aint
2N/Afmd_log_filter(fmd_log_t *lp, uint_t fc, fmd_log_filter_t *fv,
2N/A const fmd_log_record_t *rp)
2N/A{
2N/A fmd_log_filtvec_t *fav = alloca(fc * sizeof (fmd_log_filtvec_t));
2N/A uint_t i, fac = 0;
2N/A
2N/A /*
2N/A * If a filter array was provided, create an array of filtvec structs
2N/A * to perform logical AND/OR processing. See fmd_log_xiter(), below.
2N/A */
2N/A bzero(fav, fc * sizeof (fmd_log_filtvec_t));
2N/A qsort(fv, fc, sizeof (fmd_log_filter_t), fmd_log_xiter_filtcmp);
2N/A
2N/A for (i = 0; i < fc; i++) {
2N/A if (i == 0 || fv[i].filt_func != fv[i - 1].filt_func)
2N/A fav[fac++].filt_argv = &fv[i];
2N/A fav[fac - 1].filt_argc++;
2N/A }
2N/A
2N/A return (fmd_log_xiter_filter(lp, rp, fac, fav));
2N/A}
2N/A
2N/Aint
2N/Afmd_log_xiter(fmd_log_t *lp, uint_t flag, uint_t fc, fmd_log_filter_t *fv,
2N/A fmd_log_rec_f *rfunc, fmd_log_err_f *efunc, void *private, ulong_t *rcntp)
2N/A{
2N/A fmd_log_record_t rec;
2N/A fmd_log_filtvec_t *fav = NULL;
2N/A uint_t i, fac = 0;
2N/A ulong_t rcnt = 0;
2N/A int rv = 0;
2N/A
2N/A if (flag & ~FMD_LOG_XITER_MASK)
2N/A return (fmd_log_set_errno(lp, EINVAL));
2N/A
2N/A /*
2N/A * If a filter array was provided, create an array of filtvec structs
2N/A * where each filtvec holds a pointer to an equivalent list of filters,
2N/A * as determined by their filt_func. We sort the input array by func,
2N/A * and then fill in the filtvec struct array. We can then compute the
2N/A * logical OR of equivalent filters by iterating over filt_argv, and
2N/A * we can compute the logical AND of 'fv' by iterating over filt_argc.
2N/A */
2N/A if (fc != 0) {
2N/A if ((fav = calloc(fc, sizeof (fmd_log_filtvec_t))) == NULL)
2N/A return (fmd_log_set_errno(lp, EFDL_NOMEM));
2N/A
2N/A qsort(fv, fc, sizeof (fmd_log_filter_t), fmd_log_xiter_filtcmp);
2N/A
2N/A for (i = 0; i < fc; i++) {
2N/A if (i == 0 || fv[i].filt_func != fv[i - 1].filt_func)
2N/A fav[fac++].filt_argv = &fv[i];
2N/A fav[fac - 1].filt_argc++;
2N/A }
2N/A }
2N/A
2N/A lp->log_flags &= ~FMD_LF_START;
2N/A ea_clear(&lp->log_ea);
2N/A
2N/A do {
2N/A if (fmd_log_load_record(lp, flag, &rec) != 0) {
2N/A if (lp->log_errno == EFDL_EXACCT + EXR_EOF)
2N/A break; /* end-of-file reached */
2N/A rv = efunc ? efunc(lp, private) : -1;
2N/A rcnt++;
2N/A } else {
2N/A if (fc == 0 || fmd_log_xiter_filter(lp, &rec, fac, fav))
2N/A rv = rfunc(lp, &rec, private);
2N/A
2N/A fmd_log_free_record(&rec);
2N/A rcnt++;
2N/A }
2N/A } while (rv == 0);
2N/A
2N/A if (fac != 0)
2N/A free(fav);
2N/A
2N/A if (rcntp != NULL)
2N/A *rcntp = rcnt;
2N/A
2N/A return (rv);
2N/A}
2N/A
2N/Aint
2N/Afmd_log_iter(fmd_log_t *lp, fmd_log_rec_f *rfunc, void *private)
2N/A{
2N/A return (fmd_log_xiter(lp, 0, 0, NULL, rfunc, NULL, private, NULL));
2N/A}
2N/A
2N/Aint
2N/Afmd_log_seek(fmd_log_t *lp, off64_t off)
2N/A{
2N/A lp->log_flags &= ~FMD_LF_START;
2N/A ea_clear(&lp->log_ea);
2N/A
2N/A if (lseek64(lp->log_fd, off, SEEK_SET) != off)
2N/A return (fmd_log_set_errno(lp, errno));
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Astatic const char *const _fmd_errs[] = {
2N/A "client requires newer version of libfmd_log", /* EFDL_VERSION */
2N/A "required memory allocation failed", /* EFDL_NOMEM */
2N/A "log header did not contain required field", /* EFDL_BADHDR */
2N/A "log record did not contain protocol class", /* EFDL_NOCLASS */
2N/A "log record has invalid catalog tag", /* EFDL_BADTAG */
2N/A "log record has invalid cross-reference group", /* EFDL_BADREF */
2N/A "log record has invalid cross-reference dev_t", /* EFDL_BADDEV */
2N/A "log record was not of expected type", /* EFDL_EXACCT + OK */
2N/A "log access system call failed", /* EXR_SYSCALL_FAIL */
2N/A "log file corruption detected", /* EXR_CORRUPT_FILE */
2N/A "end-of-file reached", /* EXR_EOF */
2N/A "log file does not have appropriate creator", /* EXR_NO_CREATOR */
2N/A "invalid unpack buffer specified", /* EXR_INVALID_BUF */
2N/A "invalid exacct operation for log file", /* EXR_NOTSUPP */
2N/A "log file requires newer version of libexacct", /* EXR_UNKN_VERSION */
2N/A "invalid object buffer specified", /* EXR_INVALID_OBJ */
2N/A};
2N/A
2N/Astatic const int _fmd_nerr = sizeof (_fmd_errs) / sizeof (_fmd_errs[0]);
2N/A
2N/A/*ARGSUSED*/
2N/Aconst char *
2N/Afmd_log_errmsg(fmd_log_t *lp, int err)
2N/A{
2N/A const char *msg;
2N/A
2N/A if (err >= EFDL_BASE && err - EFDL_BASE < _fmd_nerr)
2N/A msg = _fmd_errs[err - EFDL_BASE];
2N/A else
2N/A msg = strerror(err);
2N/A
2N/A return (msg ? msg : "unknown error");
2N/A}
2N/A
2N/Aint
2N/Afmd_log_errno(fmd_log_t *lp)
2N/A{
2N/A return (lp->log_errno);
2N/A}