1f6e214cGavin Maltby/*
2f6e214cGavin Maltby * CDDL HEADER START
3f6e214cGavin Maltby *
4f6e214cGavin Maltby * The contents of this file are subject to the terms of the
5f6e214cGavin Maltby * Common Development and Distribution License (the "License").
6f6e214cGavin Maltby * You may not use this file except in compliance with the License.
7f6e214cGavin Maltby *
8f6e214cGavin Maltby * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9f6e214cGavin Maltby * or http://www.opensolaris.org/os/licensing.
10f6e214cGavin Maltby * See the License for the specific language governing permissions
11f6e214cGavin Maltby * and limitations under the License.
12f6e214cGavin Maltby *
13f6e214cGavin Maltby * When distributing Covered Code, include this CDDL HEADER in each
14f6e214cGavin Maltby * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15f6e214cGavin Maltby * If applicable, add the following below this CDDL HEADER, with the
16f6e214cGavin Maltby * fields enclosed by brackets "[]" replaced with your own identifying
17f6e214cGavin Maltby * information: Portions Copyright [yyyy] [name of copyright owner]
18f6e214cGavin Maltby *
19f6e214cGavin Maltby * CDDL HEADER END
20f6e214cGavin Maltby */
21f6e214cGavin Maltby
22f6e214cGavin Maltby/*
23f6e214cGavin Maltby * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24f6e214cGavin Maltby */
25f6e214cGavin Maltby
26f6e214cGavin Maltby/*
27f6e214cGavin Maltby * Simple-minded raw event publication from user context.  See extensive
28f6e214cGavin Maltby * comments in libfmevent.h.  These interfaces remain Project Private -
29f6e214cGavin Maltby * they have to evolve before rollout to Public levels.
30f6e214cGavin Maltby *
31f6e214cGavin Maltby * Events are dispatched synchronously using the GPEC sysevent mechanism.
32f6e214cGavin Maltby * The caller context must therefore be one in which a sysevent_evc_publish
33f6e214cGavin Maltby * (and possibly sysevent_evc_bind if not already bound) is safe.  We will
34f6e214cGavin Maltby * also allocate and manipulate nvlists.
35f6e214cGavin Maltby *
36f6e214cGavin Maltby * Since we use GPEC, which has no least privilege awareness, these interfaces
37f6e214cGavin Maltby * will only work for would-be producers running as root.
38f6e214cGavin Maltby *
39f6e214cGavin Maltby * There is no event rate throttling applied, so we rely on producers
40f6e214cGavin Maltby * to throttle themselves.  A future refinement should apply mandatory
41f6e214cGavin Maltby * but tuneable throttling on a per-producer basis.  In this first version
42f6e214cGavin Maltby * the only throttle is the publication event queue depth - we'll drop
43f6e214cGavin Maltby * events when the queue is full.
44f6e214cGavin Maltby *
45f6e214cGavin Maltby * We can publish over four channels, for privileged/non-privileged and
46f6e214cGavin Maltby * high/low priority.  Since only privileged producers will work now
47f6e214cGavin Maltby * (see above) we hardcode priv == B_TRUE and so only two channels are
48f6e214cGavin Maltby * actually used, separating higher and lower value streams from privileged
49f6e214cGavin Maltby * producers.
50f6e214cGavin Maltby */
51f6e214cGavin Maltby
52f6e214cGavin Maltby#include <stdarg.h>
53f6e214cGavin Maltby#include <unistd.h>
54f6e214cGavin Maltby#include <stdlib.h>
55f6e214cGavin Maltby#include <atomic.h>
56f6e214cGavin Maltby#include <errno.h>
57f6e214cGavin Maltby#include <pthread.h>
58f6e214cGavin Maltby#include <strings.h>
59f6e214cGavin Maltby
60f6e214cGavin Maltby#include "fmev_impl.h"
61f6e214cGavin Maltby
62f6e214cGavin Maltbystatic struct {
63f6e214cGavin Maltby	const char *name;		/* channel name */
64f6e214cGavin Maltby	evchan_t *binding;		/* GPEC binding, once bound */
65f6e214cGavin Maltby	const uint32_t flags;		/* flags to use in binding */
66f6e214cGavin Maltby} chaninfo[] = {
67f6e214cGavin Maltby	{ FMEV_CHAN_USER_NOPRIV_LV, NULL, 0 },
68f6e214cGavin Maltby	{ FMEV_CHAN_USER_NOPRIV_HV, NULL, 0 },
69f6e214cGavin Maltby	{ FMEV_CHAN_USER_PRIV_LV, NULL, EVCH_HOLD_PEND_INDEF },
70f6e214cGavin Maltby	{ FMEV_CHAN_USER_PRIV_HV, NULL, EVCH_HOLD_PEND_INDEF}
71f6e214cGavin Maltby};
72f6e214cGavin Maltby
73f6e214cGavin Maltby#define	CHANIDX(priv, pri) (2 * ((priv) != 0) + (pri == FMEV_HIPRI))
74f6e214cGavin Maltby
75f6e214cGavin Maltby#define	CHAN_NAME(priv, pri) (chaninfo[CHANIDX(priv, pri)].name)
76f6e214cGavin Maltby#define	CHAN_BINDING(priv, pri) (chaninfo[CHANIDX(priv, pri)].binding)
77f6e214cGavin Maltby#define	CHAN_FLAGS(priv, pri) (chaninfo[CHANIDX(priv, pri)].flags)
78f6e214cGavin Maltby
79f6e214cGavin Maltby/*
80f6e214cGavin Maltby * Called after fork in the new child.  We clear the cached event
81f6e214cGavin Maltby * channel bindings which are only valid in the process that created
82f6e214cGavin Maltby * them.
83f6e214cGavin Maltby */
84f6e214cGavin Maltbystatic void
85f6e214cGavin Maltbyclear_bindings(void)
86f6e214cGavin Maltby{
87f6e214cGavin Maltby	int i;
88f6e214cGavin Maltby
89f6e214cGavin Maltby	for (i = 0; i < sizeof (chaninfo) / sizeof chaninfo[0]; i++)
90f6e214cGavin Maltby		chaninfo[i].binding = NULL;
91f6e214cGavin Maltby}
92f6e214cGavin Maltby
93f6e214cGavin Maltby#pragma init(_fmev_publish_init)
94f6e214cGavin Maltby
95f6e214cGavin Maltbystatic void
96f6e214cGavin Maltby_fmev_publish_init(void)
97f6e214cGavin Maltby{
98f6e214cGavin Maltby	(void) pthread_atfork(NULL, NULL, clear_bindings);
99f6e214cGavin Maltby}
100f6e214cGavin Maltby
101f6e214cGavin Maltbystatic evchan_t *
102f6e214cGavin Maltbybind_channel(boolean_t priv, fmev_pri_t pri)
103f6e214cGavin Maltby{
104f6e214cGavin Maltby	evchan_t **evcpp = &CHAN_BINDING(priv, pri);
105f6e214cGavin Maltby	evchan_t *evc;
106f6e214cGavin Maltby
107f6e214cGavin Maltby	if (*evcpp != NULL)
108f6e214cGavin Maltby		return (*evcpp);
109f6e214cGavin Maltby
110f6e214cGavin Maltby	if (sysevent_evc_bind(CHAN_NAME(priv, pri), &evc,
111f6e214cGavin Maltby	    EVCH_CREAT | CHAN_FLAGS(priv, pri)) != 0)
112f6e214cGavin Maltby		return (NULL);
113f6e214cGavin Maltby
114f6e214cGavin Maltby	if (atomic_cas_ptr(evcpp, NULL, evc) != NULL)
115f6e214cGavin Maltby		(void) sysevent_evc_unbind(evc);
116f6e214cGavin Maltby
117f6e214cGavin Maltby	return (*evcpp);
118f6e214cGavin Maltby}
119f6e214cGavin Maltby
120f6e214cGavin Maltbystatic fmev_err_t
121f6e214cGavin Maltbyvrfy_ruleset(const char *ruleset)
122f6e214cGavin Maltby{
123f6e214cGavin Maltby	if (ruleset != NULL &&
124f6e214cGavin Maltby	    strnlen(ruleset, FMEV_MAX_RULESET_LEN) == FMEV_MAX_RULESET_LEN)
125f6e214cGavin Maltby		return (FMEVERR_STRING2BIG);
126f6e214cGavin Maltby
127f6e214cGavin Maltby	return (FMEV_OK);
128f6e214cGavin Maltby
129f6e214cGavin Maltby}
130f6e214cGavin Maltby
131f6e214cGavin Maltbystatic fmev_err_t
132f6e214cGavin Maltbyvrfy_class(const char *class)
133f6e214cGavin Maltby{
134f6e214cGavin Maltby	if (class == NULL || *class == '\0')
135f6e214cGavin Maltby		return (FMEVERR_API);
136f6e214cGavin Maltby
137f6e214cGavin Maltby	if (strnlen(class, FMEV_PUB_MAXCLASSLEN) == FMEV_PUB_MAXCLASSLEN)
138f6e214cGavin Maltby		return (FMEVERR_STRING2BIG);
139f6e214cGavin Maltby
140f6e214cGavin Maltby	return (FMEV_OK);
141f6e214cGavin Maltby}
142f6e214cGavin Maltby
143f6e214cGavin Maltbystatic fmev_err_t
144f6e214cGavin Maltbyvrfy_subclass(const char *subclass)
145f6e214cGavin Maltby{
146f6e214cGavin Maltby	if (subclass == NULL || *subclass == '\0')
147f6e214cGavin Maltby		return (FMEVERR_API);
148f6e214cGavin Maltby
149f6e214cGavin Maltby	if (strnlen(subclass, FMEV_PUB_MAXSUBCLASSLEN) ==
150f6e214cGavin Maltby	    FMEV_PUB_MAXSUBCLASSLEN)
151f6e214cGavin Maltby		return (FMEVERR_STRING2BIG);
152f6e214cGavin Maltby
153f6e214cGavin Maltby	return (FMEV_OK);
154f6e214cGavin Maltby}
155f6e214cGavin Maltby
156f6e214cGavin Maltbystatic fmev_err_t
157f6e214cGavin Maltbyvrfy_pri(fmev_pri_t pri)
158f6e214cGavin Maltby{
159f6e214cGavin Maltby	return (pri == FMEV_LOPRI || pri == FMEV_HIPRI ?
160f6e214cGavin Maltby	    FMEV_OK : FMEVERR_API);
161f6e214cGavin Maltby}
162f6e214cGavin Maltby
163f6e214cGavin Maltbyconst char *
164f6e214cGavin Maltbyfmev_pri_string(fmev_pri_t pri)
165f6e214cGavin Maltby{
166f6e214cGavin Maltby	static const char *pristr[] = { "low", "high" };
167f6e214cGavin Maltby
168f6e214cGavin Maltby	if (vrfy_pri(pri) != FMEV_OK)
169f6e214cGavin Maltby		return (NULL);
170f6e214cGavin Maltby
171f6e214cGavin Maltby	return (pristr[pri - FMEV_LOPRI]);
172f6e214cGavin Maltby}
173f6e214cGavin Maltby
174f6e214cGavin Maltbystatic fmev_err_t
175f6e214cGavin Maltbyvrfy(const char **rulesetp, const char **classp, const char **subclassp,
176f6e214cGavin Maltby    fmev_pri_t *prip)
177f6e214cGavin Maltby{
178f6e214cGavin Maltby	fmev_err_t rc = FMEV_OK;
179f6e214cGavin Maltby
180f6e214cGavin Maltby	if (rulesetp && (rc = vrfy_ruleset(*rulesetp)) != FMEV_OK)
181f6e214cGavin Maltby		return (rc);
182f6e214cGavin Maltby
183f6e214cGavin Maltby	if (classp && (rc = vrfy_class(*classp)) != FMEV_OK ||
184f6e214cGavin Maltby	    subclassp && (rc = vrfy_subclass(*subclassp)) != FMEV_OK ||
185f6e214cGavin Maltby	    prip && (rc = vrfy_pri(*prip)) != FMEV_OK)
186f6e214cGavin Maltby		return (rc);
187f6e214cGavin Maltby
188f6e214cGavin Maltby	return (FMEV_OK);
189f6e214cGavin Maltby}
190f6e214cGavin Maltby
191f6e214cGavin Maltbyuint_t fmev_va2nvl_maxtuples = 100;
192f6e214cGavin Maltby
193f6e214cGavin Maltbyfmev_err_t
194f6e214cGavin Maltbyva2nvl(nvlist_t **nvlp, va_list ap, uint_t ntuples)
195f6e214cGavin Maltby{
196f6e214cGavin Maltby	nvlist_t *nvl = NULL;
197f6e214cGavin Maltby	uint_t processed = 0;
198f6e214cGavin Maltby	char *name;
199f6e214cGavin Maltby
200f6e214cGavin Maltby	if (ntuples == 0)
201f6e214cGavin Maltby		return (FMEVERR_INTERNAL);
202f6e214cGavin Maltby
203f6e214cGavin Maltby	if ((name = va_arg(ap, char *)) == NULL || name == FMEV_ARG_TERM)
204f6e214cGavin Maltby		return (FMEVERR_VARARGS_MALFORMED);
205f6e214cGavin Maltby
206f6e214cGavin Maltby	if (ntuples > fmev_va2nvl_maxtuples)
207f6e214cGavin Maltby		return (FMEVERR_VARARGS_TOOLONG);
208f6e214cGavin Maltby
209f6e214cGavin Maltby	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
210f6e214cGavin Maltby		return (FMEVERR_ALLOC);
211f6e214cGavin Maltby
212f6e214cGavin Maltby	while (name != NULL && name != FMEV_ARG_TERM && processed <= ntuples) {
213f6e214cGavin Maltby		data_type_t type;
214f6e214cGavin Maltby		int err, nelem;
215f6e214cGavin Maltby
216f6e214cGavin Maltby		type = va_arg(ap, data_type_t);
217f6e214cGavin Maltby
218f6e214cGavin Maltby		switch (type) {
219f6e214cGavin Maltby		case DATA_TYPE_BYTE:
220f6e214cGavin Maltby			err = nvlist_add_byte(nvl, name,
221f6e214cGavin Maltby			    va_arg(ap, uint_t));
222f6e214cGavin Maltby			break;
223f6e214cGavin Maltby		case DATA_TYPE_BYTE_ARRAY:
224f6e214cGavin Maltby			nelem = va_arg(ap, int);
225f6e214cGavin Maltby			err = nvlist_add_byte_array(nvl, name,
226