]> git.proxmox.com Git - mirror_frr.git/blob - lib/frrscript.c
Merge pull request #12798 from donaldsharp/rib_match_multicast
[mirror_frr.git] / lib / frrscript.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* Scripting foo
3 * Copyright (C) 2020 NVIDIA Corporation
4 * Quentin Young
5 */
6 #include <zebra.h>
7
8 #ifdef HAVE_SCRIPTING
9
10 #include <stdarg.h>
11 #include <lua.h>
12
13 #include "frrscript.h"
14 #include "frrlua.h"
15 #include "memory.h"
16 #include "hash.h"
17 #include "log.h"
18
19
20 DEFINE_MTYPE_STATIC(LIB, SCRIPT, "Scripting");
21
22 /*
23 * Script name hash utilities
24 */
25
26 struct frrscript_names_head frrscript_names_hash;
27
28 /*
29 * Wrapper for frrscript_names_add
30 * Use this to register hook calls when a daemon starts up
31 */
32 int frrscript_names_add_function_name(const char *function_name)
33 {
34 struct frrscript_names_entry *insert =
35 XCALLOC(MTYPE_SCRIPT, sizeof(*insert));
36 strlcpy(insert->function_name, function_name,
37 sizeof(insert->function_name));
38
39 if (frrscript_names_add(&frrscript_names_hash, insert)) {
40 zlog_warn(
41 "Failed to add hook call function name to script_names");
42 return 1;
43 }
44 return 0;
45 }
46
47 void frrscript_names_destroy(void)
48 {
49 struct frrscript_names_entry *ne;
50
51 while ((ne = frrscript_names_pop(&frrscript_names_hash)))
52 XFREE(MTYPE_SCRIPT, ne);
53 }
54
55 /*
56 * Given a function_name, set its script_name. function_names and script_names
57 * are one-to-one. Each set will wipe the previous script_name.
58 * Return 0 if set was successful, else 1.
59 *
60 * script_name is the base name of the file, without .lua.
61 */
62 int frrscript_names_set_script_name(const char *function_name,
63 const char *script_name)
64 {
65 struct frrscript_names_entry lookup;
66
67 strlcpy(lookup.function_name, function_name,
68 sizeof(lookup.function_name));
69 struct frrscript_names_entry *snhe =
70 frrscript_names_find(&frrscript_names_hash, &lookup);
71 if (!snhe)
72 return 1;
73 strlcpy(snhe->script_name, script_name, sizeof(snhe->script_name));
74 return 0;
75 }
76
77 /*
78 * Given a function_name, get its script_name.
79 * Return NULL if function_name not found.
80 *
81 * script_name is the base name of the file, without .lua.
82 */
83 char *frrscript_names_get_script_name(const char *function_name)
84 {
85 struct frrscript_names_entry lookup;
86
87 strlcpy(lookup.function_name, function_name,
88 sizeof(lookup.function_name));
89 struct frrscript_names_entry *snhe =
90 frrscript_names_find(&frrscript_names_hash, &lookup);
91 if (!snhe)
92 return NULL;
93
94 if (snhe->script_name[0] == '\0')
95 return NULL;
96
97 return snhe->script_name;
98 }
99
100 uint32_t frrscript_names_hash_key(const struct frrscript_names_entry *snhe)
101 {
102 return string_hash_make(snhe->function_name);
103 }
104
105 int frrscript_names_hash_cmp(const struct frrscript_names_entry *snhe1,
106 const struct frrscript_names_entry *snhe2)
107 {
108 return strncmp(snhe1->function_name, snhe2->function_name,
109 sizeof(snhe1->function_name));
110 }
111
112 /* Codecs */
113
114 struct frrscript_codec frrscript_codecs_lib[] = {
115 {.typename = "integer",
116 .encoder = (encoder_func)lua_pushintegerp,
117 .decoder = lua_tointegerp},
118 {.typename = "string",
119 .encoder = (encoder_func)lua_pushstring_wrapper,
120 .decoder = lua_tostringp},
121 {.typename = "prefix",
122 .encoder = (encoder_func)lua_pushprefix,
123 .decoder = lua_toprefix},
124 {.typename = "interface",
125 .encoder = (encoder_func)lua_pushinterface,
126 .decoder = lua_tointerface},
127 {.typename = "in_addr",
128 .encoder = (encoder_func)lua_pushinaddr,
129 .decoder = lua_toinaddr},
130 {.typename = "in6_addr",
131 .encoder = (encoder_func)lua_pushin6addr,
132 .decoder = lua_toin6addr},
133 {.typename = "sockunion",
134 .encoder = (encoder_func)lua_pushsockunion,
135 .decoder = lua_tosockunion},
136 {.typename = "time_t",
137 .encoder = (encoder_func)lua_pushtimet,
138 .decoder = lua_totimet},
139 {}};
140
141 /* Type codecs */
142
143 struct hash *codec_hash;
144 char scriptdir[MAXPATHLEN];
145
146 static unsigned int codec_hash_key(const void *data)
147 {
148 const struct frrscript_codec *c = data;
149
150 return string_hash_make(c->typename);
151 }
152
153 static bool codec_hash_cmp(const void *d1, const void *d2)
154 {
155 const struct frrscript_codec *e1 = d1;
156 const struct frrscript_codec *e2 = d2;
157
158 return strmatch(e1->typename, e2->typename);
159 }
160
161 static void *codec_alloc(void *arg)
162 {
163 struct frrscript_codec *tmp = arg;
164
165 struct frrscript_codec *e =
166 XCALLOC(MTYPE_SCRIPT, sizeof(struct frrscript_codec));
167 e->typename = XSTRDUP(MTYPE_SCRIPT, tmp->typename);
168 e->encoder = tmp->encoder;
169 e->decoder = tmp->decoder;
170
171 return e;
172 }
173
174 static void codec_free(void *data)
175 {
176 struct frrscript_codec *c = data;
177 char *constworkaroundandihateit = (char *)c->typename;
178
179 XFREE(MTYPE_SCRIPT, constworkaroundandihateit);
180 XFREE(MTYPE_SCRIPT, c);
181 }
182
183 /* Lua function hash utils */
184
185 unsigned int lua_function_hash_key(const void *data)
186 {
187 const struct lua_function_state *lfs = data;
188
189 return string_hash_make(lfs->name);
190 }
191
192 bool lua_function_hash_cmp(const void *d1, const void *d2)
193 {
194 const struct lua_function_state *lfs1 = d1;
195 const struct lua_function_state *lfs2 = d2;
196
197 return strmatch(lfs1->name, lfs2->name);
198 }
199
200 void *lua_function_alloc(void *arg)
201 {
202 struct lua_function_state *tmp = arg;
203 struct lua_function_state *lfs =
204 XCALLOC(MTYPE_SCRIPT, sizeof(struct lua_function_state));
205
206 lfs->name = tmp->name;
207 lfs->L = tmp->L;
208 return lfs;
209 }
210
211 static void lua_function_free(void *data)
212 {
213 struct lua_function_state *lfs = data;
214
215 lua_close(lfs->L);
216 XFREE(MTYPE_SCRIPT, lfs);
217 }
218
219 /* internal frrscript APIs */
220
221 int _frrscript_call_lua(struct lua_function_state *lfs, int nargs)
222 {
223
224 int ret;
225 ret = lua_pcall(lfs->L, nargs, 1, 0);
226
227 switch (ret) {
228 case LUA_OK:
229 break;
230 case LUA_ERRRUN:
231 zlog_err("Lua hook call '%s' : runtime error: %s", lfs->name,
232 lua_tostring(lfs->L, -1));
233 break;
234 case LUA_ERRMEM:
235 zlog_err("Lua hook call '%s' : memory error: %s", lfs->name,
236 lua_tostring(lfs->L, -1));
237 break;
238 case LUA_ERRERR:
239 zlog_err("Lua hook call '%s' : error handler error: %s",
240 lfs->name, lua_tostring(lfs->L, -1));
241 break;
242 case LUA_ERRGCMM:
243 zlog_err("Lua hook call '%s' : garbage collector error: %s",
244 lfs->name, lua_tostring(lfs->L, -1));
245 break;
246 default:
247 zlog_err("Lua hook call '%s' : unknown error: %s", lfs->name,
248 lua_tostring(lfs->L, -1));
249 break;
250 }
251
252 if (ret != LUA_OK) {
253 lua_pop(lfs->L, 1);
254 goto done;
255 }
256
257 if (lua_gettop(lfs->L) != 1) {
258 zlog_err(
259 "Lua hook call '%s': Lua function should return only 1 result",
260 lfs->name);
261 ret = 1;
262 goto done;
263 }
264
265 if (lua_istable(lfs->L, 1) != 1) {
266 zlog_err(
267 "Lua hook call '%s': Lua function should return a Lua table",
268 lfs->name);
269 ret = 1;
270 }
271
272 done:
273 /* LUA_OK is 0, so we can just return lua_pcall's result directly */
274 return ret;
275 }
276
277 void *frrscript_get_result(struct frrscript *fs, const char *function_name,
278 const char *name,
279 void *(*lua_to)(lua_State *L, int idx))
280 {
281 void *p;
282 struct lua_function_state *lfs;
283 struct lua_function_state lookup = {.name = function_name};
284
285 lfs = hash_lookup(fs->lua_function_hash, &lookup);
286
287 if (lfs == NULL)
288 return NULL;
289
290 /* At this point, the Lua state should have only the returned table.
291 * We will then search the table for the key/value we're interested in.
292 * Then if the value is present (i.e. non-nil), call the lua_to*
293 * decoder.
294 */
295 assert(lua_gettop(lfs->L) == 1);
296 assert(lua_istable(lfs->L, -1) == 1);
297 lua_getfield(lfs->L, -1, name);
298 if (lua_isnil(lfs->L, -1)) {
299 lua_pop(lfs->L, 1);
300 zlog_warn(
301 "frrscript: '%s.lua': '%s': tried to decode '%s' as result but failed",
302 fs->name, function_name, name);
303 return NULL;
304 }
305 p = lua_to(lfs->L, 2);
306
307 /* At the end, the Lua state should be same as it was at the start
308 * i.e. containing solely the returned table.
309 */
310 assert(lua_gettop(lfs->L) == 1);
311 assert(lua_istable(lfs->L, -1) == 1);
312
313 return p;
314 }
315
316 void frrscript_register_type_codec(struct frrscript_codec *codec)
317 {
318 struct frrscript_codec c = *codec;
319
320 if (hash_lookup(codec_hash, &c)) {
321 zlog_backtrace(LOG_ERR);
322 assert(!"Type codec double-registered.");
323 }
324
325 (void)hash_get(codec_hash, &c, codec_alloc);
326 }
327
328 void frrscript_register_type_codecs(struct frrscript_codec *codecs)
329 {
330 for (int i = 0; codecs[i].typename != NULL; i++)
331 frrscript_register_type_codec(&codecs[i]);
332 }
333
334 struct frrscript *frrscript_new(const char *name)
335 {
336 struct frrscript *fs = XCALLOC(MTYPE_SCRIPT, sizeof(struct frrscript));
337
338 fs->name = XSTRDUP(MTYPE_SCRIPT, name);
339 fs->lua_function_hash =
340 hash_create(lua_function_hash_key, lua_function_hash_cmp,
341 "Lua function state hash");
342 return fs;
343 }
344
345 int frrscript_load(struct frrscript *fs, const char *function_name,
346 int (*load_cb)(struct frrscript *))
347 {
348
349 /* Set up the Lua script */
350 lua_State *L = luaL_newstate();
351
352 frrlua_export_logging(L);
353
354 char script_name[MAXPATHLEN];
355
356 if (snprintf(script_name, sizeof(script_name), "%s/%s.lua", scriptdir,
357 fs->name)
358 >= (int)sizeof(script_name)) {
359 zlog_err("frrscript: path to script %s/%s.lua is too long",
360 scriptdir, fs->name);
361 goto fail;
362 }
363
364 if (luaL_dofile(L, script_name) != 0) {
365 zlog_err("frrscript: failed loading script '%s': error: %s",
366 script_name, lua_tostring(L, -1));
367 goto fail;
368 }
369
370 /* To check the Lua function, we get it from the global table */
371 lua_getglobal(L, function_name);
372 if (lua_isfunction(L, lua_gettop(L)) == 0) {
373 zlog_err("frrscript: loaded script '%s' but %s not found",
374 script_name, function_name);
375 goto fail;
376 }
377 /* Then pop the function (frrscript_call will push it when it needs it)
378 */
379 lua_pop(L, 1);
380
381 if (load_cb && (*load_cb)(fs) != 0) {
382 zlog_err(
383 "frrscript: '%s': %s: loaded but callback returned non-zero exit code",
384 script_name, function_name);
385 goto fail;
386 }
387
388 /* Add the Lua function state to frrscript */
389 struct lua_function_state key = {.name = function_name, .L = L};
390
391 (void)hash_get(fs->lua_function_hash, &key, lua_function_alloc);
392
393 return 0;
394 fail:
395 lua_close(L);
396 return 1;
397 }
398
399 void frrscript_delete(struct frrscript *fs)
400 {
401 hash_clean(fs->lua_function_hash, lua_function_free);
402 hash_free(fs->lua_function_hash);
403 XFREE(MTYPE_SCRIPT, fs->name);
404 XFREE(MTYPE_SCRIPT, fs);
405 }
406
407 void frrscript_init(const char *sd)
408 {
409 codec_hash = hash_create(codec_hash_key, codec_hash_cmp,
410 "Lua type encoders");
411
412 strlcpy(scriptdir, sd, sizeof(scriptdir));
413
414 /* Register core library types */
415 frrscript_register_type_codecs(frrscript_codecs_lib);
416 }
417
418 void frrscript_fini(void)
419 {
420 hash_clean(codec_hash, codec_free);
421 hash_free(codec_hash);
422
423 frrscript_names_destroy();
424 }
425 #endif /* HAVE_SCRIPTING */