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 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 #pragma ident "%Z%%M% %I% %E% SMI" 26 27 #include <sys/param.h> 28 #include <sys/types.h> 29 #include <sys/systm.h> 30 #include <sys/errno.h> 31 #include <sys/kmem.h> 32 #include <sys/mutex.h> 33 #include <sys/condvar.h> 34 #include <sys/modctl.h> 35 #include <sys/hook_impl.h> 36 #include <sys/sdt.h> 37 38 /* 39 * This file provides kernel hook framework. 40 */ 41 42 static struct modldrv modlmisc = { 43 &mod_miscops, /* drv_modops */ 44 "Hooks Interface v1.0", /* drv_linkinfo */ 45 }; 46 47 static struct modlinkage modlinkage = { 48 MODREV_1, /* ml_rev */ 49 &modlmisc, /* ml_linkage */ 50 NULL 51 }; 52 53 /* 54 * Hook internal functions 55 */ 56 static hook_int_t *hook_copy(hook_t *src); 57 static hook_event_int_t *hook_event_checkdup(hook_event_t *he); 58 static hook_event_int_t *hook_event_copy(hook_event_t *src); 59 static hook_event_int_t *hook_event_find(hook_family_int_t *hfi, char *event); 60 static void hook_event_free(hook_event_int_t *hei); 61 static hook_family_int_t *hook_family_copy(hook_family_t *src); 62 static hook_family_int_t *hook_family_find(char *family); 63 static void hook_family_free(hook_family_int_t *hfi); 64 static hook_int_t *hook_find(hook_event_int_t *hei, hook_t *h); 65 static void hook_free(hook_int_t *hi); 66 static void hook_init(void); 67 68 static cvwaitlock_t familylock; /* global lock */ 69 static hook_family_int_head_t familylist; /* family list head */ 70 71 /* 72 * Module entry points. 73 */ 74 int 75 _init(void) 76 { 77 hook_init(); 78 return (mod_install(&modlinkage)); 79 } 80 81 82 int 83 _fini(void) 84 { 85 return (mod_remove(&modlinkage)); 86 } 87 88 89 int 90 _info(struct modinfo *modinfop) 91 { 92 return (mod_info(&modlinkage, modinfop)); 93 } 94 95 96 /* 97 * Function: hook_init 98 * Returns: None 99 * Parameters: None 100 * 101 * Initialize hooks 102 */ 103 static void 104 hook_init(void) 105 { 106 CVW_INIT(&familylock); 107 SLIST_INIT(&familylist); 108 } 109 110 111 /* 112 * Function: hook_run 113 * Returns: int - return value according to callback func 114 * Parameters: token(I) - event pointer 115 * info(I) - message 116 * 117 * Run hooks for specific provider. The hooks registered are stepped through 118 * until either the end of the list is reached or a hook function returns a 119 * non-zero value. If a non-zero value is returned from a hook function, we 120 * return that value back to our caller. By design, a hook function can be 121 * called more than once, simultaneously. 122 */ 123 int 124 hook_run(hook_event_token_t token, hook_data_t info) 125 { 126 hook_int_t *hi; 127 hook_event_int_t *hei; 128 int rval = 0; 129 130 ASSERT(token != NULL); 131 132 hei = (hook_event_int_t *)token; 133 DTRACE_PROBE2(hook__run__start, 134 hook_event_token_t, token, 135 hook_data_t, info); 136 137 /* Hold global read lock to ensure event will not be deleted */ 138 CVW_ENTER_READ(&familylock); 139 140 /* Hold event read lock to ensure hook will not be changed */ 141 CVW_ENTER_READ(&hei->hei_lock); 142 143 TAILQ_FOREACH(hi, &hei->hei_head, hi_entry) { 144 ASSERT(hi->hi_hook.h_func != NULL); 145 DTRACE_PROBE3(hook__func__start, 146 hook_event_token_t, token, 147 hook_data_t, info, 148 hook_int_t *, hi); 149 rval = (*hi->hi_hook.h_func)(token, info); 150 DTRACE_PROBE4(hook__func__end, 151 hook_event_token_t, token, 152 hook_data_t, info, 153 hook_int_t *, hi, 154 int, rval); 155 if (rval != 0) 156 break; 157 } 158 159 CVW_EXIT_READ(&hei->hei_lock); 160 CVW_EXIT_READ(&familylock); 161 162 DTRACE_PROBE3(hook__run__end, 163 hook_event_token_t, token, 164 hook_data_t, info, 165 hook_int_t *, hi); 166 167 return (rval); 168 } 169 170 171 /* 172 * Function: hook_family_add 173 * Returns: internal family pointer - NULL = Fail 174 * Parameters: hf(I) - family pointer 175 * 176 * Add new family to family list 177 */ 178 hook_family_int_t * 179 hook_family_add(hook_family_t *hf) 180 { 181 hook_family_int_t *hfi, *new; 182 183 ASSERT(hf != NULL); 184 ASSERT(hf->hf_name != NULL); 185 186 new = hook_family_copy(hf); 187 if (new == NULL) 188 return (NULL); 189 190 CVW_ENTER_WRITE(&familylock); 191 192 /* search family list */ 193 hfi = hook_family_find(hf->hf_name); 194 if (hfi != NULL) { 195 CVW_EXIT_WRITE(&familylock); 196 hook_family_free(new); 197 return (NULL); 198 } 199 200 /* Add to family list head */ 201 SLIST_INSERT_HEAD(&familylist, new, hfi_entry); 202 203 CVW_EXIT_WRITE(&familylock); 204 return (new); 205 } 206 207 208 /* 209 * Function: hook_family_remove 210 * Returns: int - 0 = Succ, Else = Fail 211 * Parameters: hfi(I) - internal family pointer 212 * 213 * Remove family from family list 214 */ 215 int 216 hook_family_remove(hook_family_int_t *hfi) 217 { 218 219 ASSERT(hfi != NULL); 220 221 CVW_ENTER_WRITE(&familylock); 222 223 /* Check if there are events */ 224 if (!SLIST_EMPTY(&hfi->hfi_head)) { 225 CVW_EXIT_WRITE(&familylock); 226 return (EBUSY); 227 } 228 229 /* Remove from family list */ 230 SLIST_REMOVE(&familylist, hfi, hook_family_int, hfi_entry); 231 232 CVW_EXIT_WRITE(&familylock); 233 hook_family_free(hfi); 234 235 return (0); 236 } 237 238 239 /* 240 * Function: hook_family_copy 241 * Returns: internal family pointer - NULL = Failed 242 * Parameters: src(I) - family pointer 243 * 244 * Allocate internal family block and duplicate incoming family 245 * No locks should be held across this function as it may sleep. 246 */ 247 static hook_family_int_t * 248 hook_family_copy(hook_family_t *src) 249 { 250 hook_family_int_t *new; 251 hook_family_t *dst; 252 253 ASSERT(src != NULL); 254 ASSERT(src->hf_name != NULL); 255 256 new = (hook_family_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP); 257 258 /* Copy body */ 259 SLIST_INIT(&new->hfi_head); 260 dst = &new->hfi_family; 261 *dst = *src; 262 263 /* Copy name */ 264 dst->hf_name = (char *)kmem_alloc(strlen(src->hf_name) + 1, KM_SLEEP); 265 (void) strcpy(dst->hf_name, src->hf_name); 266 267 return (new); 268 } 269 270 271 /* 272 * Function: hook_family_find 273 * Returns: internal family pointer - NULL = Not match 274 * Parameters: family(I) - family name string 275 * 276 * Search family list with family name 277 * A lock on familylock must be held when called. 278 */ 279 static hook_family_int_t * 280 hook_family_find(char *family) 281 { 282 hook_family_int_t *hfi = NULL; 283 284 ASSERT(family != NULL); 285 286 SLIST_FOREACH(hfi, &familylist, hfi_entry) { 287 if (strcmp(hfi->hfi_family.hf_name, family) == 0) 288 break; 289 } 290 return (hfi); 291 } 292 293 294 /* 295 * Function: hook_family_free 296 * Returns: None 297 * Parameters: hfi(I) - internal family pointer 298 * 299 * Free alloc memory for family 300 */ 301 static void 302 hook_family_free(hook_family_int_t *hfi) 303 { 304 ASSERT(hfi != NULL); 305 306 /* Free name space */ 307 if (hfi->hfi_family.hf_name != NULL) { 308 kmem_free(hfi->hfi_family.hf_name, 309 strlen(hfi->hfi_family.hf_name) + 1); 310 } 311 312 /* Free container */ 313 kmem_free(hfi, sizeof (*hfi)); 314 } 315 316 317 /* 318 * Function: hook_event_add 319 * Returns: internal event pointer - NULL = Fail 320 * Parameters: hfi(I) - internal family pointer 321 * he(I) - event pointer 322 * 323 * Add new event to event list on specific family. 324 * This function can fail to return successfully if (1) it cannot allocate 325 * enough memory for its own internal data structures, (2) the event has 326 * already been registered (for any hook family.) 327 */ 328 hook_event_int_t * 329 hook_event_add(hook_family_int_t *hfi, hook_event_t *he) 330 { 331 hook_event_int_t *hei, *new; 332 333 ASSERT(hfi != NULL); 334 ASSERT(he != NULL); 335 ASSERT(he->he_name != NULL); 336 337 new = hook_event_copy(he); 338 if (new == NULL) 339 return (NULL); 340 341 CVW_ENTER_WRITE(&familylock); 342 343 /* Check whether this event pointer is already registered */ 344 hei = hook_event_checkdup(he); 345 if (hei != NULL) { 346 CVW_EXIT_WRITE(&familylock); 347 hook_event_free(new); 348 return (NULL); 349 } 350 351 /* Add to event list head */ 352 SLIST_INSERT_HEAD(&hfi->hfi_head, new, hei_entry); 353 354 CVW_EXIT_WRITE(&familylock); 355 return (new); 356 } 357 358 359 /* 360 * Function: hook_event_remove 361 * Returns: int - 0 = Succ, Else = Fail 362 * Parameters: hfi(I) - internal family pointer 363 * he(I) - event pointer 364 * 365 * Remove event from event list on specific family 366 */ 367 int 368 hook_event_remove(hook_family_int_t *hfi, hook_event_t *he) 369 { 370 hook_event_int_t *hei; 371 372 ASSERT(hfi != NULL); 373 ASSERT(he != NULL); 374 375 CVW_ENTER_WRITE(&familylock); 376 377 hei = hook_event_find(hfi, he->he_name); 378 if (hei == NULL) { 379 CVW_EXIT_WRITE(&familylock); 380 return (ENXIO); 381 } 382 383 /* Check if there are registered hooks for this event */ 384 if (!TAILQ_EMPTY(&hei->hei_head)) { 385 CVW_EXIT_WRITE(&familylock); 386 return (EBUSY); 387 } 388 389 /* Remove from event list */ 390 SLIST_REMOVE(&hfi->hfi_head, hei, hook_event_int, hei_entry); 391 392 CVW_EXIT_WRITE(&familylock); 393 hook_event_free(hei); 394 395 return (0); 396 } 397 398 399 /* 400 * Function: hook_event_checkdup 401 * Returns: internal event pointer - NULL = Not match 402 * Parameters: he(I) - event pointer 403 * 404 * Search whole list with event pointer 405 * A lock on familylock must be held when called. 406 */ 407 static hook_event_int_t * 408 hook_event_checkdup(hook_event_t *he) 409 { 410 hook_family_int_t *hfi; 411 hook_event_int_t *hei; 412 413 ASSERT(he != NULL); 414 415 SLIST_FOREACH(hfi, &familylist, hfi_entry) { 416 SLIST_FOREACH(hei, &hfi->hfi_head, hei_entry) { 417 if (hei->hei_event == he) 418 return (hei); 419 } 420 } 421 422 return (NULL); 423 } 424 425 426 /* 427 * Function: hook_event_copy 428 * Returns: internal event pointer - NULL = Failed 429 * Parameters: src(I) - event pointer 430 * 431 * Allocate internal event block and duplicate incoming event 432 * No locks should be held across this function as it may sleep. 433 */ 434 static hook_event_int_t * 435 hook_event_copy(hook_event_t *src) 436 { 437 hook_event_int_t *new; 438 439 ASSERT(src != NULL); 440 ASSERT(src->he_name != NULL); 441 442 new = (hook_event_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP); 443 444 /* Copy body */ 445 TAILQ_INIT(&new->hei_head); 446 new->hei_event = src; 447 448 return (new); 449 } 450 451 452 /* 453 * Function: hook_event_find 454 * Returns: internal event pointer - NULL = Not match 455 * Parameters: hfi(I) - internal family pointer 456 * event(I) - event name string 457 * 458 * Search event list with event name 459 * A lock on familylock must be held when called. 460 */ 461 static hook_event_int_t * 462 hook_event_find(hook_family_int_t *hfi, char *event) 463 { 464 hook_event_int_t *hei = NULL; 465 466 ASSERT(hfi != NULL); 467 ASSERT(event != NULL); 468 469 SLIST_FOREACH(hei, &hfi->hfi_head, hei_entry) { 470 if (strcmp(hei->hei_event->he_name, event) == 0) 471 break; 472 } 473 return (hei); 474 } 475 476 477 /* 478 * Function: hook_event_free 479 * Returns: None 480 * Parameters: hei(I) - internal event pointer 481 * 482 * Free alloc memory for event 483 */ 484 static void 485 hook_event_free(hook_event_int_t *hei) 486 { 487 ASSERT(hei != NULL); 488 489 /* Free container */ 490 kmem_free(hei, sizeof (*hei)); 491 } 492 493 494 /* 495 * Function: hook_register 496 * Returns: int- 0 = Succ, Else = Fail 497 * Parameters: hfi(I) - internal family pointer 498 * event(I) - event name string 499 * h(I) - hook pointer 500 * 501 * Add new hook to hook list on spefic family, event 502 */ 503 int 504 hook_register(hook_family_int_t *hfi, char *event, hook_t *h) 505 { 506 hook_event_int_t *hei; 507 hook_int_t *hi, *new; 508 509 ASSERT(hfi != NULL); 510 ASSERT(event != NULL); 511 ASSERT(h != NULL); 512 513 /* Alloc hook_int_t and copy hook */ 514 new = hook_copy(h); 515 if (new == NULL) 516 return (ENOMEM); 517 518 /* 519 * Since hook add/remove only impact event, so it is unnecessary 520 * to hold global family write lock. Just get read lock here to 521 * ensure event will not be removed when doing hooks operation 522 */ 523 CVW_ENTER_READ(&familylock); 524 525 hei = hook_event_find(hfi, event); 526 if (hei == NULL) { 527 CVW_EXIT_READ(&familylock); 528 hook_free(new); 529 return (ENXIO); 530 } 531 532 CVW_ENTER_WRITE(&hei->hei_lock); 533 534 /* Multiple hooks are only allowed for read-only events. */ 535 if (((hei->hei_event->he_flags & HOOK_RDONLY) == 0) && 536 (!TAILQ_EMPTY(&hei->hei_head))) { 537 CVW_EXIT_WRITE(&hei->hei_lock); 538 CVW_EXIT_READ(&familylock); 539 hook_free(new); 540 return (EEXIST); 541 } 542 543 hi = hook_find(hei, h); 544 if (hi != NULL) { 545 CVW_EXIT_WRITE(&hei->hei_lock); 546 CVW_EXIT_READ(&familylock); 547 hook_free(new); 548 return (EEXIST); 549 } 550 551 /* Add to hook list head */ 552 TAILQ_INSERT_HEAD(&hei->hei_head, new, hi_entry); 553 hei->hei_event->he_interested = B_TRUE; 554 555 CVW_EXIT_WRITE(&hei->hei_lock); 556 CVW_EXIT_READ(&familylock); 557 return (0); 558 } 559 560 561 /* 562 * Function: hook_unregister 563 * Returns: int - 0 = Succ, Else = Fail 564 * Parameters: hfi(I) - internal family pointer 565 * event(I) - event name string 566 * h(I) - hook pointer 567 * 568 * Remove hook from hook list on specific family, event 569 */ 570 int 571 hook_unregister(hook_family_int_t *hfi, char *event, hook_t *h) 572 { 573 hook_event_int_t *hei; 574 hook_int_t *hi; 575 576 ASSERT(hfi != NULL); 577 ASSERT(h != NULL); 578 579 CVW_ENTER_READ(&familylock); 580 581 hei = hook_event_find(hfi, event); 582 if (hei == NULL) { 583 CVW_EXIT_READ(&familylock); 584 return (ENXIO); 585 } 586 587 /* Hold write lock for event */ 588 CVW_ENTER_WRITE(&hei->hei_lock); 589 590 hi = hook_find(hei, h); 591 if (hi == NULL) { 592 CVW_EXIT_WRITE(&hei->hei_lock); 593 CVW_EXIT_READ(&familylock); 594 return (ENXIO); 595 } 596 597 /* Remove from hook list */ 598 TAILQ_REMOVE(&hei->hei_head, hi, hi_entry); 599 if (TAILQ_EMPTY(&hei->hei_head)) { 600 hei->hei_event->he_interested = B_FALSE; 601 } 602 603 CVW_EXIT_WRITE(&hei->hei_lock); 604 CVW_EXIT_READ(&familylock); 605 606 hook_free(hi); 607 return (0); 608 } 609 610 611 /* 612 * Function: hook_find 613 * Returns: internal hook pointer - NULL = Not match 614 * Parameters: hei(I) - internal event pointer 615 * h(I) - hook pointer 616 * 617 * Search hook list 618 * A lock on familylock must be held when called. 619 */ 620 static hook_int_t * 621 hook_find(hook_event_int_t *hei, hook_t *h) 622 { 623 hook_int_t *hi; 624 625 ASSERT(hei != NULL); 626 ASSERT(h != NULL); 627 628 TAILQ_FOREACH(hi, &hei->hei_head, hi_entry) { 629 if (strcmp(hi->hi_hook.h_name, h->h_name) == 0) 630 break; 631 } 632 return (hi); 633 } 634 635 636 /* 637 * Function: hook_copy 638 * Returns: internal hook pointer - NULL = Failed 639 * Parameters: src(I) - hook pointer 640 * 641 * Allocate internal hook block and duplicate incoming hook. 642 * No locks should be held across this function as it may sleep. 643 */ 644 static hook_int_t * 645 hook_copy(hook_t *src) 646 { 647 hook_int_t *new; 648 hook_t *dst; 649 650 ASSERT(src != NULL); 651 ASSERT(src->h_name != NULL); 652 653 new = (hook_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP); 654 655 /* Copy body */ 656 dst = &new->hi_hook; 657 *dst = *src; 658 659 /* Copy name */ 660 dst->h_name = (char *)kmem_alloc(strlen(src->h_name) + 1, KM_SLEEP); 661 (void) strcpy(dst->h_name, src->h_name); 662 663 return (new); 664 } 665 666 /* 667 * Function: hook_free 668 * Returns: None 669 * Parameters: hi(I) - internal hook pointer 670 * 671 * Free alloc memory for hook 672 */ 673 static void 674 hook_free(hook_int_t *hi) 675 { 676 ASSERT(hi != NULL); 677 678 /* Free name space */ 679 if (hi->hi_hook.h_name != NULL) { 680 kmem_free(hi->hi_hook.h_name, strlen(hi->hi_hook.h_name) + 1); 681 } 682 683 /* Free container */ 684 kmem_free(hi, sizeof (*hi)); 685 } 686