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