149b225e1SGavin Maltby /*
249b225e1SGavin Maltby * CDDL HEADER START
349b225e1SGavin Maltby *
449b225e1SGavin Maltby * The contents of this file are subject to the terms of the
549b225e1SGavin Maltby * Common Development and Distribution License (the "License").
649b225e1SGavin Maltby * You may not use this file except in compliance with the License.
749b225e1SGavin Maltby *
849b225e1SGavin Maltby * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
949b225e1SGavin Maltby * or http://www.opensolaris.org/os/licensing.
1049b225e1SGavin Maltby * See the License for the specific language governing permissions
1149b225e1SGavin Maltby * and limitations under the License.
1249b225e1SGavin Maltby *
1349b225e1SGavin Maltby * When distributing Covered Code, include this CDDL HEADER in each
1449b225e1SGavin Maltby * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1549b225e1SGavin Maltby * If applicable, add the following below this CDDL HEADER, with the
1649b225e1SGavin Maltby * fields enclosed by brackets "[]" replaced with your own identifying
1749b225e1SGavin Maltby * information: Portions Copyright [yyyy] [name of copyright owner]
1849b225e1SGavin Maltby *
1949b225e1SGavin Maltby * CDDL HEADER END
2049b225e1SGavin Maltby */
2149b225e1SGavin Maltby
2249b225e1SGavin Maltby /*
23*f6e214c7SGavin Maltby * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
2449b225e1SGavin Maltby */
2549b225e1SGavin Maltby
2649b225e1SGavin Maltby /*
2749b225e1SGavin Maltby * FMA event subscription interfaces - subscribe to FMA protocol
2849b225e1SGavin Maltby * from outside the fault manager.
2949b225e1SGavin Maltby */
3049b225e1SGavin Maltby
3149b225e1SGavin Maltby #include <sys/types.h>
3249b225e1SGavin Maltby #include <atomic.h>
3349b225e1SGavin Maltby #include <libsysevent.h>
3449b225e1SGavin Maltby #include <libuutil.h>
3549b225e1SGavin Maltby #include <pthread.h>
3649b225e1SGavin Maltby #include <stdarg.h>
3749b225e1SGavin Maltby #include <stdlib.h>
3849b225e1SGavin Maltby #include <string.h>
3949b225e1SGavin Maltby #include <strings.h>
4049b225e1SGavin Maltby #include <unistd.h>
41*f6e214c7SGavin Maltby #include <fm/libtopo.h>
4249b225e1SGavin Maltby
4349b225e1SGavin Maltby #include <fm/libfmevent.h>
4449b225e1SGavin Maltby
4549b225e1SGavin Maltby #include "fmev_impl.h"
46*f6e214c7SGavin Maltby
47*f6e214c7SGavin Maltby static topo_hdl_t *g_topohdl;
4849b225e1SGavin Maltby
4949b225e1SGavin Maltby typedef struct {
5049b225e1SGavin Maltby struct fmev_hdl_cmn sh_cmn;
5149b225e1SGavin Maltby evchan_t *sh_binding;
5249b225e1SGavin Maltby uu_avl_pool_t *sh_pool;
5349b225e1SGavin Maltby uu_avl_t *sh_avl;
5449b225e1SGavin Maltby uint32_t sh_subcnt;
5549b225e1SGavin Maltby uint32_t sh_flags;
5649b225e1SGavin Maltby sysevent_subattr_t *sh_attr;
5749b225e1SGavin Maltby pthread_mutex_t sh_lock;
5849b225e1SGavin Maltby pthread_mutex_t sh_srlz_lock;
5949b225e1SGavin Maltby } fmev_shdl_impl_t;
6049b225e1SGavin Maltby
6149b225e1SGavin Maltby #define HDL2IHDL(hdl) ((fmev_shdl_impl_t *)(hdl))
6249b225e1SGavin Maltby #define IHDL2HDL(ihdl) ((fmev_shdl_t)(ihdl))
6349b225e1SGavin Maltby
6449b225e1SGavin Maltby #define _FMEV_SHMAGIC 0x5368446c /* ShDl */
6549b225e1SGavin Maltby #define FMEV_SHDL_VALID(ihdl) ((ihdl)->sh_cmn.hc_magic == _FMEV_SHMAGIC)
6649b225e1SGavin Maltby
6749b225e1SGavin Maltby #define SHDL_FL_SERIALIZE 0x1
6849b225e1SGavin Maltby
69*f6e214c7SGavin Maltby #define FMEV_API_ENTER(hdl, v) \
70*f6e214c7SGavin Maltby fmev_api_enter(&HDL2IHDL(hdl)->sh_cmn, LIBFMEVENT_VERSION_##v)
7149b225e1SGavin Maltby
7249b225e1SGavin Maltby /*
7349b225e1SGavin Maltby * For each subscription on a handle we add a node to an avl tree
7449b225e1SGavin Maltby * to track subscriptions.
7549b225e1SGavin Maltby */
7649b225e1SGavin Maltby
7749b225e1SGavin Maltby #define FMEV_SID_SZ (16 + 1) /* Matches MAX_SUBID_LEN */
7849b225e1SGavin Maltby
7949b225e1SGavin Maltby struct fmev_subinfo {
8049b225e1SGavin Maltby uu_avl_node_t si_node;
8149b225e1SGavin Maltby fmev_shdl_impl_t *si_ihdl;
8249b225e1SGavin Maltby char si_pat[FMEV_MAX_CLASS];
8349b225e1SGavin Maltby char si_sid[FMEV_SID_SZ];
8449b225e1SGavin Maltby fmev_cbfunc_t *si_cb;
8549b225e1SGavin Maltby void *si_cbarg;
8649b225e1SGavin Maltby };
8749b225e1SGavin Maltby
8849b225e1SGavin Maltby struct fmev_hdl_cmn *
fmev_shdl_cmn(fmev_shdl_t hdl)8949b225e1SGavin Maltby fmev_shdl_cmn(fmev_shdl_t hdl)
9049b225e1SGavin Maltby {
9149b225e1SGavin Maltby return (&HDL2IHDL(hdl)->sh_cmn);
9249b225e1SGavin Maltby }
9349b225e1SGavin Maltby
9449b225e1SGavin Maltby static int
shdlctl_start(fmev_shdl_impl_t * ihdl)9549b225e1SGavin Maltby shdlctl_start(fmev_shdl_impl_t *ihdl)
9649b225e1SGavin Maltby {
9749b225e1SGavin Maltby (void) pthread_mutex_lock(&ihdl->sh_lock);
9849b225e1SGavin Maltby
9949b225e1SGavin Maltby if (ihdl->sh_subcnt == 0) {
10049b225e1SGavin Maltby return (1); /* lock still held */
10149b225e1SGavin Maltby } else {
10249b225e1SGavin Maltby (void) pthread_mutex_unlock(&ihdl->sh_lock);
10349b225e1SGavin Maltby return (0);
10449b225e1SGavin Maltby }
10549b225e1SGavin Maltby }
10649b225e1SGavin Maltby
10749b225e1SGavin Maltby static void
shdlctl_end(fmev_shdl_impl_t * ihdl)10849b225e1SGavin Maltby shdlctl_end(fmev_shdl_impl_t *ihdl)
10949b225e1SGavin Maltby {
11049b225e1SGavin Maltby (void) pthread_mutex_unlock(&ihdl->sh_lock);
11149b225e1SGavin Maltby }
11249b225e1SGavin Maltby
11349b225e1SGavin Maltby fmev_err_t
fmev_shdlctl_serialize(fmev_shdl_t hdl)11449b225e1SGavin Maltby fmev_shdlctl_serialize(fmev_shdl_t hdl)
11549b225e1SGavin Maltby {
11649b225e1SGavin Maltby fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
11749b225e1SGavin Maltby
118*f6e214c7SGavin Maltby if (!FMEV_API_ENTER(hdl, 1))
11949b225e1SGavin Maltby return (fmev_errno);
12049b225e1SGavin Maltby
12149b225e1SGavin Maltby if (!shdlctl_start(ihdl))
12249b225e1SGavin Maltby return (fmev_seterr(FMEVERR_BUSY));
12349b225e1SGavin Maltby
12449b225e1SGavin Maltby if (!(ihdl->sh_flags & SHDL_FL_SERIALIZE)) {
12549b225e1SGavin Maltby (void) pthread_mutex_init(&ihdl->sh_srlz_lock, NULL);
12649b225e1SGavin Maltby ihdl->sh_flags |= SHDL_FL_SERIALIZE;
12749b225e1SGavin Maltby }
12849b225e1SGavin Maltby
12949b225e1SGavin Maltby shdlctl_end(ihdl);
13049b225e1SGavin Maltby return (fmev_seterr(FMEV_SUCCESS));
13149b225e1SGavin Maltby }
13249b225e1SGavin Maltby
13349b225e1SGavin Maltby fmev_err_t
fmev_shdlctl_thrattr(fmev_shdl_t hdl,pthread_attr_t * attr)13449b225e1SGavin Maltby fmev_shdlctl_thrattr(fmev_shdl_t hdl, pthread_attr_t *attr)
13549b225e1SGavin Maltby {
13649b225e1SGavin Maltby fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
13749b225e1SGavin Maltby
138*f6e214c7SGavin Maltby if (!FMEV_API_ENTER(hdl, 1))
13949b225e1SGavin Maltby return (fmev_errno);
14049b225e1SGavin Maltby
14149b225e1SGavin Maltby if (!shdlctl_start(ihdl))
14249b225e1SGavin Maltby return (fmev_seterr(FMEVERR_BUSY));
14349b225e1SGavin Maltby
14449b225e1SGavin Maltby sysevent_subattr_thrattr(ihdl->sh_attr, attr);
14549b225e1SGavin Maltby
14649b225e1SGavin Maltby shdlctl_end(ihdl);
14749b225e1SGavin Maltby return (fmev_seterr(FMEV_SUCCESS));
14849b225e1SGavin Maltby }
14949b225e1SGavin Maltby
15049b225e1SGavin Maltby fmev_err_t
fmev_shdlctl_sigmask(fmev_shdl_t hdl,sigset_t * set)15149b225e1SGavin Maltby fmev_shdlctl_sigmask(fmev_shdl_t hdl, sigset_t *set)
15249b225e1SGavin Maltby {
15349b225e1SGavin Maltby fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
15449b225e1SGavin Maltby
155*f6e214c7SGavin Maltby if (!FMEV_API_ENTER(hdl, 1))
15649b225e1SGavin Maltby return (fmev_errno);
15749b225e1SGavin Maltby
15849b225e1SGavin Maltby if (!shdlctl_start(ihdl))
15949b225e1SGavin Maltby return (fmev_seterr(FMEVERR_BUSY));
16049b225e1SGavin Maltby
16149b225e1SGavin Maltby sysevent_subattr_sigmask(ihdl->sh_attr, set);
16249b225e1SGavin Maltby
16349b225e1SGavin Maltby shdlctl_end(ihdl);
16449b225e1SGavin Maltby return (fmev_seterr(FMEV_SUCCESS));
16549b225e1SGavin Maltby }
16649b225e1SGavin Maltby
16749b225e1SGavin Maltby fmev_err_t
fmev_shdlctl_thrsetup(fmev_shdl_t hdl,door_xcreate_thrsetup_func_t * func,void * cookie)16849b225e1SGavin Maltby fmev_shdlctl_thrsetup(fmev_shdl_t hdl, door_xcreate_thrsetup_func_t *func,
16949b225e1SGavin Maltby void *cookie)
17049b225e1SGavin Maltby {
17149b225e1SGavin Maltby fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
17249b225e1SGavin Maltby
173*f6e214c7SGavin Maltby if (!FMEV_API_ENTER(hdl, 1))
17449b225e1SGavin Maltby return (fmev_errno);
17549b225e1SGavin Maltby
17649b225e1SGavin Maltby if (!shdlctl_start(ihdl))
17749b225e1SGavin Maltby return (fmev_seterr(FMEVERR_BUSY));
17849b225e1SGavin Maltby
17949b225e1SGavin Maltby sysevent_subattr_thrsetup(ihdl->sh_attr, func, cookie);
18049b225e1SGavin Maltby
18149b225e1SGavin Maltby shdlctl_end(ihdl);
18249b225e1SGavin Maltby return (fmev_seterr(FMEV_SUCCESS));
18349b225e1SGavin Maltby }
18449b225e1SGavin Maltby
18549b225e1SGavin Maltby fmev_err_t
fmev_shdlctl_thrcreate(fmev_shdl_t hdl,door_xcreate_server_func_t * func,void * cookie)18649b225e1SGavin Maltby fmev_shdlctl_thrcreate(fmev_shdl_t hdl, door_xcreate_server_func_t *func,
18749b225e1SGavin Maltby void *cookie)
18849b225e1SGavin Maltby {
18949b225e1SGavin Maltby fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
19049b225e1SGavin Maltby
191*f6e214c7SGavin Maltby if (!FMEV_API_ENTER(hdl, 1))
19249b225e1SGavin Maltby return (fmev_errno);
19349b225e1SGavin Maltby
19449b225e1SGavin Maltby if (!shdlctl_start(ihdl))
19549b225e1SGavin Maltby return (fmev_seterr(FMEVERR_BUSY));
19649b225e1SGavin Maltby
19749b225e1SGavin Maltby sysevent_subattr_thrcreate(ihdl->sh_attr, func, cookie);
19849b225e1SGavin Maltby
19949b225e1SGavin Maltby shdlctl_end(ihdl);
20049b225e1SGavin Maltby return (fmev_seterr(FMEV_SUCCESS));
20149b225e1SGavin Maltby }
20249b225e1SGavin Maltby
20349b225e1SGavin Maltby /*
20449b225e1SGavin Maltby * Our door service function. We return 0 regardless so that the kernel
20549b225e1SGavin Maltby * does not keep either retrying (EAGAIN) or bleat to cmn_err.
20649b225e1SGavin Maltby */
20749b225e1SGavin Maltby
20849b225e1SGavin Maltby uint64_t fmev_proxy_cb_inval;
20949b225e1SGavin Maltby uint64_t fmev_proxy_cb_enomem;
21049b225e1SGavin Maltby
21149b225e1SGavin Maltby int
fmev_proxy_cb(sysevent_t * sep,void * arg)21249b225e1SGavin Maltby fmev_proxy_cb(sysevent_t *sep, void *arg)
21349b225e1SGavin Maltby {
21449b225e1SGavin Maltby struct fmev_subinfo *sip = arg;
21549b225e1SGavin Maltby fmev_shdl_impl_t *ihdl = sip->si_ihdl;
21649b225e1SGavin Maltby nvlist_t *nvl;
21749b225e1SGavin Maltby char *class;
21849b225e1SGavin Maltby fmev_t ev;
21949b225e1SGavin Maltby
22049b225e1SGavin Maltby if (sip == NULL || sip->si_cb == NULL) {
22149b225e1SGavin Maltby fmev_proxy_cb_inval++;
22249b225e1SGavin Maltby return (0);
22349b225e1SGavin Maltby }
22449b225e1SGavin Maltby
22549b225e1SGavin Maltby if ((ev = fmev_sysev2fmev(IHDL2HDL(ihdl), sep, &class, &nvl)) == NULL) {
22649b225e1SGavin Maltby fmev_proxy_cb_enomem++;
22749b225e1SGavin Maltby return (0);
22849b225e1SGavin Maltby }
22949b225e1SGavin Maltby
23049b225e1SGavin Maltby if (ihdl->sh_flags & SHDL_FL_SERIALIZE)
23149b225e1SGavin Maltby (void) pthread_mutex_lock(&ihdl->sh_srlz_lock);
23249b225e1SGavin Maltby
23349b225e1SGavin Maltby sip->si_cb(ev, class, nvl, sip->si_cbarg);
23449b225e1SGavin Maltby
23549b225e1SGavin Maltby if (ihdl->sh_flags & SHDL_FL_SERIALIZE)
23649b225e1SGavin Maltby (void) pthread_mutex_unlock(&ihdl->sh_srlz_lock);
23749b225e1SGavin Maltby
23849b225e1SGavin Maltby fmev_rele(ev); /* release hold obtained in fmev_sysev2fmev */
23949b225e1SGavin Maltby
24049b225e1SGavin Maltby return (0);
24149b225e1SGavin Maltby }
24249b225e1SGavin Maltby
24349b225e1SGavin Maltby static volatile uint32_t fmev_subid;
24449b225e1SGavin Maltby
24549b225e1SGavin Maltby fmev_err_t
fmev_shdl_subscribe(fmev_shdl_t hdl,const char * pat,fmev_cbfunc_t func,void * funcarg)24649b225e1SGavin Maltby fmev_shdl_subscribe(fmev_shdl_t hdl, const char *pat, fmev_cbfunc_t func,
24749b225e1SGavin Maltby void *funcarg)
24849b225e1SGavin Maltby {
24949b225e1SGavin Maltby fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
25049b225e1SGavin Maltby struct fmev_subinfo *sip;
25149b225e1SGavin Maltby uu_avl_index_t idx;
25249b225e1SGavin Maltby uint64_t nsid;
25349b225e1SGavin Maltby int serr;
25449b225e1SGavin Maltby
255*f6e214c7SGavin Maltby if (!FMEV_API_ENTER(hdl, 1))
25649b225e1SGavin Maltby return (fmev_errno);
25749b225e1SGavin Maltby
25849b225e1SGavin Maltby if (pat == NULL || func == NULL)
25949b225e1SGavin Maltby return (fmev_seterr(FMEVERR_API));
26049b225e1SGavin Maltby
26149b225e1SGavin Maltby /*
26249b225e1SGavin Maltby * Empty class patterns are illegal, as is the sysevent magic for
26349b225e1SGavin Maltby * all classes. Also validate class length.
26449b225e1SGavin Maltby */
26549b225e1SGavin Maltby if (*pat == '\0' || strncmp(pat, EC_ALL, sizeof (EC_ALL)) == 0 ||
26649b225e1SGavin Maltby strncmp(pat, EC_SUB_ALL, sizeof (EC_SUB_ALL)) == 0 ||
26749b225e1SGavin Maltby strnlen(pat, FMEV_MAX_CLASS) == FMEV_MAX_CLASS)
26849b225e1SGavin Maltby return (fmev_seterr(FMEVERR_BADCLASS));
26949b225e1SGavin Maltby
27049b225e1SGavin Maltby if ((sip = fmev_shdl_zalloc(hdl, sizeof (*sip))) == NULL)
27149b225e1SGavin Maltby return (fmev_seterr(FMEVERR_ALLOC));
27249b225e1SGavin Maltby
27349b225e1SGavin Maltby (void) strncpy(sip->si_pat, pat, sizeof (sip->si_pat));
27449b225e1SGavin Maltby
27549b225e1SGavin Maltby uu_avl_node_init(sip, &sip->si_node, ihdl->sh_pool);
27649b225e1SGavin Maltby
27749b225e1SGavin Maltby (void) pthread_mutex_lock(&ihdl->sh_lock);
27849b225e1SGavin Maltby
27949b225e1SGavin Maltby if (uu_avl_find(ihdl->sh_avl, sip, NULL, &idx) != NULL) {
28049b225e1SGavin Maltby (void) pthread_mutex_unlock(&ihdl->sh_lock);
28149b225e1SGavin Maltby fmev_shdl_free(hdl, sip, sizeof (*sip));
28249b225e1SGavin Maltby return (fmev_seterr(FMEVERR_DUPLICATE));
28349b225e1SGavin Maltby }
28449b225e1SGavin Maltby
28549b225e1SGavin Maltby /*
28649b225e1SGavin Maltby * Generate a subscriber id for GPEC that is unique to this
28749b225e1SGavin Maltby * subscription. There is no provision for persistent
28849b225e1SGavin Maltby * subscribers. The subscriber id must be unique within
28949b225e1SGavin Maltby * this zone.
29049b225e1SGavin Maltby */
29149b225e1SGavin Maltby nsid = (uint64_t)getpid() << 32 | atomic_inc_32_nv(&fmev_subid);
29249b225e1SGavin Maltby (void) snprintf(sip->si_sid, sizeof (sip->si_sid), "%llx", nsid);
29349b225e1SGavin Maltby
29449b225e1SGavin Maltby sip->si_ihdl = ihdl;
29549b225e1SGavin Maltby sip->si_cb = func;
29649b225e1SGavin Maltby sip->si_cbarg = funcarg;
29749b225e1SGavin Maltby
29849b225e1SGavin Maltby if ((serr = sysevent_evc_xsubscribe(ihdl->sh_binding, sip->si_sid,
29949b225e1SGavin Maltby sip->si_pat, fmev_proxy_cb, sip, 0, ihdl->sh_attr)) != 0) {
30049b225e1SGavin Maltby fmev_err_t err;
30149b225e1SGavin Maltby
30249b225e1SGavin Maltby (void) pthread_mutex_unlock(&ihdl->sh_lock);
30349b225e1SGavin Maltby fmev_shdl_free(hdl, sip, sizeof (*sip));
30449b225e1SGavin Maltby
30549b225e1SGavin Maltby switch (serr) {
30649b225e1SGavin Maltby case ENOMEM:
30749b225e1SGavin Maltby err = FMEVERR_MAX_SUBSCRIBERS;
30849b225e1SGavin Maltby break;
30949b225e1SGavin Maltby
31049b225e1SGavin Maltby default:
31149b225e1SGavin Maltby err = FMEVERR_INTERNAL;
31249b225e1SGavin Maltby break;
31349b225e1SGavin Maltby }
31449b225e1SGavin Maltby
31549b225e1SGavin Maltby return (fmev_seterr(err));
31649b225e1SGavin Maltby }
31749b225e1SGavin Maltby
31849b225e1SGavin Maltby uu_avl_insert(ihdl->sh_avl, sip, idx);
31949b225e1SGavin Maltby ihdl->sh_subcnt++;
32049b225e1SGavin Maltby
32149b225e1SGavin Maltby (void) pthread_mutex_unlock(&ihdl->sh_lock);
32249b225e1SGavin Maltby
32349b225e1SGavin Maltby return (fmev_seterr(FMEV_SUCCESS));
32449b225e1SGavin Maltby }
32549b225e1SGavin Maltby
32649b225e1SGavin Maltby static int
fmev_subinfo_fini(fmev_shdl_impl_t * ihdl,struct fmev_subinfo * sip,boolean_t doavl)32749b225e1SGavin Maltby fmev_subinfo_fini(fmev_shdl_impl_t *ihdl, struct fmev_subinfo *sip,
32849b225e1SGavin Maltby boolean_t doavl)
32949b225e1SGavin Maltby {
33049b225e1SGavin Maltby int err;
33149b225e1SGavin Maltby
33249b225e1SGavin Maltby ASSERT(sip->si_ihdl == ihdl);
33349b225e1SGavin Maltby
33449b225e1SGavin Maltby err = sysevent_evc_unsubscribe(ihdl->sh_binding, sip->si_sid);
33549b225e1SGavin Maltby
33649b225e1SGavin Maltby if (err == 0) {
33749b225e1SGavin Maltby if (doavl) {
33849b225e1SGavin Maltby uu_avl_remove(ihdl->sh_avl, sip);
33949b225e1SGavin Maltby uu_avl_node_fini(sip, &sip->si_node, ihdl->sh_pool);
34049b225e1SGavin Maltby }
34149b225e1SGavin Maltby fmev_shdl_free(IHDL2HDL(ihdl), sip, sizeof (*sip));
34249b225e1SGavin Maltby ihdl->sh_subcnt--;
34349b225e1SGavin Maltby }
34449b225e1SGavin Maltby
34549b225e1SGavin Maltby return (err);
34649b225e1SGavin Maltby }
34749b225e1SGavin Maltby
34849b225e1SGavin Maltby fmev_err_t
fmev_shdl_unsubscribe(fmev_shdl_t hdl,const char * pat)34949b225e1SGavin Maltby fmev_shdl_unsubscribe(fmev_shdl_t hdl, const char *pat)
35049b225e1SGavin Maltby {
35149b225e1SGavin Maltby fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
35249b225e1SGavin Maltby fmev_err_t rv = FMEVERR_NOMATCH;
35349b225e1SGavin Maltby struct fmev_subinfo *sip;
35449b225e1SGavin Maltby struct fmev_subinfo si;
35549b225e1SGavin Maltby int err;
35649b225e1SGavin Maltby
357*f6e214c7SGavin Maltby if (!FMEV_API_ENTER(hdl, 1))
35849b225e1SGavin Maltby return (fmev_errno);
35949b225e1SGavin Maltby
36049b225e1SGavin Maltby if (pat == NULL)
36149b225e1SGavin Maltby return (fmev_seterr(FMEVERR_API));
36249b225e1SGavin Maltby
36349b225e1SGavin Maltby if (*pat == '\0' || strncmp(pat, EVCH_ALLSUB, sizeof (EC_ALL)) == 0 ||
36449b225e1SGavin Maltby strnlen(pat, FMEV_MAX_CLASS) == FMEV_MAX_CLASS)
36549b225e1SGavin Maltby return (fmev_seterr(FMEVERR_BADCLASS));
36649b225e1SGavin Maltby
36749b225e1SGavin Maltby (void) strncpy(si.si_pat, pat, sizeof (si.si_pat));
36849b225e1SGavin Maltby
36949b225e1SGavin Maltby (void) pthread_mutex_lock(&ihdl->sh_lock);
37049b225e1SGavin Maltby
37149b225e1SGavin Maltby if ((sip = uu_avl_find(ihdl->sh_avl, &si, NULL, NULL)) != NULL) {
37249b225e1SGavin Maltby if ((err = fmev_subinfo_fini(ihdl, sip, B_TRUE)) == 0) {
37349b225e1SGavin Maltby rv = FMEV_SUCCESS;
37449b225e1SGavin Maltby } else {
37549b225e1SGavin Maltby /*
37649b225e1SGavin Maltby * Return an API error if the unsubscribe was
37749b225e1SGavin Maltby * attempted from within a door callback invocation;
37849b225e1SGavin Maltby * other errors should not happen.
37949b225e1SGavin Maltby */
38049b225e1SGavin Maltby rv = (err == EDEADLK) ? FMEVERR_API : FMEVERR_INTERNAL;
38149b225e1SGavin Maltby }
38249b225e1SGavin Maltby }
38349b225e1SGavin Maltby
38449b225e1SGavin Maltby (void) pthread_mutex_unlock(&ihdl->sh_lock);
38549b225e1SGavin Maltby
38649b225e1SGavin Maltby return (fmev_seterr(rv));
38749b225e1SGavin Maltby }
38849b225e1SGavin Maltby
38949b225e1SGavin Maltby void *
fmev_shdl_alloc(fmev_shdl_t hdl,size_t sz)39049b225e1SGavin Maltby fmev_shdl_alloc(fmev_shdl_t hdl, size_t sz)
39149b225e1SGavin Maltby {
39249b225e1SGavin Maltby fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
39349b225e1SGavin Maltby
394*f6e214c7SGavin Maltby if (!FMEV_API_ENTER(hdl, 1))
395*f6e214c7SGavin Maltby return (NULL);
39649b225e1SGavin Maltby
39749b225e1SGavin Maltby return (ihdl->sh_cmn.hc_alloc(sz));
39849b225e1SGavin Maltby }
39949b225e1SGavin Maltby
40049b225e1SGavin Maltby void *
fmev_shdl_zalloc(fmev_shdl_t hdl,size_t sz)40149b225e1SGavin Maltby fmev_shdl_zalloc(fmev_shdl_t hdl, size_t sz)
40249b225e1SGavin Maltby {
40349b225e1SGavin Maltby fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
40449b225e1SGavin Maltby
405*f6e214c7SGavin Maltby if (!FMEV_API_ENTER(hdl, 1))
406*f6e214c7SGavin Maltby return (NULL);
40749b225e1SGavin Maltby
40849b225e1SGavin Maltby return (ihdl->sh_cmn.hc_zalloc(sz));
40949b225e1SGavin Maltby }
41049b225e1SGavin Maltby
41149b225e1SGavin Maltby void
fmev_shdl_free(fmev_shdl_t hdl,void * buf,size_t sz)41249b225e1SGavin Maltby fmev_shdl_free(fmev_shdl_t hdl, void *buf, size_t sz)
41349b225e1SGavin Maltby {
41449b225e1SGavin Maltby fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
41549b225e1SGavin Maltby
416*f6e214c7SGavin Maltby if (!FMEV_API_ENTER(hdl, 1))
417*f6e214c7SGavin Maltby return;
41849b225e1SGavin Maltby
41949b225e1SGavin Maltby ihdl->sh_cmn.hc_free(buf, sz);
42049b225e1SGavin Maltby }
42149b225e1SGavin Maltby
422*f6e214c7SGavin Maltby char *
fmev_shdl_strdup(fmev_shdl_t hdl,char * src)423*f6e214c7SGavin Maltby fmev_shdl_strdup(fmev_shdl_t hdl, char *src)
424*f6e214c7SGavin Maltby {
425*f6e214c7SGavin Maltby fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
426*f6e214c7SGavin Maltby size_t srclen;
427*f6e214c7SGavin Maltby char *dst;
428*f6e214c7SGavin Maltby
429*f6e214c7SGavin Maltby if (!FMEV_API_ENTER(hdl, 2))
430*f6e214c7SGavin Maltby return (NULL);
431*f6e214c7SGavin Maltby
432*f6e214c7SGavin Maltby srclen = strlen(src);
433*f6e214c7SGavin Maltby
434*f6e214c7SGavin Maltby if ((dst = ihdl->sh_cmn.hc_alloc(srclen + 1)) == NULL) {
435*f6e214c7SGavin Maltby (void) fmev_seterr(FMEVERR_ALLOC);
436*f6e214c7SGavin Maltby return (NULL);
437*f6e214c7SGavin Maltby }
438*f6e214c7SGavin Maltby
439*f6e214c7SGavin Maltby (void) strncpy(dst, src, srclen);
440*f6e214c7SGavin Maltby dst[srclen] = '\0';
441*f6e214c7SGavin Maltby return (dst);
442*f6e214c7SGavin Maltby }
443*f6e214c7SGavin Maltby
444*f6e214c7SGavin Maltby void
fmev_shdl_strfree(fmev_shdl_t hdl,char * buf)445*f6e214c7SGavin Maltby fmev_shdl_strfree(fmev_shdl_t hdl, char *buf)
446*f6e214c7SGavin Maltby {
447*f6e214c7SGavin Maltby fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
448*f6e214c7SGavin Maltby
449*f6e214c7SGavin Maltby (void) FMEV_API_ENTER(hdl, 2);
450*f6e214c7SGavin Maltby
451*f6e214c7SGavin Maltby ihdl->sh_cmn.hc_free(buf, strlen(buf) + 1);
452*f6e214c7SGavin Maltby }
453*f6e214c7SGavin Maltby
45449b225e1SGavin Maltby int
fmev_shdl_valid(fmev_shdl_t hdl)45549b225e1SGavin Maltby fmev_shdl_valid(fmev_shdl_t hdl)
45649b225e1SGavin Maltby {
45749b225e1SGavin Maltby return (FMEV_SHDL_VALID(HDL2IHDL(hdl)));
45849b225e1SGavin Maltby }
45949b225e1SGavin Maltby
46049b225e1SGavin Maltby /*ARGSUSED*/
46149b225e1SGavin Maltby static int
fmev_keycmp(const void * l,const void * r,void * arg)46249b225e1SGavin Maltby fmev_keycmp(const void *l, const void *r, void *arg)
46349b225e1SGavin Maltby {
46449b225e1SGavin Maltby struct fmev_subinfo *left = (struct fmev_subinfo *)l;
46549b225e1SGavin Maltby struct fmev_subinfo *right = (struct fmev_subinfo *)r;
46649b225e1SGavin Maltby
46749b225e1SGavin Maltby return (strncmp(left->si_pat, right->si_pat, FMEV_MAX_CLASS));
46849b225e1SGavin Maltby }
46949b225e1SGavin Maltby
47049b225e1SGavin Maltby fmev_shdl_t
fmev_shdl_init(uint32_t caller_version,void * (* hdlalloc)(size_t),void * (* hdlzalloc)(size_t),void (* hdlfree)(void *,size_t))47149b225e1SGavin Maltby fmev_shdl_init(uint32_t caller_version, void *(*hdlalloc)(size_t),
47249b225e1SGavin Maltby void *(*hdlzalloc)(size_t), void (*hdlfree)(void *, size_t))
47349b225e1SGavin Maltby {
47449b225e1SGavin Maltby fmev_shdl_impl_t *ihdl;
47549b225e1SGavin Maltby struct fmev_hdl_cmn hc;
47649b225e1SGavin Maltby const char *chan_name;
47749b225e1SGavin Maltby int err;
47849b225e1SGavin Maltby
47949b225e1SGavin Maltby hc.hc_magic = _FMEV_SHMAGIC;
48049b225e1SGavin Maltby hc.hc_api_vers = caller_version;
48149b225e1SGavin Maltby hc.hc_alloc = hdlalloc ? hdlalloc : dflt_alloc;
48249b225e1SGavin Maltby hc.hc_zalloc = hdlzalloc ? hdlzalloc : dflt_zalloc;
48349b225e1SGavin Maltby hc.hc_free = hdlfree ? hdlfree : dflt_free;
48449b225e1SGavin Maltby
48549b225e1SGavin Maltby if (!fmev_api_init(&hc))
48649b225e1SGavin Maltby return (NULL); /* error type set */
48749b225e1SGavin Maltby
48849b225e1SGavin Maltby if (!((hdlalloc == NULL && hdlzalloc == NULL && hdlfree == NULL) ||
48949b225e1SGavin Maltby (hdlalloc != NULL && hdlzalloc != NULL && hdlfree != NULL))) {
49049b225e1SGavin Maltby (void) fmev_seterr(FMEVERR_API);
49149b225e1SGavin Maltby return (NULL);
49249b225e1SGavin Maltby }
49349b225e1SGavin Maltby
49449b225e1SGavin Maltby if (hdlzalloc == NULL)
49549b225e1SGavin Maltby ihdl = dflt_zalloc(sizeof (*ihdl));
49649b225e1SGavin Maltby else
49749b225e1SGavin Maltby ihdl = hdlzalloc(sizeof (*ihdl));
49849b225e1SGavin Maltby
49949b225e1SGavin Maltby if (ihdl == NULL) {
50049b225e1SGavin Maltby (void) fmev_seterr(FMEVERR_ALLOC);
50149b225e1SGavin Maltby return (NULL);
50249b225e1SGavin Maltby }
50349b225e1SGavin Maltby
50449b225e1SGavin Maltby ihdl->sh_cmn = hc;
50549b225e1SGavin Maltby
50649b225e1SGavin Maltby if ((ihdl->sh_attr = sysevent_subattr_alloc()) == NULL) {
50749b225e1SGavin Maltby err = FMEVERR_ALLOC;
50849b225e1SGavin Maltby goto error;
50949b225e1SGavin Maltby }
51049b225e1SGavin Maltby
51149b225e1SGavin Maltby (void) pthread_mutex_init(&ihdl->sh_lock, NULL);
51249b225e1SGavin Maltby
51349b225e1SGavin Maltby /*
51449b225e1SGavin Maltby * For simulation purposes we allow an environment variable
51549b225e1SGavin Maltby * to provide a different channel name.
51649b225e1SGavin Maltby */
51749b225e1SGavin Maltby if ((chan_name = getenv("FMD_SNOOP_CHANNEL")) == NULL)
51849b225e1SGavin Maltby chan_name = FMD_SNOOP_CHANNEL;
51949b225e1SGavin Maltby
52049b225e1SGavin Maltby /*
52149b225e1SGavin Maltby * Try to bind to the event channel. If it's not already present,
52249b225e1SGavin Maltby * attempt to create the channel so that we can startup before
52349b225e1SGavin Maltby * the event producer (who will also apply choices such as
52449b225e1SGavin Maltby * channel depth when they bind to the channel).
52549b225e1SGavin Maltby */
52649b225e1SGavin Maltby if (sysevent_evc_bind(chan_name, &ihdl->sh_binding,
52749b225e1SGavin Maltby EVCH_CREAT | EVCH_HOLD_PEND_INDEF) != 0) {
52849b225e1SGavin Maltby switch (errno) {
52949b225e1SGavin Maltby case EINVAL:
53049b225e1SGavin Maltby default:
53149b225e1SGavin Maltby err = FMEVERR_INTERNAL;
53249b225e1SGavin Maltby break;
53349b225e1SGavin Maltby case ENOMEM:
53449b225e1SGavin Maltby err = FMEVERR_ALLOC;
53549b225e1SGavin Maltby break;
53649b225e1SGavin Maltby case EPERM:
53749b225e1SGavin Maltby err = FMEVERR_NOPRIV;
53849b225e1SGavin Maltby break;
53949b225e1SGavin Maltby }
54049b225e1SGavin Maltby goto error;
54149b225e1SGavin Maltby }
54249b225e1SGavin Maltby
54349b225e1SGavin Maltby if ((ihdl->sh_pool = uu_avl_pool_create("subinfo_pool",
54449b225e1SGavin Maltby sizeof (struct fmev_subinfo),
54549b225e1SGavin Maltby offsetof(struct fmev_subinfo, si_node), fmev_keycmp,
54649b225e1SGavin Maltby UU_AVL_POOL_DEBUG)) == NULL) {
54749b225e1SGavin Maltby err = FMEVERR_INTERNAL;
54849b225e1SGavin Maltby goto error;
54949b225e1SGavin Maltby }
55049b225e1SGavin Maltby
55149b225e1SGavin Maltby if ((ihdl->sh_avl = uu_avl_create(ihdl->sh_pool, NULL,
55249b225e1SGavin Maltby UU_DEFAULT)) == NULL) {
55349b225e1SGavin Maltby err = FMEVERR_INTERNAL;
55449b225e1SGavin Maltby goto error;
55549b225e1SGavin Maltby }
55649b225e1SGavin Maltby
55749b225e1SGavin Maltby return (IHDL2HDL(ihdl));
55849b225e1SGavin Maltby
55949b225e1SGavin Maltby error:
56049b225e1SGavin Maltby (void) fmev_shdl_fini(IHDL2HDL(ihdl));
56149b225e1SGavin Maltby (void) fmev_seterr(err);
56249b225e1SGavin Maltby return (NULL);
56349b225e1SGavin Maltby }
56449b225e1SGavin Maltby
56549b225e1SGavin Maltby fmev_err_t
fmev_shdl_getauthority(fmev_shdl_t hdl,nvlist_t ** nvlp)566*f6e214c7SGavin Maltby fmev_shdl_getauthority(fmev_shdl_t hdl, nvlist_t **nvlp)
567*f6e214c7SGavin Maltby {
568*f6e214c7SGavin Maltby fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
569*f6e214c7SGavin Maltby nvlist_t *propnvl;
570*f6e214c7SGavin Maltby fmev_err_t rc;
571*f6e214c7SGavin Maltby
572*f6e214c7SGavin Maltby if (!FMEV_API_ENTER(hdl, 2))
573*f6e214c7SGavin Maltby return (fmev_errno);
574*f6e214c7SGavin Maltby
575*f6e214c7SGavin Maltby (void) pthread_mutex_lock(&ihdl->sh_lock);
576*f6e214c7SGavin Maltby
577*f6e214c7SGavin Maltby if (sysevent_evc_getpropnvl(ihdl->sh_binding, &propnvl) != 0) {
578*f6e214c7SGavin Maltby *nvlp = NULL;
579*f6e214c7SGavin Maltby (void) pthread_mutex_unlock(&ihdl->sh_lock);
580*f6e214c7SGavin Maltby return (fmev_seterr(FMEVERR_UNKNOWN));
581*f6e214c7SGavin Maltby }
582*f6e214c7SGavin Maltby
583*f6e214c7SGavin Maltby if (propnvl == NULL) {
584*f6e214c7SGavin Maltby rc = FMEVERR_BUSY; /* Other end has not bound */
585*f6e214c7SGavin Maltby } else {
586*f6e214c7SGavin Maltby nvlist_t *auth;
587*f6e214c7SGavin Maltby
588*f6e214c7SGavin Maltby if (nvlist_lookup_nvlist(propnvl, "fmdauth", &auth) == 0) {
589*f6e214c7SGavin Maltby rc = (nvlist_dup(auth, nvlp, 0) == 0) ? FMEV_SUCCESS :
590*f6e214c7SGavin Maltby FMEVERR_ALLOC;
591*f6e214c7SGavin Maltby } else {
592*f6e214c7SGavin Maltby rc = FMEVERR_INTERNAL;
593*f6e214c7SGavin Maltby }
594*f6e214c7SGavin Maltby nvlist_free(propnvl);
595*f6e214c7SGavin Maltby }
596*f6e214c7SGavin Maltby
597*f6e214c7SGavin Maltby (void) pthread_mutex_unlock(&ihdl->sh_lock);
598*f6e214c7SGavin Maltby
599*f6e214c7SGavin Maltby if (rc != FMEV_SUCCESS) {
600*f6e214c7SGavin Maltby *nvlp = NULL;
601*f6e214c7SGavin Maltby (void) fmev_seterr(rc);
602*f6e214c7SGavin Maltby }
603*f6e214c7SGavin Maltby
604*f6e214c7SGavin Maltby return (rc);
605*f6e214c7SGavin Maltby }
606*f6e214c7SGavin Maltby
607*f6e214c7SGavin Maltby char *
fmev_shdl_nvl2str(fmev_shdl_t hdl,nvlist_t * nvl)608*f6e214c7SGavin Maltby fmev_shdl_nvl2str(fmev_shdl_t hdl, nvlist_t *nvl)
609*f6e214c7SGavin Maltby {
610*f6e214c7SGavin Maltby fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
611*f6e214c7SGavin Maltby char *fmri, *fmricp;
612*f6e214c7SGavin Maltby fmev_err_t err;
613*f6e214c7SGavin Maltby int topoerr;
614*f6e214c7SGavin Maltby
615*f6e214c7SGavin Maltby if (!FMEV_API_ENTER(hdl, 2))
616*f6e214c7SGavin Maltby return (NULL);
617*f6e214c7SGavin Maltby
618*f6e214c7SGavin Maltby if (g_topohdl == NULL) {
619*f6e214c7SGavin Maltby (void) pthread_mutex_lock(&ihdl->sh_lock);
620*f6e214c7SGavin Maltby if (g_topohdl == NULL)
621*f6e214c7SGavin Maltby g_topohdl = topo_open(TOPO_VERSION, NULL, &topoerr);
622*f6e214c7SGavin Maltby (void) pthread_mutex_unlock(&ihdl->sh_lock);
623*f6e214c7SGavin Maltby
624*f6e214c7SGavin Maltby if (g_topohdl == NULL) {
625*f6e214c7SGavin Maltby (void) fmev_seterr(FMEVERR_INTERNAL);
626*f6e214c7SGavin Maltby return (NULL);
627*f6e214c7SGavin Maltby }
628*f6e214c7SGavin Maltby }
629*f6e214c7SGavin Maltby
630*f6e214c7SGavin Maltby if (topo_fmri_nvl2str(g_topohdl, nvl, &fmri, &topoerr) == 0) {
631*f6e214c7SGavin Maltby fmricp = fmev_shdl_strdup(hdl, fmri);
632*f6e214c7SGavin Maltby topo_hdl_strfree(g_topohdl, fmri);
633*f6e214c7SGavin Maltby return (fmricp); /* fmev_errno set if strdup failed */
634*f6e214c7SGavin Maltby }
635*f6e214c7SGavin Maltby
636*f6e214c7SGavin Maltby switch (topoerr) {
637*f6e214c7SGavin Maltby case ETOPO_FMRI_NOMEM:
638*f6e214c7SGavin Maltby err = FMEVERR_ALLOC;
639*f6e214c7SGavin Maltby break;
640*f6e214c7SGavin Maltby
641*f6e214c7SGavin Maltby case ETOPO_FMRI_MALFORM:
642*f6e214c7SGavin Maltby case ETOPO_METHOD_NOTSUP:
643*f6e214c7SGavin Maltby case ETOPO_METHOD_INVAL:
644*f6e214c7SGavin Maltby default:
645*f6e214c7SGavin Maltby err = FMEVERR_INVALIDARG;
646*f6e214c7SGavin Maltby break;
647*f6e214c7SGavin Maltby }
648*f6e214c7SGavin Maltby
649*f6e214c7SGavin Maltby (void) fmev_seterr(err);
650*f6e214c7SGavin Maltby return (NULL);
651*f6e214c7SGavin Maltby }
652*f6e214c7SGavin Maltby
653*f6e214c7SGavin Maltby fmev_err_t
fmev_shdl_fini(fmev_shdl_t hdl)65449b225e1SGavin Maltby fmev_shdl_fini(fmev_shdl_t hdl)
65549b225e1SGavin Maltby {
65649b225e1SGavin Maltby fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
65749b225e1SGavin Maltby
658*f6e214c7SGavin Maltby if (!FMEV_API_ENTER(hdl, 1))
659*f6e214c7SGavin Maltby return (fmev_errno);
66049b225e1SGavin Maltby
66149b225e1SGavin Maltby (void) pthread_mutex_lock(&ihdl->sh_lock);
66249b225e1SGavin Maltby
66349b225e1SGavin Maltby /*
66449b225e1SGavin Maltby * Verify that we are not in callback context - return an API
66549b225e1SGavin Maltby * error if we are.
66649b225e1SGavin Maltby */
66749b225e1SGavin Maltby if (sysevent_evc_unsubscribe(ihdl->sh_binding, "invalidsid") ==
66849b225e1SGavin Maltby EDEADLK) {
66949b225e1SGavin Maltby (void) pthread_mutex_unlock(&ihdl->sh_lock);
67049b225e1SGavin Maltby return (fmev_seterr(FMEVERR_API));
67149b225e1SGavin Maltby }
67249b225e1SGavin Maltby
67349b225e1SGavin Maltby if (ihdl->sh_avl) {
67449b225e1SGavin Maltby void *cookie = NULL;
67549b225e1SGavin Maltby struct fmev_subinfo *sip;
67649b225e1SGavin Maltby
67749b225e1SGavin Maltby while ((sip = uu_avl_teardown(ihdl->sh_avl, &cookie)) != NULL)
67849b225e1SGavin Maltby (void) fmev_subinfo_fini(ihdl, sip, B_FALSE);
67949b225e1SGavin Maltby
68049b225e1SGavin Maltby uu_avl_destroy(ihdl->sh_avl);
68149b225e1SGavin Maltby ihdl->sh_avl = NULL;
68249b225e1SGavin Maltby }
68349b225e1SGavin Maltby
68449b225e1SGavin Maltby ASSERT(ihdl->sh_subcnt == 0);
68549b225e1SGavin Maltby
68649b225e1SGavin Maltby if (ihdl->sh_binding) {
68749b225e1SGavin Maltby (void) sysevent_evc_unbind(ihdl->sh_binding);
68849b225e1SGavin Maltby ihdl->sh_binding = NULL;
68949b225e1SGavin Maltby }
69049b225e1SGavin Maltby
69149b225e1SGavin Maltby if (ihdl->sh_pool) {
69249b225e1SGavin Maltby uu_avl_pool_destroy(ihdl->sh_pool);
69349b225e1SGavin Maltby ihdl->sh_pool = NULL;
69449b225e1SGavin Maltby }
69549b225e1SGavin Maltby
69649b225e1SGavin Maltby if (ihdl->sh_attr) {
69749b225e1SGavin Maltby sysevent_subattr_free(ihdl->sh_attr);
69849b225e1SGavin Maltby ihdl->sh_attr = NULL;
69949b225e1SGavin Maltby }
70049b225e1SGavin Maltby
70149b225e1SGavin Maltby ihdl->sh_cmn.hc_magic = 0;
70249b225e1SGavin Maltby
703*f6e214c7SGavin Maltby if (g_topohdl) {
704*f6e214c7SGavin Maltby topo_close(g_topohdl);
705*f6e214c7SGavin Maltby g_topohdl = NULL;
706*f6e214c7SGavin Maltby }
707*f6e214c7SGavin Maltby
70849b225e1SGavin Maltby (void) pthread_mutex_unlock(&ihdl->sh_lock);
70949b225e1SGavin Maltby (void) pthread_mutex_destroy(&ihdl->sh_lock);
71049b225e1SGavin Maltby
71149b225e1SGavin Maltby fmev_shdl_free(hdl, hdl, sizeof (*ihdl));
71249b225e1SGavin Maltby
71349b225e1SGavin Maltby fmev_api_freetsd();
71449b225e1SGavin Maltby
71549b225e1SGavin Maltby return (fmev_seterr(FMEV_SUCCESS));
71649b225e1SGavin Maltby }
717