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 
43 typedef 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 
66 uint64_t fmev_bad_attr, fmev_bad_tod, fmev_bad_class;
67 
68 fmev_t
fmev_sysev2fmev(fmev_shdl_t hdl,sysevent_t * sep,char ** clsp,nvlist_t ** nvlp)69 fmev_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 
122 static void
fmev_free(fmev_impl_t * iep)123 fmev_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 
131 void
fmev_hold(fmev_t ev)132 fmev_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 
143 void
fmev_rele(fmev_t ev)144 fmev_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 
156 fmev_t
fmev_dup(fmev_t ev)157 fmev_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 
189 nvlist_t *
fmev_attr_list(fmev_t ev)190 fmev_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 
210 const char *
fmev_class(fmev_t ev)211 fmev_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 
235 fmev_err_t
fmev_timespec(fmev_t ev,struct timespec * tp)236 fmev_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 
260 uint64_t
fmev_time_sec(fmev_t ev)261 fmev_time_sec(fmev_t ev)
262 {
263 	return (FMEV2IMPL(ev)->ei_fmtime[FM_TIME_SEC]);
264 }
265 
266 uint64_t
fmev_time_nsec(fmev_t ev)267 fmev_time_nsec(fmev_t ev)
268 {
269 	return (FMEV2IMPL(ev)->ei_fmtime[FM_TIME_NSEC]);
270 }
271 
272 struct tm *
fmev_localtime(fmev_t ev,struct tm * tm)273 fmev_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 
281 fmev_shdl_t
fmev_ev2shdl(fmev_t ev)282 fmev_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