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