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/*
23 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26/*
27 * Subscription event access interfaces.
28 */
29
30#include <sys/types.h>
31#include <limits.h>
32#include <atomic.h>
33#include <libsysevent.h>
34#include <umem.h>
35#include <fm/libfmevent.h>
36#include <sys/fm/protocol.h>
37
38#include "fmev_impl.h"
39
40#define	FMEV_API_ENTER(iep, v) \
41	fmev_api_enter(fmev_shdl_cmn(((iep)->ei_hdl)), LIBFMEVENT_VERSION_##v)
42
43typedef struct {
44	uint32_t ei_magic;		/* _FMEVMAGIC */
45	volatile uint32_t ei_refcnt;	/* reference count */
46	fmev_shdl_t ei_hdl;		/* handle received on */
47	nvlist_t *ei_nvl;		/* (duped) sysevent attribute list */
48	uint64_t ei_fmtime[2];		/* embedded protocol event time */
49} fmev_impl_t;
50
51#define	FMEV2IMPL(ev)	((fmev_impl_t *)(ev))
52#define	IMPL2FMEV(iep)	((fmev_t)(iep))
53
54#define	_FMEVMAGIC	0x466d4576	/* "FmEv" */
55
56#define	EVENT_VALID(iep) ((iep)->ei_magic == _FMEVMAGIC && \
57	(iep)->ei_refcnt > 0 && fmev_shdl_valid((iep)->ei_hdl))
58
59#define	FM_TIME_SEC	0
60#define	FM_TIME_NSEC	1
61
62/*
63 * Transform a received sysevent_t into an fmev_t.
64 */
65
66uint64_t fmev_bad_attr, fmev_bad_tod, fmev_bad_class;
67
68fmev_t
69fmev_sysev2fmev(fmev_shdl_t hdl, sysevent_t *sep, char **clsp, nvlist_t **nvlp)
70{
71	fmev_impl_t *iep;
72	uint64_t *tod;
73	uint_t nelem;
74
75	if ((iep = fmev_shdl_alloc(hdl, sizeof (*iep))) == NULL)
76		return (NULL);
77
78	/*
79	 * sysevent_get_attr_list duplicates the nvlist - we free it
80	 * in fmev_free when the reference count hits zero.
81	 */
82	if (sysevent_get_attr_list(sep, &iep->ei_nvl) != 0) {
83		fmev_shdl_free(hdl, iep, sizeof (*iep));
84		fmev_bad_attr++;
85		return (NULL);
86	}
87
88	*nvlp = iep->ei_nvl;
89
90	if (nvlist_lookup_string(iep->ei_nvl, FM_CLASS, clsp) != 0) {
91		nvlist_free(iep->ei_nvl);
92		fmev_shdl_free(hdl, iep, sizeof (*iep));
93		fmev_bad_class++;
94		return (NULL);
95	}
96
97	if (nvlist_lookup_uint64_array(iep->ei_nvl, "__tod", &tod,
98	    &nelem) != 0 || nelem != 2) {
99		nvlist_free(iep->ei_nvl);
100		fmev_shdl_free(hdl, iep, sizeof (*iep));
101		fmev_bad_tod++;
102		return (NULL);
103	}
104
105	iep->ei_fmtime[FM_TIME_SEC] = tod[0];
106	iep->ei_fmtime[FM_TIME_NSEC] = tod[1];
107
108	/*
109	 * Now remove the fmd-private __tod and __ttl members.
110	 */
111	(void) nvlist_remove_all(iep->ei_nvl, "__tod");
112	(void) nvlist_remove_all(iep->ei_nvl, "__ttl");
113
114	iep->ei_magic = _FMEVMAGIC;
115	iep->ei_hdl = hdl;
116	iep->ei_refcnt = 1;
117	ASSERT(EVENT_VALID(iep));
118
119	return (IMPL2FMEV(iep));
120}
121
122static void
123fmev_free(fmev_impl_t *iep)
124{
125	ASSERT(iep->ei_refcnt == 0);
126
127	nvlist_free(iep->ei_nvl);
128	fmev_shdl_free(iep->ei_hdl, iep, sizeof (*iep));
129}
130
131void
132fmev_hold(fmev_t ev)
133{
134	fmev_impl_t *iep = FMEV2IMPL(ev);
135
136	ASSERT(EVENT_VALID(iep));
137
138	(void) FMEV_API_ENTER(iep, 1);
139
140	atomic_inc_32(&iep->ei_refcnt);
141}
142
143void
144fmev_rele(fmev_t ev)
145{
146	fmev_impl_t *iep = FMEV2IMPL(ev);
147
148	ASSERT(EVENT_VALID(iep));
149
150	(void) FMEV_API_ENTER(iep, 1);
151
152	if (atomic_dec_32_nv(&iep->ei_refcnt) == 0)
153		fmev_free(iep);
154}
155
156fmev_t
157fmev_dup(fmev_t ev)
158{
159	fmev_impl_t *iep = FMEV2IMPL(ev);
160	fmev_impl_t *cp;
161
162	ASSERT(EVENT_VALID(iep));
163
164	if (!FMEV_API_ENTER(iep, 1))
165		return (NULL);	/* fmev_errno set */
166
167	if (ev == NULL) {
168		(void) fmev_seterr(FMEVERR_API);
169		return (NULL);
170	}
171
172	if ((cp = fmev_shdl_alloc(iep->ei_hdl, sizeof (*iep))) == NULL) {
173		(void) fmev_seterr(FMEVERR_ALLOC);
174		return (NULL);
175	}
176
177	if (nvlist_dup(iep->ei_nvl, &cp->ei_nvl, 0) != 0) {
178		fmev_shdl_free(iep->ei_hdl, cp, sizeof (*cp));
179		(void) fmev_seterr(FMEVERR_ALLOC);
180		return (NULL);
181	}
182
183	cp->ei_magic = _FMEVMAGIC;
184	cp->ei_hdl = iep->ei_hdl;
185	cp->ei_refcnt = 1;
186	return (IMPL2FMEV(cp));
187}
188
189nvlist_t *
190fmev_attr_list(fmev_t ev)
191{
192	fmev_impl_t *iep = FMEV2IMPL(ev);
193
194	ASSERT(EVENT_VALID(iep));
195
196	if (!FMEV_API_ENTER(iep, 1))
197		return (NULL);	/* fmev_errno set */
198
199	if (ev == NULL) {
200		(void) fmev_seterr(FMEVERR_API);
201		return (NULL);
202	} else if (iep->ei_nvl == NULL) {
203		(void) fmev_seterr(FMEVERR_MALFORMED_EVENT);
204		return (NULL);
205	}
206
207	return (iep->ei_nvl);
208}
209
210const char *
211fmev_class(fmev_t ev)
212{
213	fmev_impl_t *iep = FMEV2IMPL(ev);
214	const char *class;
215
216	ASSERT(EVENT_VALID(iep));
217
218	if (!FMEV_API_ENTER(iep, 1))
219		return (NULL);	/* fmev_errno set */
220
221	if (ev == NULL) {
222		(void) fmev_seterr(FMEVERR_API);
223		return ("");
224	}
225
226	if (nvlist_lookup_string(iep->ei_nvl, FM_CLASS, (char **)&class) != 0 ||
227	    *class == '\0') {
228		(void) fmev_seterr(FMEVERR_MALFORMED_EVENT);
229		return ("");
230	}
231
232	return (class);
233}
234
235fmev_err_t
236fmev_timespec(fmev_t ev, struct timespec *tp)
237{
238	fmev_impl_t *iep = FMEV2IMPL(ev);
239	uint64_t timetlimit;
240
241	ASSERT(EVENT_VALID(iep));
242	if (!FMEV_API_ENTER(iep, 1))
243		return (fmev_errno);
244
245#ifdef	_LP64
246	timetlimit = INT64_MAX;
247#else
248	timetlimit = INT32_MAX;
249#endif
250
251	if (iep->ei_fmtime[FM_TIME_SEC] > timetlimit)
252		return (FMEVERR_OVERFLOW);
253
254	tp->tv_sec = (time_t)iep->ei_fmtime[FM_TIME_SEC];
255	tp->tv_nsec = (long)iep->ei_fmtime[FM_TIME_NSEC];
256
257	return (FMEV_SUCCESS);
258}
259
260uint64_t
261fmev_time_sec(fmev_t ev)
262{
263	return (FMEV2IMPL(ev)->ei_fmtime[FM_TIME_SEC]);
264}
265
266uint64_t
267fmev_time_nsec(fmev_t ev)
268{
269	return (FMEV2IMPL(ev)->ei_fmtime[FM_TIME_NSEC]);
270}
271
272struct tm *
273fmev_localtime(fmev_t ev, struct tm *tm)
274{
275	time_t seconds;
276
277	seconds = (time_t)fmev_time_sec(ev);
278	return (localtime_r(&seconds, tm));
279}
280
281fmev_shdl_t
282fmev_ev2shdl(fmev_t ev)
283{
284	fmev_impl_t *iep = FMEV2IMPL(ev);
285
286	if (!FMEV_API_ENTER(iep, 2))
287		return (NULL);
288
289	return (iep->ei_hdl);
290}
291