root/usr/src/lib/fm/libfmevent/common/fmev_evaccess.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
 */

/*
 * Subscription event access interfaces.
 */

#include <sys/types.h>
#include <limits.h>
#include <atomic.h>
#include <libsysevent.h>
#include <umem.h>
#include <fm/libfmevent.h>
#include <sys/fm/protocol.h>

#include "fmev_impl.h"

#define FMEV_API_ENTER(iep, v) \
        fmev_api_enter(fmev_shdl_cmn(((iep)->ei_hdl)), LIBFMEVENT_VERSION_##v)

typedef struct {
        uint32_t ei_magic;              /* _FMEVMAGIC */
        volatile uint32_t ei_refcnt;    /* reference count */
        fmev_shdl_t ei_hdl;             /* handle received on */
        nvlist_t *ei_nvl;               /* (duped) sysevent attribute list */
        uint64_t ei_fmtime[2];          /* embedded protocol event time */
} fmev_impl_t;

#define FMEV2IMPL(ev)   ((fmev_impl_t *)(ev))
#define IMPL2FMEV(iep)  ((fmev_t)(iep))

#define _FMEVMAGIC      0x466d4576      /* "FmEv" */

#define EVENT_VALID(iep) ((iep)->ei_magic == _FMEVMAGIC && \
        (iep)->ei_refcnt > 0 && fmev_shdl_valid((iep)->ei_hdl))

#define FM_TIME_SEC     0
#define FM_TIME_NSEC    1

/*
 * Transform a received sysevent_t into an fmev_t.
 */

uint64_t fmev_bad_attr, fmev_bad_tod, fmev_bad_class;

fmev_t
fmev_sysev2fmev(fmev_shdl_t hdl, sysevent_t *sep, char **clsp, nvlist_t **nvlp)
{
        fmev_impl_t *iep;
        uint64_t *tod;
        uint_t nelem;

        if ((iep = fmev_shdl_alloc(hdl, sizeof (*iep))) == NULL)
                return (NULL);

        /*
         * sysevent_get_attr_list duplicates the nvlist - we free it
         * in fmev_free when the reference count hits zero.
         */
        if (sysevent_get_attr_list(sep, &iep->ei_nvl) != 0) {
                fmev_shdl_free(hdl, iep, sizeof (*iep));
                fmev_bad_attr++;
                return (NULL);
        }

        *nvlp = iep->ei_nvl;

        if (nvlist_lookup_string(iep->ei_nvl, FM_CLASS, clsp) != 0) {
                nvlist_free(iep->ei_nvl);
                fmev_shdl_free(hdl, iep, sizeof (*iep));
                fmev_bad_class++;
                return (NULL);
        }

        if (nvlist_lookup_uint64_array(iep->ei_nvl, "__tod", &tod,
            &nelem) != 0 || nelem != 2) {
                nvlist_free(iep->ei_nvl);
                fmev_shdl_free(hdl, iep, sizeof (*iep));
                fmev_bad_tod++;
                return (NULL);
        }

        iep->ei_fmtime[FM_TIME_SEC] = tod[0];
        iep->ei_fmtime[FM_TIME_NSEC] = tod[1];

        /*
         * Now remove the fmd-private __tod and __ttl members.
         */
        (void) nvlist_remove_all(iep->ei_nvl, "__tod");
        (void) nvlist_remove_all(iep->ei_nvl, "__ttl");

        iep->ei_magic = _FMEVMAGIC;
        iep->ei_hdl = hdl;
        iep->ei_refcnt = 1;
        ASSERT(EVENT_VALID(iep));

        return (IMPL2FMEV(iep));
}

static void
fmev_free(fmev_impl_t *iep)
{
        ASSERT(iep->ei_refcnt == 0);

        nvlist_free(iep->ei_nvl);
        fmev_shdl_free(iep->ei_hdl, iep, sizeof (*iep));
}

void
fmev_hold(fmev_t ev)
{
        fmev_impl_t *iep = FMEV2IMPL(ev);

        ASSERT(EVENT_VALID(iep));

        (void) FMEV_API_ENTER(iep, 1);

        atomic_inc_32(&iep->ei_refcnt);
}

void
fmev_rele(fmev_t ev)
{
        fmev_impl_t *iep = FMEV2IMPL(ev);

        ASSERT(EVENT_VALID(iep));

        (void) FMEV_API_ENTER(iep, 1);

        if (atomic_dec_32_nv(&iep->ei_refcnt) == 0)
                fmev_free(iep);
}

fmev_t
fmev_dup(fmev_t ev)
{
        fmev_impl_t *iep = FMEV2IMPL(ev);
        fmev_impl_t *cp;

        ASSERT(EVENT_VALID(iep));

        if (!FMEV_API_ENTER(iep, 1))
                return (NULL);  /* fmev_errno set */

        if (ev == NULL) {
                (void) fmev_seterr(FMEVERR_API);
                return (NULL);
        }

        if ((cp = fmev_shdl_alloc(iep->ei_hdl, sizeof (*iep))) == NULL) {
                (void) fmev_seterr(FMEVERR_ALLOC);
                return (NULL);
        }

        if (nvlist_dup(iep->ei_nvl, &cp->ei_nvl, 0) != 0) {
                fmev_shdl_free(iep->ei_hdl, cp, sizeof (*cp));
                (void) fmev_seterr(FMEVERR_ALLOC);
                return (NULL);
        }

        cp->ei_magic = _FMEVMAGIC;
        cp->ei_hdl = iep->ei_hdl;
        cp->ei_refcnt = 1;
        return (IMPL2FMEV(cp));
}

nvlist_t *
fmev_attr_list(fmev_t ev)
{
        fmev_impl_t *iep = FMEV2IMPL(ev);

        ASSERT(EVENT_VALID(iep));

        if (!FMEV_API_ENTER(iep, 1))
                return (NULL);  /* fmev_errno set */

        if (ev == NULL) {
                (void) fmev_seterr(FMEVERR_API);
                return (NULL);
        } else if (iep->ei_nvl == NULL) {
                (void) fmev_seterr(FMEVERR_MALFORMED_EVENT);
                return (NULL);
        }

        return (iep->ei_nvl);
}

const char *
fmev_class(fmev_t ev)
{
        fmev_impl_t *iep = FMEV2IMPL(ev);
        const char *class;

        ASSERT(EVENT_VALID(iep));

        if (!FMEV_API_ENTER(iep, 1))
                return (NULL);  /* fmev_errno set */

        if (ev == NULL) {
                (void) fmev_seterr(FMEVERR_API);
                return ("");
        }

        if (nvlist_lookup_string(iep->ei_nvl, FM_CLASS, (char **)&class) != 0 ||
            *class == '\0') {
                (void) fmev_seterr(FMEVERR_MALFORMED_EVENT);
                return ("");
        }

        return (class);
}

fmev_err_t
fmev_timespec(fmev_t ev, struct timespec *tp)
{
        fmev_impl_t *iep = FMEV2IMPL(ev);
        uint64_t timetlimit;

        ASSERT(EVENT_VALID(iep));
        if (!FMEV_API_ENTER(iep, 1))
                return (fmev_errno);

#ifdef  _LP64
        timetlimit = INT64_MAX;
#else
        timetlimit = INT32_MAX;
#endif

        if (iep->ei_fmtime[FM_TIME_SEC] > timetlimit)
                return (FMEVERR_OVERFLOW);

        tp->tv_sec = (time_t)iep->ei_fmtime[FM_TIME_SEC];
        tp->tv_nsec = (long)iep->ei_fmtime[FM_TIME_NSEC];

        return (FMEV_SUCCESS);
}

uint64_t
fmev_time_sec(fmev_t ev)
{
        return (FMEV2IMPL(ev)->ei_fmtime[FM_TIME_SEC]);
}

uint64_t
fmev_time_nsec(fmev_t ev)
{
        return (FMEV2IMPL(ev)->ei_fmtime[FM_TIME_NSEC]);
}

struct tm *
fmev_localtime(fmev_t ev, struct tm *tm)
{
        time_t seconds;

        seconds = (time_t)fmev_time_sec(ev);
        return (localtime_r(&seconds, tm));
}

fmev_shdl_t
fmev_ev2shdl(fmev_t ev)
{
        fmev_impl_t *iep = FMEV2IMPL(ev);

        if (!FMEV_API_ENTER(iep, 2))
                return (NULL);

        return (iep->ei_hdl);
}