summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/command.c32
-rw-r--r--lib/frrlua.c30
-rw-r--r--lib/frrlua.h6
-rw-r--r--lib/frrscript.c205
-rw-r--r--lib/frrscript.h154
5 files changed, 312 insertions, 115 deletions
diff --git a/lib/command.c b/lib/command.c
index fe17c68a8b..422544b70b 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -2422,28 +2422,30 @@ DEFUN(find,
}
#if defined(DEV_BUILD) && defined(HAVE_SCRIPTING)
-DEFUN(script,
- script_cmd,
- "script SCRIPT",
- "Test command - execute a script\n"
- "Script name (same as filename in /etc/frr/scripts/\n")
+DEFUN(script, script_cmd, "script SCRIPT FUNCTION",
+ "Test command - execute a function in a script\n"
+ "Script name (same as filename in /etc/frr/scripts/)\n"
+ "Function name (in the script)\n")
{
struct prefix p;
(void)str2prefix("1.2.3.4/24", &p);
- struct frrscript *fs = frrscript_load(argv[1]->arg, NULL);
+ struct frrscript *fs = frrscript_new(argv[1]->arg);
- if (fs == NULL) {
- vty_out(vty, "Script '/etc/frr/scripts/%s.lua' not found\n",
- argv[1]->arg);
- } else {
- int ret = frrscript_call(fs, ("p", &p));
- char buf[40];
- prefix2str(&p, buf, sizeof(buf));
- vty_out(vty, "p: %s\n", buf);
- vty_out(vty, "Script result: %d\n", ret);
+ if (frrscript_load(fs, argv[2]->arg, NULL)) {
+ vty_out(vty,
+ "/etc/frr/scripts/%s.lua or function '%s' not found\n",
+ argv[1]->arg, argv[2]->arg);
}
+ int ret = frrscript_call(fs, argv[2]->arg, ("p", &p));
+ char buf[40];
+ prefix2str(&p, buf, sizeof(buf));
+ vty_out(vty, "p: %s\n", buf);
+ vty_out(vty, "Script result: %d\n", ret);
+
+ frrscript_delete(fs);
+
return CMD_SUCCESS;
}
#endif
diff --git a/lib/frrlua.c b/lib/frrlua.c
index e97e48121c..96d7269440 100644
--- a/lib/frrlua.c
+++ b/lib/frrlua.c
@@ -29,6 +29,8 @@
#include "log.h"
#include "buffer.h"
+DEFINE_MTYPE(LIB, SCRIPT_RES, "Scripting results");
+
/* Lua stuff */
/*
@@ -81,7 +83,7 @@ void lua_decode_prefix(lua_State *L, int idx, struct prefix *prefix)
void *lua_toprefix(lua_State *L, int idx)
{
- struct prefix *p = XCALLOC(MTYPE_TMP, sizeof(struct prefix));
+ struct prefix *p = XCALLOC(MTYPE_SCRIPT_RES, sizeof(struct prefix));
lua_decode_prefix(L, idx, p);
return p;
}
@@ -153,7 +155,8 @@ void lua_decode_interface(lua_State *L, int idx, struct interface *ifp)
}
void *lua_tointerface(lua_State *L, int idx)
{
- struct interface *ifp = XCALLOC(MTYPE_TMP, sizeof(struct interface));
+ struct interface *ifp =
+ XCALLOC(MTYPE_SCRIPT_RES, sizeof(struct interface));
lua_decode_interface(L, idx, ifp);
return ifp;
@@ -183,7 +186,8 @@ void lua_decode_inaddr(lua_State *L, int idx, struct in_addr *inaddr)
void *lua_toinaddr(lua_State *L, int idx)
{
- struct in_addr *inaddr = XCALLOC(MTYPE_TMP, sizeof(struct in_addr));
+ struct in_addr *inaddr =
+ XCALLOC(MTYPE_SCRIPT_RES, sizeof(struct in_addr));
lua_decode_inaddr(L, idx, inaddr);
return inaddr;
}
@@ -213,7 +217,8 @@ void lua_decode_in6addr(lua_State *L, int idx, struct in6_addr *in6addr)
void *lua_toin6addr(lua_State *L, int idx)
{
- struct in6_addr *in6addr = XCALLOC(MTYPE_TMP, sizeof(struct in6_addr));
+ struct in6_addr *in6addr =
+ XCALLOC(MTYPE_SCRIPT_RES, sizeof(struct in6_addr));
lua_decode_in6addr(L, idx, in6addr);
return in6addr;
}
@@ -243,7 +248,8 @@ void lua_decode_sockunion(lua_State *L, int idx, union sockunion *su)
void *lua_tosockunion(lua_State *L, int idx)
{
- union sockunion *su = XCALLOC(MTYPE_TMP, sizeof(union sockunion));
+ union sockunion *su =
+ XCALLOC(MTYPE_SCRIPT_RES, sizeof(union sockunion));
lua_decode_sockunion(L, idx, su);
return su;
@@ -262,7 +268,7 @@ void lua_decode_timet(lua_State *L, int idx, time_t *t)
void *lua_totimet(lua_State *L, int idx)
{
- time_t *t = XCALLOC(MTYPE_TMP, sizeof(time_t));
+ time_t *t = XCALLOC(MTYPE_SCRIPT_RES, sizeof(time_t));
lua_decode_timet(L, idx, t);
return t;
@@ -283,7 +289,7 @@ void lua_decode_integerp(lua_State *L, int idx, long long *num)
void *lua_tointegerp(lua_State *L, int idx)
{
- long long *num = XCALLOC(MTYPE_TMP, sizeof(long long));
+ long long *num = XCALLOC(MTYPE_SCRIPT_RES, sizeof(long long));
lua_decode_integerp(L, idx, num);
return num;
@@ -297,7 +303,7 @@ void lua_decode_stringp(lua_State *L, int idx, char *str)
void *lua_tostringp(lua_State *L, int idx)
{
- char *string = XSTRDUP(MTYPE_TMP, lua_tostring(L, idx));
+ char *string = XSTRDUP(MTYPE_SCRIPT_RES, lua_tostring(L, idx));
return string;
}
@@ -309,6 +315,14 @@ void lua_decode_noop(lua_State *L, int idx, const void *ptr)
{
}
+
+/*
+ * Noop decoder for int.
+ */
+void lua_decode_integer_noop(lua_State *L, int idx, int i)
+{
+}
+
/*
* Logging.
*
diff --git a/lib/frrlua.h b/lib/frrlua.h
index c4de82740c..3e16c82e22 100644
--- a/lib/frrlua.h
+++ b/lib/frrlua.h
@@ -34,6 +34,8 @@
extern "C" {
#endif
+DECLARE_MTYPE(SCRIPT_RES);
+
/*
* gcc-10 is complaining about the wrapper function
* not being compatible with lua_pushstring returning
@@ -162,10 +164,12 @@ void lua_decode_stringp(lua_State *L, int idx, char *str);
void *lua_tostringp(lua_State *L, int idx);
/*
- * No-op decocder
+ * No-op decoders
*/
void lua_decode_noop(lua_State *L, int idx, const void *ptr);
+void lua_decode_integer_noop(lua_State *L, int idx, int i);
+
/*
* Retrieve an integer from table on the top of the stack.
*
diff --git a/lib/frrscript.c b/lib/frrscript.c
index 1a9f3639dd..d00b84ccbb 100644
--- a/lib/frrscript.c
+++ b/lib/frrscript.c
@@ -102,67 +102,136 @@ static void codec_free(struct codec *c)
}
#endif
-/* Generic script APIs */
+/* Lua function hash utils */
-int _frrscript_call(struct frrscript *fs)
+unsigned int lua_function_hash_key(const void *data)
{
+ const struct lua_function_state *lfs = data;
- int ret = lua_pcall(fs->L, 0, 0, 0);
+ return string_hash_make(lfs->name);
+}
+
+bool lua_function_hash_cmp(const void *d1, const void *d2)
+{
+ const struct lua_function_state *lfs1 = d1;
+ const struct lua_function_state *lfs2 = d2;
+
+ return strmatch(lfs1->name, lfs2->name);
+}
+
+void *lua_function_alloc(void *arg)
+{
+ struct lua_function_state *tmp = arg;
+
+ struct lua_function_state *lfs =
+ XCALLOC(MTYPE_SCRIPT, sizeof(struct lua_function_state));
+ lfs->name = tmp->name;
+ lfs->L = tmp->L;
+ return lfs;
+}
+
+static void lua_function_free(struct hash_bucket *b, void *data)
+{
+ struct lua_function_state *lfs = (struct lua_function_state *)b->data;
+ lua_close(lfs->L);
+ XFREE(MTYPE_SCRIPT, lfs);
+}
+
+/* internal frrscript APIs */
+
+int _frrscript_call_lua(struct lua_function_state *lfs, int nargs)
+{
+
+ int ret;
+ ret = lua_pcall(lfs->L, nargs, 1, 0);
switch (ret) {
case LUA_OK:
break;
case LUA_ERRRUN:
- zlog_err("Script '%s' runtime error: %s", fs->name,
- lua_tostring(fs->L, -1));
+ zlog_err("Lua hook call '%s' : runtime error: %s", lfs->name,
+ lua_tostring(lfs->L, -1));
break;
case LUA_ERRMEM:
- zlog_err("Script '%s' memory error: %s", fs->name,
- lua_tostring(fs->L, -1));
+ zlog_err("Lua hook call '%s' : memory error: %s", lfs->name,
+ lua_tostring(lfs->L, -1));
break;
case LUA_ERRERR:
- zlog_err("Script '%s' error handler error: %s", fs->name,
- lua_tostring(fs->L, -1));
+ zlog_err("Lua hook call '%s' : error handler error: %s",
+ lfs->name, lua_tostring(lfs->L, -1));
break;
case LUA_ERRGCMM:
- zlog_err("Script '%s' garbage collector error: %s", fs->name,
- lua_tostring(fs->L, -1));
+ zlog_err("Lua hook call '%s' : garbage collector error: %s",
+ lfs->name, lua_tostring(lfs->L, -1));
break;
default:
- zlog_err("Script '%s' unknown error: %s", fs->name,
- lua_tostring(fs->L, -1));
+ zlog_err("Lua hook call '%s' : unknown error: %s", lfs->name,
+ lua_tostring(lfs->L, -1));
break;
}
if (ret != LUA_OK) {
- lua_pop(fs->L, 1);
+ lua_pop(lfs->L, 1);
goto done;
}
+ if (lua_gettop(lfs->L) != 1) {
+ zlog_err(
+ "Lua hook call '%s': Lua function should return only 1 result",
+ lfs->name);
+ ret = 1;
+ goto done;
+ }
+
+ if (lua_istable(lfs->L, 1) != 1) {
+ zlog_err(
+ "Lua hook call '%s': Lua function should return a Lua table",
+ lfs->name);
+ ret = 1;
+ }
+
done:
/* LUA_OK is 0, so we can just return lua_pcall's result directly */
return ret;
}
-void *frrscript_get_result(struct frrscript *fs,
- const struct frrscript_env *result)
+void *frrscript_get_result(struct frrscript *fs, const char *function_name,
+ const char *name,
+ void *(*lua_to)(lua_State *L, int idx))
{
- void *r;
- struct frrscript_codec c = {.typename = result->typename};
+ void *p;
+ struct lua_function_state *lfs;
+ struct lua_function_state lookup = {.name = function_name};
- struct frrscript_codec *codec = hash_lookup(codec_hash, &c);
- assert(codec && "No encoder for type");
+ lfs = hash_lookup(fs->lua_function_hash, &lookup);
- if (!codec->decoder) {
- zlog_err("No script decoder for type '%s'", result->typename);
+ if (lfs == NULL)
+ return NULL;
+
+ /* At this point, the Lua state should have only the returned table.
+ * We will then search the table for the key/value we're interested in.
+ * Then if the value is present (i.e. non-nil), call the lua_to*
+ * decoder.
+ */
+ assert(lua_gettop(lfs->L) == 1);
+ assert(lua_istable(lfs->L, -1) == 1);
+ lua_getfield(lfs->L, -1, name);
+ if (lua_isnil(lfs->L, -1)) {
+ lua_pop(lfs->L, 1);
+ zlog_warn(
+ "frrscript: '%s.lua': '%s': tried to decode '%s' as result but failed",
+ fs->name, function_name, name);
return NULL;
}
+ p = lua_to(lfs->L, 2);
- lua_getglobal(fs->L, result->name);
- r = codec->decoder(fs->L, -1);
- lua_pop(fs->L, 1);
+ /* At the end, the Lua state should be same as it was at the start
+ * i.e. containing soley the returned table.
+ */
+ assert(lua_gettop(lfs->L) == 1);
+ assert(lua_istable(lfs->L, -1) == 1);
- return r;
+ return p;
}
void frrscript_register_type_codec(struct frrscript_codec *codec)
@@ -183,61 +252,99 @@ void frrscript_register_type_codecs(struct frrscript_codec *codecs)
frrscript_register_type_codec(&codecs[i]);
}
-struct frrscript *frrscript_load(const char *name,
- int (*load_cb)(struct frrscript *))
+struct frrscript *frrscript_new(const char *name)
{
struct frrscript *fs = XCALLOC(MTYPE_SCRIPT, sizeof(struct frrscript));
fs->name = XSTRDUP(MTYPE_SCRIPT, name);
- fs->L = luaL_newstate();
- frrlua_export_logging(fs->L);
+ fs->lua_function_hash =
+ hash_create(lua_function_hash_key, lua_function_hash_cmp,
+ "Lua function state hash");
+ return fs;
+}
+
+int frrscript_load(struct frrscript *fs, const char *function_name,
+ int (*load_cb)(struct frrscript *))
+{
- char fname[MAXPATHLEN * 2];
- snprintf(fname, sizeof(fname), "%s/%s.lua", scriptdir, fs->name);
+ /* Set up the Lua script */
+ lua_State *L = luaL_newstate();
- int ret = luaL_loadfile(fs->L, fname);
+ frrlua_export_logging(L);
+
+ char script_name[MAXPATHLEN];
+
+ if (snprintf(script_name, sizeof(script_name), "%s/%s.lua", scriptdir,
+ fs->name)
+ >= (int)sizeof(script_name)) {
+ zlog_err("frrscript: path to script %s/%s.lua is too long",
+ scriptdir, fs->name);
+ goto fail;
+ }
+ int ret = luaL_dofile(L, script_name);
switch (ret) {
case LUA_OK:
break;
case LUA_ERRSYNTAX:
- zlog_err("Failed loading script '%s': syntax error: %s", fname,
- lua_tostring(fs->L, -1));
+ zlog_err(
+ "frrscript: failed loading script '%s.lua': syntax error: %s",
+ script_name, lua_tostring(L, -1));
break;
case LUA_ERRMEM:
- zlog_err("Failed loading script '%s': out-of-memory error: %s",
- fname, lua_tostring(fs->L, -1));
+ zlog_err(
+ "frrscript: failed loading script '%s.lua': out-of-memory error: %s",
+ script_name, lua_tostring(L, -1));
break;
case LUA_ERRGCMM:
zlog_err(
- "Failed loading script '%s': garbage collector error: %s",
- fname, lua_tostring(fs->L, -1));
+ "frrscript: failed loading script '%s.lua': garbage collector error: %s",
+ script_name, lua_tostring(L, -1));
break;
case LUA_ERRFILE:
- zlog_err("Failed loading script '%s': file read error: %s",
- fname, lua_tostring(fs->L, -1));
+ zlog_err(
+ "frrscript: failed loading script '%s.lua': file read error: %s",
+ script_name, lua_tostring(L, -1));
break;
default:
- zlog_err("Failed loading script '%s': unknown error: %s", fname,
- lua_tostring(fs->L, -1));
+ zlog_err(
+ "frrscript: failed loading script '%s.lua': unknown error: %s",
+ script_name, lua_tostring(L, -1));
break;
}
if (ret != LUA_OK)
goto fail;
- if (load_cb && (*load_cb)(fs) != 0)
+ /* Push the Lua function we want */
+ lua_getglobal(L, function_name);
+ if (lua_isfunction(L, lua_gettop(L)) == 0) {
+ zlog_err("frrscript: loaded script '%s.lua' but %s not found",
+ script_name, function_name);
goto fail;
+ }
- return fs;
+ if (load_cb && (*load_cb)(fs) != 0) {
+ zlog_err(
+ "frrscript: '%s.lua': %s: loaded but callback returned non-zero exit code",
+ script_name, function_name);
+ goto fail;
+ }
+
+ /* Add the Lua function state to frrscript */
+ struct lua_function_state key = {.name = function_name, .L = L};
+
+ hash_get(fs->lua_function_hash, &key, lua_function_alloc);
+
+ return 0;
fail:
- frrscript_unload(fs);
- return NULL;
+ lua_close(L);
+ return 1;
}
-void frrscript_unload(struct frrscript *fs)
+void frrscript_delete(struct frrscript *fs)
{
- lua_close(fs->L);
+ hash_iterate(fs->lua_function_hash, lua_function_free, NULL);
XFREE(MTYPE_SCRIPT, fs->name);
XFREE(MTYPE_SCRIPT, fs);
}
diff --git a/lib/frrscript.h b/lib/frrscript.h
index 8612c602f3..540676c099 100644
--- a/lib/frrscript.h
+++ b/lib/frrscript.h
@@ -25,7 +25,7 @@
#include <lua.h>
#include "frrlua.h"
-#include "../bgpd/bgp_script.h"
+#include "bgpd/bgp_script.h" // for peer and attr encoders/decoders
#ifdef __cplusplus
extern "C" {
@@ -40,14 +40,30 @@ struct frrscript_codec {
decoder_func decoder;
};
+struct lua_function_state {
+ const char *name;
+ lua_State *L;
+};
+
struct frrscript {
/* Script name */
char *name;
- /* Lua state */
- struct lua_State *L;
+ /* Hash of Lua function name to Lua function state */
+ struct hash *lua_function_hash;
};
+
+/*
+ * Hash related functions for lua_function_hash
+ */
+
+void *lua_function_alloc(void *arg);
+
+unsigned int lua_function_hash_key(const void *data);
+
+bool lua_function_hash_cmp(const void *d1, const void *d2);
+
struct frrscript_env {
/* Value type */
const char *typename;
@@ -60,15 +76,24 @@ struct frrscript_env {
};
/*
- * Create new FRR script.
+ * Create new struct frrscript for a Lua script.
+ * This will hold the states for the Lua functions in this script.
+ *
+ * scriptname
+ * Name of the Lua script file, without the .lua
*/
-struct frrscript *frrscript_load(const char *name,
- int (*load_cb)(struct frrscript *));
+struct frrscript *frrscript_new(const char *scriptname);
/*
- * Destroy FRR script.
+ * Load a function into frrscript, run callback if any
*/
-void frrscript_unload(struct frrscript *fs);
+int frrscript_load(struct frrscript *fs, const char *function_name,
+ int (*load_cb)(struct frrscript *));
+
+/*
+ * Delete Lua function states and frrscript
+ */
+void frrscript_delete(struct frrscript *fs);
/*
* Register a Lua codec for a type.
@@ -97,16 +122,31 @@ void frrscript_register_type_codecs(struct frrscript_codec *codecs);
*/
void frrscript_init(const char *scriptdir);
-#define ENCODE_ARGS(name, value) \
- do { \
- ENCODE_ARGS_WITH_STATE(L, value); \
- lua_setglobal(L, name); \
- } while (0)
+/*
+ * This macro is mapped to every (name, value) in frrscript_call,
+ * so this in turn maps them onto their encoders
+ */
+#define ENCODE_ARGS(name, value) ENCODE_ARGS_WITH_STATE(lfs->L, (value))
+/*
+ * This macro is also mapped to every (name, value) in frrscript_call, but
+ * not every value can be mapped to its decoder - only those that appear
+ * in the returned table will. To find out if they appear in the returned
+ * table, first pop the value and check if its nil. Only call the decoder
+ * if non-nil.
+ *
+ * At the end, the only thing left on the stack should be the
+ * returned table.
+ */
#define DECODE_ARGS(name, value) \
do { \
- lua_getglobal(L, name); \
- DECODE_ARGS_WITH_STATE(L, value); \
+ lua_getfield(lfs->L, 1, (name)); \
+ if (lua_isnil(lfs->L, 2)) { \
+ lua_pop(lfs->L, 1); \
+ } else { \
+ DECODE_ARGS_WITH_STATE(lfs->L, (value)); \
+ } \
+ assert(lua_gettop(lfs->L) == 1); \
} while (0)
/*
@@ -120,6 +160,7 @@ void frrscript_init(const char *scriptdir);
*/
#define ENCODE_ARGS_WITH_STATE(L, value) \
_Generic((value), \
+int : lua_pushinteger, \
long long * : lua_pushintegerp, \
struct prefix * : lua_pushprefix, \
struct interface * : lua_pushinterface, \
@@ -131,10 +172,11 @@ char * : lua_pushstring_wrapper, \
struct attr * : lua_pushattr, \
struct peer * : lua_pushpeer, \
const struct prefix * : lua_pushprefix \
-)(L, value)
+)((L), (value))
#define DECODE_ARGS_WITH_STATE(L, value) \
_Generic((value), \
+int : lua_decode_integer_noop, \
long long * : lua_decode_integerp, \
struct prefix * : lua_decode_prefix, \
struct interface * : lua_decode_interface, \
@@ -146,56 +188,84 @@ char * : lua_decode_stringp, \
struct attr * : lua_decode_attr, \
struct peer * : lua_decode_noop, \
const struct prefix * : lua_decode_noop \
-)(L, -1, value)
+)((L), -1, (value))
/*
- * Call script.
+ * Call Lua function state (abstraction for a single Lua function)
*
- * fs
- * The script to call; this is obtained from frrscript_load().
+ * lfs
+ * The Lua function to call; this should have been loaded in by
+ * frrscript_load(). nargs Number of arguments the function accepts
*
* Returns:
* 0 if the script ran successfully, nonzero otherwise.
*/
-int _frrscript_call(struct frrscript *fs);
+int _frrscript_call_lua(struct lua_function_state *lfs, int nargs);
/*
- * Wrapper for call script. Maps values passed in to their encoder
- * and decoder types.
+ * Wrapper for calling Lua function state. Maps values passed in to their
+ * encoder and decoder types.
*
* fs
- * The script to call; this is obtained from frrscript_load().
+ * The struct frrscript in which the Lua fuction was loaded into
+ * f
+ * Name of the Lua function.
*
* Returns:
* 0 if the script ran successfully, nonzero otherwise.
*/
-#define frrscript_call(fs, ...) \
- ({ \
- lua_State *L = fs->L; \
- MAP_LISTS(ENCODE_ARGS, ##__VA_ARGS__); \
- int ret = _frrscript_call(fs); \
- if (ret == 0) { \
- MAP_LISTS(DECODE_ARGS, ##__VA_ARGS__); \
- } \
- ret; \
+#define frrscript_call(fs, f, ...) \
+ ({ \
+ struct lua_function_state lookup = {.name = (f)}; \
+ struct lua_function_state *lfs; \
+ lfs = hash_lookup((fs)->lua_function_hash, &lookup); \
+ lfs == NULL ? ({ \
+ zlog_err( \
+ "frrscript: '%s.lua': '%s': tried to call this function but it was not loaded", \
+ (fs)->name, (f)); \
+ 1; \
+ }) \
+ : ({ \
+ MAP_LISTS(ENCODE_ARGS, ##__VA_ARGS__); \
+ _frrscript_call_lua( \
+ lfs, PP_NARG(__VA_ARGS__)); \
+ }) != 0 \
+ ? ({ \
+ zlog_err( \
+ "frrscript: '%s.lua': '%s': this function called but returned non-zero exit code. No variables modified.", \
+ (fs)->name, (f)); \
+ 1; \
+ }) \
+ : ({ \
+ MAP_LISTS(DECODE_ARGS, \
+ ##__VA_ARGS__); \
+ 0; \
+ }); \
})
/*
- * Get result from finished script.
+ * Get result from finished function
*
* fs
* The script. This script must have been run already.
- *
- * result
- * The result to extract from the script.
- * This reuses the frrscript_env type, but only the typename and name fields
- * need to be set. The value is returned directly.
+ * function_name
+ * Name of the Lua function.
+ * name
+ * Name of the result.
+ * This will be used as a string key to retrieve from the table that the
+ * Lua function returns.
+ * The name here should *not* appear in frrscript_call.
+ * lua_to
+ * Function pointer to a lua_to decoder function.
+ * This function should allocate and decode a value from the Lua state.
*
* Returns:
- * The script result of the specified name and type, or NULL.
+ * A pointer to the decoded value from the Lua state, or NULL if no such
+ * value.
*/
-void *frrscript_get_result(struct frrscript *fs,
- const struct frrscript_env *result);
+void *frrscript_get_result(struct frrscript *fs, const char *function_name,
+ const char *name,
+ void *(*lua_to)(lua_State *L, int idx));
#ifdef __cplusplus
}