view usr/src/lib/fm/libfmevent/common/fmev_evaccess.c @ 11102:b91faef0c984

PSARC/2009/554 door_xcreate - extended door creation interface for private doors PSARC/2009/573 libfmevent - external subscriptions to FMA protocol events PSARC/2009/574 GPEC interface changes and additions 6893144 add door_xcreate for creating private doors with per-door thread creation control 6896220 sysevent_evc_xsubscribe and other GPEC modifications 6900975 sysevent_evc_{unbind,unsubscribe} off-by-one in subscriber list traversal 6868087 facility to allow external processes to subscribe to FMA protocol events 6896205 fmd module to forward selected protocol events for external subscription
author Gavin Maltby <Gavin.Maltby@Sun.COM>
date Thu, 19 Nov 2009 15:28:11 +1100
parents
children ab9ae749152f
line wrap: on
line source

/*
 * 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 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * 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	API_ENTERV1(iep) \
	((void) fmev_api_enter(fmev_shdl_cmn(((iep)->ei_hdl)), \
	LIBFMEVENT_VERSION_1))

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));

	API_ENTERV1(iep);

	atomic_inc_32(&iep->ei_refcnt);
}

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

	ASSERT(EVENT_VALID(iep));

	API_ENTERV1(iep);

	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));

	API_ENTERV1(iep);

	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));

	API_ENTERV1(iep);

	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));

	API_ENTERV1(iep);

	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));
	API_ENTERV1(iep);

#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));
}