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 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * FMA event subscription interfaces - subscribe to FMA protocol 29 * from outside the fault manager. 30 */ 31 32 #include <sys/types.h> 33 #include <atomic.h> 34 #include <libsysevent.h> 35 #include <libuutil.h> 36 #include <pthread.h> 37 #include <stdarg.h> 38 #include <stdlib.h> 39 #include <string.h> 40 #include <strings.h> 41 #include <umem.h> 42 #include <unistd.h> 43 44 #include <fm/libfmevent.h> 45 46 #include "fmev_impl.h" 47 #include "fmev_channels.h" 48 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; 60 61 #define HDL2IHDL(hdl) ((fmev_shdl_impl_t *)(hdl)) 62 #define IHDL2HDL(ihdl) ((fmev_shdl_t)(ihdl)) 63 64 #define _FMEV_SHMAGIC 0x5368446c /* ShDl */ 65 #define FMEV_SHDL_VALID(ihdl) ((ihdl)->sh_cmn.hc_magic == _FMEV_SHMAGIC) 66 67 #define SHDL_FL_SERIALIZE 0x1 68 69 #define API_ENTERV1(hdl) \ 70 fmev_api_enter(&HDL2IHDL(hdl)->sh_cmn, LIBFMEVENT_VERSION_1) 71 72 /* 73 * For each subscription on a handle we add a node to an avl tree 74 * to track subscriptions. 75 */ 76 77 #define FMEV_SID_SZ (16 + 1) /* Matches MAX_SUBID_LEN */ 78 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; 86 }; 87 88 struct fmev_hdl_cmn * 89 fmev_shdl_cmn(fmev_shdl_t hdl) 90 { 91 return (&HDL2IHDL(hdl)->sh_cmn); 92 } 93 94 static int 95 shdlctl_start(fmev_shdl_impl_t *ihdl) 96 { 97 (void) pthread_mutex_lock(&ihdl->sh_lock); 98 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); 104 } 105 } 106 107 static void 108 shdlctl_end(fmev_shdl_impl_t *ihdl) 109 { 110 (void) pthread_mutex_unlock(&ihdl->sh_lock); 111 } 112 113 fmev_err_t 114 fmev_shdlctl_serialize(fmev_shdl_t hdl) 115 { 116 fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl); 117 118 if (!API_ENTERV1(hdl)) 119 return (fmev_errno); 120 121 if (!shdlctl_start(ihdl)) 122 return (fmev_seterr(FMEVERR_BUSY)); 123 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; 127 } 128 129 shdlctl_end(ihdl); 130 return (fmev_seterr(FMEV_SUCCESS)); 131 } 132 133 fmev_err_t 134 fmev_shdlctl_thrattr(fmev_shdl_t hdl, pthread_attr_t *attr) 135 { 136 fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl); 137 138 if (!API_ENTERV1(hdl)) 139 return (fmev_errno); 140 141 if (!shdlctl_start(ihdl)) 142 return (fmev_seterr(FMEVERR_BUSY)); 143 144 sysevent_subattr_thrattr(ihdl->sh_attr, attr); 145 146 shdlctl_end(ihdl); 147 return (fmev_seterr(FMEV_SUCCESS)); 148 } 149 150 fmev_err_t 151 fmev_shdlctl_sigmask(fmev_shdl_t hdl, sigset_t *set) 152 { 153 fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl); 154 155 if (!API_ENTERV1(hdl)) 156 return (fmev_errno); 157 158 if (!shdlctl_start(ihdl)) 159 return (fmev_seterr(FMEVERR_BUSY)); 160 161 sysevent_subattr_sigmask(ihdl->sh_attr, set); 162 163 shdlctl_end(ihdl); 164 return (fmev_seterr(FMEV_SUCCESS)); 165 } 166 167 fmev_err_t 168 fmev_shdlctl_thrsetup(fmev_shdl_t hdl, door_xcreate_thrsetup_func_t *func, 169 void *cookie) 170 { 171 fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl); 172 173 if (!API_ENTERV1(hdl)) 174 return (fmev_errno); 175 176 if (!shdlctl_start(ihdl)) 177 return (fmev_seterr(FMEVERR_BUSY)); 178 179 sysevent_subattr_thrsetup(ihdl->sh_attr, func, cookie); 180 181 shdlctl_end(ihdl); 182 return (fmev_seterr(FMEV_SUCCESS)); 183 } 184 185 fmev_err_t 186 fmev_shdlctl_thrcreate(fmev_shdl_t hdl, door_xcreate_server_func_t *func, 187 void *cookie) 188 { 189 fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl); 190 191 if (!API_ENTERV1(hdl)) 192 return (fmev_errno); 193 194 if (!shdlctl_start(ihdl)) 195 return (fmev_seterr(FMEVERR_BUSY)); 196 197 sysevent_subattr_thrcreate(ihdl->sh_attr, func, cookie); 198 199 shdlctl_end(ihdl); 200 return (fmev_seterr(FMEV_SUCCESS)); 201 } 202 203 /* 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. 206 */ 207 208 uint64_t fmev_proxy_cb_inval; 209 uint64_t fmev_proxy_cb_enomem; 210 211 int 212 fmev_proxy_cb(sysevent_t *sep, void *arg) 213 { 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; 219 220 if (sip == NULL || sip->si_cb == NULL) { 221 fmev_proxy_cb_inval++; 222 return (0); 223 } 224 225 if ((ev = fmev_sysev2fmev(IHDL2HDL(ihdl), sep, &class, &nvl)) == NULL) { 226 fmev_proxy_cb_enomem++; 227 return (0); 228 } 229 230 if (ihdl->sh_flags & SHDL_FL_SERIALIZE) 231 (void) pthread_mutex_lock(&ihdl->sh_srlz_lock); 232 233 sip->si_cb(ev, class, nvl, sip->si_cbarg); 234 235 if (ihdl->sh_flags & SHDL_FL_SERIALIZE) 236 (void) pthread_mutex_unlock(&ihdl->sh_srlz_lock); 237 238 fmev_rele(ev); /* release hold obtained in fmev_sysev2fmev */ 239 240 return (0); 241 } 242 243 static volatile uint32_t fmev_subid; 244 245 fmev_err_t 246 fmev_shdl_subscribe(fmev_shdl_t hdl, const char *pat, fmev_cbfunc_t func, 247 void *funcarg) 248 { 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; 254 255 if (!API_ENTERV1(hdl)) 256 return (fmev_errno); 257 258 if (pat == NULL || func == NULL) 259 return (fmev_seterr(FMEVERR_API)); 260 261 /* 262 * Empty class patterns are illegal, as is the sysevent magic for 263 * all classes. Also validate class length. 264 */ 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)); 269 270 if ((sip = fmev_shdl_zalloc(hdl, sizeof (*sip))) == NULL) 271 return (fmev_seterr(FMEVERR_ALLOC)); 272 273 (void) strncpy(sip->si_pat, pat, sizeof (sip->si_pat)); 274 275 uu_avl_node_init(sip, &sip->si_node, ihdl->sh_pool); 276 277 (void) pthread_mutex_lock(&ihdl->sh_lock); 278 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)); 283 } 284 285 /* 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. 290 */ 291 nsid = (uint64_t)getpid() << 32 | atomic_inc_32_nv(&fmev_subid); 292 (void) snprintf(sip->si_sid, sizeof (sip->si_sid), "%llx", nsid); 293 294 sip->si_ihdl = ihdl; 295 sip->si_cb = func; 296 sip->si_cbarg = funcarg; 297 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; 301 302 (void) pthread_mutex_unlock(&ihdl->sh_lock); 303 fmev_shdl_free(hdl, sip, sizeof (*sip)); 304 305 switch (serr) { 306 case ENOMEM: 307 err = FMEVERR_MAX_SUBSCRIBERS; 308 break; 309 310 default: 311 err = FMEVERR_INTERNAL; 312 break; 313 } 314 315 return (fmev_seterr(err)); 316 } 317 318 uu_avl_insert(ihdl->sh_avl, sip, idx); 319 ihdl->sh_subcnt++; 320 321 (void) pthread_mutex_unlock(&ihdl->sh_lock); 322 323 return (fmev_seterr(FMEV_SUCCESS)); 324 } 325 326 static int 327 fmev_subinfo_fini(fmev_shdl_impl_t *ihdl, struct fmev_subinfo *sip, 328 boolean_t doavl) 329 { 330 int err; 331 332 ASSERT(sip->si_ihdl == ihdl); 333 334 err = sysevent_evc_unsubscribe(ihdl->sh_binding, sip->si_sid); 335 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); 340 } 341 fmev_shdl_free(IHDL2HDL(ihdl), sip, sizeof (*sip)); 342 ihdl->sh_subcnt--; 343 } 344 345 return (err); 346 } 347 348 fmev_err_t 349 fmev_shdl_unsubscribe(fmev_shdl_t hdl, const char *pat) 350 { 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; 356 357 if (!API_ENTERV1(hdl)) 358 return (fmev_errno); 359 360 if (pat == NULL) 361 return (fmev_seterr(FMEVERR_API)); 362 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)); 366 367 (void) strncpy(si.si_pat, pat, sizeof (si.si_pat)); 368 369 (void) pthread_mutex_lock(&ihdl->sh_lock); 370 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 { 375 /* 376 * Return an API error if the unsubscribe was 377 * attempted from within a door callback invocation; 378 * other errors should not happen. 379 */ 380 rv = (err == EDEADLK) ? FMEVERR_API : FMEVERR_INTERNAL; 381 } 382 } 383 384 (void) pthread_mutex_unlock(&ihdl->sh_lock); 385 386 return (fmev_seterr(rv)); 387 } 388 389 static void * 390 dflt_alloc(size_t sz) 391 { 392 return (umem_alloc(sz, UMEM_DEFAULT)); 393 } 394 395 static void * 396 dflt_zalloc(size_t sz) 397 { 398 return (umem_zalloc(sz, UMEM_DEFAULT)); 399 } 400 401 static void 402 dflt_free(void *buf, size_t sz) 403 { 404 umem_free(buf, sz); 405 } 406 407 void * 408 fmev_shdl_alloc(fmev_shdl_t hdl, size_t sz) 409 { 410 fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl); 411 412 (void) API_ENTERV1(hdl); 413 414 return (ihdl->sh_cmn.hc_alloc(sz)); 415 } 416 417 void * 418 fmev_shdl_zalloc(fmev_shdl_t hdl, size_t sz) 419 { 420 fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl); 421 422 (void) API_ENTERV1(hdl); 423 424 return (ihdl->sh_cmn.hc_zalloc(sz)); 425 } 426 427 void 428 fmev_shdl_free(fmev_shdl_t hdl, void *buf, size_t sz) 429 { 430 fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl); 431 432 (void) API_ENTERV1(hdl); 433 434 ihdl->sh_cmn.hc_free(buf, sz); 435 } 436 437 int 438 fmev_shdl_valid(fmev_shdl_t hdl) 439 { 440 return (FMEV_SHDL_VALID(HDL2IHDL(hdl))); 441 } 442 443 /*ARGSUSED*/ 444 static int 445 fmev_keycmp(const void *l, const void *r, void *arg) 446 { 447 struct fmev_subinfo *left = (struct fmev_subinfo *)l; 448 struct fmev_subinfo *right = (struct fmev_subinfo *)r; 449 450 return (strncmp(left->si_pat, right->si_pat, FMEV_MAX_CLASS)); 451 } 452 453 fmev_shdl_t 454 fmev_shdl_init(uint32_t caller_version, void *(*hdlalloc)(size_t), 455 void *(*hdlzalloc)(size_t), void (*hdlfree)(void *, size_t)) 456 { 457 fmev_shdl_impl_t *ihdl; 458 struct fmev_hdl_cmn hc; 459 const char *chan_name; 460 int err; 461 462 hc.hc_magic = _FMEV_SHMAGIC; 463 hc.hc_api_vers = caller_version; 464 hc.hc_alloc = hdlalloc ? hdlalloc : dflt_alloc; 465 hc.hc_zalloc = hdlzalloc ? hdlzalloc : dflt_zalloc; 466 hc.hc_free = hdlfree ? hdlfree : dflt_free; 467 468 if (!fmev_api_init(&hc)) 469 return (NULL); /* error type set */ 470 471 if (!((hdlalloc == NULL && hdlzalloc == NULL && hdlfree == NULL) || 472 (hdlalloc != NULL && hdlzalloc != NULL && hdlfree != NULL))) { 473 (void) fmev_seterr(FMEVERR_API); 474 return (NULL); 475 } 476 477 if (hdlzalloc == NULL) 478 ihdl = dflt_zalloc(sizeof (*ihdl)); 479 else 480 ihdl = hdlzalloc(sizeof (*ihdl)); 481 482 if (ihdl == NULL) { 483 (void) fmev_seterr(FMEVERR_ALLOC); 484 return (NULL); 485 } 486 487 ihdl->sh_cmn = hc; 488 489 if ((ihdl->sh_attr = sysevent_subattr_alloc()) == NULL) { 490 err = FMEVERR_ALLOC; 491 goto error; 492 } 493 494 (void) pthread_mutex_init(&ihdl->sh_lock, NULL); 495 496 /* 497 * For simulation purposes we allow an environment variable 498 * to provide a different channel name. 499 */ 500 if ((chan_name = getenv("FMD_SNOOP_CHANNEL")) == NULL) 501 chan_name = FMD_SNOOP_CHANNEL; 502 503 /* 504 * Try to bind to the event channel. If it's not already present, 505 * attempt to create the channel so that we can startup before 506 * the event producer (who will also apply choices such as 507 * channel depth when they bind to the channel). 508 */ 509 if (sysevent_evc_bind(chan_name, &ihdl->sh_binding, 510 EVCH_CREAT | EVCH_HOLD_PEND_INDEF) != 0) { 511 switch (errno) { 512 case EINVAL: 513 default: 514 err = FMEVERR_INTERNAL; 515 break; 516 case ENOMEM: 517 err = FMEVERR_ALLOC; 518 break; 519 case EPERM: 520 err = FMEVERR_NOPRIV; 521 break; 522 } 523 goto error; 524 } 525 526 if ((ihdl->sh_pool = uu_avl_pool_create("subinfo_pool", 527 sizeof (struct fmev_subinfo), 528 offsetof(struct fmev_subinfo, si_node), fmev_keycmp, 529 UU_AVL_POOL_DEBUG)) == NULL) { 530 err = FMEVERR_INTERNAL; 531 goto error; 532 } 533 534 if ((ihdl->sh_avl = uu_avl_create(ihdl->sh_pool, NULL, 535 UU_DEFAULT)) == NULL) { 536 err = FMEVERR_INTERNAL; 537 goto error; 538 } 539 540 return (IHDL2HDL(ihdl)); 541 542 error: 543 (void) fmev_shdl_fini(IHDL2HDL(ihdl)); 544 (void) fmev_seterr(err); 545 return (NULL); 546 } 547 548 fmev_err_t 549 fmev_shdl_fini(fmev_shdl_t hdl) 550 { 551 fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl); 552 553 (void) API_ENTERV1(hdl); 554 555 (void) pthread_mutex_lock(&ihdl->sh_lock); 556 557 /* 558 * Verify that we are not in callback context - return an API 559 * error if we are. 560 */ 561 if (sysevent_evc_unsubscribe(ihdl->sh_binding, "invalidsid") == 562 EDEADLK) { 563 (void) pthread_mutex_unlock(&ihdl->sh_lock); 564 return (fmev_seterr(FMEVERR_API)); 565 } 566 567 if (ihdl->sh_avl) { 568 void *cookie = NULL; 569 struct fmev_subinfo *sip; 570 571 while ((sip = uu_avl_teardown(ihdl->sh_avl, &cookie)) != NULL) 572 (void) fmev_subinfo_fini(ihdl, sip, B_FALSE); 573 574 uu_avl_destroy(ihdl->sh_avl); 575 ihdl->sh_avl = NULL; 576 } 577 578 ASSERT(ihdl->sh_subcnt == 0); 579 580 if (ihdl->sh_binding) { 581 (void) sysevent_evc_unbind(ihdl->sh_binding); 582 ihdl->sh_binding = NULL; 583 } 584 585 if (ihdl->sh_pool) { 586 uu_avl_pool_destroy(ihdl->sh_pool); 587 ihdl->sh_pool = NULL; 588 } 589 590 if (ihdl->sh_attr) { 591 sysevent_subattr_free(ihdl->sh_attr); 592 ihdl->sh_attr = NULL; 593 } 594 595 ihdl->sh_cmn.hc_magic = 0; 596 597 (void) pthread_mutex_unlock(&ihdl->sh_lock); 598 (void) pthread_mutex_destroy(&ihdl->sh_lock); 599 600 fmev_shdl_free(hdl, hdl, sizeof (*ihdl)); 601 602 fmev_api_freetsd(); 603 604 return (fmev_seterr(FMEV_SUCCESS)); 605 } 606