]> git.proxmox.com Git - mirror_frr.git/blame - lib/frrscript.c
Merge pull request #12798 from donaldsharp/rib_match_multicast
[mirror_frr.git] / lib / frrscript.c
CommitLineData
acddc0ed 1// SPDX-License-Identifier: GPL-2.0-or-later
5f98c815
QY
2/* Scripting foo
3 * Copyright (C) 2020 NVIDIA Corporation
4 * Quentin Young
5f98c815 5 */
5f98c815 6#include <zebra.h>
fa22080d
QY
7
8#ifdef HAVE_SCRIPTING
9
3b002f19 10#include <stdarg.h>
1a3a91e2 11#include <lua.h>
5f98c815
QY
12
13#include "frrscript.h"
1a3a91e2 14#include "frrlua.h"
5f98c815
QY
15#include "memory.h"
16#include "hash.h"
17#include "log.h"
18
fa22080d 19
1a3a91e2 20DEFINE_MTYPE_STATIC(LIB, SCRIPT, "Scripting");
5f98c815 21
a3ef7ae8
DL
22/*
23 * Script name hash utilities
24 */
25
26struct 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 */
32int 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
47void 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 */
62int 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 */
83char *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;
821a877f
DS
93
94 if (snhe->script_name[0] == '\0')
95 return NULL;
96
a3ef7ae8
DL
97 return snhe->script_name;
98}
99
100uint32_t frrscript_names_hash_key(const struct frrscript_names_entry *snhe)
101{
102 return string_hash_make(snhe->function_name);
103}
104
105int 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
f869ab17
QY
112/* Codecs */
113
114struct frrscript_codec frrscript_codecs_lib[] = {
115 {.typename = "integer",
116 .encoder = (encoder_func)lua_pushintegerp,
117 .decoder = lua_tointegerp},
118 {.typename = "string",
ea6caa1f 119 .encoder = (encoder_func)lua_pushstring_wrapper,
f869ab17
QY
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
143struct hash *codec_hash;
e4e0229a 144char scriptdir[MAXPATHLEN];
f869ab17
QY
145
146static unsigned int codec_hash_key(const void *data)
5f98c815 147{
f869ab17 148 const struct frrscript_codec *c = data;
5f98c815 149
f869ab17 150 return string_hash_make(c->typename);
5f98c815
QY
151}
152
f869ab17 153static bool codec_hash_cmp(const void *d1, const void *d2)
5f98c815 154{
f869ab17
QY
155 const struct frrscript_codec *e1 = d1;
156 const struct frrscript_codec *e2 = d2;
923431ef
QY
157
158 return strmatch(e1->typename, e2->typename);
5f98c815
QY
159}
160
f869ab17 161static void *codec_alloc(void *arg)
5f98c815 162{
f869ab17 163 struct frrscript_codec *tmp = arg;
5f98c815 164
f869ab17 165 struct frrscript_codec *e =
1a3a91e2
QY
166 XCALLOC(MTYPE_SCRIPT, sizeof(struct frrscript_codec));
167 e->typename = XSTRDUP(MTYPE_SCRIPT, tmp->typename);
923431ef 168 e->encoder = tmp->encoder;
f869ab17 169 e->decoder = tmp->decoder;
5f98c815
QY
170
171 return e;
172}
173
ca28a0f6 174static void codec_free(void *data)
5f98c815 175{
ca28a0f6
DS
176 struct frrscript_codec *c = data;
177 char *constworkaroundandihateit = (char *)c->typename;
178
179 XFREE(MTYPE_SCRIPT, constworkaroundandihateit);
180 XFREE(MTYPE_SCRIPT, c);
5f98c815 181}
5f98c815 182
fae19fa5 183/* Lua function hash utils */
105ba9af
DL
184
185unsigned 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
192bool 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
200void *lua_function_alloc(void *arg)
201{
202 struct lua_function_state *tmp = arg;
105ba9af
DL
203 struct lua_function_state *lfs =
204 XCALLOC(MTYPE_SCRIPT, sizeof(struct lua_function_state));
ca28a0f6 205
105ba9af
DL
206 lfs->name = tmp->name;
207 lfs->L = tmp->L;
208 return lfs;
209}
210
ca28a0f6 211static void lua_function_free(void *data)
105ba9af 212{
ca28a0f6
DS
213 struct lua_function_state *lfs = data;
214
105ba9af 215 lua_close(lfs->L);
fae19fa5 216 XFREE(MTYPE_SCRIPT, lfs);
105ba9af
DL
217}
218
b6640929 219/* internal frrscript APIs */
5f98c815 220
40d038d2 221int _frrscript_call_lua(struct lua_function_state *lfs, int nargs)
5f98c815 222{
3b002f19 223
40d038d2
DL
224 int ret;
225 ret = lua_pcall(lfs->L, nargs, 1, 0);
3b002f19
QY
226
227 switch (ret) {
646b0cce
QY
228 case LUA_OK:
229 break;
3b002f19 230 case LUA_ERRRUN:
40d038d2
DL
231 zlog_err("Lua hook call '%s' : runtime error: %s", lfs->name,
232 lua_tostring(lfs->L, -1));
3b002f19
QY
233 break;
234 case LUA_ERRMEM:
40d038d2
DL
235 zlog_err("Lua hook call '%s' : memory error: %s", lfs->name,
236 lua_tostring(lfs->L, -1));
3b002f19
QY
237 break;
238 case LUA_ERRERR:
40d038d2
DL
239 zlog_err("Lua hook call '%s' : error handler error: %s",
240 lfs->name, lua_tostring(lfs->L, -1));
3b002f19
QY
241 break;
242 case LUA_ERRGCMM:
40d038d2
DL
243 zlog_err("Lua hook call '%s' : garbage collector error: %s",
244 lfs->name, lua_tostring(lfs->L, -1));
3b002f19
QY
245 break;
246 default:
40d038d2
DL
247 zlog_err("Lua hook call '%s' : unknown error: %s", lfs->name,
248 lua_tostring(lfs->L, -1));
3b002f19
QY
249 break;
250 }
251
f869ab17 252 if (ret != LUA_OK) {
40d038d2 253 lua_pop(lfs->L, 1);
f869ab17 254 goto done;
3b002f19 255 }
5f98c815 256
40d038d2
DL
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
f869ab17 272done:
5f98c815
QY
273 /* LUA_OK is 0, so we can just return lua_pcall's result directly */
274 return ret;
275}
276
06947dde
DL
277void *frrscript_get_result(struct frrscript *fs, const char *function_name,
278 const char *name,
279 void *(*lua_to)(lua_State *L, int idx))
5f98c815 280{
06947dde
DL
281 void *p;
282 struct lua_function_state *lfs;
283 struct lua_function_state lookup = {.name = function_name};
f869ab17 284
06947dde 285 lfs = hash_lookup(fs->lua_function_hash, &lookup);
1a3a91e2 286
2ce634e2 287 if (lfs == NULL)
1a3a91e2 288 return NULL;
f869ab17 289
1da9c4bd
DL
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.
2ce634e2 294 */
1da9c4bd
DL
295 assert(lua_gettop(lfs->L) == 1);
296 assert(lua_istable(lfs->L, -1) == 1);
297 lua_getfield(lfs->L, -1, name);
06947dde
DL
298 if (lua_isnil(lfs->L, -1)) {
299 lua_pop(lfs->L, 1);
b6640929
DL
300 zlog_warn(
301 "frrscript: '%s.lua': '%s': tried to decode '%s' as result but failed",
302 fs->name, function_name, name);
06947dde
DL
303 return NULL;
304 }
305 p = lua_to(lfs->L, 2);
f869ab17 306
1da9c4bd 307 /* At the end, the Lua state should be same as it was at the start
214d8a60 308 * i.e. containing solely the returned table.
84c92002 309 */
1da9c4bd
DL
310 assert(lua_gettop(lfs->L) == 1);
311 assert(lua_istable(lfs->L, -1) == 1);
312
06947dde 313 return p;
f869ab17 314}
5f98c815 315
f869ab17
QY
316void frrscript_register_type_codec(struct frrscript_codec *codec)
317{
318 struct frrscript_codec c = *codec;
319
f869ab17 320 if (hash_lookup(codec_hash, &c)) {
5f98c815 321 zlog_backtrace(LOG_ERR);
f869ab17 322 assert(!"Type codec double-registered.");
5f98c815
QY
323 }
324
8e3aae66 325 (void)hash_get(codec_hash, &c, codec_alloc);
5f98c815
QY
326}
327
f869ab17
QY
328void 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}
5f98c815 333
f0cddf95 334struct frrscript *frrscript_new(const char *name)
5f98c815 335{
1a3a91e2 336 struct frrscript *fs = XCALLOC(MTYPE_SCRIPT, sizeof(struct frrscript));
5f98c815 337
1a3a91e2 338 fs->name = XSTRDUP(MTYPE_SCRIPT, name);
f0cddf95
DL
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
345int 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();
2ce634e2 351
f0cddf95 352 frrlua_export_logging(L);
5f98c815 353
1763ed26
DL
354 char script_name[MAXPATHLEN];
355
356 if (snprintf(script_name, sizeof(script_name), "%s/%s.lua", scriptdir,
357 fs->name)
9e3a277b 358 >= (int)sizeof(script_name)) {
1763ed26
DL
359 zlog_err("frrscript: path to script %s/%s.lua is too long",
360 scriptdir, fs->name);
361 goto fail;
362 }
646b0cce 363
868b41d9 364 if (luaL_dofile(L, script_name) != 0) {
93b2d38a 365 zlog_err("frrscript: failed loading script '%s': error: %s",
868b41d9 366 script_name, lua_tostring(L, -1));
5f98c815 367 goto fail;
868b41d9 368 }
5f98c815 369
2ed598ba 370 /* To check the Lua function, we get it from the global table */
f0cddf95 371 lua_getglobal(L, function_name);
4093300e 372 if (lua_isfunction(L, lua_gettop(L)) == 0) {
93b2d38a 373 zlog_err("frrscript: loaded script '%s' but %s not found",
4093300e 374 script_name, function_name);
f0cddf95 375 goto fail;
4093300e 376 }
2ed598ba
DL
377 /* Then pop the function (frrscript_call will push it when it needs it)
378 */
379 lua_pop(L, 1);
f0cddf95 380
4093300e
DL
381 if (load_cb && (*load_cb)(fs) != 0) {
382 zlog_err(
93b2d38a 383 "frrscript: '%s': %s: loaded but callback returned non-zero exit code",
4093300e 384 script_name, function_name);
5f98c815 385 goto fail;
4093300e 386 }
5f98c815 387
f0cddf95
DL
388 /* Add the Lua function state to frrscript */
389 struct lua_function_state key = {.name = function_name, .L = L};
390
8e3aae66 391 (void)hash_get(fs->lua_function_hash, &key, lua_function_alloc);
f0cddf95
DL
392
393 return 0;
5f98c815 394fail:
f0cddf95
DL
395 lua_close(L);
396 return 1;
5f98c815
QY
397}
398
64d457d7 399void frrscript_delete(struct frrscript *fs)
5f98c815 400{
ca28a0f6
DS
401 hash_clean(fs->lua_function_hash, lua_function_free);
402 hash_free(fs->lua_function_hash);
1a3a91e2
QY
403 XFREE(MTYPE_SCRIPT, fs->name);
404 XFREE(MTYPE_SCRIPT, fs);
5f98c815
QY
405}
406
e4e0229a 407void frrscript_init(const char *sd)
5f98c815 408{
f869ab17
QY
409 codec_hash = hash_create(codec_hash_key, codec_hash_cmp,
410 "Lua type encoders");
3b002f19 411
e4e0229a
QY
412 strlcpy(scriptdir, sd, sizeof(scriptdir));
413
3b002f19 414 /* Register core library types */
f869ab17 415 frrscript_register_type_codecs(frrscript_codecs_lib);
5f98c815 416}
fa22080d 417
ca28a0f6
DS
418void frrscript_fini(void)
419{
420 hash_clean(codec_hash, codec_free);
421 hash_free(codec_hash);
422
423 frrscript_names_destroy();
424}
fa22080d 425#endif /* HAVE_SCRIPTING */