fm/modules/ext-event-transport: fix 64-bit compilation
[unleashed.git] / usr / src / cmd / fm / modules / common / ext-event-transport / fmevt_inbound.c
blobc4e492593e74e74bc778b5de278bed3b7b1e7b29
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
27 * Receive (on GPEC channels) raw events published by a few select producers
28 * using the private libfmevent publication interfaces, and massage those
29 * raw events into full protocol events. Each raw event selects a "ruleset"
30 * by which to perform the transformation into a protocol event.
32 * Only publication from userland running privileged is supported; two
33 * channels are used - one for high-value and one for low-value events.
34 * There is some planning in the implementation below for kernel hi and low
35 * value channels, and for non-privileged userland low and hi value channels.
38 #include <fm/fmd_api.h>
39 #include <fm/libfmevent.h>
40 #include <uuid/uuid.h>
41 #include <libsysevent.h>
42 #include <pthread.h>
43 #include <libnvpair.h>
44 #include <strings.h>
45 #include <zone.h>
47 #include "fmevt.h"
49 static struct fmevt_inbound_stats {
50 fmd_stat_t raw_callbacks;
51 fmd_stat_t raw_noattrlist;
52 fmd_stat_t raw_nodetector;
53 fmd_stat_t pp_bad_ruleset;
54 fmd_stat_t pp_explicitdrop;
55 fmd_stat_t pp_fallthrurule;
56 fmd_stat_t pp_fanoutmax;
57 fmd_stat_t pp_intldrop;
58 fmd_stat_t pp_badclass;
59 fmd_stat_t pp_nvlallocfail;
60 fmd_stat_t pp_nvlbuildfail;
61 fmd_stat_t pp_badreturn;
62 fmd_stat_t xprt_posted;
63 } inbound_stats = {
64 { "raw_callbacks", FMD_TYPE_UINT64,
65 "total raw event callbacks from producers" },
66 { "raw_noattrlist", FMD_TYPE_UINT64,
67 "missing attribute list" },
68 { "raw_nodetector", FMD_TYPE_UINT64,
69 "unable to add detector" },
70 { "pp_bad_ruleset", FMD_TYPE_UINT64,
71 "post-process bad ruleset" },
72 { "pp_explicitdrop", FMD_TYPE_UINT64,
73 "ruleset drops event with NULL func" },
74 { "pp_fanoutmax", FMD_TYPE_UINT64,
75 "post-processing produced too many events" },
76 { "pp_intldrop", FMD_TYPE_UINT64,
77 "post-processing requested event drop" },
78 { "pp_badclass", FMD_TYPE_UINT64,
79 "post-processing produced invalid event class" },
80 { "pp_nvlallocfail", FMD_TYPE_UINT64,
81 "fmd_nvl_alloc failed" },
82 { "pp_nvlbuildfail", FMD_TYPE_UINT64,
83 "nvlist_add_foo failed in building event" },
84 { "pp_badreturn", FMD_TYPE_UINT64,
85 "inconsistent number of events returned" },
86 { "xprt_posted", FMD_TYPE_UINT64,
87 "protocol events posted with fmd_xprt_post" },
90 static int isglobalzone;
91 static char zonename[ZONENAME_MAX];
93 #define BUMPSTAT(stat) inbound_stats.stat.fmds_value.ui64++
95 #define CBF_USER 0x1U
96 #define CBF_PRIV 0x2U
97 #define CBF_LV 0x4U
98 #define CBF_HV 0x8U
99 #define CBF_ALL (CBF_USER | CBF_PRIV | CBF_LV | CBF_HV)
101 static struct fmevt_chaninfo {
102 const char *ci_propname; /* property to get channel name */
103 evchan_t *ci_binding; /* GPEC binding for this channel */
104 char ci_sid[MAX_SUBID_LEN]; /* subscriber id */
105 uint32_t ci_cbarg; /* callback cookie */
106 uint32_t ci_sflags; /* subscription flags to use */
107 } chaninfo[] = {
108 { "user_priv_highval_channel", NULL, { 0 },
109 CBF_USER | CBF_PRIV | CBF_HV, EVCH_SUB_KEEP },
110 { "user_priv_lowval_channel", NULL, { 0 },
111 CBF_USER | CBF_PRIV | CBF_LV, EVCH_SUB_KEEP },
114 static pthread_cond_t fmevt_cv = PTHREAD_COND_INITIALIZER;
115 static pthread_mutex_t fmevt_lock = PTHREAD_MUTEX_INITIALIZER;
116 static int fmevt_exiting;
118 static fmd_xprt_t *fmevt_xprt;
119 static uint32_t fmevt_xprt_refcnt;
120 static sysevent_subattr_t *subattr;
123 * Rulesets we recognize and who handles them. Additions and changes
124 * must follow the Portfolio Review process. At ths time only
125 * the FMEV_RULESET_ON_SUNOS and FMEVT_RULESET_SMF rulesets are
126 * formally recognized by that process - the others here are experimental.
128 static struct fmevt_rs {
129 char *rs_pat;
130 fmevt_pp_func_t *rs_ppfunc;
131 char *rs_namespace;
132 char *rs_subsys;
133 } rulelist[] = {
134 { FMEV_RULESET_SMF, fmevt_pp_smf },
135 { FMEV_RULESET_ON_EREPORT, fmevt_pp_on_ereport },
136 { FMEV_RULESET_ON_SUNOS, fmevt_pp_on_sunos },
137 { FMEV_RULESET_ON_PRIVATE, fmevt_pp_on_private },
138 { FMEV_RULESET_UNREGISTERED, fmevt_pp_unregistered }
142 * Take a ruleset specification string and separate it into namespace
143 * and subsystem components.
145 static int
146 fmevt_rs_burst(fmd_hdl_t *hdl, char *ruleset, char **nsp, char **subsysp,
147 boolean_t alloc)
149 char *ns, *s;
150 size_t len;
152 if (ruleset == NULL || *ruleset == '\0' ||
153 strnlen(ruleset, FMEV_MAX_RULESET_LEN) == FMEV_MAX_RULESET_LEN)
154 return (0);
156 if (alloc == B_FALSE) {
157 s = ruleset;
158 ns = strsep(&s, FMEV_RS_SEPARATOR);
160 if (s == NULL || s == ns + 1)
161 return (0);
162 } else {
163 if ((s = strstr(ruleset, FMEV_RS_SEPARATOR)) == NULL ||
164 s == ruleset + strlen(ruleset) - 1)
165 return (0);
167 len = s - ruleset;
169 ns = fmd_hdl_alloc(hdl, len + 1, FMD_SLEEP);
170 (void) strncpy(ns, ruleset, len);
171 ns[len] = '\0';
173 s++;
176 if (nsp)
177 *nsp = ns; /* caller must free if alloc == B_TRUE */
179 if (subsysp)
180 *subsysp = s; /* always within original ruleset string */
182 return (1);
185 static int
186 fmevt_rs_init(fmd_hdl_t *hdl)
188 int i;
190 for (i = 0; i < sizeof (rulelist) / sizeof (rulelist[0]); i++) {
191 struct fmevt_rs *rsp = &rulelist[i];
193 if (!fmevt_rs_burst(hdl, rsp->rs_pat, &rsp->rs_namespace,
194 &rsp->rs_subsys, B_TRUE))
195 return (0);
198 return (1);
202 * Construct a "sw" scheme detector FMRI.
204 * We make no use of priv or pri.
206 /*ARGSUSED3*/
207 static nvlist_t *
208 fmevt_detector(nvlist_t *attr, char *ruleset, int user, int priv,
209 fmev_pri_t pri)
211 char buf[FMEV_MAX_RULESET_LEN + 1];
212 char *ns, *subsys;
213 nvlist_t *obj, *dtcr, *site, *ctxt;
214 char *execname = NULL;
215 int32_t i32;
216 int64_t i64;
217 int err = 0;
218 char *str;
220 (void) strncpy(buf, ruleset, sizeof (buf));
221 if (!fmevt_rs_burst(NULL, buf, &ns, &subsys, B_FALSE))
222 return (NULL);
224 obj = fmd_nvl_alloc(fmevt_hdl, FMD_SLEEP);
225 dtcr = fmd_nvl_alloc(fmevt_hdl, FMD_SLEEP);
226 site = fmd_nvl_alloc(fmevt_hdl, FMD_SLEEP);
227 ctxt = fmd_nvl_alloc(fmevt_hdl, FMD_SLEEP);
229 if (obj == NULL || dtcr == NULL || site == NULL || ctxt == NULL) {
230 err++;
231 goto done;
235 * Build up 'object' nvlist.
237 if (nvlist_lookup_string(attr, "__fmev_execname", &execname) == 0)
238 err += nvlist_add_string(obj, FM_FMRI_SW_OBJ_PATH, execname);
241 * Build up 'site' nvlist. We should have source file and line
242 * number and, if the producer was compiled with C99, function name.
244 if (nvlist_lookup_string(attr, "__fmev_file", &str) == 0) {
245 err += nvlist_add_string(site, FM_FMRI_SW_SITE_FILE, str);
246 (void) nvlist_remove(attr, "__fmev_file", DATA_TYPE_STRING);
249 if (nvlist_lookup_string(attr, "__fmev_func", &str) == 0) {
250 err += nvlist_add_string(site, FM_FMRI_SW_SITE_FUNC, str);
251 (void) nvlist_remove(attr, "__fmev_func", DATA_TYPE_STRING);
254 if (nvlist_lookup_int64(attr, "__fmev_line", &i64) == 0) {
255 err += nvlist_add_int64(site, FM_FMRI_SW_SITE_LINE, i64);
256 (void) nvlist_remove(attr, "__fmev_line", DATA_TYPE_INT64);
260 * Build up 'context' nvlist. We do not include contract id at
261 * this time.
264 err += nvlist_add_string(ctxt, FM_FMRI_SW_CTXT_ORIGIN,
265 user ? "userland" : "kernel");
267 if (execname) {
268 err += nvlist_add_string(ctxt, FM_FMRI_SW_CTXT_EXECNAME,
269 execname);
270 (void) nvlist_remove(attr, "__fmev_execname", DATA_TYPE_STRING);
273 if (nvlist_lookup_int32(attr, "__fmev_pid", &i32) == 0) {
274 err += nvlist_add_int32(ctxt, FM_FMRI_SW_CTXT_PID, i32);
275 (void) nvlist_remove(attr, "__fmev_pid", DATA_TYPE_INT32);
278 if (!isglobalzone)
279 err += nvlist_add_string(ctxt, FM_FMRI_SW_CTXT_ZONE, zonename);
281 /* Put it all together */
283 err += nvlist_add_uint8(dtcr, FM_VERSION, SW_SCHEME_VERSION0);
284 err += nvlist_add_string(dtcr, FM_FMRI_SCHEME, FM_FMRI_SCHEME_SW);
285 err += nvlist_add_nvlist(dtcr, FM_FMRI_SW_OBJ, obj);
286 err += nvlist_add_nvlist(dtcr, FM_FMRI_SW_SITE, site);
287 err += nvlist_add_nvlist(dtcr, FM_FMRI_SW_CTXT, ctxt);
289 done:
290 nvlist_free(obj);
291 nvlist_free(site);
292 nvlist_free(ctxt);
294 if (err == 0) {
295 return (dtcr);
296 } else {
297 nvlist_free(dtcr);
298 return (NULL);
302 static int
303 class_ok(char *class)
305 static const char *approved[] = {
306 FM_IREPORT_CLASS ".",
307 FM_EREPORT_CLASS "."
310 int i;
312 for (i = 0; i < sizeof (approved) / sizeof (approved[0]); i++) {
313 if (strncmp(class, approved[i], strlen(approved[i])) == 0)
314 return (1);
317 return (0);
320 static void
321 fmevt_postprocess(char *ruleset, nvlist_t *dtcr, nvlist_t *rawattr,
322 struct fmevt_ppargs *eap)
324 uint_t expected = 0, processed = 0;
325 char rs2burst[FMEV_MAX_RULESET_LEN + 1];
326 char *class[FMEVT_FANOUT_MAX];
327 nvlist_t *attr[FMEVT_FANOUT_MAX];
328 fmevt_pp_func_t *dispf = NULL;
329 char buf[FMEV_MAX_CLASS];
330 char *ns, *subsys;
331 int i, found = 0;
332 uuid_t uu;
334 (void) strncpy(rs2burst, ruleset, sizeof (rs2burst));
335 if (!fmevt_rs_burst(NULL, rs2burst, &ns, &subsys, B_FALSE)) {
336 BUMPSTAT(pp_bad_ruleset);
337 return;
341 * Lookup a matching rule in our table.
343 for (i = 0; i < sizeof (rulelist) / sizeof (rulelist[0]); i++) {
344 struct fmevt_rs *rsp = &rulelist[i];
346 if (*ns != '*' && *rsp->rs_namespace != '*' &&
347 strcmp(ns, rsp->rs_namespace) != 0)
348 continue;
350 if (*subsys != '*' && *rsp->rs_subsys != '*' &&
351 strcmp(subsys, rsp->rs_subsys) != 0)
352 continue;
354 dispf = rsp->rs_ppfunc;
355 found = 1;
356 break;
361 * If a ruleset matches but specifies a NULL function then
362 * it's electing to drop the event. If no rule was matched
363 * then default to unregistered processing.
365 if (dispf == NULL) {
366 if (found) {
367 BUMPSTAT(pp_explicitdrop);
368 return;
369 } else {
370 BUMPSTAT(pp_fallthrurule);
371 dispf = fmevt_pp_unregistered;
376 * Clear the arrays in which class strings and attribute
377 * nvlists can be returned. Pass a pointer to our stack buffer
378 * that the callee can use for the first event class (for others
379 * it must fmd_hdl_alloc and we'll free below). We will free
380 * and nvlists that are returned.
382 bzero(class, sizeof (class));
383 bzero(attr, sizeof (attr));
384 class[0] = buf;
387 * Generate an event UUID which will be used for the first
388 * event generated by post-processing; if post-processing
389 * fans out into more than one event the additional events
390 * can reference this uuid (but we don't generate their
391 * UUIDs until later).
393 uuid_generate(uu);
394 uuid_unparse(uu, eap->pp_uuidstr);
397 * Call selected post-processing function. See block comment
398 * in fmevt.h for a description of this process.
400 expected = (*dispf)(class, attr, ruleset,
401 (const nvlist_t *)dtcr, rawattr,
402 (const struct fmevt_ppargs *)eap);
404 if (expected > FMEVT_FANOUT_MAX) {
405 BUMPSTAT(pp_fanoutmax);
406 return; /* without freeing class and nvl - could leak */
407 } else if (expected == 0) {
408 BUMPSTAT(pp_intldrop);
409 return;
413 * Post as many events as the callback completed.
415 for (i = 0; i < FMEVT_FANOUT_MAX; i++) {
416 char uuidstr[36 + 1];
417 char *uuidstrp;
418 nvlist_t *nvl;
419 int err = 0;
421 if (class[i] == NULL)
422 continue;
424 if (!class_ok(class[i])) {
425 BUMPSTAT(pp_badclass);
426 continue;
429 if (processed++ == 0) {
430 uuidstrp = eap->pp_uuidstr;
431 } else {
432 uuid_generate(uu);
433 uuid_unparse(uu, uuidstr);
434 uuidstrp = uuidstr;
437 if ((nvl = fmd_nvl_alloc(fmevt_hdl, FMD_SLEEP)) == NULL) {
438 BUMPSTAT(pp_nvlallocfail);
439 continue;
442 err += nvlist_add_uint8(nvl, FM_VERSION, 0);
443 err += nvlist_add_string(nvl, FM_CLASS, (const char *)class[i]);
444 err += nvlist_add_string(nvl, FM_IREPORT_UUID, uuidstrp);
445 err += nvlist_add_nvlist(nvl, FM_IREPORT_DETECTOR, dtcr);
446 err += nvlist_add_string(nvl, FM_IREPORT_PRIORITY,
447 fmev_pri_string(eap->pp_pri) ?
448 fmev_pri_string(eap->pp_pri) : "?");
450 if (attr[i] != NULL)
451 err += nvlist_add_nvlist(nvl, FM_IREPORT_ATTRIBUTES,
452 attr[i]);
455 * If we post the event into fmd_xport_post then the
456 * transport code is responsible for freeing the nvl we
457 * posted.
459 if (err == 0) {
460 fmd_xprt_post(fmevt_hdl, fmevt_xprt, nvl,
461 eap->pp_hrt);
462 } else {
463 BUMPSTAT(pp_nvlbuildfail);
464 nvlist_free(nvl);
468 if (processed != expected)
469 BUMPSTAT(pp_badreturn);
471 for (i = 0; i < FMEVT_FANOUT_MAX; i++) {
473 * We provided storage for class[0] but any
474 * additional events have allocated a string.
476 if (i > 0 && class[i] != NULL)
477 fmd_hdl_strfree(fmevt_hdl, class[i]);
480 * Free all attribute lists passed in if they are not
481 * just a pointer to the raw attributes
483 if (attr[i] != NULL && attr[i] != rawattr)
484 nvlist_free(attr[i]);
488 static int
489 fmevt_cb(sysevent_t *sep, void *arg)
491 char *ruleset = NULL, *rawclass, *rawsubclass;
492 uint32_t cbarg = (uintptr_t)arg;
493 nvlist_t *rawattr = NULL;
494 struct fmevt_ppargs ea;
495 nvlist_t *dtcr;
496 int user, priv;
497 fmev_pri_t pri;
499 BUMPSTAT(raw_callbacks);
501 if (cbarg & ~CBF_ALL)
502 fmd_hdl_abort(fmevt_hdl, "event receipt callback with "
503 "invalid flags\n");
505 user = (cbarg & CBF_USER) != 0;
506 priv = (cbarg & CBF_PRIV) != 0;
507 pri = (cbarg & CBF_HV ? FMEV_HIPRI : FMEV_LOPRI);
509 (void) pthread_mutex_lock(&fmevt_lock);
511 if (fmevt_exiting) {
512 while (fmevt_xprt_refcnt > 0)
513 (void) pthread_cond_wait(&fmevt_cv, &fmevt_lock);
514 (void) pthread_mutex_unlock(&fmevt_lock);
515 return (0); /* discard event */
518 fmevt_xprt_refcnt++;
519 (void) pthread_mutex_unlock(&fmevt_lock);
521 ruleset = sysevent_get_vendor_name(sep); /* must free */
522 rawclass = sysevent_get_class_name(sep); /* valid with sep */
523 rawsubclass = sysevent_get_subclass_name(sep); /* valid with sep */
525 if (sysevent_get_attr_list(sep, &rawattr) != 0) {
526 BUMPSTAT(raw_noattrlist);
527 goto done;
530 if ((dtcr = fmevt_detector(rawattr, ruleset, user, priv,
531 pri)) == NULL) {
532 BUMPSTAT(raw_nodetector);
533 goto done;
536 ea.pp_rawclass = rawclass;
537 ea.pp_rawsubclass = rawsubclass;
538 sysevent_get_time(sep, &ea.pp_hrt);
539 ea.pp_user = user;
540 ea.pp_priv = priv;
541 ea.pp_pri = pri;
543 fmevt_postprocess(ruleset, dtcr, rawattr, &ea);
544 nvlist_free(dtcr);
545 done:
546 (void) pthread_mutex_lock(&fmevt_lock);
548 if (--fmevt_xprt_refcnt == 0 && fmevt_exiting)
549 (void) pthread_cond_broadcast(&fmevt_cv);
551 (void) pthread_mutex_unlock(&fmevt_lock);
553 free(ruleset);
555 nvlist_free(rawattr);
557 return (0); /* in all cases consider the event delivered */
560 void
561 fmevt_init_inbound(fmd_hdl_t *hdl)
563 char *sidpfx;
564 zoneid_t zoneid;
565 int i;
567 if (!fmevt_rs_init(hdl))
568 fmd_hdl_abort(hdl, "error in fmevt_rs_init\n");
570 (void) fmd_stat_create(hdl, FMD_STAT_NOALLOC, sizeof (inbound_stats) /
571 sizeof (fmd_stat_t), (fmd_stat_t *)&inbound_stats);
573 zoneid = getzoneid();
574 isglobalzone = (zoneid == GLOBAL_ZONEID);
575 if (getzonenamebyid(zoneid, zonename, sizeof (zonename)) == -1)
576 fmd_hdl_abort(hdl, "getzonenamebyid failed");
578 if ((subattr = sysevent_subattr_alloc()) == NULL)
579 fmd_hdl_abort(hdl, "failed to allocate subscription "
580 "attributes: %s");
582 sysevent_subattr_thrcreate(subattr, fmd_doorthr_create, NULL);
583 sysevent_subattr_thrsetup(subattr, fmd_doorthr_setup, NULL);
585 sidpfx = fmd_prop_get_string(hdl, "sidprefix");
586 fmevt_xprt = fmd_xprt_open(hdl, FMD_XPRT_RDONLY, NULL, NULL);
588 for (i = 0; i < sizeof (chaninfo) / sizeof (chaninfo[0]); i++) {
589 struct fmevt_chaninfo *cip = &chaninfo[i];
590 char *channel = fmd_prop_get_string(hdl, cip->ci_propname);
591 int err;
593 if (sysevent_evc_bind(channel, &cip->ci_binding,
594 EVCH_CREAT | EVCH_HOLD_PEND_INDEF) != 0)
595 fmd_hdl_abort(hdl, "failed to bind GPEC channel for "
596 "channel %s", channel);
598 (void) snprintf(cip->ci_sid, sizeof (cip->ci_sid),
599 "%s_%c%c%c", sidpfx,
600 cip->ci_cbarg & CBF_USER ? 'u' : 'k',
601 cip->ci_cbarg & CBF_PRIV ? 'p' : 'n',
602 cip->ci_cbarg & CBF_HV ? 'h' : 'l');
604 err = sysevent_evc_xsubscribe(cip->ci_binding, cip->ci_sid,
605 EC_ALL, fmevt_cb, (void *)(uintptr_t)cip->ci_cbarg,
606 cip->ci_sflags, subattr);
608 if (err == EEXIST)
609 fmd_hdl_abort(hdl, "another fmd is active on "
610 "channel %s\n", channel);
611 else if (err != 0)
612 fmd_hdl_abort(hdl, "failed to subscribe to channel %s",
613 channel);
615 fmd_prop_free_string(hdl, channel);
618 fmd_prop_free_string(hdl, sidpfx);
621 void
622 fmevt_fini_inbound(fmd_hdl_t *hdl)
624 int i;
626 for (i = 0; i < sizeof (chaninfo) / sizeof (chaninfo[0]); i++) {
627 struct fmevt_chaninfo *cip = &chaninfo[i];
629 if (cip->ci_binding) {
630 (void) sysevent_evc_unsubscribe(cip->ci_binding,
631 cip->ci_sid);
632 (void) sysevent_evc_unbind(cip->ci_binding);
633 cip->ci_binding = NULL;
637 if (subattr) {
638 sysevent_subattr_free(subattr);
639 subattr = NULL;
642 if (fmevt_xprt) {
643 /* drain before destruction */
644 (void) pthread_mutex_lock(&fmevt_lock);
645 fmevt_exiting = 1;
646 while (fmevt_xprt_refcnt > 0)
647 (void) pthread_cond_wait(&fmevt_cv, &fmevt_lock);
648 (void) pthread_mutex_unlock(&fmevt_lock);
650 fmd_xprt_close(hdl, fmevt_xprt);