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) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
27 * FMA event subscription interfaces - subscribe to FMA protocol
28 * from outside the fault manager.
31 #include <sys/types.h>
33 #include <libsysevent.h>
41 #include <fm/libtopo.h>
43 #include <fm/libfmevent.h>
45 #include "fmev_impl.h"
47 static topo_hdl_t
*g_topohdl
;
50 struct fmev_hdl_cmn sh_cmn
;
52 uu_avl_pool_t
*sh_pool
;
56 sysevent_subattr_t
*sh_attr
;
57 pthread_mutex_t sh_lock
;
58 pthread_mutex_t sh_srlz_lock
;
61 #define HDL2IHDL(hdl) ((fmev_shdl_impl_t *)(hdl))
62 #define IHDL2HDL(ihdl) ((fmev_shdl_t)(ihdl))
64 #define _FMEV_SHMAGIC 0x5368446c /* ShDl */
65 #define FMEV_SHDL_VALID(ihdl) ((ihdl)->sh_cmn.hc_magic == _FMEV_SHMAGIC)
67 #define SHDL_FL_SERIALIZE 0x1
69 #define FMEV_API_ENTER(hdl, v) \
70 fmev_api_enter(&HDL2IHDL(hdl)->sh_cmn, LIBFMEVENT_VERSION_##v)
73 * For each subscription on a handle we add a node to an avl tree
74 * to track subscriptions.
77 #define FMEV_SID_SZ (16 + 1) /* Matches MAX_SUBID_LEN */
80 uu_avl_node_t si_node
;
81 fmev_shdl_impl_t
*si_ihdl
;
82 char si_pat
[FMEV_MAX_CLASS
];
83 char si_sid
[FMEV_SID_SZ
];
89 fmev_shdl_cmn(fmev_shdl_t hdl
)
91 return (&HDL2IHDL(hdl
)->sh_cmn
);
95 shdlctl_start(fmev_shdl_impl_t
*ihdl
)
97 (void) pthread_mutex_lock(&ihdl
->sh_lock
);
99 if (ihdl
->sh_subcnt
== 0) {
100 return (1); /* lock still held */
102 (void) pthread_mutex_unlock(&ihdl
->sh_lock
);
108 shdlctl_end(fmev_shdl_impl_t
*ihdl
)
110 (void) pthread_mutex_unlock(&ihdl
->sh_lock
);
114 fmev_shdlctl_serialize(fmev_shdl_t hdl
)
116 fmev_shdl_impl_t
*ihdl
= HDL2IHDL(hdl
);
118 if (!FMEV_API_ENTER(hdl
, 1))
121 if (!shdlctl_start(ihdl
))
122 return (fmev_seterr(FMEVERR_BUSY
));
124 if (!(ihdl
->sh_flags
& SHDL_FL_SERIALIZE
)) {
125 (void) pthread_mutex_init(&ihdl
->sh_srlz_lock
, NULL
);
126 ihdl
->sh_flags
|= SHDL_FL_SERIALIZE
;
130 return (fmev_seterr(FMEV_SUCCESS
));
134 fmev_shdlctl_thrattr(fmev_shdl_t hdl
, pthread_attr_t
*attr
)
136 fmev_shdl_impl_t
*ihdl
= HDL2IHDL(hdl
);
138 if (!FMEV_API_ENTER(hdl
, 1))
141 if (!shdlctl_start(ihdl
))
142 return (fmev_seterr(FMEVERR_BUSY
));
144 sysevent_subattr_thrattr(ihdl
->sh_attr
, attr
);
147 return (fmev_seterr(FMEV_SUCCESS
));
151 fmev_shdlctl_sigmask(fmev_shdl_t hdl
, sigset_t
*set
)
153 fmev_shdl_impl_t
*ihdl
= HDL2IHDL(hdl
);
155 if (!FMEV_API_ENTER(hdl
, 1))
158 if (!shdlctl_start(ihdl
))
159 return (fmev_seterr(FMEVERR_BUSY
));
161 sysevent_subattr_sigmask(ihdl
->sh_attr
, set
);
164 return (fmev_seterr(FMEV_SUCCESS
));
168 fmev_shdlctl_thrsetup(fmev_shdl_t hdl
, door_xcreate_thrsetup_func_t
*func
,
171 fmev_shdl_impl_t
*ihdl
= HDL2IHDL(hdl
);
173 if (!FMEV_API_ENTER(hdl
, 1))
176 if (!shdlctl_start(ihdl
))
177 return (fmev_seterr(FMEVERR_BUSY
));
179 sysevent_subattr_thrsetup(ihdl
->sh_attr
, func
, cookie
);
182 return (fmev_seterr(FMEV_SUCCESS
));
186 fmev_shdlctl_thrcreate(fmev_shdl_t hdl
, door_xcreate_server_func_t
*func
,
189 fmev_shdl_impl_t
*ihdl
= HDL2IHDL(hdl
);
191 if (!FMEV_API_ENTER(hdl
, 1))
194 if (!shdlctl_start(ihdl
))
195 return (fmev_seterr(FMEVERR_BUSY
));
197 sysevent_subattr_thrcreate(ihdl
->sh_attr
, func
, cookie
);
200 return (fmev_seterr(FMEV_SUCCESS
));
204 * Our door service function. We return 0 regardless so that the kernel
205 * does not keep either retrying (EAGAIN) or bleat to cmn_err.
208 uint64_t fmev_proxy_cb_inval
;
209 uint64_t fmev_proxy_cb_enomem
;
212 fmev_proxy_cb(sysevent_t
*sep
, void *arg
)
214 struct fmev_subinfo
*sip
= arg
;
215 fmev_shdl_impl_t
*ihdl
= sip
->si_ihdl
;
220 if (sip
== NULL
|| sip
->si_cb
== NULL
) {
221 fmev_proxy_cb_inval
++;
225 if ((ev
= fmev_sysev2fmev(IHDL2HDL(ihdl
), sep
, &class, &nvl
)) == NULL
) {
226 fmev_proxy_cb_enomem
++;
230 if (ihdl
->sh_flags
& SHDL_FL_SERIALIZE
)
231 (void) pthread_mutex_lock(&ihdl
->sh_srlz_lock
);
233 sip
->si_cb(ev
, class, nvl
, sip
->si_cbarg
);
235 if (ihdl
->sh_flags
& SHDL_FL_SERIALIZE
)
236 (void) pthread_mutex_unlock(&ihdl
->sh_srlz_lock
);
238 fmev_rele(ev
); /* release hold obtained in fmev_sysev2fmev */
243 static volatile uint32_t fmev_subid
;
246 fmev_shdl_subscribe(fmev_shdl_t hdl
, const char *pat
, fmev_cbfunc_t func
,
249 fmev_shdl_impl_t
*ihdl
= HDL2IHDL(hdl
);
250 struct fmev_subinfo
*sip
;
255 if (!FMEV_API_ENTER(hdl
, 1))
258 if (pat
== NULL
|| func
== NULL
)
259 return (fmev_seterr(FMEVERR_API
));
262 * Empty class patterns are illegal, as is the sysevent magic for
263 * all classes. Also validate class length.
265 if (*pat
== '\0' || strncmp(pat
, EC_ALL
, sizeof (EC_ALL
)) == 0 ||
266 strncmp(pat
, EC_SUB_ALL
, sizeof (EC_SUB_ALL
)) == 0 ||
267 strnlen(pat
, FMEV_MAX_CLASS
) == FMEV_MAX_CLASS
)
268 return (fmev_seterr(FMEVERR_BADCLASS
));
270 if ((sip
= fmev_shdl_zalloc(hdl
, sizeof (*sip
))) == NULL
)
271 return (fmev_seterr(FMEVERR_ALLOC
));
273 (void) strncpy(sip
->si_pat
, pat
, sizeof (sip
->si_pat
));
275 uu_avl_node_init(sip
, &sip
->si_node
, ihdl
->sh_pool
);
277 (void) pthread_mutex_lock(&ihdl
->sh_lock
);
279 if (uu_avl_find(ihdl
->sh_avl
, sip
, NULL
, &idx
) != NULL
) {
280 (void) pthread_mutex_unlock(&ihdl
->sh_lock
);
281 fmev_shdl_free(hdl
, sip
, sizeof (*sip
));
282 return (fmev_seterr(FMEVERR_DUPLICATE
));
286 * Generate a subscriber id for GPEC that is unique to this
287 * subscription. There is no provision for persistent
288 * subscribers. The subscriber id must be unique within
291 nsid
= (uint64_t)getpid() << 32 | atomic_inc_32_nv(&fmev_subid
);
292 (void) snprintf(sip
->si_sid
, sizeof (sip
->si_sid
), "%llx", nsid
);
296 sip
->si_cbarg
= funcarg
;
298 if ((serr
= sysevent_evc_xsubscribe(ihdl
->sh_binding
, sip
->si_sid
,
299 sip
->si_pat
, fmev_proxy_cb
, sip
, 0, ihdl
->sh_attr
)) != 0) {
302 (void) pthread_mutex_unlock(&ihdl
->sh_lock
);
303 fmev_shdl_free(hdl
, sip
, sizeof (*sip
));
307 err
= FMEVERR_MAX_SUBSCRIBERS
;
311 err
= FMEVERR_INTERNAL
;
315 return (fmev_seterr(err
));
318 uu_avl_insert(ihdl
->sh_avl
, sip
, idx
);
321 (void) pthread_mutex_unlock(&ihdl
->sh_lock
);
323 return (fmev_seterr(FMEV_SUCCESS
));
327 fmev_subinfo_fini(fmev_shdl_impl_t
*ihdl
, struct fmev_subinfo
*sip
,
332 ASSERT(sip
->si_ihdl
== ihdl
);
334 err
= sysevent_evc_unsubscribe(ihdl
->sh_binding
, sip
->si_sid
);
338 uu_avl_remove(ihdl
->sh_avl
, sip
);
339 uu_avl_node_fini(sip
, &sip
->si_node
, ihdl
->sh_pool
);
341 fmev_shdl_free(IHDL2HDL(ihdl
), sip
, sizeof (*sip
));
349 fmev_shdl_unsubscribe(fmev_shdl_t hdl
, const char *pat
)
351 fmev_shdl_impl_t
*ihdl
= HDL2IHDL(hdl
);
352 fmev_err_t rv
= FMEVERR_NOMATCH
;
353 struct fmev_subinfo
*sip
;
354 struct fmev_subinfo si
;
357 if (!FMEV_API_ENTER(hdl
, 1))
361 return (fmev_seterr(FMEVERR_API
));
363 if (*pat
== '\0' || strncmp(pat
, EVCH_ALLSUB
, sizeof (EC_ALL
)) == 0 ||
364 strnlen(pat
, FMEV_MAX_CLASS
) == FMEV_MAX_CLASS
)
365 return (fmev_seterr(FMEVERR_BADCLASS
));
367 (void) strncpy(si
.si_pat
, pat
, sizeof (si
.si_pat
));
369 (void) pthread_mutex_lock(&ihdl
->sh_lock
);
371 if ((sip
= uu_avl_find(ihdl
->sh_avl
, &si
, NULL
, NULL
)) != NULL
) {
372 if ((err
= fmev_subinfo_fini(ihdl
, sip
, B_TRUE
)) == 0) {
376 * Return an API error if the unsubscribe was
377 * attempted from within a door callback invocation;
378 * other errors should not happen.
380 rv
= (err
== EDEADLK
) ? FMEVERR_API
: FMEVERR_INTERNAL
;
384 (void) pthread_mutex_unlock(&ihdl
->sh_lock
);
386 return (fmev_seterr(rv
));
390 fmev_shdl_alloc(fmev_shdl_t hdl
, size_t sz
)
392 fmev_shdl_impl_t
*ihdl
= HDL2IHDL(hdl
);
394 if (!FMEV_API_ENTER(hdl
, 1))
397 return (ihdl
->sh_cmn
.hc_alloc(sz
));
401 fmev_shdl_zalloc(fmev_shdl_t hdl
, size_t sz
)
403 fmev_shdl_impl_t
*ihdl
= HDL2IHDL(hdl
);
405 if (!FMEV_API_ENTER(hdl
, 1))
408 return (ihdl
->sh_cmn
.hc_zalloc(sz
));
412 fmev_shdl_free(fmev_shdl_t hdl
, void *buf
, size_t sz
)
414 fmev_shdl_impl_t
*ihdl
= HDL2IHDL(hdl
);
416 if (!FMEV_API_ENTER(hdl
, 1))
419 ihdl
->sh_cmn
.hc_free(buf
, sz
);
423 fmev_shdl_strdup(fmev_shdl_t hdl
, char *src
)
425 fmev_shdl_impl_t
*ihdl
= HDL2IHDL(hdl
);
429 if (!FMEV_API_ENTER(hdl
, 2))
432 srclen
= strlen(src
);
434 if ((dst
= ihdl
->sh_cmn
.hc_alloc(srclen
+ 1)) == NULL
) {
435 (void) fmev_seterr(FMEVERR_ALLOC
);
439 (void) strncpy(dst
, src
, srclen
);
445 fmev_shdl_strfree(fmev_shdl_t hdl
, char *buf
)
447 fmev_shdl_impl_t
*ihdl
= HDL2IHDL(hdl
);
449 (void) FMEV_API_ENTER(hdl
, 2);
451 ihdl
->sh_cmn
.hc_free(buf
, strlen(buf
) + 1);
455 fmev_shdl_valid(fmev_shdl_t hdl
)
457 return (FMEV_SHDL_VALID(HDL2IHDL(hdl
)));
462 fmev_keycmp(const void *l
, const void *r
, void *arg
)
464 struct fmev_subinfo
*left
= (struct fmev_subinfo
*)l
;
465 struct fmev_subinfo
*right
= (struct fmev_subinfo
*)r
;
467 return (strncmp(left
->si_pat
, right
->si_pat
, FMEV_MAX_CLASS
));
471 fmev_shdl_init(uint32_t caller_version
, void *(*hdlalloc
)(size_t),
472 void *(*hdlzalloc
)(size_t), void (*hdlfree
)(void *, size_t))
474 fmev_shdl_impl_t
*ihdl
;
475 struct fmev_hdl_cmn hc
;
476 const char *chan_name
;
479 hc
.hc_magic
= _FMEV_SHMAGIC
;
480 hc
.hc_api_vers
= caller_version
;
481 hc
.hc_alloc
= hdlalloc
? hdlalloc
: dflt_alloc
;
482 hc
.hc_zalloc
= hdlzalloc
? hdlzalloc
: dflt_zalloc
;
483 hc
.hc_free
= hdlfree
? hdlfree
: dflt_free
;
485 if (!fmev_api_init(&hc
))
486 return (NULL
); /* error type set */
488 if (!((hdlalloc
== NULL
&& hdlzalloc
== NULL
&& hdlfree
== NULL
) ||
489 (hdlalloc
!= NULL
&& hdlzalloc
!= NULL
&& hdlfree
!= NULL
))) {
490 (void) fmev_seterr(FMEVERR_API
);
494 if (hdlzalloc
== NULL
)
495 ihdl
= dflt_zalloc(sizeof (*ihdl
));
497 ihdl
= hdlzalloc(sizeof (*ihdl
));
500 (void) fmev_seterr(FMEVERR_ALLOC
);
506 if ((ihdl
->sh_attr
= sysevent_subattr_alloc()) == NULL
) {
511 (void) pthread_mutex_init(&ihdl
->sh_lock
, NULL
);
514 * For simulation purposes we allow an environment variable
515 * to provide a different channel name.
517 if ((chan_name
= getenv("FMD_SNOOP_CHANNEL")) == NULL
)
518 chan_name
= FMD_SNOOP_CHANNEL
;
521 * Try to bind to the event channel. If it's not already present,
522 * attempt to create the channel so that we can startup before
523 * the event producer (who will also apply choices such as
524 * channel depth when they bind to the channel).
526 if (sysevent_evc_bind(chan_name
, &ihdl
->sh_binding
,
527 EVCH_CREAT
| EVCH_HOLD_PEND_INDEF
) != 0) {
531 err
= FMEVERR_INTERNAL
;
537 err
= FMEVERR_NOPRIV
;
543 if ((ihdl
->sh_pool
= uu_avl_pool_create("subinfo_pool",
544 sizeof (struct fmev_subinfo
),
545 offsetof(struct fmev_subinfo
, si_node
), fmev_keycmp
,
546 UU_AVL_POOL_DEBUG
)) == NULL
) {
547 err
= FMEVERR_INTERNAL
;
551 if ((ihdl
->sh_avl
= uu_avl_create(ihdl
->sh_pool
, NULL
,
552 UU_DEFAULT
)) == NULL
) {
553 err
= FMEVERR_INTERNAL
;
557 return (IHDL2HDL(ihdl
));
560 (void) fmev_shdl_fini(IHDL2HDL(ihdl
));
561 (void) fmev_seterr(err
);
566 fmev_shdl_getauthority(fmev_shdl_t hdl
, nvlist_t
**nvlp
)
568 fmev_shdl_impl_t
*ihdl
= HDL2IHDL(hdl
);
572 if (!FMEV_API_ENTER(hdl
, 2))
575 (void) pthread_mutex_lock(&ihdl
->sh_lock
);
577 if (sysevent_evc_getpropnvl(ihdl
->sh_binding
, &propnvl
) != 0) {
579 (void) pthread_mutex_unlock(&ihdl
->sh_lock
);
580 return (fmev_seterr(FMEVERR_UNKNOWN
));
583 if (propnvl
== NULL
) {
584 rc
= FMEVERR_BUSY
; /* Other end has not bound */
588 if (nvlist_lookup_nvlist(propnvl
, "fmdauth", &auth
) == 0) {
589 rc
= (nvlist_dup(auth
, nvlp
, 0) == 0) ? FMEV_SUCCESS
:
592 rc
= FMEVERR_INTERNAL
;
594 nvlist_free(propnvl
);
597 (void) pthread_mutex_unlock(&ihdl
->sh_lock
);
599 if (rc
!= FMEV_SUCCESS
) {
601 (void) fmev_seterr(rc
);
608 fmev_shdl_nvl2str(fmev_shdl_t hdl
, nvlist_t
*nvl
)
610 fmev_shdl_impl_t
*ihdl
= HDL2IHDL(hdl
);
615 if (!FMEV_API_ENTER(hdl
, 2))
618 if (g_topohdl
== NULL
) {
619 (void) pthread_mutex_lock(&ihdl
->sh_lock
);
620 if (g_topohdl
== NULL
)
621 g_topohdl
= topo_open(TOPO_VERSION
, NULL
, &topoerr
);
622 (void) pthread_mutex_unlock(&ihdl
->sh_lock
);
624 if (g_topohdl
== NULL
) {
625 (void) fmev_seterr(FMEVERR_INTERNAL
);
630 if (topo_fmri_nvl2str(g_topohdl
, nvl
, &fmri
, &topoerr
) == 0) {
631 fmricp
= fmev_shdl_strdup(hdl
, fmri
);
632 topo_hdl_strfree(g_topohdl
, fmri
);
633 return (fmricp
); /* fmev_errno set if strdup failed */
637 case ETOPO_FMRI_NOMEM
:
641 case ETOPO_FMRI_MALFORM
:
642 case ETOPO_METHOD_NOTSUP
:
643 case ETOPO_METHOD_INVAL
:
645 err
= FMEVERR_INVALIDARG
;
649 (void) fmev_seterr(err
);
654 fmev_shdl_fini(fmev_shdl_t hdl
)
656 fmev_shdl_impl_t
*ihdl
= HDL2IHDL(hdl
);
658 if (!FMEV_API_ENTER(hdl
, 1))
661 (void) pthread_mutex_lock(&ihdl
->sh_lock
);
664 * Verify that we are not in callback context - return an API
667 if (sysevent_evc_unsubscribe(ihdl
->sh_binding
, "invalidsid") ==
669 (void) pthread_mutex_unlock(&ihdl
->sh_lock
);
670 return (fmev_seterr(FMEVERR_API
));
675 struct fmev_subinfo
*sip
;
677 while ((sip
= uu_avl_teardown(ihdl
->sh_avl
, &cookie
)) != NULL
)
678 (void) fmev_subinfo_fini(ihdl
, sip
, B_FALSE
);
680 uu_avl_destroy(ihdl
->sh_avl
);
684 ASSERT(ihdl
->sh_subcnt
== 0);
686 if (ihdl
->sh_binding
) {
687 (void) sysevent_evc_unbind(ihdl
->sh_binding
);
688 ihdl
->sh_binding
= NULL
;
692 uu_avl_pool_destroy(ihdl
->sh_pool
);
693 ihdl
->sh_pool
= NULL
;
697 sysevent_subattr_free(ihdl
->sh_attr
);
698 ihdl
->sh_attr
= NULL
;
701 ihdl
->sh_cmn
.hc_magic
= 0;
704 topo_close(g_topohdl
);
708 (void) pthread_mutex_unlock(&ihdl
->sh_lock
);
709 (void) pthread_mutex_destroy(&ihdl
->sh_lock
);
711 fmev_shdl_free(hdl
, hdl
, sizeof (*ihdl
));
715 return (fmev_seterr(FMEV_SUCCESS
));