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