1381a2a9aSdr146992 /* 2381a2a9aSdr146992 * CDDL HEADER START 3381a2a9aSdr146992 * 4381a2a9aSdr146992 * The contents of this file are subject to the terms of the 5381a2a9aSdr146992 * Common Development and Distribution License (the "License"). 6381a2a9aSdr146992 * You may not use this file except in compliance with the License. 7381a2a9aSdr146992 * 8381a2a9aSdr146992 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9381a2a9aSdr146992 * or http://www.opensolaris.org/os/licensing. 10381a2a9aSdr146992 * See the License for the specific language governing permissions 11381a2a9aSdr146992 * and limitations under the License. 12381a2a9aSdr146992 * 13381a2a9aSdr146992 * When distributing Covered Code, include this CDDL HEADER in each 14381a2a9aSdr146992 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15381a2a9aSdr146992 * If applicable, add the following below this CDDL HEADER, with the 16381a2a9aSdr146992 * fields enclosed by brackets "[]" replaced with your own identifying 17381a2a9aSdr146992 * information: Portions Copyright [yyyy] [name of copyright owner] 18381a2a9aSdr146992 * 19381a2a9aSdr146992 * CDDL HEADER END 20381a2a9aSdr146992 */ 21381a2a9aSdr146992 /* 22*f4b3ec61Sdh155122 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23381a2a9aSdr146992 * Use is subject to license terms. 24381a2a9aSdr146992 */ 25381a2a9aSdr146992 #pragma ident "%Z%%M% %I% %E% SMI" 26381a2a9aSdr146992 27381a2a9aSdr146992 #include <sys/param.h> 28381a2a9aSdr146992 #include <sys/types.h> 29381a2a9aSdr146992 #include <sys/systm.h> 30381a2a9aSdr146992 #include <sys/errno.h> 31381a2a9aSdr146992 #include <sys/kmem.h> 32381a2a9aSdr146992 #include <sys/mutex.h> 33381a2a9aSdr146992 #include <sys/condvar.h> 34381a2a9aSdr146992 #include <sys/modctl.h> 35381a2a9aSdr146992 #include <sys/hook_impl.h> 36381a2a9aSdr146992 #include <sys/sdt.h> 37381a2a9aSdr146992 38381a2a9aSdr146992 /* 39381a2a9aSdr146992 * This file provides kernel hook framework. 40381a2a9aSdr146992 */ 41381a2a9aSdr146992 42381a2a9aSdr146992 static struct modldrv modlmisc = { 43381a2a9aSdr146992 &mod_miscops, /* drv_modops */ 44381a2a9aSdr146992 "Hooks Interface v1.0", /* drv_linkinfo */ 45381a2a9aSdr146992 }; 46381a2a9aSdr146992 47381a2a9aSdr146992 static struct modlinkage modlinkage = { 48381a2a9aSdr146992 MODREV_1, /* ml_rev */ 49381a2a9aSdr146992 &modlmisc, /* ml_linkage */ 50381a2a9aSdr146992 NULL 51381a2a9aSdr146992 }; 52381a2a9aSdr146992 53381a2a9aSdr146992 /* 54381a2a9aSdr146992 * Hook internal functions 55381a2a9aSdr146992 */ 56381a2a9aSdr146992 static hook_int_t *hook_copy(hook_t *src); 57*f4b3ec61Sdh155122 static hook_event_int_t *hook_event_checkdup(hook_event_t *he, 58*f4b3ec61Sdh155122 hook_stack_t *hks); 59381a2a9aSdr146992 static hook_event_int_t *hook_event_copy(hook_event_t *src); 60381a2a9aSdr146992 static hook_event_int_t *hook_event_find(hook_family_int_t *hfi, char *event); 61381a2a9aSdr146992 static void hook_event_free(hook_event_int_t *hei); 62381a2a9aSdr146992 static hook_family_int_t *hook_family_copy(hook_family_t *src); 63*f4b3ec61Sdh155122 static hook_family_int_t *hook_family_find(char *family, hook_stack_t *hks); 64381a2a9aSdr146992 static void hook_family_free(hook_family_int_t *hfi); 65381a2a9aSdr146992 static hook_int_t *hook_find(hook_event_int_t *hei, hook_t *h); 66381a2a9aSdr146992 static void hook_free(hook_int_t *hi); 67381a2a9aSdr146992 static void hook_init(void); 68*f4b3ec61Sdh155122 static void hook_fini(void); 69*f4b3ec61Sdh155122 static void *hook_stack_init(netstackid_t stackid, netstack_t *ns); 70*f4b3ec61Sdh155122 static void hook_stack_fini(netstackid_t stackid, void *arg); 71381a2a9aSdr146992 72381a2a9aSdr146992 /* 73381a2a9aSdr146992 * Module entry points. 74381a2a9aSdr146992 */ 75381a2a9aSdr146992 int 76381a2a9aSdr146992 _init(void) 77381a2a9aSdr146992 { 78*f4b3ec61Sdh155122 int error; 79*f4b3ec61Sdh155122 80381a2a9aSdr146992 hook_init(); 81*f4b3ec61Sdh155122 error = mod_install(&modlinkage); 82*f4b3ec61Sdh155122 if (error != 0) 83*f4b3ec61Sdh155122 hook_fini(); 84*f4b3ec61Sdh155122 85*f4b3ec61Sdh155122 return (error); 86381a2a9aSdr146992 } 87381a2a9aSdr146992 88381a2a9aSdr146992 89381a2a9aSdr146992 int 90381a2a9aSdr146992 _fini(void) 91381a2a9aSdr146992 { 92*f4b3ec61Sdh155122 int error; 93*f4b3ec61Sdh155122 94*f4b3ec61Sdh155122 error = mod_remove(&modlinkage); 95*f4b3ec61Sdh155122 if (error == 0) 96*f4b3ec61Sdh155122 hook_fini(); 97*f4b3ec61Sdh155122 98*f4b3ec61Sdh155122 return (error); 99381a2a9aSdr146992 } 100381a2a9aSdr146992 101381a2a9aSdr146992 102381a2a9aSdr146992 int 103381a2a9aSdr146992 _info(struct modinfo *modinfop) 104381a2a9aSdr146992 { 105381a2a9aSdr146992 return (mod_info(&modlinkage, modinfop)); 106381a2a9aSdr146992 } 107381a2a9aSdr146992 108381a2a9aSdr146992 109381a2a9aSdr146992 /* 110381a2a9aSdr146992 * Function: hook_init 111381a2a9aSdr146992 * Returns: None 112381a2a9aSdr146992 * Parameters: None 113381a2a9aSdr146992 * 114381a2a9aSdr146992 * Initialize hooks 115381a2a9aSdr146992 */ 116381a2a9aSdr146992 static void 117381a2a9aSdr146992 hook_init(void) 118381a2a9aSdr146992 { 119*f4b3ec61Sdh155122 /* 120*f4b3ec61Sdh155122 * We want to be informed each time a stack is created or 121*f4b3ec61Sdh155122 * destroyed in the kernel. 122*f4b3ec61Sdh155122 */ 123*f4b3ec61Sdh155122 netstack_register(NS_HOOK, hook_stack_init, NULL, 124*f4b3ec61Sdh155122 hook_stack_fini); 125381a2a9aSdr146992 } 126381a2a9aSdr146992 127*f4b3ec61Sdh155122 /* 128*f4b3ec61Sdh155122 * Function: hook_fini 129*f4b3ec61Sdh155122 * Returns: None 130*f4b3ec61Sdh155122 * Parameters: None 131*f4b3ec61Sdh155122 * 132*f4b3ec61Sdh155122 * Deinitialize hooks 133*f4b3ec61Sdh155122 */ 134*f4b3ec61Sdh155122 static void 135*f4b3ec61Sdh155122 hook_fini(void) 136*f4b3ec61Sdh155122 { 137*f4b3ec61Sdh155122 netstack_unregister(NS_HOOK); 138*f4b3ec61Sdh155122 } 139*f4b3ec61Sdh155122 140*f4b3ec61Sdh155122 /* 141*f4b3ec61Sdh155122 * Initialize the hook stack instance. 142*f4b3ec61Sdh155122 */ 143*f4b3ec61Sdh155122 /*ARGSUSED*/ 144*f4b3ec61Sdh155122 static void * 145*f4b3ec61Sdh155122 hook_stack_init(netstackid_t stackid, netstack_t *ns) 146*f4b3ec61Sdh155122 { 147*f4b3ec61Sdh155122 hook_stack_t *hks; 148*f4b3ec61Sdh155122 149*f4b3ec61Sdh155122 #ifdef NS_DEBUG 150*f4b3ec61Sdh155122 printf("hook_stack_init(stack %d)\n", stackid); 151*f4b3ec61Sdh155122 #endif 152*f4b3ec61Sdh155122 153*f4b3ec61Sdh155122 hks = (hook_stack_t *)kmem_zalloc(sizeof (*hks), KM_SLEEP); 154*f4b3ec61Sdh155122 hks->hk_netstack = ns; 155*f4b3ec61Sdh155122 156*f4b3ec61Sdh155122 CVW_INIT(&hks->hks_familylock); 157*f4b3ec61Sdh155122 SLIST_INIT(&hks->hks_familylist); 158*f4b3ec61Sdh155122 159*f4b3ec61Sdh155122 return (hks); 160*f4b3ec61Sdh155122 } 161*f4b3ec61Sdh155122 162*f4b3ec61Sdh155122 /* 163*f4b3ec61Sdh155122 * Free the hook stack instance. 164*f4b3ec61Sdh155122 */ 165*f4b3ec61Sdh155122 /*ARGSUSED*/ 166*f4b3ec61Sdh155122 static void 167*f4b3ec61Sdh155122 hook_stack_fini(netstackid_t stackid, void *arg) 168*f4b3ec61Sdh155122 { 169*f4b3ec61Sdh155122 hook_stack_t *hks = (hook_stack_t *)arg; 170*f4b3ec61Sdh155122 #ifdef NS_DEBUG 171*f4b3ec61Sdh155122 printf("hook_stack_fini(%p, stack %d)\n", arg, stackid); 172*f4b3ec61Sdh155122 #endif 173*f4b3ec61Sdh155122 CVW_DESTROY(&hks->hks_familylock); 174*f4b3ec61Sdh155122 kmem_free(hks, sizeof (*hks)); 175*f4b3ec61Sdh155122 } 176381a2a9aSdr146992 177381a2a9aSdr146992 /* 178381a2a9aSdr146992 * Function: hook_run 179381a2a9aSdr146992 * Returns: int - return value according to callback func 180381a2a9aSdr146992 * Parameters: token(I) - event pointer 181381a2a9aSdr146992 * info(I) - message 182381a2a9aSdr146992 * 183381a2a9aSdr146992 * Run hooks for specific provider. The hooks registered are stepped through 184381a2a9aSdr146992 * until either the end of the list is reached or a hook function returns a 185381a2a9aSdr146992 * non-zero value. If a non-zero value is returned from a hook function, we 186381a2a9aSdr146992 * return that value back to our caller. By design, a hook function can be 187381a2a9aSdr146992 * called more than once, simultaneously. 188381a2a9aSdr146992 */ 189381a2a9aSdr146992 int 190*f4b3ec61Sdh155122 hook_run(hook_event_token_t token, hook_data_t info, netstack_t *ns) 191381a2a9aSdr146992 { 192381a2a9aSdr146992 hook_int_t *hi; 193381a2a9aSdr146992 hook_event_int_t *hei; 194*f4b3ec61Sdh155122 hook_stack_t *hks = ns->netstack_hook; 195381a2a9aSdr146992 int rval = 0; 196381a2a9aSdr146992 197381a2a9aSdr146992 ASSERT(token != NULL); 198381a2a9aSdr146992 199381a2a9aSdr146992 hei = (hook_event_int_t *)token; 200381a2a9aSdr146992 DTRACE_PROBE2(hook__run__start, 201381a2a9aSdr146992 hook_event_token_t, token, 202381a2a9aSdr146992 hook_data_t, info); 203381a2a9aSdr146992 204381a2a9aSdr146992 /* Hold global read lock to ensure event will not be deleted */ 205*f4b3ec61Sdh155122 CVW_ENTER_READ(&hks->hks_familylock); 206381a2a9aSdr146992 207381a2a9aSdr146992 /* Hold event read lock to ensure hook will not be changed */ 208381a2a9aSdr146992 CVW_ENTER_READ(&hei->hei_lock); 209381a2a9aSdr146992 210381a2a9aSdr146992 TAILQ_FOREACH(hi, &hei->hei_head, hi_entry) { 211381a2a9aSdr146992 ASSERT(hi->hi_hook.h_func != NULL); 212381a2a9aSdr146992 DTRACE_PROBE3(hook__func__start, 213381a2a9aSdr146992 hook_event_token_t, token, 214381a2a9aSdr146992 hook_data_t, info, 215381a2a9aSdr146992 hook_int_t *, hi); 216*f4b3ec61Sdh155122 rval = (*hi->hi_hook.h_func)(token, info, ns); 217381a2a9aSdr146992 DTRACE_PROBE4(hook__func__end, 218381a2a9aSdr146992 hook_event_token_t, token, 219381a2a9aSdr146992 hook_data_t, info, 220381a2a9aSdr146992 hook_int_t *, hi, 221381a2a9aSdr146992 int, rval); 222381a2a9aSdr146992 if (rval != 0) 223381a2a9aSdr146992 break; 224381a2a9aSdr146992 } 225381a2a9aSdr146992 226381a2a9aSdr146992 CVW_EXIT_READ(&hei->hei_lock); 227*f4b3ec61Sdh155122 CVW_EXIT_READ(&hks->hks_familylock); 228381a2a9aSdr146992 229381a2a9aSdr146992 DTRACE_PROBE3(hook__run__end, 230381a2a9aSdr146992 hook_event_token_t, token, 231381a2a9aSdr146992 hook_data_t, info, 232381a2a9aSdr146992 hook_int_t *, hi); 233381a2a9aSdr146992 234381a2a9aSdr146992 return (rval); 235381a2a9aSdr146992 } 236381a2a9aSdr146992 237381a2a9aSdr146992 238381a2a9aSdr146992 /* 239381a2a9aSdr146992 * Function: hook_family_add 240381a2a9aSdr146992 * Returns: internal family pointer - NULL = Fail 241381a2a9aSdr146992 * Parameters: hf(I) - family pointer 242381a2a9aSdr146992 * 243381a2a9aSdr146992 * Add new family to family list 244381a2a9aSdr146992 */ 245381a2a9aSdr146992 hook_family_int_t * 246*f4b3ec61Sdh155122 hook_family_add(hook_family_t *hf, hook_stack_t *hks) 247381a2a9aSdr146992 { 248381a2a9aSdr146992 hook_family_int_t *hfi, *new; 249381a2a9aSdr146992 250381a2a9aSdr146992 ASSERT(hf != NULL); 251381a2a9aSdr146992 ASSERT(hf->hf_name != NULL); 252381a2a9aSdr146992 253381a2a9aSdr146992 new = hook_family_copy(hf); 254381a2a9aSdr146992 if (new == NULL) 255381a2a9aSdr146992 return (NULL); 256381a2a9aSdr146992 257*f4b3ec61Sdh155122 CVW_ENTER_WRITE(&hks->hks_familylock); 258381a2a9aSdr146992 259381a2a9aSdr146992 /* search family list */ 260*f4b3ec61Sdh155122 hfi = hook_family_find(hf->hf_name, hks); 261381a2a9aSdr146992 if (hfi != NULL) { 262*f4b3ec61Sdh155122 CVW_EXIT_WRITE(&hks->hks_familylock); 263381a2a9aSdr146992 hook_family_free(new); 264381a2a9aSdr146992 return (NULL); 265381a2a9aSdr146992 } 266381a2a9aSdr146992 267*f4b3ec61Sdh155122 new->hfi_ptr = (void *)hks; 268381a2a9aSdr146992 269*f4b3ec61Sdh155122 /* Add to family list head */ 270*f4b3ec61Sdh155122 SLIST_INSERT_HEAD(&hks->hks_familylist, new, hfi_entry); 271*f4b3ec61Sdh155122 272*f4b3ec61Sdh155122 CVW_EXIT_WRITE(&hks->hks_familylock); 273381a2a9aSdr146992 return (new); 274381a2a9aSdr146992 } 275381a2a9aSdr146992 276381a2a9aSdr146992 277381a2a9aSdr146992 /* 278381a2a9aSdr146992 * Function: hook_family_remove 279381a2a9aSdr146992 * Returns: int - 0 = Succ, Else = Fail 280381a2a9aSdr146992 * Parameters: hfi(I) - internal family pointer 281381a2a9aSdr146992 * 282381a2a9aSdr146992 * Remove family from family list 283381a2a9aSdr146992 */ 284381a2a9aSdr146992 int 285381a2a9aSdr146992 hook_family_remove(hook_family_int_t *hfi) 286381a2a9aSdr146992 { 287*f4b3ec61Sdh155122 hook_stack_t *hks; 288381a2a9aSdr146992 289381a2a9aSdr146992 ASSERT(hfi != NULL); 290*f4b3ec61Sdh155122 hks = (hook_stack_t *)hfi->hfi_ptr; 291381a2a9aSdr146992 292*f4b3ec61Sdh155122 CVW_ENTER_WRITE(&hks->hks_familylock); 293381a2a9aSdr146992 294381a2a9aSdr146992 /* Check if there are events */ 295381a2a9aSdr146992 if (!SLIST_EMPTY(&hfi->hfi_head)) { 296*f4b3ec61Sdh155122 CVW_EXIT_WRITE(&hks->hks_familylock); 297381a2a9aSdr146992 return (EBUSY); 298381a2a9aSdr146992 } 299381a2a9aSdr146992 300381a2a9aSdr146992 /* Remove from family list */ 301*f4b3ec61Sdh155122 SLIST_REMOVE(&hks->hks_familylist, hfi, hook_family_int, hfi_entry); 302381a2a9aSdr146992 303*f4b3ec61Sdh155122 CVW_EXIT_WRITE(&hks->hks_familylock); 304381a2a9aSdr146992 hook_family_free(hfi); 305381a2a9aSdr146992 306381a2a9aSdr146992 return (0); 307381a2a9aSdr146992 } 308381a2a9aSdr146992 309381a2a9aSdr146992 310381a2a9aSdr146992 /* 311381a2a9aSdr146992 * Function: hook_family_copy 312381a2a9aSdr146992 * Returns: internal family pointer - NULL = Failed 313381a2a9aSdr146992 * Parameters: src(I) - family pointer 314381a2a9aSdr146992 * 315381a2a9aSdr146992 * Allocate internal family block and duplicate incoming family 316381a2a9aSdr146992 * No locks should be held across this function as it may sleep. 317381a2a9aSdr146992 */ 318381a2a9aSdr146992 static hook_family_int_t * 319381a2a9aSdr146992 hook_family_copy(hook_family_t *src) 320381a2a9aSdr146992 { 321381a2a9aSdr146992 hook_family_int_t *new; 322381a2a9aSdr146992 hook_family_t *dst; 323381a2a9aSdr146992 324381a2a9aSdr146992 ASSERT(src != NULL); 325381a2a9aSdr146992 ASSERT(src->hf_name != NULL); 326381a2a9aSdr146992 327381a2a9aSdr146992 new = (hook_family_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP); 328381a2a9aSdr146992 329381a2a9aSdr146992 /* Copy body */ 330381a2a9aSdr146992 SLIST_INIT(&new->hfi_head); 331381a2a9aSdr146992 dst = &new->hfi_family; 332381a2a9aSdr146992 *dst = *src; 333381a2a9aSdr146992 334381a2a9aSdr146992 /* Copy name */ 335381a2a9aSdr146992 dst->hf_name = (char *)kmem_alloc(strlen(src->hf_name) + 1, KM_SLEEP); 336381a2a9aSdr146992 (void) strcpy(dst->hf_name, src->hf_name); 337381a2a9aSdr146992 338381a2a9aSdr146992 return (new); 339381a2a9aSdr146992 } 340381a2a9aSdr146992 341381a2a9aSdr146992 342381a2a9aSdr146992 /* 343381a2a9aSdr146992 * Returns: internal family pointer - NULL = Not match 344381a2a9aSdr146992 * Parameters: family(I) - family name string 345381a2a9aSdr146992 * 346381a2a9aSdr146992 * Search family list with family name 347381a2a9aSdr146992 * A lock on familylock must be held when called. 348381a2a9aSdr146992 */ 349381a2a9aSdr146992 static hook_family_int_t * 350*f4b3ec61Sdh155122 hook_family_find(char *family, hook_stack_t *hks) 351381a2a9aSdr146992 { 352381a2a9aSdr146992 hook_family_int_t *hfi = NULL; 353381a2a9aSdr146992 354381a2a9aSdr146992 ASSERT(family != NULL); 355381a2a9aSdr146992 356*f4b3ec61Sdh155122 SLIST_FOREACH(hfi, &hks->hks_familylist, hfi_entry) { 357381a2a9aSdr146992 if (strcmp(hfi->hfi_family.hf_name, family) == 0) 358381a2a9aSdr146992 break; 359381a2a9aSdr146992 } 360381a2a9aSdr146992 return (hfi); 361381a2a9aSdr146992 } 362381a2a9aSdr146992 363381a2a9aSdr146992 364381a2a9aSdr146992 /* 365381a2a9aSdr146992 * Function: hook_family_free 366381a2a9aSdr146992 * Returns: None 367381a2a9aSdr146992 * Parameters: hfi(I) - internal family pointer 368381a2a9aSdr146992 * 369381a2a9aSdr146992 * Free alloc memory for family 370381a2a9aSdr146992 */ 371381a2a9aSdr146992 static void 372381a2a9aSdr146992 hook_family_free(hook_family_int_t *hfi) 373381a2a9aSdr146992 { 374381a2a9aSdr146992 ASSERT(hfi != NULL); 375381a2a9aSdr146992 376381a2a9aSdr146992 /* Free name space */ 377381a2a9aSdr146992 if (hfi->hfi_family.hf_name != NULL) { 378381a2a9aSdr146992 kmem_free(hfi->hfi_family.hf_name, 379381a2a9aSdr146992 strlen(hfi->hfi_family.hf_name) + 1); 380381a2a9aSdr146992 } 381381a2a9aSdr146992 382381a2a9aSdr146992 /* Free container */ 383381a2a9aSdr146992 kmem_free(hfi, sizeof (*hfi)); 384381a2a9aSdr146992 } 385381a2a9aSdr146992 386381a2a9aSdr146992 387381a2a9aSdr146992 /* 388381a2a9aSdr146992 * Function: hook_event_add 389381a2a9aSdr146992 * Returns: internal event pointer - NULL = Fail 390381a2a9aSdr146992 * Parameters: hfi(I) - internal family pointer 391381a2a9aSdr146992 * he(I) - event pointer 392381a2a9aSdr146992 * 393381a2a9aSdr146992 * Add new event to event list on specific family. 394381a2a9aSdr146992 * This function can fail to return successfully if (1) it cannot allocate 395381a2a9aSdr146992 * enough memory for its own internal data structures, (2) the event has 396381a2a9aSdr146992 * already been registered (for any hook family.) 397381a2a9aSdr146992 */ 398381a2a9aSdr146992 hook_event_int_t * 399381a2a9aSdr146992 hook_event_add(hook_family_int_t *hfi, hook_event_t *he) 400381a2a9aSdr146992 { 401*f4b3ec61Sdh155122 hook_stack_t *hks; 402381a2a9aSdr146992 hook_event_int_t *hei, *new; 403381a2a9aSdr146992 404381a2a9aSdr146992 ASSERT(hfi != NULL); 405381a2a9aSdr146992 ASSERT(he != NULL); 406381a2a9aSdr146992 ASSERT(he->he_name != NULL); 407*f4b3ec61Sdh155122 hks = (hook_stack_t *)hfi->hfi_ptr; 408381a2a9aSdr146992 409381a2a9aSdr146992 new = hook_event_copy(he); 410381a2a9aSdr146992 if (new == NULL) 411381a2a9aSdr146992 return (NULL); 412381a2a9aSdr146992 413*f4b3ec61Sdh155122 CVW_ENTER_WRITE(&hks->hks_familylock); 414381a2a9aSdr146992 415381a2a9aSdr146992 /* Check whether this event pointer is already registered */ 416*f4b3ec61Sdh155122 hei = hook_event_checkdup(he, hks); 417381a2a9aSdr146992 if (hei != NULL) { 418*f4b3ec61Sdh155122 CVW_EXIT_WRITE(&hks->hks_familylock); 419381a2a9aSdr146992 hook_event_free(new); 420381a2a9aSdr146992 return (NULL); 421381a2a9aSdr146992 } 422381a2a9aSdr146992 423381a2a9aSdr146992 /* Add to event list head */ 424381a2a9aSdr146992 SLIST_INSERT_HEAD(&hfi->hfi_head, new, hei_entry); 425381a2a9aSdr146992 426*f4b3ec61Sdh155122 CVW_EXIT_WRITE(&hks->hks_familylock); 427381a2a9aSdr146992 return (new); 428381a2a9aSdr146992 } 429381a2a9aSdr146992 430381a2a9aSdr146992 431381a2a9aSdr146992 /* 432381a2a9aSdr146992 * Function: hook_event_remove 433381a2a9aSdr146992 * Returns: int - 0 = Succ, Else = Fail 434381a2a9aSdr146992 * Parameters: hfi(I) - internal family pointer 435381a2a9aSdr146992 * he(I) - event pointer 436381a2a9aSdr146992 * 437381a2a9aSdr146992 * Remove event from event list on specific family 438381a2a9aSdr146992 */ 439381a2a9aSdr146992 int 440381a2a9aSdr146992 hook_event_remove(hook_family_int_t *hfi, hook_event_t *he) 441381a2a9aSdr146992 { 442*f4b3ec61Sdh155122 hook_stack_t *hks; 443381a2a9aSdr146992 hook_event_int_t *hei; 444381a2a9aSdr146992 445381a2a9aSdr146992 ASSERT(hfi != NULL); 446381a2a9aSdr146992 ASSERT(he != NULL); 447*f4b3ec61Sdh155122 hks = (hook_stack_t *)hfi->hfi_ptr; 448381a2a9aSdr146992 449*f4b3ec61Sdh155122 CVW_ENTER_WRITE(&hks->hks_familylock); 450381a2a9aSdr146992 451381a2a9aSdr146992 hei = hook_event_find(hfi, he->he_name); 452381a2a9aSdr146992 if (hei == NULL) { 453*f4b3ec61Sdh155122 CVW_EXIT_WRITE(&hks->hks_familylock); 454381a2a9aSdr146992 return (ENXIO); 455381a2a9aSdr146992 } 456381a2a9aSdr146992 457381a2a9aSdr146992 /* Check if there are registered hooks for this event */ 458381a2a9aSdr146992 if (!TAILQ_EMPTY(&hei->hei_head)) { 459*f4b3ec61Sdh155122 CVW_EXIT_WRITE(&hks->hks_familylock); 460381a2a9aSdr146992 return (EBUSY); 461381a2a9aSdr146992 } 462381a2a9aSdr146992 463381a2a9aSdr146992 /* Remove from event list */ 464381a2a9aSdr146992 SLIST_REMOVE(&hfi->hfi_head, hei, hook_event_int, hei_entry); 465381a2a9aSdr146992 466*f4b3ec61Sdh155122 CVW_EXIT_WRITE(&hks->hks_familylock); 467381a2a9aSdr146992 hook_event_free(hei); 468381a2a9aSdr146992 469381a2a9aSdr146992 return (0); 470381a2a9aSdr146992 } 471381a2a9aSdr146992 472381a2a9aSdr146992 473381a2a9aSdr146992 /* 474381a2a9aSdr146992 * Function: hook_event_checkdup 475381a2a9aSdr146992 * Returns: internal event pointer - NULL = Not match 476381a2a9aSdr146992 * Parameters: he(I) - event pointer 477381a2a9aSdr146992 * 478381a2a9aSdr146992 * Search whole list with event pointer 479381a2a9aSdr146992 * A lock on familylock must be held when called. 480381a2a9aSdr146992 */ 481381a2a9aSdr146992 static hook_event_int_t * 482*f4b3ec61Sdh155122 hook_event_checkdup(hook_event_t *he, hook_stack_t *hks) 483381a2a9aSdr146992 { 484381a2a9aSdr146992 hook_family_int_t *hfi; 485381a2a9aSdr146992 hook_event_int_t *hei; 486381a2a9aSdr146992 487381a2a9aSdr146992 ASSERT(he != NULL); 488381a2a9aSdr146992 489*f4b3ec61Sdh155122 SLIST_FOREACH(hfi, &hks->hks_familylist, hfi_entry) { 490381a2a9aSdr146992 SLIST_FOREACH(hei, &hfi->hfi_head, hei_entry) { 491381a2a9aSdr146992 if (hei->hei_event == he) 492381a2a9aSdr146992 return (hei); 493381a2a9aSdr146992 } 494381a2a9aSdr146992 } 495381a2a9aSdr146992 496381a2a9aSdr146992 return (NULL); 497381a2a9aSdr146992 } 498381a2a9aSdr146992 499381a2a9aSdr146992 500381a2a9aSdr146992 /* 501381a2a9aSdr146992 * Function: hook_event_copy 502381a2a9aSdr146992 * Returns: internal event pointer - NULL = Failed 503381a2a9aSdr146992 * Parameters: src(I) - event pointer 504381a2a9aSdr146992 * 505381a2a9aSdr146992 * Allocate internal event block and duplicate incoming event 506381a2a9aSdr146992 * No locks should be held across this function as it may sleep. 507381a2a9aSdr146992 */ 508381a2a9aSdr146992 static hook_event_int_t * 509381a2a9aSdr146992 hook_event_copy(hook_event_t *src) 510381a2a9aSdr146992 { 511381a2a9aSdr146992 hook_event_int_t *new; 512381a2a9aSdr146992 513381a2a9aSdr146992 ASSERT(src != NULL); 514381a2a9aSdr146992 ASSERT(src->he_name != NULL); 515381a2a9aSdr146992 516381a2a9aSdr146992 new = (hook_event_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP); 517381a2a9aSdr146992 518381a2a9aSdr146992 /* Copy body */ 519381a2a9aSdr146992 TAILQ_INIT(&new->hei_head); 520381a2a9aSdr146992 new->hei_event = src; 521381a2a9aSdr146992 522381a2a9aSdr146992 return (new); 523381a2a9aSdr146992 } 524381a2a9aSdr146992 525381a2a9aSdr146992 526381a2a9aSdr146992 /* 527381a2a9aSdr146992 * Function: hook_event_find 528381a2a9aSdr146992 * Returns: internal event pointer - NULL = Not match 529381a2a9aSdr146992 * Parameters: hfi(I) - internal family pointer 530381a2a9aSdr146992 * event(I) - event name string 531381a2a9aSdr146992 * 532381a2a9aSdr146992 * Search event list with event name 533*f4b3ec61Sdh155122 * A lock on hks->hks_familylock must be held when called. 534381a2a9aSdr146992 */ 535381a2a9aSdr146992 static hook_event_int_t * 536381a2a9aSdr146992 hook_event_find(hook_family_int_t *hfi, char *event) 537381a2a9aSdr146992 { 538381a2a9aSdr146992 hook_event_int_t *hei = NULL; 539381a2a9aSdr146992 540381a2a9aSdr146992 ASSERT(hfi != NULL); 541381a2a9aSdr146992 ASSERT(event != NULL); 542381a2a9aSdr146992 543381a2a9aSdr146992 SLIST_FOREACH(hei, &hfi->hfi_head, hei_entry) { 544381a2a9aSdr146992 if (strcmp(hei->hei_event->he_name, event) == 0) 545381a2a9aSdr146992 break; 546381a2a9aSdr146992 } 547381a2a9aSdr146992 return (hei); 548381a2a9aSdr146992 } 549381a2a9aSdr146992 550381a2a9aSdr146992 551381a2a9aSdr146992 /* 552381a2a9aSdr146992 * Function: hook_event_free 553381a2a9aSdr146992 * Returns: None 554381a2a9aSdr146992 * Parameters: hei(I) - internal event pointer 555381a2a9aSdr146992 * 556381a2a9aSdr146992 * Free alloc memory for event 557381a2a9aSdr146992 */ 558381a2a9aSdr146992 static void 559381a2a9aSdr146992 hook_event_free(hook_event_int_t *hei) 560381a2a9aSdr146992 { 561381a2a9aSdr146992 ASSERT(hei != NULL); 562381a2a9aSdr146992 563381a2a9aSdr146992 /* Free container */ 564381a2a9aSdr146992 kmem_free(hei, sizeof (*hei)); 565381a2a9aSdr146992 } 566381a2a9aSdr146992 567381a2a9aSdr146992 568381a2a9aSdr146992 /* 569381a2a9aSdr146992 * Function: hook_register 570381a2a9aSdr146992 * Returns: int- 0 = Succ, Else = Fail 571381a2a9aSdr146992 * Parameters: hfi(I) - internal family pointer 572381a2a9aSdr146992 * event(I) - event name string 573381a2a9aSdr146992 * h(I) - hook pointer 574381a2a9aSdr146992 * 575381a2a9aSdr146992 * Add new hook to hook list on spefic family, event 576381a2a9aSdr146992 */ 577381a2a9aSdr146992 int 578381a2a9aSdr146992 hook_register(hook_family_int_t *hfi, char *event, hook_t *h) 579381a2a9aSdr146992 { 580*f4b3ec61Sdh155122 hook_stack_t *hks; 581381a2a9aSdr146992 hook_event_int_t *hei; 582381a2a9aSdr146992 hook_int_t *hi, *new; 583381a2a9aSdr146992 584381a2a9aSdr146992 ASSERT(hfi != NULL); 585381a2a9aSdr146992 ASSERT(event != NULL); 586381a2a9aSdr146992 ASSERT(h != NULL); 587*f4b3ec61Sdh155122 hks = (hook_stack_t *)hfi->hfi_ptr; 588381a2a9aSdr146992 589381a2a9aSdr146992 /* Alloc hook_int_t and copy hook */ 590381a2a9aSdr146992 new = hook_copy(h); 591381a2a9aSdr146992 if (new == NULL) 592381a2a9aSdr146992 return (ENOMEM); 593381a2a9aSdr146992 594381a2a9aSdr146992 /* 595381a2a9aSdr146992 * Since hook add/remove only impact event, so it is unnecessary 596381a2a9aSdr146992 * to hold global family write lock. Just get read lock here to 597381a2a9aSdr146992 * ensure event will not be removed when doing hooks operation 598381a2a9aSdr146992 */ 599*f4b3ec61Sdh155122 CVW_ENTER_READ(&hks->hks_familylock); 600381a2a9aSdr146992 601381a2a9aSdr146992 hei = hook_event_find(hfi, event); 602381a2a9aSdr146992 if (hei == NULL) { 603*f4b3ec61Sdh155122 CVW_EXIT_READ(&hks->hks_familylock); 604381a2a9aSdr146992 hook_free(new); 605381a2a9aSdr146992 return (ENXIO); 606381a2a9aSdr146992 } 607381a2a9aSdr146992 608381a2a9aSdr146992 CVW_ENTER_WRITE(&hei->hei_lock); 609381a2a9aSdr146992 610381a2a9aSdr146992 /* Multiple hooks are only allowed for read-only events. */ 611381a2a9aSdr146992 if (((hei->hei_event->he_flags & HOOK_RDONLY) == 0) && 612381a2a9aSdr146992 (!TAILQ_EMPTY(&hei->hei_head))) { 613381a2a9aSdr146992 CVW_EXIT_WRITE(&hei->hei_lock); 614*f4b3ec61Sdh155122 CVW_EXIT_READ(&hks->hks_familylock); 615381a2a9aSdr146992 hook_free(new); 616381a2a9aSdr146992 return (EEXIST); 617381a2a9aSdr146992 } 618381a2a9aSdr146992 619381a2a9aSdr146992 hi = hook_find(hei, h); 620381a2a9aSdr146992 if (hi != NULL) { 621381a2a9aSdr146992 CVW_EXIT_WRITE(&hei->hei_lock); 622*f4b3ec61Sdh155122 CVW_EXIT_READ(&hks->hks_familylock); 623381a2a9aSdr146992 hook_free(new); 624381a2a9aSdr146992 return (EEXIST); 625381a2a9aSdr146992 } 626381a2a9aSdr146992 627381a2a9aSdr146992 /* Add to hook list head */ 628381a2a9aSdr146992 TAILQ_INSERT_HEAD(&hei->hei_head, new, hi_entry); 629381a2a9aSdr146992 hei->hei_event->he_interested = B_TRUE; 630381a2a9aSdr146992 631381a2a9aSdr146992 CVW_EXIT_WRITE(&hei->hei_lock); 632*f4b3ec61Sdh155122 CVW_EXIT_READ(&hks->hks_familylock); 633381a2a9aSdr146992 return (0); 634381a2a9aSdr146992 } 635381a2a9aSdr146992 636381a2a9aSdr146992 637381a2a9aSdr146992 /* 638381a2a9aSdr146992 * Function: hook_unregister 639381a2a9aSdr146992 * Returns: int - 0 = Succ, Else = Fail 640381a2a9aSdr146992 * Parameters: hfi(I) - internal family pointer 641381a2a9aSdr146992 * event(I) - event name string 642381a2a9aSdr146992 * h(I) - hook pointer 643381a2a9aSdr146992 * 644381a2a9aSdr146992 * Remove hook from hook list on specific family, event 645381a2a9aSdr146992 */ 646381a2a9aSdr146992 int 647381a2a9aSdr146992 hook_unregister(hook_family_int_t *hfi, char *event, hook_t *h) 648381a2a9aSdr146992 { 649*f4b3ec61Sdh155122 hook_stack_t *hks; 650381a2a9aSdr146992 hook_event_int_t *hei; 651381a2a9aSdr146992 hook_int_t *hi; 652381a2a9aSdr146992 653381a2a9aSdr146992 ASSERT(hfi != NULL); 654381a2a9aSdr146992 ASSERT(h != NULL); 655*f4b3ec61Sdh155122 hks = (hook_stack_t *)hfi->hfi_ptr; 656381a2a9aSdr146992 657*f4b3ec61Sdh155122 CVW_ENTER_READ(&hks->hks_familylock); 658381a2a9aSdr146992 659381a2a9aSdr146992 hei = hook_event_find(hfi, event); 660381a2a9aSdr146992 if (hei == NULL) { 661*f4b3ec61Sdh155122 CVW_EXIT_READ(&hks->hks_familylock); 662381a2a9aSdr146992 return (ENXIO); 663381a2a9aSdr146992 } 664381a2a9aSdr146992 665381a2a9aSdr146992 /* Hold write lock for event */ 666381a2a9aSdr146992 CVW_ENTER_WRITE(&hei->hei_lock); 667381a2a9aSdr146992 668381a2a9aSdr146992 hi = hook_find(hei, h); 669381a2a9aSdr146992 if (hi == NULL) { 670381a2a9aSdr146992 CVW_EXIT_WRITE(&hei->hei_lock); 671*f4b3ec61Sdh155122 CVW_EXIT_READ(&hks->hks_familylock); 672381a2a9aSdr146992 return (ENXIO); 673381a2a9aSdr146992 } 674381a2a9aSdr146992 675381a2a9aSdr146992 /* Remove from hook list */ 676381a2a9aSdr146992 TAILQ_REMOVE(&hei->hei_head, hi, hi_entry); 677381a2a9aSdr146992 if (TAILQ_EMPTY(&hei->hei_head)) { 678381a2a9aSdr146992 hei->hei_event->he_interested = B_FALSE; 679381a2a9aSdr146992 } 680381a2a9aSdr146992 681381a2a9aSdr146992 CVW_EXIT_WRITE(&hei->hei_lock); 682*f4b3ec61Sdh155122 CVW_EXIT_READ(&hks->hks_familylock); 683381a2a9aSdr146992 684381a2a9aSdr146992 hook_free(hi); 685381a2a9aSdr146992 return (0); 686381a2a9aSdr146992 } 687381a2a9aSdr146992 688381a2a9aSdr146992 689381a2a9aSdr146992 /* 690381a2a9aSdr146992 * Function: hook_find 691381a2a9aSdr146992 * Returns: internal hook pointer - NULL = Not match 692381a2a9aSdr146992 * Parameters: hei(I) - internal event pointer 693381a2a9aSdr146992 * h(I) - hook pointer 694381a2a9aSdr146992 * 695381a2a9aSdr146992 * Search hook list 696381a2a9aSdr146992 * A lock on familylock must be held when called. 697381a2a9aSdr146992 */ 698381a2a9aSdr146992 static hook_int_t * 699381a2a9aSdr146992 hook_find(hook_event_int_t *hei, hook_t *h) 700381a2a9aSdr146992 { 701381a2a9aSdr146992 hook_int_t *hi; 702381a2a9aSdr146992 703381a2a9aSdr146992 ASSERT(hei != NULL); 704381a2a9aSdr146992 ASSERT(h != NULL); 705381a2a9aSdr146992 706381a2a9aSdr146992 TAILQ_FOREACH(hi, &hei->hei_head, hi_entry) { 707381a2a9aSdr146992 if (strcmp(hi->hi_hook.h_name, h->h_name) == 0) 708381a2a9aSdr146992 break; 709381a2a9aSdr146992 } 710381a2a9aSdr146992 return (hi); 711381a2a9aSdr146992 } 712381a2a9aSdr146992 713381a2a9aSdr146992 714381a2a9aSdr146992 /* 715381a2a9aSdr146992 * Function: hook_copy 716381a2a9aSdr146992 * Returns: internal hook pointer - NULL = Failed 717381a2a9aSdr146992 * Parameters: src(I) - hook pointer 718381a2a9aSdr146992 * 719381a2a9aSdr146992 * Allocate internal hook block and duplicate incoming hook. 720381a2a9aSdr146992 * No locks should be held across this function as it may sleep. 721381a2a9aSdr146992 */ 722381a2a9aSdr146992 static hook_int_t * 723381a2a9aSdr146992 hook_copy(hook_t *src) 724381a2a9aSdr146992 { 725381a2a9aSdr146992 hook_int_t *new; 726381a2a9aSdr146992 hook_t *dst; 727381a2a9aSdr146992 728381a2a9aSdr146992 ASSERT(src != NULL); 729381a2a9aSdr146992 ASSERT(src->h_name != NULL); 730381a2a9aSdr146992 731381a2a9aSdr146992 new = (hook_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP); 732381a2a9aSdr146992 733381a2a9aSdr146992 /* Copy body */ 734381a2a9aSdr146992 dst = &new->hi_hook; 735381a2a9aSdr146992 *dst = *src; 736381a2a9aSdr146992 737381a2a9aSdr146992 /* Copy name */ 738381a2a9aSdr146992 dst->h_name = (char *)kmem_alloc(strlen(src->h_name) + 1, KM_SLEEP); 739381a2a9aSdr146992 (void) strcpy(dst->h_name, src->h_name); 740381a2a9aSdr146992 741381a2a9aSdr146992 return (new); 742381a2a9aSdr146992 } 743381a2a9aSdr146992 744381a2a9aSdr146992 /* 745381a2a9aSdr146992 * Function: hook_free 746381a2a9aSdr146992 * Returns: None 747381a2a9aSdr146992 * Parameters: hi(I) - internal hook pointer 748381a2a9aSdr146992 * 749381a2a9aSdr146992 * Free alloc memory for hook 750381a2a9aSdr146992 */ 751381a2a9aSdr146992 static void 752381a2a9aSdr146992 hook_free(hook_int_t *hi) 753381a2a9aSdr146992 { 754381a2a9aSdr146992 ASSERT(hi != NULL); 755381a2a9aSdr146992 756381a2a9aSdr146992 /* Free name space */ 757381a2a9aSdr146992 if (hi->hi_hook.h_name != NULL) { 758381a2a9aSdr146992 kmem_free(hi->hi_hook.h_name, strlen(hi->hi_hook.h_name) + 1); 759381a2a9aSdr146992 } 760381a2a9aSdr146992 761381a2a9aSdr146992 /* Free container */ 762381a2a9aSdr146992 kmem_free(hi, sizeof (*hi)); 763381a2a9aSdr146992 } 764