]>
Commit | Line | Data |
---|---|---|
1 | // SPDX-License-Identifier: GPL-2.0-or-later | |
2 | /* Scripting foo | |
3 | * Copyright (C) 2020 NVIDIA Corporation | |
4 | * Quentin Young | |
5 | */ | |
6 | #ifndef __FRRSCRIPT_H__ | |
7 | #define __FRRSCRIPT_H__ | |
8 | ||
9 | #include <zebra.h> | |
10 | ||
11 | #ifdef HAVE_SCRIPTING | |
12 | ||
13 | #include <lua.h> | |
14 | #include <nexthop.h> | |
15 | #include <nexthop_group.h> | |
16 | #include "frrlua.h" | |
17 | #include "bgpd/bgp_script.h" // for peer and attr encoders/decoders | |
18 | ||
19 | #ifdef __cplusplus | |
20 | extern "C" { | |
21 | #endif | |
22 | ||
23 | /* Forward declarations */ | |
24 | struct zebra_dplane_ctx; | |
25 | extern void lua_pushzebra_dplane_ctx(lua_State *L, | |
26 | const struct zebra_dplane_ctx *ctx); | |
27 | extern void lua_decode_zebra_dplane_ctx(lua_State *L, int idx, | |
28 | struct zebra_dplane_ctx *ctx); | |
29 | ||
30 | /* | |
31 | * Script name hash | |
32 | */ | |
33 | PREDECL_HASH(frrscript_names); | |
34 | ||
35 | struct frrscript_names_entry { | |
36 | /* Name of a Lua hook call */ | |
37 | char function_name[MAXPATHLEN]; | |
38 | ||
39 | /* Lua script in which to look for it */ | |
40 | char script_name[MAXPATHLEN]; | |
41 | ||
42 | struct frrscript_names_item item; | |
43 | }; | |
44 | ||
45 | extern struct frrscript_names_head frrscript_names_hash; | |
46 | ||
47 | int frrscript_names_hash_cmp(const struct frrscript_names_entry *snhe1, | |
48 | const struct frrscript_names_entry *snhe2); | |
49 | uint32_t frrscript_names_hash_key(const struct frrscript_names_entry *snhe); | |
50 | ||
51 | DECLARE_HASH(frrscript_names, struct frrscript_names_entry, item, | |
52 | frrscript_names_hash_cmp, frrscript_names_hash_key); | |
53 | ||
54 | int frrscript_names_add_function_name(const char *function_name); | |
55 | void frrscript_names_destroy(void); | |
56 | int frrscript_names_set_script_name(const char *function_name, | |
57 | const char *script_name); | |
58 | char *frrscript_names_get_script_name(const char *function_name); | |
59 | ||
60 | typedef void (*encoder_func)(lua_State *, const void *); | |
61 | typedef void *(*decoder_func)(lua_State *, int); | |
62 | ||
63 | struct frrscript_codec { | |
64 | const char *typename; | |
65 | encoder_func encoder; | |
66 | decoder_func decoder; | |
67 | }; | |
68 | ||
69 | struct lua_function_state { | |
70 | const char *name; | |
71 | lua_State *L; | |
72 | }; | |
73 | ||
74 | struct frrscript { | |
75 | /* Script name */ | |
76 | char *name; | |
77 | ||
78 | /* Hash of Lua function name to Lua function state */ | |
79 | struct hash *lua_function_hash; | |
80 | }; | |
81 | ||
82 | ||
83 | /* | |
84 | * Hash related functions for lua_function_hash | |
85 | */ | |
86 | ||
87 | void *lua_function_alloc(void *arg); | |
88 | ||
89 | unsigned int lua_function_hash_key(const void *data); | |
90 | ||
91 | bool lua_function_hash_cmp(const void *d1, const void *d2); | |
92 | ||
93 | struct frrscript_env { | |
94 | /* Value type */ | |
95 | const char *typename; | |
96 | ||
97 | /* Binding name */ | |
98 | const char *name; | |
99 | ||
100 | /* Value */ | |
101 | const void *val; | |
102 | }; | |
103 | ||
104 | /* | |
105 | * Create new struct frrscript for a Lua script. | |
106 | * This will hold the states for the Lua functions in this script. | |
107 | * | |
108 | * scriptname | |
109 | * Name of the Lua script file, without the .lua | |
110 | */ | |
111 | struct frrscript *frrscript_new(const char *scriptname); | |
112 | ||
113 | /* | |
114 | * Load a function into frrscript, run callback if any | |
115 | */ | |
116 | int frrscript_load(struct frrscript *fs, const char *function_name, | |
117 | int (*load_cb)(struct frrscript *)); | |
118 | ||
119 | /* | |
120 | * Delete Lua function states and frrscript | |
121 | */ | |
122 | void frrscript_delete(struct frrscript *fs); | |
123 | ||
124 | /* | |
125 | * Register a Lua codec for a type. | |
126 | * | |
127 | * tname | |
128 | * Name of type; e.g., "peer", "ospf_interface", etc. Chosen at will. | |
129 | * | |
130 | * codec(s) | |
131 | * Function pointer to codec struct. Encoder function should push a Lua | |
132 | * table representing the passed argument - which will have the C type | |
133 | * associated with the chosen 'tname' to the provided stack. The decoder | |
134 | * function should pop a value from the top of the stack and return a heap | |
135 | * chunk containing that value. Allocations should be made with MTYPE_TMP. | |
136 | * | |
137 | * If using the plural function variant, pass a NULL-terminated array. | |
138 | * | |
139 | */ | |
140 | void frrscript_register_type_codec(struct frrscript_codec *codec); | |
141 | void frrscript_register_type_codecs(struct frrscript_codec *codecs); | |
142 | ||
143 | /* | |
144 | * Initialize scripting subsystem. Call this before anything else. | |
145 | * | |
146 | * scriptdir | |
147 | * Directory in which to look for scripts | |
148 | */ | |
149 | void frrscript_init(const char *scriptdir); | |
150 | ||
151 | /* | |
152 | * On shutdown clean up memory associated with the scripting subsystem | |
153 | */ | |
154 | void frrscript_fini(void); | |
155 | ||
156 | /* | |
157 | * This macro is mapped to every (name, value) in frrscript_call, | |
158 | * so this in turn maps them onto their encoders | |
159 | */ | |
160 | #define ENCODE_ARGS(name, value) ENCODE_ARGS_WITH_STATE(lfs->L, (value)) | |
161 | ||
162 | /* | |
163 | * This macro is also mapped to every (name, value) in frrscript_call, but | |
164 | * not every value can be mapped to its decoder - only those that appear | |
165 | * in the returned table will. To find out if they appear in the returned | |
166 | * table, first pop the value and check if its nil. Only call the decoder | |
167 | * if non-nil. | |
168 | * | |
169 | * At the end, the only thing left on the stack should be the | |
170 | * returned table. | |
171 | */ | |
172 | #define DECODE_ARGS(name, value) \ | |
173 | do { \ | |
174 | lua_getfield(lfs->L, 1, (name)); \ | |
175 | if (lua_isnil(lfs->L, 2)) { \ | |
176 | lua_pop(lfs->L, 1); \ | |
177 | } else { \ | |
178 | DECODE_ARGS_WITH_STATE(lfs->L, (value)); \ | |
179 | } \ | |
180 | assert(lua_gettop(lfs->L) == 1); \ | |
181 | } while (0) | |
182 | ||
183 | /* | |
184 | * Maps the type of value to its encoder/decoder. | |
185 | * Add new mappings here. | |
186 | * | |
187 | * L | |
188 | * Lua state | |
189 | * scriptdir | |
190 | * Directory in which to look for scripts | |
191 | */ | |
192 | #define ENCODE_ARGS_WITH_STATE(L, value) \ | |
193 | _Generic((value), \ | |
194 | int : lua_pushinteger, \ | |
195 | long long * : lua_pushintegerp, \ | |
196 | struct prefix * : lua_pushprefix, \ | |
197 | struct interface * : lua_pushinterface, \ | |
198 | struct in_addr * : lua_pushinaddr, \ | |
199 | struct in6_addr * : lua_pushin6addr, \ | |
200 | union sockunion * : lua_pushsockunion, \ | |
201 | time_t * : lua_pushtimet, \ | |
202 | char * : lua_pushstring_wrapper, \ | |
203 | struct attr * : lua_pushattr, \ | |
204 | struct peer * : lua_pushpeer, \ | |
205 | const struct prefix * : lua_pushprefix, \ | |
206 | const struct ipaddr * : lua_pushipaddr, \ | |
207 | const struct ethaddr * : lua_pushethaddr, \ | |
208 | const struct nexthop_group * : lua_pushnexthop_group, \ | |
209 | const struct nexthop * : lua_pushnexthop, \ | |
210 | struct zebra_dplane_ctx * : lua_pushzebra_dplane_ctx \ | |
211 | )((L), (value)) | |
212 | ||
213 | #define DECODE_ARGS_WITH_STATE(L, value) \ | |
214 | _Generic((value), \ | |
215 | int : lua_decode_integer_noop, \ | |
216 | long long * : lua_decode_integerp, \ | |
217 | struct prefix * : lua_decode_prefix, \ | |
218 | struct interface * : lua_decode_interface, \ | |
219 | struct in_addr * : lua_decode_inaddr, \ | |
220 | struct in6_addr * : lua_decode_in6addr, \ | |
221 | union sockunion * : lua_decode_sockunion, \ | |
222 | time_t * : lua_decode_timet, \ | |
223 | char * : lua_decode_stringp, \ | |
224 | struct attr * : lua_decode_attr, \ | |
225 | struct peer * : lua_decode_noop, \ | |
226 | const struct prefix * : lua_decode_noop, \ | |
227 | const struct ipaddr * : lua_decode_noop, \ | |
228 | const struct ethaddr * : lua_decode_noop, \ | |
229 | const struct nexthop_group * : lua_decode_noop, \ | |
230 | const struct nexthop * : lua_decode_noop, \ | |
231 | struct zebra_dplane_ctx * : lua_decode_noop \ | |
232 | )((L), -1, (value)) | |
233 | ||
234 | /* | |
235 | * Call Lua function state (abstraction for a single Lua function) | |
236 | * | |
237 | * lfs | |
238 | * The Lua function to call; this should have been loaded in by | |
239 | * frrscript_load(). nargs Number of arguments the function accepts | |
240 | * | |
241 | * Returns: | |
242 | * 0 if the script ran successfully, nonzero otherwise. | |
243 | */ | |
244 | int _frrscript_call_lua(struct lua_function_state *lfs, int nargs); | |
245 | ||
246 | /* | |
247 | * Wrapper for calling Lua function state. | |
248 | * | |
249 | * The Lua function name (f) to run should have already been checked by | |
250 | * frrscript_load. So this wrapper will: | |
251 | * 1) Find the Lua function state, which contains the Lua state | |
252 | * 2) Clear the Lua state (there may be leftovers items from previous call) | |
253 | * 3) Push the Lua function (f) | |
254 | * 4) Map frrscript_call arguments onto their encoder and decoders, push those | |
255 | * 5) Call _frrscript_call_lua (Lua execution takes place) | |
256 | * 6) Write back to frrscript_call arguments using their decoders | |
257 | * | |
258 | * This wrapper can be called multiple times (after one frrscript_load). | |
259 | * | |
260 | * fs | |
261 | * The struct frrscript in which the Lua fuction was loaded into | |
262 | * f | |
263 | * Name of the Lua function. | |
264 | * | |
265 | * Returns: | |
266 | * 0 if the script ran successfully, nonzero otherwise. | |
267 | */ | |
268 | #define frrscript_call(fs, f, ...) \ | |
269 | ({ \ | |
270 | struct lua_function_state lookup = {.name = (f)}; \ | |
271 | struct lua_function_state *lfs; \ | |
272 | lfs = hash_lookup((fs)->lua_function_hash, &lookup); \ | |
273 | lfs == NULL ? ({ \ | |
274 | zlog_err( \ | |
275 | "frrscript: '%s.lua': '%s': tried to call this function but it was not loaded", \ | |
276 | (fs)->name, (f)); \ | |
277 | 1; \ | |
278 | }) \ | |
279 | : ({ \ | |
280 | lua_settop(lfs->L, 0); \ | |
281 | lua_getglobal(lfs->L, f); \ | |
282 | MAP_LISTS(ENCODE_ARGS, ##__VA_ARGS__); \ | |
283 | _frrscript_call_lua( \ | |
284 | lfs, PP_NARG(__VA_ARGS__)); \ | |
285 | }) != 0 \ | |
286 | ? ({ \ | |
287 | zlog_err( \ | |
288 | "frrscript: '%s.lua': '%s': this function called but returned non-zero exit code. No variables modified.", \ | |
289 | (fs)->name, (f)); \ | |
290 | 1; \ | |
291 | }) \ | |
292 | : ({ \ | |
293 | MAP_LISTS(DECODE_ARGS, \ | |
294 | ##__VA_ARGS__); \ | |
295 | 0; \ | |
296 | }); \ | |
297 | }) | |
298 | ||
299 | /* | |
300 | * Get result from finished function | |
301 | * | |
302 | * fs | |
303 | * The script. This script must have been run already. | |
304 | * function_name | |
305 | * Name of the Lua function. | |
306 | * name | |
307 | * Name of the result. | |
308 | * This will be used as a string key to retrieve from the table that the | |
309 | * Lua function returns. | |
310 | * The name here should *not* appear in frrscript_call. | |
311 | * lua_to | |
312 | * Function pointer to a lua_to decoder function. | |
313 | * This function should allocate and decode a value from the Lua state. | |
314 | * | |
315 | * Returns: | |
316 | * A pointer to the decoded value from the Lua state, or NULL if no such | |
317 | * value. | |
318 | */ | |
319 | void *frrscript_get_result(struct frrscript *fs, const char *function_name, | |
320 | const char *name, | |
321 | void *(*lua_to)(lua_State *L, int idx)); | |
322 | ||
323 | #ifdef __cplusplus | |
324 | } | |
325 | #endif /* __cplusplus */ | |
326 | ||
327 | #endif /* HAVE_SCRIPTING */ | |
328 | ||
329 | #endif /* __FRRSCRIPT_H__ */ |