1 /*
2 * CDDL HEADER START
3 *
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.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26 /*
27 * Copyright (c) 2018, Joyent, Inc.
28 */
29
30 /*
31 * FMA event subscription interfaces - subscribe to FMA protocol
32 * from outside the fault manager.
33 */
34
35 #include <sys/types.h>
36 #include <atomic.h>
37 #include <libsysevent.h>
38 #include <libuutil.h>
39 #include <pthread.h>
40 #include <stdarg.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <strings.h>
44 #include <unistd.h>
45 #include <fm/libtopo.h>
46
47 #include <fm/libfmevent.h>
48
49 #include "fmev_impl.h"
50
51 static topo_hdl_t *g_topohdl;
52
53 typedef struct {
54 struct fmev_hdl_cmn sh_cmn;
55 evchan_t *sh_binding;
56 uu_avl_pool_t *sh_pool;
57 uu_avl_t *sh_avl;
58 uint32_t sh_subcnt;
59 uint32_t sh_flags;
60 sysevent_subattr_t *sh_attr;
61 pthread_mutex_t sh_lock;
62 pthread_mutex_t sh_srlz_lock;
63 } fmev_shdl_impl_t;
64
65 #define HDL2IHDL(hdl) ((fmev_shdl_impl_t *)(hdl))
66 #define IHDL2HDL(ihdl) ((fmev_shdl_t)(ihdl))
67
68 #define _FMEV_SHMAGIC 0x5368446c /* ShDl */
69 #define FMEV_SHDL_VALID(ihdl) ((ihdl)->sh_cmn.hc_magic == _FMEV_SHMAGIC)
70
71 #define SHDL_FL_SERIALIZE 0x1
72
73 #define FMEV_API_ENTER(hdl, v) \
74 fmev_api_enter(&HDL2IHDL(hdl)->sh_cmn, LIBFMEVENT_VERSION_##v)
75
76 /*
77 * For each subscription on a handle we add a node to an avl tree
78 * to track subscriptions.
79 */
80
81 #define FMEV_SID_SZ (16 + 1) /* Matches MAX_SUBID_LEN */
82
83 struct fmev_subinfo {
84 uu_avl_node_t si_node;
85 fmev_shdl_impl_t *si_ihdl;
86 char si_pat[FMEV_MAX_CLASS];
87 char si_sid[FMEV_SID_SZ];
88 fmev_cbfunc_t *si_cb;
89 void *si_cbarg;
90 };
91
92 struct fmev_hdl_cmn *
fmev_shdl_cmn(fmev_shdl_t hdl)93 fmev_shdl_cmn(fmev_shdl_t hdl)
94 {
95 return (&HDL2IHDL(hdl)->sh_cmn);
96 }
97
98 static int
shdlctl_start(fmev_shdl_impl_t * ihdl)99 shdlctl_start(fmev_shdl_impl_t *ihdl)
100 {
101 (void) pthread_mutex_lock(&ihdl->sh_lock);
102
103 if (ihdl->sh_subcnt == 0) {
104 return (1); /* lock still held */
105 } else {
106 (void) pthread_mutex_unlock(&ihdl->sh_lock);
107 return (0);
108 }
109 }
110
111 static void
shdlctl_end(fmev_shdl_impl_t * ihdl)112 shdlctl_end(fmev_shdl_impl_t *ihdl)
113 {
114 (void) pthread_mutex_unlock(&ihdl->sh_lock);
115 }
116
117 fmev_err_t
fmev_shdlctl_serialize(fmev_shdl_t hdl)118 fmev_shdlctl_serialize(fmev_shdl_t hdl)
119 {
120 fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
121
122 if (!FMEV_API_ENTER(hdl, 1))
123 return (fmev_errno);
124
125 if (!shdlctl_start(ihdl))
126 return (fmev_seterr(FMEVERR_BUSY));
127
128 if (!(ihdl->sh_flags & SHDL_FL_SERIALIZE)) {
129 (void) pthread_mutex_init(&ihdl->sh_srlz_lock, NULL);
130 ihdl->sh_flags |= SHDL_FL_SERIALIZE;
131 }
132
133 shdlctl_end(ihdl);
134 return (fmev_seterr(FMEV_SUCCESS));
135 }
136
137 fmev_err_t
fmev_shdlctl_thrattr(fmev_shdl_t hdl,pthread_attr_t * attr)138 fmev_shdlctl_thrattr(fmev_shdl_t hdl, pthread_attr_t *attr)
139 {
140 fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
141
142 if (!FMEV_API_ENTER(hdl, 1))
143 return (fmev_errno);
144
145 if (!shdlctl_start(ihdl))
146 return (fmev_seterr(FMEVERR_BUSY));
147
148 sysevent_subattr_thrattr(ihdl->sh_attr, attr);
149
150 shdlctl_end(ihdl);
151 return (fmev_seterr(FMEV_SUCCESS));
152 }
153
154 fmev_err_t
fmev_shdlctl_sigmask(fmev_shdl_t hdl,sigset_t * set)155 fmev_shdlctl_sigmask(fmev_shdl_t hdl, sigset_t *set)
156 {
157 fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
158
159 if (!FMEV_API_ENTER(hdl, 1))
160 return (fmev_errno);
161
162 if (!shdlctl_start(ihdl))
163 return (fmev_seterr(FMEVERR_BUSY));
164
165 sysevent_subattr_sigmask(ihdl->sh_attr, set);
166
167 shdlctl_end(ihdl);
168 return (fmev_seterr(FMEV_SUCCESS));
169 }
170
171 fmev_err_t
fmev_shdlctl_thrsetup(fmev_shdl_t hdl,door_xcreate_thrsetup_func_t * func,void * cookie)172 fmev_shdlctl_thrsetup(fmev_shdl_t hdl, door_xcreate_thrsetup_func_t *func,
173 void *cookie)
174 {
175 fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
176
177 if (!FMEV_API_ENTER(hdl, 1))
178 return (fmev_errno);
179
180 if (!shdlctl_start(ihdl))
181 return (fmev_seterr(FMEVERR_BUSY));
182
183 sysevent_subattr_thrsetup(ihdl->sh_attr, func, cookie);
184
185 shdlctl_end(ihdl);
186 return (fmev_seterr(FMEV_SUCCESS));
187 }
188
189 fmev_err_t
fmev_shdlctl_thrcreate(fmev_shdl_t hdl,door_xcreate_server_func_t * func,void * cookie)190 fmev_shdlctl_thrcreate(fmev_shdl_t hdl, door_xcreate_server_func_t *func,
191 void *cookie)
192 {
193 fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
194
195 if (!FMEV_API_ENTER(hdl, 1))
196 return (fmev_errno);
197
198 if (!shdlctl_start(ihdl))
199 return (fmev_seterr(FMEVERR_BUSY));
200
201 sysevent_subattr_thrcreate(ihdl->sh_attr, func, cookie);
202
203 shdlctl_end(ihdl);
204 return (fmev_seterr(FMEV_SUCCESS));
205 }
206
207 /*
208 * Our door service function. We return 0 regardless so that the kernel
209 * does not keep either retrying (EAGAIN) or bleat to cmn_err.
210 */
211
212 uint64_t fmev_proxy_cb_enomem;
213
214 static int
fmev_proxy_cb(sysevent_t * sep,void * arg)215 fmev_proxy_cb(sysevent_t *sep, void *arg)
216 {
217 struct fmev_subinfo *sip = arg;
218 fmev_shdl_impl_t *ihdl = sip->si_ihdl;
219 nvlist_t *nvl;
220 char *class;
221 fmev_t ev;
222
223 if ((ev = fmev_sysev2fmev(IHDL2HDL(ihdl), sep, &class, &nvl)) == NULL) {
224 fmev_proxy_cb_enomem++;
225 return (0);
226 }
227
228 if (ihdl->sh_flags & SHDL_FL_SERIALIZE)
229 (void) pthread_mutex_lock(&ihdl->sh_srlz_lock);
230
231 sip->si_cb(ev, class, nvl, sip->si_cbarg);
232
233 if (ihdl->sh_flags & SHDL_FL_SERIALIZE)
234 (void) pthread_mutex_unlock(&ihdl->sh_srlz_lock);
235
236 fmev_rele(ev); /* release hold obtained in fmev_sysev2fmev */
237
238 return (0);
239 }
240
241 static volatile uint32_t fmev_subid;
242
243 fmev_err_t
fmev_shdl_subscribe(fmev_shdl_t hdl,const char * pat,fmev_cbfunc_t func,void * funcarg)244 fmev_shdl_subscribe(fmev_shdl_t hdl, const char *pat, fmev_cbfunc_t func,
245 void *funcarg)
246 {
247 fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
248 struct fmev_subinfo *sip;
249 uu_avl_index_t idx;
250 uint64_t nsid;
251 int serr;
252
253 if (!FMEV_API_ENTER(hdl, 1))
254 return (fmev_errno);
255
256 if (pat == NULL || func == NULL)
257 return (fmev_seterr(FMEVERR_API));
258
259 /*
260 * Empty class patterns are illegal, as is the sysevent magic for
261 * all classes. Also validate class length.
262 */
263 if (*pat == '\0' || strncmp(pat, EC_ALL, sizeof (EC_ALL)) == 0 ||
264 strncmp(pat, EC_SUB_ALL, sizeof (EC_SUB_ALL)) == 0 ||
265 strnlen(pat, FMEV_MAX_CLASS) == FMEV_MAX_CLASS)
266 return (fmev_seterr(FMEVERR_BADCLASS));
267
268 if ((sip = fmev_shdl_zalloc(hdl, sizeof (*sip))) == NULL)
269 return (fmev_seterr(FMEVERR_ALLOC));
270
271 (void) strncpy(sip->si_pat, pat, sizeof (sip->si_pat));
272
273 uu_avl_node_init(sip, &sip->si_node, ihdl->sh_pool);
274
275 (void) pthread_mutex_lock(&ihdl->sh_lock);
276
277 if (uu_avl_find(ihdl->sh_avl, sip, NULL, &idx) != NULL) {
278 (void) pthread_mutex_unlock(&ihdl->sh_lock);
279 fmev_shdl_free(hdl, sip, sizeof (*sip));
280 return (fmev_seterr(FMEVERR_DUPLICATE));
281 }
282
283 /*
284 * Generate a subscriber id for GPEC that is unique to this
285 * subscription. There is no provision for persistent
286 * subscribers. The subscriber id must be unique within
287 * this zone.
288 */
289 nsid = (uint64_t)getpid() << 32 | atomic_inc_32_nv(&fmev_subid);
290 (void) snprintf(sip->si_sid, sizeof (sip->si_sid), "%llx", nsid);
291
292 sip->si_ihdl = ihdl;
293 sip->si_cb = func;
294 sip->si_cbarg = funcarg;
295
296 if ((serr = sysevent_evc_xsubscribe(ihdl->sh_binding, sip->si_sid,
297 sip->si_pat, fmev_proxy_cb, sip, 0, ihdl->sh_attr)) != 0) {
298 fmev_err_t err;
299
300 (void) pthread_mutex_unlock(&ihdl->sh_lock);
301 fmev_shdl_free(hdl, sip, sizeof (*sip));
302
303 switch (serr) {
304 case ENOMEM:
305 err = FMEVERR_MAX_SUBSCRIBERS;
306 break;
307
308 default:
309 err = FMEVERR_INTERNAL;
310 break;
311 }
312
313 return (fmev_seterr(err));
314 }
315
316 uu_avl_insert(ihdl->sh_avl, sip, idx);
317 ihdl->sh_subcnt++;
318
319 (void) pthread_mutex_unlock(&ihdl->sh_lock);
320
321 return (fmev_seterr(FMEV_SUCCESS));
322 }
323
324 static int
fmev_subinfo_fini(fmev_shdl_impl_t * ihdl,struct fmev_subinfo * sip,boolean_t doavl)325 fmev_subinfo_fini(fmev_shdl_impl_t *ihdl, struct fmev_subinfo *sip,
326 boolean_t doavl)
327 {
328 int err;
329
330 ASSERT(sip->si_ihdl == ihdl);
331
332 err = sysevent_evc_unsubscribe(ihdl->sh_binding, sip->si_sid);
333
334 if (err == 0) {
335 if (doavl) {
336 uu_avl_remove(ihdl->sh_avl, sip);
337 uu_avl_node_fini(sip, &sip->si_node, ihdl->sh_pool);
338 }
339 fmev_shdl_free(IHDL2HDL(ihdl), sip, sizeof (*sip));
340 ihdl->sh_subcnt--;
341 }
342
343 return (err);
344 }
345
346 fmev_err_t
fmev_shdl_unsubscribe(fmev_shdl_t hdl,const char * pat)347 fmev_shdl_unsubscribe(fmev_shdl_t hdl, const char *pat)
348 {
349 fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
350 fmev_err_t rv = FMEVERR_NOMATCH;
351 struct fmev_subinfo *sip;
352 struct fmev_subinfo si;
353 int err;
354
355 if (!FMEV_API_ENTER(hdl, 1))
356 return (fmev_errno);
357
358 if (pat == NULL)
359 return (fmev_seterr(FMEVERR_API));
360
361 if (*pat == '\0' || strncmp(pat, EVCH_ALLSUB, sizeof (EC_ALL)) == 0 ||
362 strnlen(pat, FMEV_MAX_CLASS) == FMEV_MAX_CLASS)
363 return (fmev_seterr(FMEVERR_BADCLASS));
364
365 (void) strncpy(si.si_pat, pat, sizeof (si.si_pat));
366
367 (void) pthread_mutex_lock(&ihdl->sh_lock);
368
369 if ((sip = uu_avl_find(ihdl->sh_avl, &si, NULL, NULL)) != NULL) {
370 if ((err = fmev_subinfo_fini(ihdl, sip, B_TRUE)) == 0) {
371 rv = FMEV_SUCCESS;
372 } else {
373 /*
374 * Return an API error if the unsubscribe was
375 * attempted from within a door callback invocation;
376 * other errors should not happen.
377 */
378 rv = (err == EDEADLK) ? FMEVERR_API : FMEVERR_INTERNAL;
379 }
380 }
381
382 (void) pthread_mutex_unlock(&ihdl->sh_lock);
383
384 return (fmev_seterr(rv));
385 }
386
387 void *
fmev_shdl_alloc(fmev_shdl_t hdl,size_t sz)388 fmev_shdl_alloc(fmev_shdl_t hdl, size_t sz)
389 {
390 fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
391
392 if (!FMEV_API_ENTER(hdl, 1))
393 return (NULL);
394
395 return (ihdl->sh_cmn.hc_alloc(sz));
396 }
397
398 void *
fmev_shdl_zalloc(fmev_shdl_t hdl,size_t sz)399 fmev_shdl_zalloc(fmev_shdl_t hdl, size_t sz)
400 {
401 fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
402
403 if (!FMEV_API_ENTER(hdl, 1))
404 return (NULL);
405
406 return (ihdl->sh_cmn.hc_zalloc(sz));
407 }
408
409 void
fmev_shdl_free(fmev_shdl_t hdl,void * buf,size_t sz)410 fmev_shdl_free(fmev_shdl_t hdl, void *buf, size_t sz)
411 {
412 fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
413
414 if (!FMEV_API_ENTER(hdl, 1))
415 return;
416
417 ihdl->sh_cmn.hc_free(buf, sz);
418 }
419
420 char *
fmev_shdl_strdup(fmev_shdl_t hdl,char * src)421 fmev_shdl_strdup(fmev_shdl_t hdl, char *src)
422 {
423 fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
424 size_t srclen;
425 char *dst;
426
427 if (!FMEV_API_ENTER(hdl, 2))
428 return (NULL);
429
430 srclen = strlen(src);
431
432 if ((dst = ihdl->sh_cmn.hc_alloc(srclen + 1)) == NULL) {
433 (void) fmev_seterr(FMEVERR_ALLOC);
434 return (NULL);
435 }
436
437 (void) strncpy(dst, src, srclen);
438 dst[srclen] = '\0';
439 return (dst);
440 }
441
442 void
fmev_shdl_strfree(fmev_shdl_t hdl,char * buf)443 fmev_shdl_strfree(fmev_shdl_t hdl, char *buf)
444 {
445 fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
446
447 (void) FMEV_API_ENTER(hdl, 2);
448
449 ihdl->sh_cmn.hc_free(buf, strlen(buf) + 1);
450 }
451
452 int
fmev_shdl_valid(fmev_shdl_t hdl)453 fmev_shdl_valid(fmev_shdl_t hdl)
454 {
455 return (FMEV_SHDL_VALID(HDL2IHDL(hdl)));
456 }
457
458 /*ARGSUSED*/
459 static int
fmev_keycmp(const void * l,const void * r,void * arg)460 fmev_keycmp(const void *l, const void *r, void *arg)
461 {
462 struct fmev_subinfo *left = (struct fmev_subinfo *)l;
463 struct fmev_subinfo *right = (struct fmev_subinfo *)r;
464
465 return (strncmp(left->si_pat, right->si_pat, FMEV_MAX_CLASS));
466 }
467
468 fmev_shdl_t
fmev_shdl_init(uint32_t caller_version,void * (* hdlalloc)(size_t),void * (* hdlzalloc)(size_t),void (* hdlfree)(void *,size_t))469 fmev_shdl_init(uint32_t caller_version, void *(*hdlalloc)(size_t),
470 void *(*hdlzalloc)(size_t), void (*hdlfree)(void *, size_t))
471 {
472 fmev_shdl_impl_t *ihdl;
473 struct fmev_hdl_cmn hc;
474 const char *chan_name;
475 int err;
476
477 hc.hc_magic = _FMEV_SHMAGIC;
478 hc.hc_api_vers = caller_version;
479 hc.hc_alloc = hdlalloc ? hdlalloc : dflt_alloc;
480 hc.hc_zalloc = hdlzalloc ? hdlzalloc : dflt_zalloc;
481 hc.hc_free = hdlfree ? hdlfree : dflt_free;
482
483 if (!fmev_api_init(&hc))
484 return (NULL); /* error type set */
485
486 if (!((hdlalloc == NULL && hdlzalloc == NULL && hdlfree == NULL) ||
487 (hdlalloc != NULL && hdlzalloc != NULL && hdlfree != NULL))) {
488 (void) fmev_seterr(FMEVERR_API);
489 return (NULL);
490 }
491
492 if (hdlzalloc == NULL)
493 ihdl = dflt_zalloc(sizeof (*ihdl));
494 else
495 ihdl = hdlzalloc(sizeof (*ihdl));
496
497 if (ihdl == NULL) {
498 (void) fmev_seterr(FMEVERR_ALLOC);
499 return (NULL);
500 }
501
502 ihdl->sh_cmn = hc;
503
504 if ((ihdl->sh_attr = sysevent_subattr_alloc()) == NULL) {
505 err = FMEVERR_ALLOC;
506 goto error;
507 }
508
509 (void) pthread_mutex_init(&ihdl->sh_lock, NULL);
510
511 /*
512 * For simulation purposes we allow an environment variable
513 * to provide a different channel name.
514 */
515 if ((chan_name = getenv("FMD_SNOOP_CHANNEL")) == NULL)
516 chan_name = FMD_SNOOP_CHANNEL;
517
518 /*
519 * Try to bind to the event channel. If it's not already present,
520 * attempt to create the channel so that we can startup before
521 * the event producer (who will also apply choices such as
522 * channel depth when they bind to the channel).
523 */
524 if (sysevent_evc_bind(chan_name, &ihdl->sh_binding,
525 EVCH_CREAT | EVCH_HOLD_PEND_INDEF) != 0) {
526 switch (errno) {
527 case EINVAL:
528 default:
529 err = FMEVERR_INTERNAL;
530 break;
531 case ENOMEM:
532 err = FMEVERR_ALLOC;
533 break;
534 case EPERM:
535 err = FMEVERR_NOPRIV;
536 break;
537 }
538 goto error;
539 }
540
541 if ((ihdl->sh_pool = uu_avl_pool_create("subinfo_pool",
542 sizeof (struct fmev_subinfo),
543 offsetof(struct fmev_subinfo, si_node), fmev_keycmp,
544 UU_AVL_POOL_DEBUG)) == NULL) {
545 err = FMEVERR_INTERNAL;
546 goto error;
547 }
548
549 if ((ihdl->sh_avl = uu_avl_create(ihdl->sh_pool, NULL,
550 UU_DEFAULT)) == NULL) {
551 err = FMEVERR_INTERNAL;
552 goto error;
553 }
554
555 return (IHDL2HDL(ihdl));
556
557 error:
558 (void) fmev_shdl_fini(IHDL2HDL(ihdl));
559 (void) fmev_seterr(err);
560 return (NULL);
561 }
562
563 fmev_err_t
fmev_shdl_getauthority(fmev_shdl_t hdl,nvlist_t ** nvlp)564 fmev_shdl_getauthority(fmev_shdl_t hdl, nvlist_t **nvlp)
565 {
566 fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
567 nvlist_t *propnvl;
568 fmev_err_t rc;
569
570 if (!FMEV_API_ENTER(hdl, 2))
571 return (fmev_errno);
572
573 (void) pthread_mutex_lock(&ihdl->sh_lock);
574
575 if (sysevent_evc_getpropnvl(ihdl->sh_binding, &propnvl) != 0) {
576 *nvlp = NULL;
577 (void) pthread_mutex_unlock(&ihdl->sh_lock);
578 return (fmev_seterr(FMEVERR_UNKNOWN));
579 }
580
581 if (propnvl == NULL) {
582 rc = FMEVERR_BUSY; /* Other end has not bound */
583 } else {
584 nvlist_t *auth;
585
586 if (nvlist_lookup_nvlist(propnvl, "fmdauth", &auth) == 0) {
587 rc = (nvlist_dup(auth, nvlp, 0) == 0) ? FMEV_SUCCESS :
588 FMEVERR_ALLOC;
589 } else {
590 rc = FMEVERR_INTERNAL;
591 }
592 nvlist_free(propnvl);
593 }
594
595 (void) pthread_mutex_unlock(&ihdl->sh_lock);
596
597 if (rc != FMEV_SUCCESS) {
598 *nvlp = NULL;
599 (void) fmev_seterr(rc);
600 }
601
602 return (rc);
603 }
604
605 char *
fmev_shdl_nvl2str(fmev_shdl_t hdl,nvlist_t * nvl)606 fmev_shdl_nvl2str(fmev_shdl_t hdl, nvlist_t *nvl)
607 {
608 fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
609 char *fmri, *fmricp;
610 fmev_err_t err;
611 int topoerr;
612
613 if (!FMEV_API_ENTER(hdl, 2))
614 return (NULL);
615
616 if (g_topohdl == NULL) {
617 (void) pthread_mutex_lock(&ihdl->sh_lock);
618 if (g_topohdl == NULL)
619 g_topohdl = topo_open(TOPO_VERSION, NULL, &topoerr);
620 (void) pthread_mutex_unlock(&ihdl->sh_lock);
621
622 if (g_topohdl == NULL) {
623 (void) fmev_seterr(FMEVERR_INTERNAL);
624 return (NULL);
625 }
626 }
627
628 if (topo_fmri_nvl2str(g_topohdl, nvl, &fmri, &topoerr) == 0) {
629 fmricp = fmev_shdl_strdup(hdl, fmri);
630 topo_hdl_strfree(g_topohdl, fmri);
631 return (fmricp); /* fmev_errno set if strdup failed */
632 }
633
634 switch (topoerr) {
635 case ETOPO_FMRI_NOMEM:
636 err = FMEVERR_ALLOC;
637 break;
638
639 case ETOPO_FMRI_MALFORM:
640 case ETOPO_METHOD_NOTSUP:
641 case ETOPO_METHOD_INVAL:
642 default:
643 err = FMEVERR_INVALIDARG;
644 break;
645 }
646
647 (void) fmev_seterr(err);
648 return (NULL);
649 }
650
651 fmev_err_t
fmev_shdl_fini(fmev_shdl_t hdl)652 fmev_shdl_fini(fmev_shdl_t hdl)
653 {
654 fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
655
656 if (!FMEV_API_ENTER(hdl, 1))
657 return (fmev_errno);
658
659 (void) pthread_mutex_lock(&ihdl->sh_lock);
660
661 /*
662 * Verify that we are not in callback context - return an API
663 * error if we are.
664 */
665 if (sysevent_evc_unsubscribe(ihdl->sh_binding, "invalidsid") ==
666 EDEADLK) {
667 (void) pthread_mutex_unlock(&ihdl->sh_lock);
668 return (fmev_seterr(FMEVERR_API));
669 }
670
671 if (ihdl->sh_avl) {
672 void *cookie = NULL;
673 struct fmev_subinfo *sip;
674
675 while ((sip = uu_avl_teardown(ihdl->sh_avl, &cookie)) != NULL)
676 (void) fmev_subinfo_fini(ihdl, sip, B_FALSE);
677
678 uu_avl_destroy(ihdl->sh_avl);
679 ihdl->sh_avl = NULL;
680 }
681
682 ASSERT(ihdl->sh_subcnt == 0);
683
684 if (ihdl->sh_binding) {
685 (void) sysevent_evc_unbind(ihdl->sh_binding);
686 ihdl->sh_binding = NULL;
687 }
688
689 if (ihdl->sh_pool) {
690 uu_avl_pool_destroy(ihdl->sh_pool);
691 ihdl->sh_pool = NULL;
692 }
693
694 if (ihdl->sh_attr) {
695 sysevent_subattr_free(ihdl->sh_attr);
696 ihdl->sh_attr = NULL;
697 }
698
699 ihdl->sh_cmn.hc_magic = 0;
700
701 if (g_topohdl) {
702 topo_close(g_topohdl);
703 g_topohdl = NULL;
704 }
705
706 (void) pthread_mutex_unlock(&ihdl->sh_lock);
707 (void) pthread_mutex_destroy(&ihdl->sh_lock);
708
709 fmev_shdl_free(hdl, hdl, sizeof (*ihdl));
710
711 fmev_api_freetsd();
712
713 return (fmev_seterr(FMEV_SUCCESS));
714 }
715