import less(1)
[unleashed/tickless.git] / usr / src / lib / fm / libfmevent / common / fmev_subscribe.c
blobeebdd4e1e2783027cc8238d136c601a9ac03f452
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) 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>
32 #include <atomic.h>
33 #include <libsysevent.h>
34 #include <libuutil.h>
35 #include <pthread.h>
36 #include <stdarg.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <strings.h>
40 #include <unistd.h>
41 #include <fm/libtopo.h>
43 #include <fm/libfmevent.h>
45 #include "fmev_impl.h"
47 static topo_hdl_t *g_topohdl;
49 typedef struct {
50 struct fmev_hdl_cmn sh_cmn;
51 evchan_t *sh_binding;
52 uu_avl_pool_t *sh_pool;
53 uu_avl_t *sh_avl;
54 uint32_t sh_subcnt;
55 uint32_t sh_flags;
56 sysevent_subattr_t *sh_attr;
57 pthread_mutex_t sh_lock;
58 pthread_mutex_t sh_srlz_lock;
59 } fmev_shdl_impl_t;
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 */
79 struct fmev_subinfo {
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];
84 fmev_cbfunc_t *si_cb;
85 void *si_cbarg;
88 struct fmev_hdl_cmn *
89 fmev_shdl_cmn(fmev_shdl_t hdl)
91 return (&HDL2IHDL(hdl)->sh_cmn);
94 static int
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 */
101 } else {
102 (void) pthread_mutex_unlock(&ihdl->sh_lock);
103 return (0);
107 static void
108 shdlctl_end(fmev_shdl_impl_t *ihdl)
110 (void) pthread_mutex_unlock(&ihdl->sh_lock);
113 fmev_err_t
114 fmev_shdlctl_serialize(fmev_shdl_t hdl)
116 fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
118 if (!FMEV_API_ENTER(hdl, 1))
119 return (fmev_errno);
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;
129 shdlctl_end(ihdl);
130 return (fmev_seterr(FMEV_SUCCESS));
133 fmev_err_t
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))
139 return (fmev_errno);
141 if (!shdlctl_start(ihdl))
142 return (fmev_seterr(FMEVERR_BUSY));
144 sysevent_subattr_thrattr(ihdl->sh_attr, attr);
146 shdlctl_end(ihdl);
147 return (fmev_seterr(FMEV_SUCCESS));
150 fmev_err_t
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))
156 return (fmev_errno);
158 if (!shdlctl_start(ihdl))
159 return (fmev_seterr(FMEVERR_BUSY));
161 sysevent_subattr_sigmask(ihdl->sh_attr, set);
163 shdlctl_end(ihdl);
164 return (fmev_seterr(FMEV_SUCCESS));
167 fmev_err_t
168 fmev_shdlctl_thrsetup(fmev_shdl_t hdl, door_xcreate_thrsetup_func_t *func,
169 void *cookie)
171 fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
173 if (!FMEV_API_ENTER(hdl, 1))
174 return (fmev_errno);
176 if (!shdlctl_start(ihdl))
177 return (fmev_seterr(FMEVERR_BUSY));
179 sysevent_subattr_thrsetup(ihdl->sh_attr, func, cookie);
181 shdlctl_end(ihdl);
182 return (fmev_seterr(FMEV_SUCCESS));
185 fmev_err_t
186 fmev_shdlctl_thrcreate(fmev_shdl_t hdl, door_xcreate_server_func_t *func,
187 void *cookie)
189 fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
191 if (!FMEV_API_ENTER(hdl, 1))
192 return (fmev_errno);
194 if (!shdlctl_start(ihdl))
195 return (fmev_seterr(FMEVERR_BUSY));
197 sysevent_subattr_thrcreate(ihdl->sh_attr, func, cookie);
199 shdlctl_end(ihdl);
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;
216 nvlist_t *nvl;
217 char *class;
218 fmev_t ev;
220 if (sip == NULL || sip->si_cb == NULL) {
221 fmev_proxy_cb_inval++;
222 return (0);
225 if ((ev = fmev_sysev2fmev(IHDL2HDL(ihdl), sep, &class, &nvl)) == NULL) {
226 fmev_proxy_cb_enomem++;
227 return (0);
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 */
240 return (0);
243 static volatile uint32_t fmev_subid;
245 fmev_err_t
246 fmev_shdl_subscribe(fmev_shdl_t hdl, const char *pat, fmev_cbfunc_t func,
247 void *funcarg)
249 fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
250 struct fmev_subinfo *sip;
251 uu_avl_index_t idx;
252 uint64_t nsid;
253 int serr;
255 if (!FMEV_API_ENTER(hdl, 1))
256 return (fmev_errno);
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
289 * this zone.
291 nsid = (uint64_t)getpid() << 32 | atomic_inc_32_nv(&fmev_subid);
292 (void) snprintf(sip->si_sid, sizeof (sip->si_sid), "%llx", nsid);
294 sip->si_ihdl = ihdl;
295 sip->si_cb = func;
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) {
300 fmev_err_t err;
302 (void) pthread_mutex_unlock(&ihdl->sh_lock);
303 fmev_shdl_free(hdl, sip, sizeof (*sip));
305 switch (serr) {
306 case ENOMEM:
307 err = FMEVERR_MAX_SUBSCRIBERS;
308 break;
310 default:
311 err = FMEVERR_INTERNAL;
312 break;
315 return (fmev_seterr(err));
318 uu_avl_insert(ihdl->sh_avl, sip, idx);
319 ihdl->sh_subcnt++;
321 (void) pthread_mutex_unlock(&ihdl->sh_lock);
323 return (fmev_seterr(FMEV_SUCCESS));
326 static int
327 fmev_subinfo_fini(fmev_shdl_impl_t *ihdl, struct fmev_subinfo *sip,
328 boolean_t doavl)
330 int err;
332 ASSERT(sip->si_ihdl == ihdl);
334 err = sysevent_evc_unsubscribe(ihdl->sh_binding, sip->si_sid);
336 if (err == 0) {
337 if (doavl) {
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));
342 ihdl->sh_subcnt--;
345 return (err);
348 fmev_err_t
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;
355 int err;
357 if (!FMEV_API_ENTER(hdl, 1))
358 return (fmev_errno);
360 if (pat == NULL)
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) {
373 rv = FMEV_SUCCESS;
374 } else {
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));
389 void *
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))
395 return (NULL);
397 return (ihdl->sh_cmn.hc_alloc(sz));
400 void *
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))
406 return (NULL);
408 return (ihdl->sh_cmn.hc_zalloc(sz));
411 void
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))
417 return;
419 ihdl->sh_cmn.hc_free(buf, sz);
422 char *
423 fmev_shdl_strdup(fmev_shdl_t hdl, char *src)
425 fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
426 size_t srclen;
427 char *dst;
429 if (!FMEV_API_ENTER(hdl, 2))
430 return (NULL);
432 srclen = strlen(src);
434 if ((dst = ihdl->sh_cmn.hc_alloc(srclen + 1)) == NULL) {
435 (void) fmev_seterr(FMEVERR_ALLOC);
436 return (NULL);
439 (void) strncpy(dst, src, srclen);
440 dst[srclen] = '\0';
441 return (dst);
444 void
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)));
460 /*ARGSUSED*/
461 static int
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));
470 fmev_shdl_t
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;
477 int err;
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);
491 return (NULL);
494 if (hdlzalloc == NULL)
495 ihdl = dflt_zalloc(sizeof (*ihdl));
496 else
497 ihdl = hdlzalloc(sizeof (*ihdl));
499 if (ihdl == NULL) {
500 (void) fmev_seterr(FMEVERR_ALLOC);
501 return (NULL);
504 ihdl->sh_cmn = hc;
506 if ((ihdl->sh_attr = sysevent_subattr_alloc()) == NULL) {
507 err = FMEVERR_ALLOC;
508 goto error;
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) {
528 switch (errno) {
529 case EINVAL:
530 default:
531 err = FMEVERR_INTERNAL;
532 break;
533 case ENOMEM:
534 err = FMEVERR_ALLOC;
535 break;
536 case EPERM:
537 err = FMEVERR_NOPRIV;
538 break;
540 goto error;
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;
548 goto error;
551 if ((ihdl->sh_avl = uu_avl_create(ihdl->sh_pool, NULL,
552 UU_DEFAULT)) == NULL) {
553 err = FMEVERR_INTERNAL;
554 goto error;
557 return (IHDL2HDL(ihdl));
559 error:
560 (void) fmev_shdl_fini(IHDL2HDL(ihdl));
561 (void) fmev_seterr(err);
562 return (NULL);
565 fmev_err_t
566 fmev_shdl_getauthority(fmev_shdl_t hdl, nvlist_t **nvlp)
568 fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
569 nvlist_t *propnvl;
570 fmev_err_t rc;
572 if (!FMEV_API_ENTER(hdl, 2))
573 return (fmev_errno);
575 (void) pthread_mutex_lock(&ihdl->sh_lock);
577 if (sysevent_evc_getpropnvl(ihdl->sh_binding, &propnvl) != 0) {
578 *nvlp = NULL;
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 */
585 } else {
586 nvlist_t *auth;
588 if (nvlist_lookup_nvlist(propnvl, "fmdauth", &auth) == 0) {
589 rc = (nvlist_dup(auth, nvlp, 0) == 0) ? FMEV_SUCCESS :
590 FMEVERR_ALLOC;
591 } else {
592 rc = FMEVERR_INTERNAL;
594 nvlist_free(propnvl);
597 (void) pthread_mutex_unlock(&ihdl->sh_lock);
599 if (rc != FMEV_SUCCESS) {
600 *nvlp = NULL;
601 (void) fmev_seterr(rc);
604 return (rc);
607 char *
608 fmev_shdl_nvl2str(fmev_shdl_t hdl, nvlist_t *nvl)
610 fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
611 char *fmri, *fmricp;
612 fmev_err_t err;
613 int topoerr;
615 if (!FMEV_API_ENTER(hdl, 2))
616 return (NULL);
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);
626 return (NULL);
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 */
636 switch (topoerr) {
637 case ETOPO_FMRI_NOMEM:
638 err = FMEVERR_ALLOC;
639 break;
641 case ETOPO_FMRI_MALFORM:
642 case ETOPO_METHOD_NOTSUP:
643 case ETOPO_METHOD_INVAL:
644 default:
645 err = FMEVERR_INVALIDARG;
646 break;
649 (void) fmev_seterr(err);
650 return (NULL);
653 fmev_err_t
654 fmev_shdl_fini(fmev_shdl_t hdl)
656 fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
658 if (!FMEV_API_ENTER(hdl, 1))
659 return (fmev_errno);
661 (void) pthread_mutex_lock(&ihdl->sh_lock);
664 * Verify that we are not in callback context - return an API
665 * error if we are.
667 if (sysevent_evc_unsubscribe(ihdl->sh_binding, "invalidsid") ==
668 EDEADLK) {
669 (void) pthread_mutex_unlock(&ihdl->sh_lock);
670 return (fmev_seterr(FMEVERR_API));
673 if (ihdl->sh_avl) {
674 void *cookie = NULL;
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);
681 ihdl->sh_avl = NULL;
684 ASSERT(ihdl->sh_subcnt == 0);
686 if (ihdl->sh_binding) {
687 (void) sysevent_evc_unbind(ihdl->sh_binding);
688 ihdl->sh_binding = NULL;
691 if (ihdl->sh_pool) {
692 uu_avl_pool_destroy(ihdl->sh_pool);
693 ihdl->sh_pool = NULL;
696 if (ihdl->sh_attr) {
697 sysevent_subattr_free(ihdl->sh_attr);
698 ihdl->sh_attr = NULL;
701 ihdl->sh_cmn.hc_magic = 0;
703 if (g_topohdl) {
704 topo_close(g_topohdl);
705 g_topohdl = NULL;
708 (void) pthread_mutex_unlock(&ihdl->sh_lock);
709 (void) pthread_mutex_destroy(&ihdl->sh_lock);
711 fmev_shdl_free(hdl, hdl, sizeof (*ihdl));
713 fmev_api_freetsd();
715 return (fmev_seterr(FMEV_SUCCESS));