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.
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.
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]
23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
27 * Simple-minded raw event publication from user context. See extensive
28 * comments in libfmevent.h. These interfaces remain Project Private -
29 * they have to evolve before rollout to Public levels.
31 * Events are dispatched synchronously using the GPEC sysevent mechanism.
32 * The caller context must therefore be one in which a sysevent_evc_publish
33 * (and possibly sysevent_evc_bind if not already bound) is safe. We will
34 * also allocate and manipulate nvlists.
36 * Since we use GPEC, which has no least privilege awareness, these interfaces
37 * will only work for would-be producers running as root.
39 * There is no event rate throttling applied, so we rely on producers
40 * to throttle themselves. A future refinement should apply mandatory
41 * but tuneable throttling on a per-producer basis. In this first version
42 * the only throttle is the publication event queue depth - we'll drop
43 * events when the queue is full.
45 * We can publish over four channels, for privileged/non-privileged and
46 * high/low priority. Since only privileged producers will work now
47 * (see above) we hardcode priv == B_TRUE and so only two channels are
48 * actually used, separating higher and lower value streams from privileged
60 #include "fmev_impl.h"
63 const char *name
; /* channel name */
64 evchan_t
*binding
; /* GPEC binding, once bound */
65 const uint32_t flags
; /* flags to use in binding */
67 { FMEV_CHAN_USER_NOPRIV_LV
, NULL
, 0 },
68 { FMEV_CHAN_USER_NOPRIV_HV
, NULL
, 0 },
69 { FMEV_CHAN_USER_PRIV_LV
, NULL
, EVCH_HOLD_PEND_INDEF
},
70 { FMEV_CHAN_USER_PRIV_HV
, NULL
, EVCH_HOLD_PEND_INDEF
}
73 #define CHANIDX(priv, pri) (2 * ((priv) != 0) + (pri == FMEV_HIPRI))
75 #define CHAN_NAME(priv, pri) (chaninfo[CHANIDX(priv, pri)].name)
76 #define CHAN_BINDING(priv, pri) (chaninfo[CHANIDX(priv, pri)].binding)
77 #define CHAN_FLAGS(priv, pri) (chaninfo[CHANIDX(priv, pri)].flags)
80 * Called after fork in the new child. We clear the cached event
81 * channel bindings which are only valid in the process that created
89 for (i
= 0; i
< sizeof (chaninfo
) / sizeof chaninfo
[0]; i
++)
90 chaninfo
[i
].binding
= NULL
;
93 #pragma init(_fmev_publish_init)
96 _fmev_publish_init(void)
98 (void) pthread_atfork(NULL
, NULL
, clear_bindings
);
102 bind_channel(boolean_t priv
, fmev_pri_t pri
)
104 evchan_t
**evcpp
= &CHAN_BINDING(priv
, pri
);
110 if (sysevent_evc_bind(CHAN_NAME(priv
, pri
), &evc
,
111 EVCH_CREAT
| CHAN_FLAGS(priv
, pri
)) != 0)
114 if (atomic_cas_ptr(evcpp
, NULL
, evc
) != NULL
)
115 (void) sysevent_evc_unbind(evc
);
121 vrfy_ruleset(const char *ruleset
)
123 if (ruleset
!= NULL
&&
124 strnlen(ruleset
, FMEV_MAX_RULESET_LEN
) == FMEV_MAX_RULESET_LEN
)
125 return (FMEVERR_STRING2BIG
);
132 vrfy_class(const char *class)
134 if (class == NULL
|| *class == '\0')
135 return (FMEVERR_API
);
137 if (strnlen(class, FMEV_PUB_MAXCLASSLEN
) == FMEV_PUB_MAXCLASSLEN
)
138 return (FMEVERR_STRING2BIG
);
144 vrfy_subclass(const char *subclass
)
146 if (subclass
== NULL
|| *subclass
== '\0')
147 return (FMEVERR_API
);
149 if (strnlen(subclass
, FMEV_PUB_MAXSUBCLASSLEN
) ==
150 FMEV_PUB_MAXSUBCLASSLEN
)
151 return (FMEVERR_STRING2BIG
);
157 vrfy_pri(fmev_pri_t pri
)
159 return (pri
== FMEV_LOPRI
|| pri
== FMEV_HIPRI
?
160 FMEV_OK
: FMEVERR_API
);
164 fmev_pri_string(fmev_pri_t pri
)
166 static const char *pristr
[] = { "low", "high" };
168 if (vrfy_pri(pri
) != FMEV_OK
)
171 return (pristr
[pri
- FMEV_LOPRI
]);
175 vrfy(const char **rulesetp
, const char **classp
, const char **subclassp
,
178 fmev_err_t rc
= FMEV_OK
;
180 if (rulesetp
&& (rc
= vrfy_ruleset(*rulesetp
)) != FMEV_OK
)
183 if (classp
&& (rc
= vrfy_class(*classp
)) != FMEV_OK
||
184 subclassp
&& (rc
= vrfy_subclass(*subclassp
)) != FMEV_OK
||
185 prip
&& (rc
= vrfy_pri(*prip
)) != FMEV_OK
)
191 uint_t fmev_va2nvl_maxtuples
= 100;
194 va2nvl(nvlist_t
**nvlp
, va_list ap
, uint_t ntuples
)
196 nvlist_t
*nvl
= NULL
;
197 uint_t processed
= 0;
201 return (FMEVERR_INTERNAL
);
203 if ((name
= va_arg(ap
, char *)) == NULL
|| name
== FMEV_ARG_TERM
)
204 return (FMEVERR_VARARGS_MALFORMED
);
206 if (ntuples
> fmev_va2nvl_maxtuples
)
207 return (FMEVERR_VARARGS_TOOLONG
);
209 if (nvlist_alloc(&nvl
, NV_UNIQUE_NAME
, 0) != 0)
210 return (FMEVERR_ALLOC
);
212 while (name
!= NULL
&& name
!= FMEV_ARG_TERM
&& processed
<= ntuples
) {
216 type
= va_arg(ap
, data_type_t
);
220 err
= nvlist_add_byte(nvl
, name
,
223 case DATA_TYPE_BYTE_ARRAY
:
224 nelem
= va_arg(ap
, int);
225 err
= nvlist_add_byte_array(nvl
, name
,
226 va_arg(ap
, uchar_t
*), nelem
);
228 case DATA_TYPE_BOOLEAN_VALUE
:
229 err
= nvlist_add_boolean_value(nvl
, name
,
230 va_arg(ap
, boolean_t
));
232 case DATA_TYPE_BOOLEAN_ARRAY
:
233 nelem
= va_arg(ap
, int);
234 err
= nvlist_add_boolean_array(nvl
, name
,
235 va_arg(ap
, boolean_t
*), nelem
);
238 err
= nvlist_add_int8(nvl
, name
,
241 case DATA_TYPE_INT8_ARRAY
:
242 nelem
= va_arg(ap
, int);
243 err
= nvlist_add_int8_array(nvl
, name
,
244 va_arg(ap
, int8_t *), nelem
);
246 case DATA_TYPE_UINT8
:
247 err
= nvlist_add_uint8(nvl
, name
,
250 case DATA_TYPE_UINT8_ARRAY
:
251 nelem
= va_arg(ap
, int);
252 err
= nvlist_add_uint8_array(nvl
, name
,
253 va_arg(ap
, uint8_t *), nelem
);
255 case DATA_TYPE_INT16
:
256 err
= nvlist_add_int16(nvl
, name
,
259 case DATA_TYPE_INT16_ARRAY
:
260 nelem
= va_arg(ap
, int);
261 err
= nvlist_add_int16_array(nvl
, name
,
262 va_arg(ap
, int16_t *), nelem
);
264 case DATA_TYPE_UINT16
:
265 err
= nvlist_add_uint16(nvl
, name
,
268 case DATA_TYPE_UINT16_ARRAY
:
269 nelem
= va_arg(ap
, int);
270 err
= nvlist_add_uint16_array(nvl
, name
,
271 va_arg(ap
, uint16_t *), nelem
);
273 case DATA_TYPE_INT32
:
274 err
= nvlist_add_int32(nvl
, name
,
275 va_arg(ap
, int32_t));
277 case DATA_TYPE_INT32_ARRAY
:
278 nelem
= va_arg(ap
, int);
279 err
= nvlist_add_int32_array(nvl
, name
,
280 va_arg(ap
, int32_t *), nelem
);
282 case DATA_TYPE_UINT32
:
283 err
= nvlist_add_uint32(nvl
, name
,
284 va_arg(ap
, uint32_t));
286 case DATA_TYPE_UINT32_ARRAY
:
287 nelem
= va_arg(ap
, int);
288 err
= nvlist_add_uint32_array(nvl
, name
,
289 va_arg(ap
, uint32_t *), nelem
);
291 case DATA_TYPE_INT64
:
292 err
= nvlist_add_int64(nvl
, name
,
293 va_arg(ap
, int64_t));
295 case DATA_TYPE_INT64_ARRAY
:
296 nelem
= va_arg(ap
, int);
297 err
= nvlist_add_int64_array(nvl
, name
,
298 va_arg(ap
, int64_t *), nelem
);
300 case DATA_TYPE_UINT64
:
301 err
= nvlist_add_uint64(nvl
, name
,
302 va_arg(ap
, uint64_t));
304 case DATA_TYPE_UINT64_ARRAY
:
305 nelem
= va_arg(ap
, int);
306 err
= nvlist_add_uint64_array(nvl
, name
,
307 va_arg(ap
, uint64_t *), nelem
);
309 case DATA_TYPE_STRING
:
310 err
= nvlist_add_string(nvl
, name
,
313 case DATA_TYPE_STRING_ARRAY
:
314 nelem
= va_arg(ap
, int);
315 err
= nvlist_add_string_array(nvl
, name
,
316 va_arg(ap
, char **), nelem
);
318 case DATA_TYPE_NVLIST
:
319 err
= nvlist_add_nvlist(nvl
, name
,
320 va_arg(ap
, nvlist_t
*));
322 case DATA_TYPE_NVLIST_ARRAY
:
323 nelem
= va_arg(ap
, int);
324 err
= nvlist_add_nvlist_array(nvl
, name
,
325 va_arg(ap
, nvlist_t
**), nelem
);
327 case DATA_TYPE_HRTIME
:
328 err
= nvlist_add_hrtime(nvl
, name
,
329 va_arg(ap
, hrtime_t
));
331 case DATA_TYPE_DOUBLE
:
332 err
= nvlist_add_double(nvl
, name
,
340 break; /* terminate on first error */
343 name
= va_arg(ap
, char *);
346 if (name
!= FMEV_ARG_TERM
|| processed
!= ntuples
) {
349 return (FMEVERR_VARARGS_MALFORMED
);
353 return (FMEV_SUCCESS
);
357 do_publish(const char *file
, const char *func
, int64_t line
,
358 const char *ruleset
, const char *class, const char *subclass
,
359 fmev_pri_t pri
, nvlist_t
*nvl
, uint_t ntuples
, va_list ap
)
361 fmev_err_t rc
= FMEVERR_INTERNAL
;
362 boolean_t priv
= B_TRUE
;
363 nvlist_t
*tmpnvl
= NULL
;
368 ASSERT(ntuples
== 0);
371 * Enforce NV_UNIQUE_NAME
373 if ((nvlist_nvflag(nvl
) & NV_UNIQUE_NAME
) != NV_UNIQUE_NAME
)
374 return (FMEVERR_NVLIST
);
378 } else if (ntuples
!= 0) {
381 err
= va2nvl(&tmpnvl
, ap
, ntuples
);
382 if (err
!= FMEV_SUCCESS
)
388 * Even if the caller has no tuples to publish (just an event
389 * class and subclass), we are going to add some detector
390 * information so we need some nvlist.
392 if (nvlist_alloc(&tmpnvl
, NV_UNIQUE_NAME
, 0) != 0)
393 return (FMEVERR_ALLOC
);
398 evc
= bind_channel(priv
, pri
);
401 rc
= FMEVERR_INTERNAL
;
407 * Add detector information
409 if (file
&& nvlist_add_string(pub
, "__fmev_file", file
) != 0 ||
410 func
&& nvlist_add_string(pub
, "__fmev_func", func
) != 0 ||
411 line
!= -1 && nvlist_add_int64(pub
, "__fmev_line", line
) != 0 ||
412 nvlist_add_int32(pub
, "__fmev_pid", getpid()) != 0 ||
413 nvlist_add_string(pub
, "__fmev_execname", getexecname()) != 0) {
419 ruleset
= FMEV_RULESET_DEFAULT
;
422 * We abuse the GPEC publication arguments as follows:
424 * GPEC argument Our usage
425 * -------------------- -----------------
426 * const char *class Raw class
427 * const char *subclass Raw subclass
428 * const char *vendor Ruleset name
429 * const char *pub_name Unused
430 * nvlist_t *attr_list Event attributes
432 rc
= (sysevent_evc_publish(evc
, class, subclass
, ruleset
, "",
433 pub
, EVCH_NOSLEEP
) == 0) ? FMEV_SUCCESS
: FMEVERR_TRANSPORT
;
436 /* Free a passed in nvlist iff success */
437 if (rc
== FMEV_SUCCESS
)
447 const char *file
, const char *func
, int64_t line
,
448 const char *ruleset
, const char *class, const char *subclass
,
449 fmev_pri_t pri
, nvlist_t
*attr
)
453 if ((rc
= vrfy(&ruleset
, &class, &subclass
, &pri
)) != FMEV_OK
)
454 return (rc
); /* any attr not freed */
456 return (do_publish(file
, func
, line
,
457 ruleset
, class, subclass
,
458 pri
, attr
, 0, NULL
)); /* any attr freed iff success */
463 const char *file
, const char *func
, int64_t line
,
464 const char *ruleset
, const char *class, const char *subclass
,
471 if ((rc
= vrfy(&ruleset
, &class, &subclass
, &pri
)) != FMEV_OK
)
475 va_start(ap
, ntuples
);
477 rc
= do_publish(file
, func
, line
,
478 ruleset
, class, subclass
,
479 pri
, NULL
, ntuples
, ap
);
488 #pragma weak fmev_publish = _fmev_publish
489 #pragma weak fmev_rspublish = _fmev_rspublish
492 _fmev_publish(const char *class, const char *subclass
, fmev_pri_t pri
,
498 if ((rc
= vrfy(NULL
, &class, &subclass
, &pri
)) != FMEV_OK
)
502 va_start(ap
, ntuples
);
504 rc
= do_publish(NULL
, NULL
, -1,
505 FMEV_RULESET_DEFAULT
, class, subclass
,
506 pri
, NULL
, ntuples
, ap
);
515 _fmev_rspublish(const char *ruleset
, const char *class, const char *subclass
,
516 fmev_pri_t pri
, uint_t ntuples
, ...)
521 if ((rc
= vrfy(&ruleset
, &class, &subclass
, &pri
)) != FMEV_OK
)
525 va_start(ap
, ntuples
);
527 rc
= do_publish(NULL
, NULL
, -1,
528 ruleset
, class, subclass
,
529 pri
, NULL
, ntuples
, ap
);