]>
Commit | Line | Data |
---|---|---|
a5b38c5b DL |
1 | /* |
2 | * Copyright (c) 2016 David Lamparter, for NetDEF, Inc. | |
3 | * | |
bbfeedb5 DL |
4 | * Permission to use, copy, modify, and distribute this software for any |
5 | * purpose with or without fee is hereby granted, provided that the above | |
6 | * copyright notice and this permission notice appear in all copies. | |
7 | * | |
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
a5b38c5b DL |
15 | */ |
16 | ||
17 | #ifndef _FRR_HOOK_H | |
18 | #define _FRR_HOOK_H | |
19 | ||
20 | #include <stdbool.h> | |
21 | ||
22 | #include "module.h" | |
23 | #include "memory.h" | |
24 | ||
5e244469 RW |
25 | #ifdef __cplusplus |
26 | extern "C" { | |
27 | #endif | |
28 | ||
a5b38c5b DL |
29 | /* type-safe subscribable hook points |
30 | * | |
31 | * where "type-safe" applies to the function pointers used for subscriptions | |
32 | * | |
33 | * overall usage: | |
34 | * - to create a hook: | |
35 | * | |
36 | * mydaemon.h: | |
37 | * #include "hook.h" | |
8451921b | 38 | * DECLARE_HOOK (some_update_event, (struct eventinfo *info), (info)); |
a5b38c5b DL |
39 | * |
40 | * mydaemon.c: | |
8451921b | 41 | * DEFINE_HOOK (some_update_event, (struct eventinfo *info), (info)); |
a5b38c5b DL |
42 | * ... |
43 | * hook_call (some_update_event, info) | |
44 | * | |
45 | * Note: the second and third macro args must be the hook function's | |
46 | * parameter list, with the same names for each parameter. The second | |
47 | * macro arg is with types (used for defining things), the third arg is | |
48 | * just the names (used for passing along parameters). | |
49 | * | |
50 | * Do not use parameter names starting with "hook", these can collide with | |
51 | * names used by the hook code itself. | |
52 | * | |
53 | * The return value is always "int" for now; hook_call will sum up the | |
54 | * return values from each registered user. Default is 0. | |
55 | * | |
56 | * There are no pre-defined semantics for the value, in most cases it is | |
57 | * ignored. For success/failure indication, 0 should be success, and | |
58 | * handlers should make sure to only return 0 or 1 (not -1 or other values). | |
59 | * | |
60 | * | |
61 | * - to use a hook / create a handler: | |
62 | * | |
63 | * #include "mydaemon.h" | |
64 | * int event_handler (struct eventinfo *info) { ... } | |
65 | * hook_register (some_update_event, event_handler); | |
66 | * | |
67 | * or, if you need an argument to be passed along (addonptr will be added | |
68 | * as first argument when calling the handler): | |
69 | * | |
70 | * #include "mydaemon.h" | |
71 | * int event_handler (void *addonptr, struct eventinfo *info) { ... } | |
72 | * hook_register_arg (some_update_event, event_handler, addonptr); | |
73 | * | |
74 | * (addonptr isn't typesafe, but that should be manageable.) | |
08c4c73b DL |
75 | * |
76 | * Hooks also support a "priority" value for ordering registered calls | |
77 | * relative to each other. The priority is a signed integer where lower | |
78 | * values are called earlier. There is also "Koohs", which is hooks with | |
79 | * reverse priority ordering (for cleanup/deinit hooks, so you can use the | |
80 | * same priority value). | |
81 | * | |
82 | * Recommended priority value ranges are: | |
83 | * | |
84 | * -999 ... 0 ... 999 - main executable / daemon, or library | |
85 | * -1999 ... -1000 - modules registering calls that should run before | |
86 | * the daemon's bits | |
87 | * 1000 ... 1999 - modules calls that should run after daemon's | |
88 | * | |
89 | * Note: the default value is 1000, based on the following 2 expectations: | |
90 | * - most hook_register() usage will be in loadable modules | |
91 | * - usage of hook_register() in the daemon itself may need relative ordering | |
92 | * to itself, making an explicit value the expected case | |
93 | * | |
94 | * The priority value is passed as extra argument on hook_register_prio() / | |
95 | * hook_register_arg_prio(). Whether a hook runs in reverse is determined | |
96 | * solely by the code defining / calling the hook. (DECLARE_KOOH is actually | |
97 | * the same thing as DECLARE_HOOK, it's just there to make it obvious.) | |
a5b38c5b DL |
98 | */ |
99 | ||
100 | /* TODO: | |
101 | * - hook_unregister_all_module() | |
102 | * - introspection / CLI / debug | |
103 | * - testcases ;) | |
104 | * | |
105 | * For loadable modules, the idea is that hooks could be automatically | |
106 | * unregistered when a module is unloaded. | |
107 | * | |
108 | * It's also possible to add a constructor (MTYPE style) to DEFINE_HOOK, | |
109 | * which would make it possible for the CLI to show all hooks and all | |
110 | * registered handlers. | |
111 | */ | |
112 | ||
113 | struct hookent { | |
114 | struct hookent *next; | |
d62a17ae | 115 | void *hookfn; /* actually a function pointer */ |
a5b38c5b | 116 | void *hookarg; |
8d0a2918 DL |
117 | bool has_arg : 1; |
118 | bool ent_on_heap : 1; | |
119 | bool ent_used : 1; | |
08c4c73b | 120 | int priority; |
a5b38c5b DL |
121 | struct frrmod_runtime *module; |
122 | const char *fnname; | |
123 | }; | |
124 | ||
125 | struct hook { | |
126 | const char *name; | |
127 | struct hookent *entries; | |
08c4c73b | 128 | bool reverse; |
a5b38c5b DL |
129 | }; |
130 | ||
08c4c73b DL |
131 | #define HOOK_DEFAULT_PRIORITY 1000 |
132 | ||
a5b38c5b DL |
133 | /* subscribe/add callback function to a hook |
134 | * | |
135 | * always use hook_register(), which uses the static inline helper from | |
136 | * DECLARE_HOOK in order to get type safety | |
137 | */ | |
8d0a2918 DL |
138 | extern void _hook_register(struct hook *hook, struct hookent *stackent, |
139 | void *funcptr, void *arg, bool has_arg, | |
140 | struct frrmod_runtime *module, | |
08c4c73b | 141 | const char *funcname, int priority); |
8d0a2918 DL |
142 | |
143 | /* most hook_register calls are not in a loop or similar and can use a | |
144 | * statically allocated "struct hookent" from the data segment | |
145 | */ | |
146 | #define _hook_reg_svar(hook, funcptr, arg, has_arg, module, funcname, prio) \ | |
147 | do { \ | |
e01a788c | 148 | static struct hookent stack_hookent = {}; \ |
8d0a2918 DL |
149 | _hook_register(hook, &stack_hookent, funcptr, arg, has_arg, \ |
150 | module, funcname, prio); \ | |
151 | } while (0) | |
152 | ||
d62a17ae | 153 | #define hook_register(hookname, func) \ |
8d0a2918 | 154 | _hook_reg_svar(&_hook_##hookname, _hook_typecheck_##hookname(func), \ |
08c4c73b | 155 | NULL, false, THIS_MODULE, #func, HOOK_DEFAULT_PRIORITY) |
d62a17ae | 156 | #define hook_register_arg(hookname, func, arg) \ |
8d0a2918 | 157 | _hook_reg_svar(&_hook_##hookname, \ |
d62a17ae | 158 | _hook_typecheck_arg_##hookname(func), arg, true, \ |
08c4c73b DL |
159 | THIS_MODULE, #func, HOOK_DEFAULT_PRIORITY) |
160 | #define hook_register_prio(hookname, prio, func) \ | |
8d0a2918 | 161 | _hook_reg_svar(&_hook_##hookname, _hook_typecheck_##hookname(func), \ |
08c4c73b DL |
162 | NULL, false, THIS_MODULE, #func, prio) |
163 | #define hook_register_arg_prio(hookname, prio, func, arg) \ | |
8d0a2918 | 164 | _hook_reg_svar(&_hook_##hookname, \ |
996c9314 LB |
165 | _hook_typecheck_arg_##hookname(func), arg, true, \ |
166 | THIS_MODULE, #func, prio) | |
a5b38c5b DL |
167 | |
168 | extern void _hook_unregister(struct hook *hook, void *funcptr, void *arg, | |
169 | bool has_arg); | |
d62a17ae | 170 | #define hook_unregister(hookname, func) \ |
171 | _hook_unregister(&_hook_##hookname, _hook_typecheck_##hookname(func), \ | |
172 | NULL, false) | |
173 | #define hook_unregister_arg(hookname, func, arg) \ | |
174 | _hook_unregister(&_hook_##hookname, \ | |
175 | _hook_typecheck_arg_##hookname(func), arg, true) | |
a5b38c5b DL |
176 | |
177 | /* invoke hooks | |
178 | * this is private (static) to the file that has the DEFINE_HOOK statement | |
179 | */ | |
d62a17ae | 180 | #define hook_call(hookname, ...) hook_call_##hookname(__VA_ARGS__) |
a5b38c5b DL |
181 | |
182 | /* helpers to add the void * arg */ | |
183 | #define HOOK_ADDDEF(...) (void *hookarg , ## __VA_ARGS__) | |
184 | #define HOOK_ADDARG(...) (hookarg , ## __VA_ARGS__) | |
185 | ||
14040553 DL |
186 | /* and another helper to convert () into (void) to get a proper prototype */ |
187 | #define _SKIP_10(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, ret, ...) ret | |
188 | #define _MAKE_VOID(...) _SKIP_10(, ##__VA_ARGS__, , , , , , , , , , void) | |
189 | ||
190 | #define HOOK_VOIDIFY(...) (_MAKE_VOID(__VA_ARGS__) __VA_ARGS__) | |
191 | ||
a5b38c5b | 192 | /* use in header file - declares the hook and its arguments |
8451921b | 193 | * usage: DECLARE_HOOK(my_hook, (int arg1, struct foo *arg2), (arg1, arg2)); |
a5b38c5b DL |
194 | * as above, "passlist" must use the same order and same names as "arglist" |
195 | * | |
214d8a60 | 196 | * theoretically passlist is not necessary, but let's keep things simple and |
a5b38c5b DL |
197 | * use exact same args on DECLARE and DEFINE. |
198 | */ | |
d62a17ae | 199 | #define DECLARE_HOOK(hookname, arglist, passlist) \ |
200 | extern struct hook _hook_##hookname; \ | |
14040553 DL |
201 | __attribute__((unused)) static inline void * \ |
202 | _hook_typecheck_##hookname(int(*funcptr) HOOK_VOIDIFY arglist) \ | |
d62a17ae | 203 | { \ |
204 | return (void *)funcptr; \ | |
205 | } \ | |
14040553 DL |
206 | __attribute__((unused)) static inline void \ |
207 | *_hook_typecheck_arg_##hookname(int(*funcptr) \ | |
208 | HOOK_ADDDEF arglist) \ | |
d62a17ae | 209 | { \ |
210 | return (void *)funcptr; \ | |
8451921b DL |
211 | } \ |
212 | MACRO_REQUIRE_SEMICOLON() /* end */ | |
213 | ||
996c9314 | 214 | #define DECLARE_KOOH(hookname, arglist, passlist) \ |
08c4c73b | 215 | DECLARE_HOOK(hookname, arglist, passlist) |
a5b38c5b DL |
216 | |
217 | /* use in source file - contains hook-related definitions. | |
218 | */ | |
08c4c73b | 219 | #define DEFINE_HOOK_INT(hookname, arglist, passlist, rev) \ |
d62a17ae | 220 | struct hook _hook_##hookname = { \ |
08c4c73b | 221 | .name = #hookname, .entries = NULL, .reverse = rev, \ |
d62a17ae | 222 | }; \ |
14040553 | 223 | static int hook_call_##hookname HOOK_VOIDIFY arglist \ |
d62a17ae | 224 | { \ |
225 | int hooksum = 0; \ | |
226 | struct hookent *he = _hook_##hookname.entries; \ | |
227 | void *hookarg; \ | |
228 | union { \ | |
229 | void *voidptr; \ | |
14040553 | 230 | int(*fptr) HOOK_VOIDIFY arglist; \ |
d62a17ae | 231 | int(*farg) HOOK_ADDDEF arglist; \ |
232 | } hookp; \ | |
233 | for (; he; he = he->next) { \ | |
234 | hookarg = he->hookarg; \ | |
235 | hookp.voidptr = he->hookfn; \ | |
236 | if (!he->has_arg) \ | |
237 | hooksum += hookp.fptr passlist; \ | |
238 | else \ | |
239 | hooksum += hookp.farg HOOK_ADDARG passlist; \ | |
240 | } \ | |
241 | return hooksum; \ | |
8451921b DL |
242 | } \ |
243 | MACRO_REQUIRE_SEMICOLON() /* end */ | |
a5b38c5b | 244 | |
996c9314 | 245 | #define DEFINE_HOOK(hookname, arglist, passlist) \ |
08c4c73b | 246 | DEFINE_HOOK_INT(hookname, arglist, passlist, false) |
996c9314 | 247 | #define DEFINE_KOOH(hookname, arglist, passlist) \ |
08c4c73b DL |
248 | DEFINE_HOOK_INT(hookname, arglist, passlist, true) |
249 | ||
5e244469 RW |
250 | #ifdef __cplusplus |
251 | } | |
252 | #endif | |
253 | ||
a5b38c5b | 254 | #endif /* _FRR_HOOK_H */ |