1 | /* $NetBSD: kern_hook.c,v 1.6 2013/11/22 21:04:11 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.6 2013/11/22 21:04:11 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 | ((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(device_t 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 | TAILQ_ENTRY(powerhook_desc) sfd_list; |
318 | void (*sfd_fn)(int, void *); |
319 | void *sfd_arg; |
320 | char sfd_name[16]; |
321 | }; |
322 | |
323 | static TAILQ_HEAD(powerhook_head, powerhook_desc) powerhook_list = |
324 | TAILQ_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 | TAILQ_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 | TAILQ_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 | TAILQ_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 | TAILQ_FOREACH_REVERSE(dp, &powerhook_list, powerhook_head, |
376 | sfd_list) |
377 | { |
378 | if (powerhook_debug) |
379 | printf("dopowerhooks %s: %s (%p)\n" , |
380 | why_name, dp->sfd_name, dp); |
381 | (*dp->sfd_fn)(why, dp->sfd_arg); |
382 | } |
383 | } else { |
384 | TAILQ_FOREACH(dp, &powerhook_list, sfd_list) { |
385 | if (powerhook_debug) |
386 | printf("dopowerhooks %s: %s (%p)\n" , |
387 | why_name, dp->sfd_name, dp); |
388 | (*dp->sfd_fn)(why, dp->sfd_arg); |
389 | } |
390 | } |
391 | |
392 | if (powerhook_debug) |
393 | printf("dopowerhooks: %s done\n" , why_name); |
394 | } |
395 | |