1 /* $NetBSD: kern_hook.c,v 1.4 2010/12/11 22:27:53 matt Exp $ */ 2 3 /*- 4 * Copyright (c) 1997, 1998, 1999, 2002, 2007, 2008 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, 9 * NASA Ames Research Center, and by Luke Mewburn. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 #include <sys/cdefs.h> 34 __KERNEL_RCSID(0, "$NetBSD: kern_hook.c,v 1.4 2010/12/11 22:27:53 matt Exp $"); 35 36 #include <sys/param.h> 37 #include <sys/malloc.h> 38 #include <sys/rwlock.h> 39 #include <sys/systm.h> 40 #include <sys/device.h> 41 42 /* 43 * A generic linear hook. 44 */ 45 struct hook_desc { 46 LIST_ENTRY(hook_desc) hk_list; 47 void (*hk_fn)(void *); 48 void *hk_arg; 49 }; 50 typedef LIST_HEAD(, hook_desc) hook_list_t; 51 52 int powerhook_debug = 0; 53 54 static void * 55 hook_establish(hook_list_t *list, void (*fn)(void *), void *arg) 56 { 57 struct hook_desc *hd; 58 59 hd = malloc(sizeof(*hd), M_DEVBUF, M_NOWAIT); 60 if (hd == NULL) 61 return (NULL); 62 63 hd->hk_fn = fn; 64 hd->hk_arg = arg; 65 LIST_INSERT_HEAD(list, hd, hk_list); 66 67 return (hd); 68 } 69 70 static void 71 hook_disestablish(hook_list_t *list, void *vhook) 72 { 73 #ifdef DIAGNOSTIC 74 struct hook_desc *hd; 75 76 LIST_FOREACH(hd, list, hk_list) { 77 if (hd == vhook) 78 break; 79 } 80 81 if (hd == NULL) 82 panic("hook_disestablish: hook %p not established", vhook); 83 #endif 84 LIST_REMOVE((struct hook_desc *)vhook, hk_list); 85 free(vhook, M_DEVBUF); 86 } 87 88 static void 89 hook_destroy(hook_list_t *list) 90 { 91 struct hook_desc *hd; 92 93 while ((hd = LIST_FIRST(list)) != NULL) { 94 LIST_REMOVE(hd, hk_list); 95 free(hd, M_DEVBUF); 96 } 97 } 98 99 static void 100 hook_proc_run(hook_list_t *list, struct proc *p) 101 { 102 struct hook_desc *hd; 103 104 LIST_FOREACH(hd, list, hk_list) 105 ((void (*)(struct proc *, void *))*hd->hk_fn)(p, hd->hk_arg); 106 } 107 108 /* 109 * "Shutdown hook" types, functions, and variables. 110 * 111 * Should be invoked immediately before the 112 * system is halted or rebooted, i.e. after file systems unmounted, 113 * after crash dump done, etc. 114 * 115 * Each shutdown hook is removed from the list before it's run, so that 116 * it won't be run again. 117 */ 118 119 static hook_list_t shutdownhook_list = LIST_HEAD_INITIALIZER(shutdownhook_list); 120 121 void * 122 shutdownhook_establish(void (*fn)(void *), void *arg) 123 { 124 return hook_establish(&shutdownhook_list, fn, arg); 125 } 126 127 void 128 shutdownhook_disestablish(void *vhook) 129 { 130 hook_disestablish(&shutdownhook_list, vhook); 131 } 132 133 /* 134 * Run shutdown hooks. Should be invoked immediately before the 135 * system is halted or rebooted, i.e. after file systems unmounted, 136 * after crash dump done, etc. 137 * 138 * Each shutdown hook is removed from the list before it's run, so that 139 * it won't be run again. 140 */ 141 void 142 doshutdownhooks(void) 143 { 144 struct hook_desc *dp; 145 146 while ((dp = LIST_FIRST(&shutdownhook_list)) != NULL) { 147 LIST_REMOVE(dp, hk_list); 148 (*dp->hk_fn)(dp->hk_arg); 149 #if 0 150 /* 151 * Don't bother freeing the hook structure,, since we may 152 * be rebooting because of a memory corruption problem, 153 * and this might only make things worse. It doesn't 154 * matter, anyway, since the system is just about to 155 * reboot. 156 */ 157 free(dp, M_DEVBUF); 158 #endif 159 } 160 } 161 162 /* 163 * "Mountroot hook" types, functions, and variables. 164 */ 165 166 static hook_list_t mountroothook_list=LIST_HEAD_INITIALIZER(mountroothook_list); 167 168 void * 169 mountroothook_establish(void (*fn)(device_t), device_t dev) 170 { 171 return hook_establish(&mountroothook_list, (void (*)(void *))fn, dev); 172 } 173 174 void 175 mountroothook_disestablish(void *vhook) 176 { 177 hook_disestablish(&mountroothook_list, vhook); 178 } 179 180 void 181 mountroothook_destroy(void) 182 { 183 hook_destroy(&mountroothook_list); 184 } 185 186 void 187 domountroothook(struct device *therootdev) 188 { 189 struct hook_desc *hd; 190 191 LIST_FOREACH(hd, &mountroothook_list, hk_list) { 192 if (hd->hk_arg == therootdev) { 193 (*hd->hk_fn)(hd->hk_arg); 194 return; 195 } 196 } 197 } 198 199 static hook_list_t exechook_list = LIST_HEAD_INITIALIZER(exechook_list); 200 201 void * 202 exechook_establish(void (*fn)(struct proc *, void *), void *arg) 203 { 204 return hook_establish(&exechook_list, (void (*)(void *))fn, arg); 205 } 206 207 void 208 exechook_disestablish(void *vhook) 209 { 210 hook_disestablish(&exechook_list, vhook); 211 } 212 213 /* 214 * Run exec hooks. 215 */ 216 void 217 doexechooks(struct proc *p) 218 { 219 hook_proc_run(&exechook_list, p); 220 } 221 222 static hook_list_t exithook_list = LIST_HEAD_INITIALIZER(exithook_list); 223 extern krwlock_t exec_lock; 224 225 void * 226 exithook_establish(void (*fn)(struct proc *, void *), void *arg) 227 { 228 void *rv; 229 230 rw_enter(&exec_lock, RW_WRITER); 231 rv = hook_establish(&exithook_list, (void (*)(void *))fn, arg); 232 rw_exit(&exec_lock); 233 return rv; 234 } 235 236 void 237 exithook_disestablish(void *vhook) 238 { 239 240 rw_enter(&exec_lock, RW_WRITER); 241 hook_disestablish(&exithook_list, vhook); 242 rw_exit(&exec_lock); 243 } 244 245 /* 246 * Run exit hooks. 247 */ 248 void 249 doexithooks(struct proc *p) 250 { 251 hook_proc_run(&exithook_list, p); 252 } 253 254 static hook_list_t forkhook_list = LIST_HEAD_INITIALIZER(forkhook_list); 255 256 void * 257 forkhook_establish(void (*fn)(struct proc *, struct proc *)) 258 { 259 return hook_establish(&forkhook_list, (void (*)(void *))fn, NULL); 260 } 261 262 void 263 forkhook_disestablish(void *vhook) 264 { 265 hook_disestablish(&forkhook_list, vhook); 266 } 267 268 /* 269 * Run fork hooks. 270 */ 271 void 272 doforkhooks(struct proc *p2, struct proc *p1) 273 { 274 struct hook_desc *hd; 275 276 LIST_FOREACH(hd, &forkhook_list, hk_list) { 277 ((void (*)(struct proc *, struct proc *))*hd->hk_fn) 278 (p2, p1); 279 } 280 } 281 282 static hook_list_t critpollhook_list = LIST_HEAD_INITIALIZER(critpollhook_list); 283 284 void * 285 critpollhook_establish(void (*fn)(void *), void *arg) 286 { 287 return hook_establish(&critpollhook_list, fn, arg); 288 } 289 290 void 291 critpollhook_disestablish(void *vhook) 292 { 293 hook_disestablish(&critpollhook_list, vhook); 294 } 295 296 /* 297 * Run critical polling hooks. 298 */ 299 void 300 docritpollhooks(void) 301 { 302 struct hook_desc *hd; 303 304 LIST_FOREACH(hd, &critpollhook_list, hk_list) { 305 (*hd->hk_fn)(hd->hk_arg); 306 } 307 } 308 309 /* 310 * "Power hook" types, functions, and variables. 311 * The list of power hooks is kept ordered with the last registered hook 312 * first. 313 * When running the hooks on power down the hooks are called in reverse 314 * registration order, when powering up in registration order. 315 */ 316 struct powerhook_desc { 317 CIRCLEQ_ENTRY(powerhook_desc) sfd_list; 318 void (*sfd_fn)(int, void *); 319 void *sfd_arg; 320 char sfd_name[16]; 321 }; 322 323 static CIRCLEQ_HEAD(, powerhook_desc) powerhook_list = 324 CIRCLEQ_HEAD_INITIALIZER(powerhook_list); 325 326 void * 327 powerhook_establish(const char *name, void (*fn)(int, void *), void *arg) 328 { 329 struct powerhook_desc *ndp; 330 331 ndp = (struct powerhook_desc *) 332 malloc(sizeof(*ndp), M_DEVBUF, M_NOWAIT); 333 if (ndp == NULL) 334 return (NULL); 335 336 ndp->sfd_fn = fn; 337 ndp->sfd_arg = arg; 338 strlcpy(ndp->sfd_name, name, sizeof(ndp->sfd_name)); 339 CIRCLEQ_INSERT_HEAD(&powerhook_list, ndp, sfd_list); 340 341 aprint_error("%s: WARNING: powerhook_establish is deprecated\n", name); 342 return (ndp); 343 } 344 345 void 346 powerhook_disestablish(void *vhook) 347 { 348 #ifdef DIAGNOSTIC 349 struct powerhook_desc *dp; 350 351 CIRCLEQ_FOREACH(dp, &powerhook_list, sfd_list) 352 if (dp == vhook) 353 goto found; 354 panic("powerhook_disestablish: hook %p not established", vhook); 355 found: 356 #endif 357 358 CIRCLEQ_REMOVE(&powerhook_list, (struct powerhook_desc *)vhook, 359 sfd_list); 360 free(vhook, M_DEVBUF); 361 } 362 363 /* 364 * Run power hooks. 365 */ 366 void 367 dopowerhooks(int why) 368 { 369 struct powerhook_desc *dp; 370 const char *why_name; 371 static const char * pwr_names[] = {PWR_NAMES}; 372 why_name = why < __arraycount(pwr_names) ? pwr_names[why] : "???"; 373 374 if (why == PWR_RESUME || why == PWR_SOFTRESUME) { 375 CIRCLEQ_FOREACH_REVERSE(dp, &powerhook_list, sfd_list) { 376 if (powerhook_debug) 377 printf("dopowerhooks %s: %s (%p)\n", 378 why_name, dp->sfd_name, dp); 379 (*dp->sfd_fn)(why, dp->sfd_arg); 380 } 381 } else { 382 CIRCLEQ_FOREACH(dp, &powerhook_list, sfd_list) { 383 if (powerhook_debug) 384 printf("dopowerhooks %s: %s (%p)\n", 385 why_name, dp->sfd_name, dp); 386 (*dp->sfd_fn)(why, dp->sfd_arg); 387 } 388 } 389 390 if (powerhook_debug) 391 printf("dopowerhooks: %s done\n", why_name); 392 } 393