12958Sdr146992 /* 22958Sdr146992 * CDDL HEADER START 32958Sdr146992 * 42958Sdr146992 * The contents of this file are subject to the terms of the 52958Sdr146992 * Common Development and Distribution License (the "License"). 62958Sdr146992 * You may not use this file except in compliance with the License. 72958Sdr146992 * 82958Sdr146992 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 92958Sdr146992 * or http://www.opensolaris.org/os/licensing. 102958Sdr146992 * See the License for the specific language governing permissions 112958Sdr146992 * and limitations under the License. 122958Sdr146992 * 132958Sdr146992 * When distributing Covered Code, include this CDDL HEADER in each 142958Sdr146992 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 152958Sdr146992 * If applicable, add the following below this CDDL HEADER, with the 162958Sdr146992 * fields enclosed by brackets "[]" replaced with your own identifying 172958Sdr146992 * information: Portions Copyright [yyyy] [name of copyright owner] 182958Sdr146992 * 192958Sdr146992 * CDDL HEADER END 202958Sdr146992 */ 212958Sdr146992 /* 22*3448Sdh155122 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 232958Sdr146992 * Use is subject to license terms. 242958Sdr146992 */ 252958Sdr146992 #pragma ident "%Z%%M% %I% %E% SMI" 262958Sdr146992 272958Sdr146992 #include <sys/param.h> 282958Sdr146992 #include <sys/types.h> 292958Sdr146992 #include <sys/systm.h> 302958Sdr146992 #include <sys/errno.h> 312958Sdr146992 #include <sys/kmem.h> 322958Sdr146992 #include <sys/mutex.h> 332958Sdr146992 #include <sys/condvar.h> 342958Sdr146992 #include <sys/modctl.h> 352958Sdr146992 #include <sys/hook_impl.h> 362958Sdr146992 #include <sys/sdt.h> 372958Sdr146992 382958Sdr146992 /* 392958Sdr146992 * This file provides kernel hook framework. 402958Sdr146992 */ 412958Sdr146992 422958Sdr146992 static struct modldrv modlmisc = { 432958Sdr146992 &mod_miscops, /* drv_modops */ 442958Sdr146992 "Hooks Interface v1.0", /* drv_linkinfo */ 452958Sdr146992 }; 462958Sdr146992 472958Sdr146992 static struct modlinkage modlinkage = { 482958Sdr146992 MODREV_1, /* ml_rev */ 492958Sdr146992 &modlmisc, /* ml_linkage */ 502958Sdr146992 NULL 512958Sdr146992 }; 522958Sdr146992 532958Sdr146992 /* 542958Sdr146992 * Hook internal functions 552958Sdr146992 */ 562958Sdr146992 static hook_int_t *hook_copy(hook_t *src); 57*3448Sdh155122 static hook_event_int_t *hook_event_checkdup(hook_event_t *he, 58*3448Sdh155122 hook_stack_t *hks); 592958Sdr146992 static hook_event_int_t *hook_event_copy(hook_event_t *src); 602958Sdr146992 static hook_event_int_t *hook_event_find(hook_family_int_t *hfi, char *event); 612958Sdr146992 static void hook_event_free(hook_event_int_t *hei); 622958Sdr146992 static hook_family_int_t *hook_family_copy(hook_family_t *src); 63*3448Sdh155122 static hook_family_int_t *hook_family_find(char *family, hook_stack_t *hks); 642958Sdr146992 static void hook_family_free(hook_family_int_t *hfi); 652958Sdr146992 static hook_int_t *hook_find(hook_event_int_t *hei, hook_t *h); 662958Sdr146992 static void hook_free(hook_int_t *hi); 672958Sdr146992 static void hook_init(void); 68*3448Sdh155122 static void hook_fini(void); 69*3448Sdh155122 static void *hook_stack_init(netstackid_t stackid, netstack_t *ns); 70*3448Sdh155122 static void hook_stack_fini(netstackid_t stackid, void *arg); 712958Sdr146992 722958Sdr146992 /* 732958Sdr146992 * Module entry points. 742958Sdr146992 */ 752958Sdr146992 int 762958Sdr146992 _init(void) 772958Sdr146992 { 78*3448Sdh155122 int error; 79*3448Sdh155122 802958Sdr146992 hook_init(); 81*3448Sdh155122 error = mod_install(&modlinkage); 82*3448Sdh155122 if (error != 0) 83*3448Sdh155122 hook_fini(); 84*3448Sdh155122 85*3448Sdh155122 return (error); 862958Sdr146992 } 872958Sdr146992 882958Sdr146992 892958Sdr146992 int 902958Sdr146992 _fini(void) 912958Sdr146992 { 92*3448Sdh155122 int error; 93*3448Sdh155122 94*3448Sdh155122 error = mod_remove(&modlinkage); 95*3448Sdh155122 if (error == 0) 96*3448Sdh155122 hook_fini(); 97*3448Sdh155122 98*3448Sdh155122 return (error); 992958Sdr146992 } 1002958Sdr146992 1012958Sdr146992 1022958Sdr146992 int 1032958Sdr146992 _info(struct modinfo *modinfop) 1042958Sdr146992 { 1052958Sdr146992 return (mod_info(&modlinkage, modinfop)); 1062958Sdr146992 } 1072958Sdr146992 1082958Sdr146992 1092958Sdr146992 /* 1102958Sdr146992 * Function: hook_init 1112958Sdr146992 * Returns: None 1122958Sdr146992 * Parameters: None 1132958Sdr146992 * 1142958Sdr146992 * Initialize hooks 1152958Sdr146992 */ 1162958Sdr146992 static void 1172958Sdr146992 hook_init(void) 1182958Sdr146992 { 119*3448Sdh155122 /* 120*3448Sdh155122 * We want to be informed each time a stack is created or 121*3448Sdh155122 * destroyed in the kernel. 122*3448Sdh155122 */ 123*3448Sdh155122 netstack_register(NS_HOOK, hook_stack_init, NULL, 124*3448Sdh155122 hook_stack_fini); 125*3448Sdh155122 } 126*3448Sdh155122 127*3448Sdh155122 /* 128*3448Sdh155122 * Function: hook_fini 129*3448Sdh155122 * Returns: None 130*3448Sdh155122 * Parameters: None 131*3448Sdh155122 * 132*3448Sdh155122 * Deinitialize hooks 133*3448Sdh155122 */ 134*3448Sdh155122 static void 135*3448Sdh155122 hook_fini(void) 136*3448Sdh155122 { 137*3448Sdh155122 netstack_unregister(NS_HOOK); 1382958Sdr146992 } 1392958Sdr146992 140*3448Sdh155122 /* 141*3448Sdh155122 * Initialize the hook stack instance. 142*3448Sdh155122 */ 143*3448Sdh155122 /*ARGSUSED*/ 144*3448Sdh155122 static void * 145*3448Sdh155122 hook_stack_init(netstackid_t stackid, netstack_t *ns) 146*3448Sdh155122 { 147*3448Sdh155122 hook_stack_t *hks; 148*3448Sdh155122 149*3448Sdh155122 #ifdef NS_DEBUG 150*3448Sdh155122 printf("hook_stack_init(stack %d)\n", stackid); 151*3448Sdh155122 #endif 152*3448Sdh155122 153*3448Sdh155122 hks = (hook_stack_t *)kmem_zalloc(sizeof (*hks), KM_SLEEP); 154*3448Sdh155122 hks->hk_netstack = ns; 155*3448Sdh155122 156*3448Sdh155122 CVW_INIT(&hks->hks_familylock); 157*3448Sdh155122 SLIST_INIT(&hks->hks_familylist); 158*3448Sdh155122 159*3448Sdh155122 return (hks); 160*3448Sdh155122 } 161*3448Sdh155122 162*3448Sdh155122 /* 163*3448Sdh155122 * Free the hook stack instance. 164*3448Sdh155122 */ 165*3448Sdh155122 /*ARGSUSED*/ 166*3448Sdh155122 static void 167*3448Sdh155122 hook_stack_fini(netstackid_t stackid, void *arg) 168*3448Sdh155122 { 169*3448Sdh155122 hook_stack_t *hks = (hook_stack_t *)arg; 170*3448Sdh155122 #ifdef NS_DEBUG 171*3448Sdh155122 printf("hook_stack_fini(%p, stack %d)\n", arg, stackid); 172*3448Sdh155122 #endif 173*3448Sdh155122 CVW_DESTROY(&hks->hks_familylock); 174*3448Sdh155122 kmem_free(hks, sizeof (*hks)); 175*3448Sdh155122 } 1762958Sdr146992 1772958Sdr146992 /* 1782958Sdr146992 * Function: hook_run 1792958Sdr146992 * Returns: int - return value according to callback func 1802958Sdr146992 * Parameters: token(I) - event pointer 1812958Sdr146992 * info(I) - message 1822958Sdr146992 * 1832958Sdr146992 * Run hooks for specific provider. The hooks registered are stepped through 1842958Sdr146992 * until either the end of the list is reached or a hook function returns a 1852958Sdr146992 * non-zero value. If a non-zero value is returned from a hook function, we 1862958Sdr146992 * return that value back to our caller. By design, a hook function can be 1872958Sdr146992 * called more than once, simultaneously. 1882958Sdr146992 */ 1892958Sdr146992 int 190*3448Sdh155122 hook_run(hook_event_token_t token, hook_data_t info, netstack_t *ns) 1912958Sdr146992 { 1922958Sdr146992 hook_int_t *hi; 1932958Sdr146992 hook_event_int_t *hei; 194*3448Sdh155122 hook_stack_t *hks = ns->netstack_hook; 1952958Sdr146992 int rval = 0; 1962958Sdr146992 1972958Sdr146992 ASSERT(token != NULL); 1982958Sdr146992 1992958Sdr146992 hei = (hook_event_int_t *)token; 2002958Sdr146992 DTRACE_PROBE2(hook__run__start, 2012958Sdr146992 hook_event_token_t, token, 2022958Sdr146992 hook_data_t, info); 2032958Sdr146992 2042958Sdr146992 /* Hold global read lock to ensure event will not be deleted */ 205*3448Sdh155122 CVW_ENTER_READ(&hks->hks_familylock); 2062958Sdr146992 2072958Sdr146992 /* Hold event read lock to ensure hook will not be changed */ 2082958Sdr146992 CVW_ENTER_READ(&hei->hei_lock); 2092958Sdr146992 2102958Sdr146992 TAILQ_FOREACH(hi, &hei->hei_head, hi_entry) { 2112958Sdr146992 ASSERT(hi->hi_hook.h_func != NULL); 2122958Sdr146992 DTRACE_PROBE3(hook__func__start, 2132958Sdr146992 hook_event_token_t, token, 2142958Sdr146992 hook_data_t, info, 2152958Sdr146992 hook_int_t *, hi); 216*3448Sdh155122 rval = (*hi->hi_hook.h_func)(token, info, ns); 2172958Sdr146992 DTRACE_PROBE4(hook__func__end, 2182958Sdr146992 hook_event_token_t, token, 2192958Sdr146992 hook_data_t, info, 2202958Sdr146992 hook_int_t *, hi, 2212958Sdr146992 int, rval); 2222958Sdr146992 if (rval != 0) 2232958Sdr146992 break; 2242958Sdr146992 } 2252958Sdr146992 2262958Sdr146992 CVW_EXIT_READ(&hei->hei_lock); 227*3448Sdh155122 CVW_EXIT_READ(&hks->hks_familylock); 2282958Sdr146992 2292958Sdr146992 DTRACE_PROBE3(hook__run__end, 2302958Sdr146992 hook_event_token_t, token, 2312958Sdr146992 hook_data_t, info, 2322958Sdr146992 hook_int_t *, hi); 2332958Sdr146992 2342958Sdr146992 return (rval); 2352958Sdr146992 } 2362958Sdr146992 2372958Sdr146992 2382958Sdr146992 /* 2392958Sdr146992 * Function: hook_family_add 2402958Sdr146992 * Returns: internal family pointer - NULL = Fail 2412958Sdr146992 * Parameters: hf(I) - family pointer 2422958Sdr146992 * 2432958Sdr146992 * Add new family to family list 2442958Sdr146992 */ 2452958Sdr146992 hook_family_int_t * 246*3448Sdh155122 hook_family_add(hook_family_t *hf, hook_stack_t *hks) 2472958Sdr146992 { 2482958Sdr146992 hook_family_int_t *hfi, *new; 2492958Sdr146992 2502958Sdr146992 ASSERT(hf != NULL); 2512958Sdr146992 ASSERT(hf->hf_name != NULL); 2522958Sdr146992 2532958Sdr146992 new = hook_family_copy(hf); 2542958Sdr146992 if (new == NULL) 2552958Sdr146992 return (NULL); 2562958Sdr146992 257*3448Sdh155122 CVW_ENTER_WRITE(&hks->hks_familylock); 2582958Sdr146992 2592958Sdr146992 /* search family list */ 260*3448Sdh155122 hfi = hook_family_find(hf->hf_name, hks); 2612958Sdr146992 if (hfi != NULL) { 262*3448Sdh155122 CVW_EXIT_WRITE(&hks->hks_familylock); 2632958Sdr146992 hook_family_free(new); 2642958Sdr146992 return (NULL); 2652958Sdr146992 } 2662958Sdr146992 267*3448Sdh155122 new->hfi_ptr = (void *)hks; 268*3448Sdh155122 2692958Sdr146992 /* Add to family list head */ 270*3448Sdh155122 SLIST_INSERT_HEAD(&hks->hks_familylist, new, hfi_entry); 2712958Sdr146992 272*3448Sdh155122 CVW_EXIT_WRITE(&hks->hks_familylock); 2732958Sdr146992 return (new); 2742958Sdr146992 } 2752958Sdr146992 2762958Sdr146992 2772958Sdr146992 /* 2782958Sdr146992 * Function: hook_family_remove 2792958Sdr146992 * Returns: int - 0 = Succ, Else = Fail 2802958Sdr146992 * Parameters: hfi(I) - internal family pointer 2812958Sdr146992 * 2822958Sdr146992 * Remove family from family list 2832958Sdr146992 */ 2842958Sdr146992 int 2852958Sdr146992 hook_family_remove(hook_family_int_t *hfi) 2862958Sdr146992 { 287*3448Sdh155122 hook_stack_t *hks; 2882958Sdr146992 2892958Sdr146992 ASSERT(hfi != NULL); 290*3448Sdh155122 hks = (hook_stack_t *)hfi->hfi_ptr; 2912958Sdr146992 292*3448Sdh155122 CVW_ENTER_WRITE(&hks->hks_familylock); 2932958Sdr146992 2942958Sdr146992 /* Check if there are events */ 2952958Sdr146992 if (!SLIST_EMPTY(&hfi->hfi_head)) { 296*3448Sdh155122 CVW_EXIT_WRITE(&hks->hks_familylock); 2972958Sdr146992 return (EBUSY); 2982958Sdr146992 } 2992958Sdr146992 3002958Sdr146992 /* Remove from family list */ 301*3448Sdh155122 SLIST_REMOVE(&hks->hks_familylist, hfi, hook_family_int, hfi_entry); 3022958Sdr146992 303*3448Sdh155122 CVW_EXIT_WRITE(&hks->hks_familylock); 3042958Sdr146992 hook_family_free(hfi); 3052958Sdr146992 3062958Sdr146992 return (0); 3072958Sdr146992 } 3082958Sdr146992 3092958Sdr146992 3102958Sdr146992 /* 3112958Sdr146992 * Function: hook_family_copy 3122958Sdr146992 * Returns: internal family pointer - NULL = Failed 3132958Sdr146992 * Parameters: src(I) - family pointer 3142958Sdr146992 * 3152958Sdr146992 * Allocate internal family block and duplicate incoming family 3162958Sdr146992 * No locks should be held across this function as it may sleep. 3172958Sdr146992 */ 3182958Sdr146992 static hook_family_int_t * 3192958Sdr146992 hook_family_copy(hook_family_t *src) 3202958Sdr146992 { 3212958Sdr146992 hook_family_int_t *new; 3222958Sdr146992 hook_family_t *dst; 3232958Sdr146992 3242958Sdr146992 ASSERT(src != NULL); 3252958Sdr146992 ASSERT(src->hf_name != NULL); 3262958Sdr146992 3272958Sdr146992 new = (hook_family_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP); 3282958Sdr146992 3292958Sdr146992 /* Copy body */ 3302958Sdr146992 SLIST_INIT(&new->hfi_head); 3312958Sdr146992 dst = &new->hfi_family; 3322958Sdr146992 *dst = *src; 3332958Sdr146992 3342958Sdr146992 /* Copy name */ 3352958Sdr146992 dst->hf_name = (char *)kmem_alloc(strlen(src->hf_name) + 1, KM_SLEEP); 3362958Sdr146992 (void) strcpy(dst->hf_name, src->hf_name); 3372958Sdr146992 3382958Sdr146992 return (new); 3392958Sdr146992 } 3402958Sdr146992 3412958Sdr146992 3422958Sdr146992 /* 3432958Sdr146992 * Returns: internal family pointer - NULL = Not match 3442958Sdr146992 * Parameters: family(I) - family name string 3452958Sdr146992 * 3462958Sdr146992 * Search family list with family name 3472958Sdr146992 * A lock on familylock must be held when called. 3482958Sdr146992 */ 3492958Sdr146992 static hook_family_int_t * 350*3448Sdh155122 hook_family_find(char *family, hook_stack_t *hks) 3512958Sdr146992 { 3522958Sdr146992 hook_family_int_t *hfi = NULL; 3532958Sdr146992 3542958Sdr146992 ASSERT(family != NULL); 3552958Sdr146992 356*3448Sdh155122 SLIST_FOREACH(hfi, &hks->hks_familylist, hfi_entry) { 3572958Sdr146992 if (strcmp(hfi->hfi_family.hf_name, family) == 0) 3582958Sdr146992 break; 3592958Sdr146992 } 3602958Sdr146992 return (hfi); 3612958Sdr146992 } 3622958Sdr146992 3632958Sdr146992 3642958Sdr146992 /* 3652958Sdr146992 * Function: hook_family_free 3662958Sdr146992 * Returns: None 3672958Sdr146992 * Parameters: hfi(I) - internal family pointer 3682958Sdr146992 * 3692958Sdr146992 * Free alloc memory for family 3702958Sdr146992 */ 3712958Sdr146992 static void 3722958Sdr146992 hook_family_free(hook_family_int_t *hfi) 3732958Sdr146992 { 3742958Sdr146992 ASSERT(hfi != NULL); 3752958Sdr146992 3762958Sdr146992 /* Free name space */ 3772958Sdr146992 if (hfi->hfi_family.hf_name != NULL) { 3782958Sdr146992 kmem_free(hfi->hfi_family.hf_name, 3792958Sdr146992 strlen(hfi->hfi_family.hf_name) + 1); 3802958Sdr146992 } 3812958Sdr146992 3822958Sdr146992 /* Free container */ 3832958Sdr146992 kmem_free(hfi, sizeof (*hfi)); 3842958Sdr146992 } 3852958Sdr146992 3862958Sdr146992 3872958Sdr146992 /* 3882958Sdr146992 * Function: hook_event_add 3892958Sdr146992 * Returns: internal event pointer - NULL = Fail 3902958Sdr146992 * Parameters: hfi(I) - internal family pointer 3912958Sdr146992 * he(I) - event pointer 3922958Sdr146992 * 3932958Sdr146992 * Add new event to event list on specific family. 3942958Sdr146992 * This function can fail to return successfully if (1) it cannot allocate 3952958Sdr146992 * enough memory for its own internal data structures, (2) the event has 3962958Sdr146992 * already been registered (for any hook family.) 3972958Sdr146992 */ 3982958Sdr146992 hook_event_int_t * 3992958Sdr146992 hook_event_add(hook_family_int_t *hfi, hook_event_t *he) 4002958Sdr146992 { 401*3448Sdh155122 hook_stack_t *hks; 4022958Sdr146992 hook_event_int_t *hei, *new; 4032958Sdr146992 4042958Sdr146992 ASSERT(hfi != NULL); 4052958Sdr146992 ASSERT(he != NULL); 4062958Sdr146992 ASSERT(he->he_name != NULL); 407*3448Sdh155122 hks = (hook_stack_t *)hfi->hfi_ptr; 4082958Sdr146992 4092958Sdr146992 new = hook_event_copy(he); 4102958Sdr146992 if (new == NULL) 4112958Sdr146992 return (NULL); 4122958Sdr146992 413*3448Sdh155122 CVW_ENTER_WRITE(&hks->hks_familylock); 4142958Sdr146992 4152958Sdr146992 /* Check whether this event pointer is already registered */ 416*3448Sdh155122 hei = hook_event_checkdup(he, hks); 4172958Sdr146992 if (hei != NULL) { 418*3448Sdh155122 CVW_EXIT_WRITE(&hks->hks_familylock); 4192958Sdr146992 hook_event_free(new); 4202958Sdr146992 return (NULL); 4212958Sdr146992 } 4222958Sdr146992 4232958Sdr146992 /* Add to event list head */ 4242958Sdr146992 SLIST_INSERT_HEAD(&hfi->hfi_head, new, hei_entry); 4252958Sdr146992 426*3448Sdh155122 CVW_EXIT_WRITE(&hks->hks_familylock); 4272958Sdr146992 return (new); 4282958Sdr146992 } 4292958Sdr146992 4302958Sdr146992 4312958Sdr146992 /* 4322958Sdr146992 * Function: hook_event_remove 4332958Sdr146992 * Returns: int - 0 = Succ, Else = Fail 4342958Sdr146992 * Parameters: hfi(I) - internal family pointer 4352958Sdr146992 * he(I) - event pointer 4362958Sdr146992 * 4372958Sdr146992 * Remove event from event list on specific family 4382958Sdr146992 */ 4392958Sdr146992 int 4402958Sdr146992 hook_event_remove(hook_family_int_t *hfi, hook_event_t *he) 4412958Sdr146992 { 442*3448Sdh155122 hook_stack_t *hks; 4432958Sdr146992 hook_event_int_t *hei; 4442958Sdr146992 4452958Sdr146992 ASSERT(hfi != NULL); 4462958Sdr146992 ASSERT(he != NULL); 447*3448Sdh155122 hks = (hook_stack_t *)hfi->hfi_ptr; 4482958Sdr146992 449*3448Sdh155122 CVW_ENTER_WRITE(&hks->hks_familylock); 4502958Sdr146992 4512958Sdr146992 hei = hook_event_find(hfi, he->he_name); 4522958Sdr146992 if (hei == NULL) { 453*3448Sdh155122 CVW_EXIT_WRITE(&hks->hks_familylock); 4542958Sdr146992 return (ENXIO); 4552958Sdr146992 } 4562958Sdr146992 4572958Sdr146992 /* Check if there are registered hooks for this event */ 4582958Sdr146992 if (!TAILQ_EMPTY(&hei->hei_head)) { 459*3448Sdh155122 CVW_EXIT_WRITE(&hks->hks_familylock); 4602958Sdr146992 return (EBUSY); 4612958Sdr146992 } 4622958Sdr146992 4632958Sdr146992 /* Remove from event list */ 4642958Sdr146992 SLIST_REMOVE(&hfi->hfi_head, hei, hook_event_int, hei_entry); 4652958Sdr146992 466*3448Sdh155122 CVW_EXIT_WRITE(&hks->hks_familylock); 4672958Sdr146992 hook_event_free(hei); 4682958Sdr146992 4692958Sdr146992 return (0); 4702958Sdr146992 } 4712958Sdr146992 4722958Sdr146992 4732958Sdr146992 /* 4742958Sdr146992 * Function: hook_event_checkdup 4752958Sdr146992 * Returns: internal event pointer - NULL = Not match 4762958Sdr146992 * Parameters: he(I) - event pointer 4772958Sdr146992 * 4782958Sdr146992 * Search whole list with event pointer 4792958Sdr146992 * A lock on familylock must be held when called. 4802958Sdr146992 */ 4812958Sdr146992 static hook_event_int_t * 482*3448Sdh155122 hook_event_checkdup(hook_event_t *he, hook_stack_t *hks) 4832958Sdr146992 { 4842958Sdr146992 hook_family_int_t *hfi; 4852958Sdr146992 hook_event_int_t *hei; 4862958Sdr146992 4872958Sdr146992 ASSERT(he != NULL); 4882958Sdr146992 489*3448Sdh155122 SLIST_FOREACH(hfi, &hks->hks_familylist, hfi_entry) { 4902958Sdr146992 SLIST_FOREACH(hei, &hfi->hfi_head, hei_entry) { 4912958Sdr146992 if (hei->hei_event == he) 4922958Sdr146992 return (hei); 4932958Sdr146992 } 4942958Sdr146992 } 4952958Sdr146992 4962958Sdr146992 return (NULL); 4972958Sdr146992 } 4982958Sdr146992 4992958Sdr146992 5002958Sdr146992 /* 5012958Sdr146992 * Function: hook_event_copy 5022958Sdr146992 * Returns: internal event pointer - NULL = Failed 5032958Sdr146992 * Parameters: src(I) - event pointer 5042958Sdr146992 * 5052958Sdr146992 * Allocate internal event block and duplicate incoming event 5062958Sdr146992 * No locks should be held across this function as it may sleep. 5072958Sdr146992 */ 5082958Sdr146992 static hook_event_int_t * 5092958Sdr146992 hook_event_copy(hook_event_t *src) 5102958Sdr146992 { 5112958Sdr146992 hook_event_int_t *new; 5122958Sdr146992 5132958Sdr146992 ASSERT(src != NULL); 5142958Sdr146992 ASSERT(src->he_name != NULL); 5152958Sdr146992 5162958Sdr146992 new = (hook_event_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP); 5172958Sdr146992 5182958Sdr146992 /* Copy body */ 5192958Sdr146992 TAILQ_INIT(&new->hei_head); 5202958Sdr146992 new->hei_event = src; 5212958Sdr146992 5222958Sdr146992 return (new); 5232958Sdr146992 } 5242958Sdr146992 5252958Sdr146992 5262958Sdr146992 /* 5272958Sdr146992 * Function: hook_event_find 5282958Sdr146992 * Returns: internal event pointer - NULL = Not match 5292958Sdr146992 * Parameters: hfi(I) - internal family pointer 5302958Sdr146992 * event(I) - event name string 5312958Sdr146992 * 5322958Sdr146992 * Search event list with event name 533*3448Sdh155122 * A lock on hks->hks_familylock must be held when called. 5342958Sdr146992 */ 5352958Sdr146992 static hook_event_int_t * 5362958Sdr146992 hook_event_find(hook_family_int_t *hfi, char *event) 5372958Sdr146992 { 5382958Sdr146992 hook_event_int_t *hei = NULL; 5392958Sdr146992 5402958Sdr146992 ASSERT(hfi != NULL); 5412958Sdr146992 ASSERT(event != NULL); 5422958Sdr146992 5432958Sdr146992 SLIST_FOREACH(hei, &hfi->hfi_head, hei_entry) { 5442958Sdr146992 if (strcmp(hei->hei_event->he_name, event) == 0) 5452958Sdr146992 break; 5462958Sdr146992 } 5472958Sdr146992 return (hei); 5482958Sdr146992 } 5492958Sdr146992 5502958Sdr146992 5512958Sdr146992 /* 5522958Sdr146992 * Function: hook_event_free 5532958Sdr146992 * Returns: None 5542958Sdr146992 * Parameters: hei(I) - internal event pointer 5552958Sdr146992 * 5562958Sdr146992 * Free alloc memory for event 5572958Sdr146992 */ 5582958Sdr146992 static void 5592958Sdr146992 hook_event_free(hook_event_int_t *hei) 5602958Sdr146992 { 5612958Sdr146992 ASSERT(hei != NULL); 5622958Sdr146992 5632958Sdr146992 /* Free container */ 5642958Sdr146992 kmem_free(hei, sizeof (*hei)); 5652958Sdr146992 } 5662958Sdr146992 5672958Sdr146992 5682958Sdr146992 /* 5692958Sdr146992 * Function: hook_register 5702958Sdr146992 * Returns: int- 0 = Succ, Else = Fail 5712958Sdr146992 * Parameters: hfi(I) - internal family pointer 5722958Sdr146992 * event(I) - event name string 5732958Sdr146992 * h(I) - hook pointer 5742958Sdr146992 * 5752958Sdr146992 * Add new hook to hook list on spefic family, event 5762958Sdr146992 */ 5772958Sdr146992 int 5782958Sdr146992 hook_register(hook_family_int_t *hfi, char *event, hook_t *h) 5792958Sdr146992 { 580*3448Sdh155122 hook_stack_t *hks; 5812958Sdr146992 hook_event_int_t *hei; 5822958Sdr146992 hook_int_t *hi, *new; 5832958Sdr146992 5842958Sdr146992 ASSERT(hfi != NULL); 5852958Sdr146992 ASSERT(event != NULL); 5862958Sdr146992 ASSERT(h != NULL); 587*3448Sdh155122 hks = (hook_stack_t *)hfi->hfi_ptr; 5882958Sdr146992 5892958Sdr146992 /* Alloc hook_int_t and copy hook */ 5902958Sdr146992 new = hook_copy(h); 5912958Sdr146992 if (new == NULL) 5922958Sdr146992 return (ENOMEM); 5932958Sdr146992 5942958Sdr146992 /* 5952958Sdr146992 * Since hook add/remove only impact event, so it is unnecessary 5962958Sdr146992 * to hold global family write lock. Just get read lock here to 5972958Sdr146992 * ensure event will not be removed when doing hooks operation 5982958Sdr146992 */ 599*3448Sdh155122 CVW_ENTER_READ(&hks->hks_familylock); 6002958Sdr146992 6012958Sdr146992 hei = hook_event_find(hfi, event); 6022958Sdr146992 if (hei == NULL) { 603*3448Sdh155122 CVW_EXIT_READ(&hks->hks_familylock); 6042958Sdr146992 hook_free(new); 6052958Sdr146992 return (ENXIO); 6062958Sdr146992 } 6072958Sdr146992 6082958Sdr146992 CVW_ENTER_WRITE(&hei->hei_lock); 6092958Sdr146992 6102958Sdr146992 /* Multiple hooks are only allowed for read-only events. */ 6112958Sdr146992 if (((hei->hei_event->he_flags & HOOK_RDONLY) == 0) && 6122958Sdr146992 (!TAILQ_EMPTY(&hei->hei_head))) { 6132958Sdr146992 CVW_EXIT_WRITE(&hei->hei_lock); 614*3448Sdh155122 CVW_EXIT_READ(&hks->hks_familylock); 6152958Sdr146992 hook_free(new); 6162958Sdr146992 return (EEXIST); 6172958Sdr146992 } 6182958Sdr146992 6192958Sdr146992 hi = hook_find(hei, h); 6202958Sdr146992 if (hi != NULL) { 6212958Sdr146992 CVW_EXIT_WRITE(&hei->hei_lock); 622*3448Sdh155122 CVW_EXIT_READ(&hks->hks_familylock); 6232958Sdr146992 hook_free(new); 6242958Sdr146992 return (EEXIST); 6252958Sdr146992 } 6262958Sdr146992 6272958Sdr146992 /* Add to hook list head */ 6282958Sdr146992 TAILQ_INSERT_HEAD(&hei->hei_head, new, hi_entry); 6292958Sdr146992 hei->hei_event->he_interested = B_TRUE; 6302958Sdr146992 6312958Sdr146992 CVW_EXIT_WRITE(&hei->hei_lock); 632*3448Sdh155122 CVW_EXIT_READ(&hks->hks_familylock); 6332958Sdr146992 return (0); 6342958Sdr146992 } 6352958Sdr146992 6362958Sdr146992 6372958Sdr146992 /* 6382958Sdr146992 * Function: hook_unregister 6392958Sdr146992 * Returns: int - 0 = Succ, Else = Fail 6402958Sdr146992 * Parameters: hfi(I) - internal family pointer 6412958Sdr146992 * event(I) - event name string 6422958Sdr146992 * h(I) - hook pointer 6432958Sdr146992 * 6442958Sdr146992 * Remove hook from hook list on specific family, event 6452958Sdr146992 */ 6462958Sdr146992 int 6472958Sdr146992 hook_unregister(hook_family_int_t *hfi, char *event, hook_t *h) 6482958Sdr146992 { 649*3448Sdh155122 hook_stack_t *hks; 6502958Sdr146992 hook_event_int_t *hei; 6512958Sdr146992 hook_int_t *hi; 6522958Sdr146992 6532958Sdr146992 ASSERT(hfi != NULL); 6542958Sdr146992 ASSERT(h != NULL); 655*3448Sdh155122 hks = (hook_stack_t *)hfi->hfi_ptr; 6562958Sdr146992 657*3448Sdh155122 CVW_ENTER_READ(&hks->hks_familylock); 6582958Sdr146992 6592958Sdr146992 hei = hook_event_find(hfi, event); 6602958Sdr146992 if (hei == NULL) { 661*3448Sdh155122 CVW_EXIT_READ(&hks->hks_familylock); 6622958Sdr146992 return (ENXIO); 6632958Sdr146992 } 6642958Sdr146992 6652958Sdr146992 /* Hold write lock for event */ 6662958Sdr146992 CVW_ENTER_WRITE(&hei->hei_lock); 6672958Sdr146992 6682958Sdr146992 hi = hook_find(hei, h); 6692958Sdr146992 if (hi == NULL) { 6702958Sdr146992 CVW_EXIT_WRITE(&hei->hei_lock); 671*3448Sdh155122 CVW_EXIT_READ(&hks->hks_familylock); 6722958Sdr146992 return (ENXIO); 6732958Sdr146992 } 6742958Sdr146992 6752958Sdr146992 /* Remove from hook list */ 6762958Sdr146992 TAILQ_REMOVE(&hei->hei_head, hi, hi_entry); 6772958Sdr146992 if (TAILQ_EMPTY(&hei->hei_head)) { 6782958Sdr146992 hei->hei_event->he_interested = B_FALSE; 6792958Sdr146992 } 6802958Sdr146992 6812958Sdr146992 CVW_EXIT_WRITE(&hei->hei_lock); 682*3448Sdh155122 CVW_EXIT_READ(&hks->hks_familylock); 6832958Sdr146992 6842958Sdr146992 hook_free(hi); 6852958Sdr146992 return (0); 6862958Sdr146992 } 6872958Sdr146992 6882958Sdr146992 6892958Sdr146992 /* 6902958Sdr146992 * Function: hook_find 6912958Sdr146992 * Returns: internal hook pointer - NULL = Not match 6922958Sdr146992 * Parameters: hei(I) - internal event pointer 6932958Sdr146992 * h(I) - hook pointer 6942958Sdr146992 * 6952958Sdr146992 * Search hook list 6962958Sdr146992 * A lock on familylock must be held when called. 6972958Sdr146992 */ 6982958Sdr146992 static hook_int_t * 6992958Sdr146992 hook_find(hook_event_int_t *hei, hook_t *h) 7002958Sdr146992 { 7012958Sdr146992 hook_int_t *hi; 7022958Sdr146992 7032958Sdr146992 ASSERT(hei != NULL); 7042958Sdr146992 ASSERT(h != NULL); 7052958Sdr146992 7062958Sdr146992 TAILQ_FOREACH(hi, &hei->hei_head, hi_entry) { 7072958Sdr146992 if (strcmp(hi->hi_hook.h_name, h->h_name) == 0) 7082958Sdr146992 break; 7092958Sdr146992 } 7102958Sdr146992 return (hi); 7112958Sdr146992 } 7122958Sdr146992 7132958Sdr146992 7142958Sdr146992 /* 7152958Sdr146992 * Function: hook_copy 7162958Sdr146992 * Returns: internal hook pointer - NULL = Failed 7172958Sdr146992 * Parameters: src(I) - hook pointer 7182958Sdr146992 * 7192958Sdr146992 * Allocate internal hook block and duplicate incoming hook. 7202958Sdr146992 * No locks should be held across this function as it may sleep. 7212958Sdr146992 */ 7222958Sdr146992 static hook_int_t * 7232958Sdr146992 hook_copy(hook_t *src) 7242958Sdr146992 { 7252958Sdr146992 hook_int_t *new; 7262958Sdr146992 hook_t *dst; 7272958Sdr146992 7282958Sdr146992 ASSERT(src != NULL); 7292958Sdr146992 ASSERT(src->h_name != NULL); 7302958Sdr146992 7312958Sdr146992 new = (hook_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP); 7322958Sdr146992 7332958Sdr146992 /* Copy body */ 7342958Sdr146992 dst = &new->hi_hook; 7352958Sdr146992 *dst = *src; 7362958Sdr146992 7372958Sdr146992 /* Copy name */ 7382958Sdr146992 dst->h_name = (char *)kmem_alloc(strlen(src->h_name) + 1, KM_SLEEP); 7392958Sdr146992 (void) strcpy(dst->h_name, src->h_name); 7402958Sdr146992 7412958Sdr146992 return (new); 7422958Sdr146992 } 7432958Sdr146992 7442958Sdr146992 /* 7452958Sdr146992 * Function: hook_free 7462958Sdr146992 * Returns: None 7472958Sdr146992 * Parameters: hi(I) - internal hook pointer 7482958Sdr146992 * 7492958Sdr146992 * Free alloc memory for hook 7502958Sdr146992 */ 7512958Sdr146992 static void 7522958Sdr146992 hook_free(hook_int_t *hi) 7532958Sdr146992 { 7542958Sdr146992 ASSERT(hi != NULL); 7552958Sdr146992 7562958Sdr146992 /* Free name space */ 7572958Sdr146992 if (hi->hi_hook.h_name != NULL) { 7582958Sdr146992 kmem_free(hi->hi_hook.h_name, strlen(hi->hi_hook.h_name) + 1); 7592958Sdr146992 } 7602958Sdr146992 7612958Sdr146992 /* Free container */ 7622958Sdr146992 kmem_free(hi, sizeof (*hi)); 7632958Sdr146992 } 764