1 /* $NetBSD: kern_hook.c,v 1.8 2019/10/16 18:29:49 christos 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.8 2019/10/16 18:29:49 christos 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 __FPTRCAST(void (*)(struct proc *, void *), *hd->hk_fn)(p, 106 hd->hk_arg); 107 } 108 } 109 110 /* 111 * "Shutdown hook" types, functions, and variables. 112 * 113 * Should be invoked immediately before the 114 * system is halted or rebooted, i.e. after file systems unmounted, 115 * after crash dump done, etc. 116 * 117 * Each shutdown hook is removed from the list before it's run, so that 118 * it won't be run again. 119 */ 120 121 static hook_list_t shutdownhook_list = LIST_HEAD_INITIALIZER(shutdownhook_list); 122 123 void * 124 shutdownhook_establish(void (*fn)(void *), void *arg) 125 { 126 return hook_establish(&shutdownhook_list, fn, arg); 127 } 128 129 void 130 shutdownhook_disestablish(void *vhook) 131 { 132 hook_disestablish(&shutdownhook_list, vhook); 133 } 134 135 /* 136 * Run shutdown hooks. Should be invoked immediately before the 137 * system is halted or rebooted, i.e. after file systems unmounted, 138 * after crash dump done, etc. 139 * 140 * Each shutdown hook is removed from the list before it's run, so that 141 * it won't be run again. 142 */ 143 void 144 doshutdownhooks(void) 145 { 146 struct hook_desc *dp; 147 148 while ((dp = LIST_FIRST(&shutdownhook_list)) != NULL) { 149 LIST_REMOVE(dp, hk_list); 150 (*dp->hk_fn)(dp->hk_arg); 151 #if 0 152 /* 153 * Don't bother freeing the hook structure,, since we may 154 * be rebooting because of a memory corruption problem, 155 * and this might only make things worse. It doesn't 156 * matter, anyway, since the system is just about to 157 * reboot. 158 */ 159 free(dp, M_DEVBUF); 160 #endif 161 } 162 } 163 164 /* 165 * "Mountroot hook" types, functions, and variables. 166 */ 167 168 static hook_list_t mountroothook_list=LIST_HEAD_INITIALIZER(mountroothook_list); 169 170 void * 171 mountroothook_establish(void (*fn)(device_t), device_t dev) 172 { 173 return hook_establish(&mountroothook_list, __FPTRCAST(void (*), fn), 174 dev); 175 } 176 177 void 178 mountroothook_disestablish(void *vhook) 179 { 180 hook_disestablish(&mountroothook_list, vhook); 181 } 182 183 void 184 mountroothook_destroy(void) 185 { 186 hook_destroy(&mountroothook_list); 187 } 188 189 void 190 domountroothook(device_t therootdev) 191 { 192 struct hook_desc *hd; 193 194 LIST_FOREACH(hd, &mountroothook_list, hk_list) { 195 if (hd->hk_arg == therootdev) { 196 (*hd->hk_fn)(hd->hk_arg); 197 return; 198 } 199 } 200 } 201 202 static hook_list_t exechook_list = LIST_HEAD_INITIALIZER(exechook_list); 203 204 void * 205 exechook_establish(void (*fn)(struct proc *, void *), void *arg) 206 { 207 return hook_establish(&exechook_list, __FPTRCAST(void (*)(void *), fn), 208 arg); 209 } 210 211 void 212 exechook_disestablish(void *vhook) 213 { 214 hook_disestablish(&exechook_list, vhook); 215 } 216 217 /* 218 * Run exec hooks. 219 */ 220 void 221 doexechooks(struct proc *p) 222 { 223 hook_proc_run(&exechook_list, p); 224 } 225 226 static hook_list_t exithook_list = LIST_HEAD_INITIALIZER(exithook_list); 227 extern krwlock_t exec_lock; 228 229 void * 230 exithook_establish(void (*fn)(struct proc *, void *), void *arg) 231 { 232 void *rv; 233 234 rw_enter(&exec_lock, RW_WRITER); 235 rv = hook_establish(&exithook_list, __FPTRCAST(void (*)(void *), fn), 236 arg); 237 rw_exit(&exec_lock); 238 return rv; 239 } 240 241 void 242 exithook_disestablish(void *vhook) 243 { 244 245 rw_enter(&exec_lock, RW_WRITER); 246 hook_disestablish(&exithook_list, vhook); 247 rw_exit(&exec_lock); 248 } 249 250 /* 251 * Run exit hooks. 252 */ 253 void 254 doexithooks(struct proc *p) 255 { 256 hook_proc_run(&exithook_list, p); 257 } 258 259 static hook_list_t forkhook_list = LIST_HEAD_INITIALIZER(forkhook_list); 260 261 void * 262 forkhook_establish(void (*fn)(struct proc *, struct proc *)) 263 { 264 return hook_establish(&forkhook_list, __FPTRCAST(void (*)(void *), fn), 265 NULL); 266 } 267 268 void 269 forkhook_disestablish(void *vhook) 270 { 271 hook_disestablish(&forkhook_list, vhook); 272 } 273 274 /* 275 * Run fork hooks. 276 */ 277 void 278 doforkhooks(struct proc *p2, struct proc *p1) 279 { 280 struct hook_desc *hd; 281 282 LIST_FOREACH(hd, &forkhook_list, hk_list) { 283 __FPTRCAST(void (*)(struct proc *, struct proc *), *hd->hk_fn) 284 (p2, p1); 285 } 286 } 287 288 static hook_list_t critpollhook_list = LIST_HEAD_INITIALIZER(critpollhook_list); 289 290 void * 291 critpollhook_establish(void (*fn)(void *), void *arg) 292 { 293 return hook_establish(&critpollhook_list, fn, arg); 294 } 295 296 void 297 critpollhook_disestablish(void *vhook) 298 { 299 hook_disestablish(&critpollhook_list, vhook); 300 } 301 302 /* 303 * Run critical polling hooks. 304 */ 305 void 306 docritpollhooks(void) 307 { 308 struct hook_desc *hd; 309 310 LIST_FOREACH(hd, &critpollhook_list, hk_list) { 311 (*hd->hk_fn)(hd->hk_arg); 312 } 313 } 314 315 /* 316 * "Power hook" types, functions, and variables. 317 * The list of power hooks is kept ordered with the last registered hook 318 * first. 319 * When running the hooks on power down the hooks are called in reverse 320 * registration order, when powering up in registration order. 321 */ 322 struct powerhook_desc { 323 TAILQ_ENTRY(powerhook_desc) sfd_list; 324 void (*sfd_fn)(int, void *); 325 void *sfd_arg; 326 char sfd_name[16]; 327 }; 328 329 static TAILQ_HEAD(powerhook_head, powerhook_desc) powerhook_list = 330 TAILQ_HEAD_INITIALIZER(powerhook_list); 331 332 void * 333 powerhook_establish(const char *name, void (*fn)(int, void *), void *arg) 334 { 335 struct powerhook_desc *ndp; 336 337 ndp = (struct powerhook_desc *) 338 malloc(sizeof(*ndp), M_DEVBUF, M_NOWAIT); 339 if (ndp == NULL) 340 return (NULL); 341 342 ndp->sfd_fn = fn; 343 ndp->sfd_arg = arg; 344 strlcpy(ndp->sfd_name, name, sizeof(ndp->sfd_name)); 345 TAILQ_INSERT_HEAD(&powerhook_list, ndp, sfd_list); 346 347 aprint_error("%s: WARNING: powerhook_establish is deprecated\n", name); 348 return (ndp); 349 } 350 351 void 352 powerhook_disestablish(void *vhook) 353 { 354 #ifdef DIAGNOSTIC 355 struct powerhook_desc *dp; 356 357 TAILQ_FOREACH(dp, &powerhook_list, sfd_list) 358 if (dp == vhook) 359 goto found; 360 panic("powerhook_disestablish: hook %p not established", vhook); 361 found: 362 #endif 363 364 TAILQ_REMOVE(&powerhook_list, (struct powerhook_desc *)vhook, 365 sfd_list); 366 free(vhook, M_DEVBUF); 367 } 368 369 /* 370 * Run power hooks. 371 */ 372 void 373 dopowerhooks(int why) 374 { 375 struct powerhook_desc *dp; 376 const char *why_name; 377 static const char * pwr_names[] = {PWR_NAMES}; 378 why_name = why < __arraycount(pwr_names) ? pwr_names[why] : "???"; 379 380 if (why == PWR_RESUME || why == PWR_SOFTRESUME) { 381 TAILQ_FOREACH_REVERSE(dp, &powerhook_list, powerhook_head, 382 sfd_list) 383 { 384 if (powerhook_debug) 385 printf("dopowerhooks %s: %s (%p)\n", 386 why_name, dp->sfd_name, dp); 387 (*dp->sfd_fn)(why, dp->sfd_arg); 388 } 389 } else { 390 TAILQ_FOREACH(dp, &powerhook_list, sfd_list) { 391 if (powerhook_debug) 392 printf("dopowerhooks %s: %s (%p)\n", 393 why_name, dp->sfd_name, dp); 394 (*dp->sfd_fn)(why, dp->sfd_arg); 395 } 396 } 397 398 if (powerhook_debug) 399 printf("dopowerhooks: %s done\n", why_name); 400 } 401