vty_out(vty, "Script '/etc/frr/scripts/%s.lua' not found\n",
argv[1]->arg);
} else {
- int ret = frrscript_call(fs, FRRSCRIPT_ARGS("cool", "prefix", &p), FRRSCRIPT_RESULTS());
+ int ret = frrscript_call(fs, NULL);
vty_out(vty, "Script result: %d\n", ret);
}
#include "frrlua.h"
#include "log.h"
#include "buffer.h"
-#include "frrscript.h"
/* Lua stuff */
lua_setfield(L, -2, "family");
}
+void *lua_toprefix(lua_State *L, int idx)
+{
+ struct prefix *p = XCALLOC(MTYPE_TMP, sizeof(struct prefix));
+
+ lua_getfield(L, idx, "network");
+ str2prefix(lua_tostring(L, -1), p);
+ lua_pop(L, 1);
+
+ return p;
+}
+
void lua_pushinterface(lua_State *L, const struct interface *ifp)
{
zlog_debug("frrlua: pushing interface table");
lua_setfield(L, -2, "linklayer_type");
}
+void *lua_tointerface(lua_State *L, int idx)
+{
+ struct interface *ifp = XCALLOC(MTYPE_TMP, sizeof(struct interface));
+
+ lua_getfield(L, idx, "name");
+ strlcpy(ifp->name, lua_tostring(L, -1), sizeof(ifp->name));
+ lua_pop(L, 1);
+ lua_getfield(L, idx, "ifindex");
+ ifp->ifindex = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+ lua_getfield(L, idx, "status");
+ ifp->status = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+ lua_getfield(L, idx, "flags");
+ ifp->flags = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+ lua_getfield(L, idx, "metric");
+ ifp->metric = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+ lua_getfield(L, idx, "speed");
+ ifp->speed = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+ lua_getfield(L, idx, "mtu");
+ ifp->mtu = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+ lua_getfield(L, idx, "mtu6");
+ ifp->mtu6 = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+ lua_getfield(L, idx, "bandwidth");
+ ifp->bandwidth = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+ lua_getfield(L, idx, "link_ifindex");
+ ifp->link_ifindex = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+ lua_getfield(L, idx, "linklayer_type");
+ ifp->ll_type = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+
+ return ifp;
+}
+
void lua_pushinaddr(lua_State *L, const struct in_addr *addr)
{
zlog_debug("frrlua: pushing inaddr table");
lua_setfield(L, -2, "string");
}
+void *lua_toinaddr(lua_State *L, int idx)
+{
+ struct in_addr *inaddr = XCALLOC(MTYPE_TMP, sizeof(struct in_addr));
+
+ lua_getfield(L, idx, "value");
+ inaddr->s_addr = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+
+ return inaddr;
+}
+
void lua_pushin6addr(lua_State *L, const struct in6_addr *addr)
{
lua_setfield(L, -2, "string");
}
+void *lua_toin6addr(lua_State *L, int idx)
+{
+ struct in6_addr *in6addr = XCALLOC(MTYPE_TMP, sizeof(struct in6_addr));
+
+ lua_getfield(L, idx, "string");
+ inet_pton(AF_INET6, lua_tostring(L, -1), in6addr);
+ lua_pop(L, 1);
+
+ return in6addr;
+}
+
void lua_pushsockunion(lua_State *L, const union sockunion *su)
{
zlog_debug("frrlua: pushing sockunion table");
lua_setfield(L, -2, "string");
}
+void *lua_tosockunion(lua_State *L, int idx)
+{
+ union sockunion *su = XCALLOC(MTYPE_TMP, sizeof(union sockunion));
+
+ lua_getfield(L, idx, "string");
+ str2sockunion(lua_tostring(L, -1), su);
+
+ return su;
+}
+
void lua_pushtimet(lua_State *L, const time_t *time)
{
lua_pushinteger(L, *time);
}
-void lua_pushintegerp(lua_State *L, const int *num)
+void *lua_totimet(lua_State *L, int idx)
+{
+ time_t *t = XCALLOC(MTYPE_TMP, sizeof(time_t));
+
+ *t = lua_tointeger(L, idx);
+
+ return t;
+}
+
+void lua_pushintegerp(lua_State *L, const long long *num)
{
lua_pushinteger(L, *num);
}
+void *lua_tointegerp(lua_State *L, int idx)
+{
+ int isnum;
+ long long *num = XCALLOC(MTYPE_TMP, sizeof(long long));
+
+ *num = lua_tonumberx(L, idx, &isnum);
+ assert(isnum);
+
+ return num;
+}
+
+void *lua_tostringp(lua_State *L, int idx)
+{
+ char *string = XSTRDUP(MTYPE_TMP, lua_tostring(L, idx));
+
+ return string;
+}
+
/*
* Logging.
*
#endif
/*
- * Pushes a new table containing relevant fields from a prefix structure.
+ * Converts a prefix to a Lua value and pushes it on the stack.
*/
void lua_pushprefix(lua_State *L, const struct prefix *prefix);
/*
- * Pushes a new table containing relevant fields from an interface structure.
+ * Converts the Lua value at idx to a prefix.
+ *
+ * Returns:
+ * struct prefix allocated with MTYPE_TMP
+ */
+void *lua_toprefix(lua_State *L, int idx);
+
+/*
+ * Converts an interface to a Lua value and pushes it on the stack.
*/
void lua_pushinterface(lua_State *L, const struct interface *ifp);
/*
- * Pushes a new table containing both numeric and string representations of an
- * in_addr to the stack.
+ * Converts the Lua value at idx to an interface.
+ *
+ * Returns:
+ * struct interface allocated with MTYPE_TMP. This interface is not hooked
+ * to anything, nor is it inserted in the global interface tree.
+ */
+void *lua_tointerface(lua_State *L, int idx);
+
+/*
+ * Converts an in_addr to a Lua value and pushes it on the stack.
*/
void lua_pushinaddr(lua_State *L, const struct in_addr *addr);
/*
- * Pushes a new table containing both numeric and string representations of an
- * in6_addr to the stack.
+ * Converts the Lua value at idx to an in_addr.
+ *
+ * Returns:
+ * struct in_addr allocated with MTYPE_TMP.
+ */
+void *lua_toinaddr(lua_State *L, int idx);
+
+/*
+ * Converts an in6_addr to a Lua value and pushes it on the stack.
*/
void lua_pushin6addr(lua_State *L, const struct in6_addr *addr);
/*
- * Pushes a time_t to the stack.
+ * Converts the Lua value at idx to an in6_addr.
+ *
+ * Returns:
+ * struct in6_addr allocated with MTYPE_TMP.
+ */
+void *lua_toin6addr(lua_State *L, int idx);
+
+/*
+ * Converts a time_t to a Lua value and pushes it on the stack.
*/
void lua_pushtimet(lua_State *L, const time_t *time);
/*
- * Pushes a table representing a sockunion to the stack.
+ * Converts the Lua value at idx to a time_t.
+ *
+ * Returns:
+ * time_t allocated with MTYPE_TMP.
+ */
+void *lua_totimet(lua_State *L, int idx);
+
+/*
+ * Converts a sockunion to a Lua value and pushes it on the stack.
*/
void lua_pushsockunion(lua_State *L, const union sockunion *su);
/*
- * Push integer. This just wraps lua_pushinteger(), but it takes a pointer, so
- * as to be compatible with the encoder_func signature.
+ * Converts the Lua value at idx to a sockunion.
+ *
+ * Returns:
+ * sockunion allocated with MTYPE_TMP.
+ */
+void *lua_tosockunion(lua_State *L, int idx);
+
+/*
+ * Converts an int to a Lua value and pushes it on the stack.
+ */
+void lua_pushintegerp(lua_State *L, const long long *num);
+
+/*
+ * Converts the Lua value at idx to an int.
+ *
+ * Returns:
+ * int allocated with MTYPE_TMP.
+ */
+void *lua_tointegerp(lua_State *L, int idx);
+
+/*
+ * Pop string.
+ *
+ * Sets *string to a copy of the string at the top of the stack. The copy is
+ * allocated with MTYPE_TMP and the caller is responsible for freeing it.
*/
-void lua_pushintegerp(lua_State *L, const int *num);
+void *lua_tostringp(lua_State *L, int idx);
/*
* Retrieve an integer from table on the top of the stack.
#include "frrlua.h"
-/* Type encoders */
-
-struct encoder {
- char *typename;
- encoder_func encoder;
-};
-
-struct hash *encoder_hash;
-
-static unsigned int encoder_hash_key(const void *data)
+/* Codecs */
+
+struct frrscript_codec frrscript_codecs_lib[] = {
+ {.typename = "integer",
+ .encoder = (encoder_func)lua_pushintegerp,
+ .decoder = lua_tointegerp},
+ {.typename = "string",
+ .encoder = (encoder_func)lua_pushstring,
+ .decoder = lua_tostringp},
+ {.typename = "prefix",
+ .encoder = (encoder_func)lua_pushprefix,
+ .decoder = lua_toprefix},
+ {.typename = "interface",
+ .encoder = (encoder_func)lua_pushinterface,
+ .decoder = lua_tointerface},
+ {.typename = "in_addr",
+ .encoder = (encoder_func)lua_pushinaddr,
+ .decoder = lua_toinaddr},
+ {.typename = "in6_addr",
+ .encoder = (encoder_func)lua_pushin6addr,
+ .decoder = lua_toin6addr},
+ {.typename = "sockunion",
+ .encoder = (encoder_func)lua_pushsockunion,
+ .decoder = lua_tosockunion},
+ {.typename = "time_t",
+ .encoder = (encoder_func)lua_pushtimet,
+ .decoder = lua_totimet},
+ {}};
+
+/* Type codecs */
+
+struct hash *codec_hash;
+
+static unsigned int codec_hash_key(const void *data)
{
- const struct encoder *e = data;
+ const struct frrscript_codec *c = data;
- return string_hash_make(e->typename);
+ return string_hash_make(c->typename);
}
-static bool encoder_hash_cmp(const void *d1, const void *d2)
+static bool codec_hash_cmp(const void *d1, const void *d2)
{
- const struct encoder *e1 = d1;
- const struct encoder *e2 = d2;
+ const struct frrscript_codec *e1 = d1;
+ const struct frrscript_codec *e2 = d2;
return strmatch(e1->typename, e2->typename);
}
-static void *encoder_alloc(void *arg)
+static void *codec_alloc(void *arg)
{
- struct encoder *tmp = arg;
+ struct frrscript_codec *tmp = arg;
- struct encoder *e = XCALLOC(MTYPE_TMP, sizeof(struct encoder));
+ struct frrscript_codec *e =
+ XCALLOC(MTYPE_TMP, sizeof(struct frrscript_codec));
e->typename = XSTRDUP(MTYPE_TMP, tmp->typename);
e->encoder = tmp->encoder;
+ e->decoder = tmp->decoder;
return e;
}
#if 0
-static void encoder_free(struct encoder *e)
+static void codec_free(struct codec *c)
{
- XFREE(MTYPE_TMP, e->typename);
- XFREE(MTYPE_TMP, e);
+ XFREE(MTYPE_TMP, c->typename);
+ XFREE(MTYPE_TMP, c);
}
#endif
/* Generic script APIs */
-int frrscript_lua_call(struct frrscript *fs, ...)
+int frrscript_call(struct frrscript *fs, struct frrscript_env *env)
{
- va_list vl;
- va_start(vl, fs);
-
- int nargs = va_arg(vl, int);
- assert(nargs % 3 == 0);
-
- zlog_debug("%s: Script '%s' called with # args: %d", __func__, fs->name,
- nargs);
-
- struct encoder e = {};
- void *arg;
+ struct frrscript_codec c = {};
+ const void *arg;
const char *bindname;
/* Encode script arguments */
- for (int i = 0; i < nargs; i += 3) {
- bindname = va_arg(vl, const char *);
- e.typename = va_arg(vl, char *);
- arg = va_arg(vl, void *);
+ for (int i = 0; env && env[i].val != NULL; i++) {
+ bindname = env[i].name;
+ c.typename = env[i].typename;
+ arg = env[i].val;
zlog_debug("Script argument | Bind name: %s | Type: %s",
- bindname, e.typename);
+ bindname, c.typename);
- struct encoder *enc = hash_lookup(encoder_hash, &e);
- assert(enc
+ struct frrscript_codec *codec = hash_lookup(codec_hash, &c);
+ assert(codec
&& "No encoder for type; rerun with debug logs to see more");
- enc->encoder(fs->L, arg);
+ codec->encoder(fs->L, arg);
lua_setglobal(fs->L, bindname);
}
- int nresults = va_arg(vl, int);
- zlog_debug("Expected script results: %d", nresults);
-
- int ret = lua_pcall(fs->L, 0, nresults, 0);
+ int ret = lua_pcall(fs->L, 0, 0, 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("Script '%s' runtime error: %s", fs->name,
+ lua_tostring(fs->L, -1));
break;
case LUA_ERRMEM:
- zlog_err("Script '%s' memory error: %s", fs->name, lua_tostring(fs->L, -1));
+ zlog_err("Script '%s' memory error: %s", fs->name,
+ lua_tostring(fs->L, -1));
break;
case LUA_ERRERR:
- zlog_err("Script '%s' error handler error: %s", fs->name, lua_tostring(fs->L, -1));
+ zlog_err("Script '%s' error handler error: %s", fs->name,
+ lua_tostring(fs->L, -1));
break;
case LUA_ERRGCMM:
- zlog_err("Script '%s' garbage collector error: %s", fs->name, lua_tostring(fs->L, -1));
+ zlog_err("Script '%s' garbage collector error: %s", fs->name,
+ lua_tostring(fs->L, -1));
break;
default:
- zlog_err("Script '%s' unknown error: %s", fs->name, lua_tostring(fs->L, -1));
+ zlog_err("Script '%s' unknown error: %s", fs->name,
+ lua_tostring(fs->L, -1));
break;
}
- if (ret != LUA_OK)
+ if (ret != LUA_OK) {
lua_pop(fs->L, 1);
-
- /* After script returns, decode results */
- for (int i = 0; i < nresults; i++) {
- const char *resultname = va_arg(vl, const char *);
- fprintf(stderr, "result: %s\n", resultname);
+ goto done;
}
+done:
/* LUA_OK is 0, so we can just return lua_pcall's result directly */
return ret;
}
-void frrscript_register_type_encoder(const char *typename, encoder_func encoder)
+void *frrscript_get_result(struct frrscript *fs,
+ const struct frrscript_env *result)
{
- struct encoder e = {.typename = (char *)typename, .encoder = encoder};
+ void *r;
+ struct frrscript_codec c = {.typename = result->typename};
+
+ struct frrscript_codec *codec = hash_lookup(codec_hash, &c);
+
+ lua_getglobal(fs->L, result->name);
+ r = codec->decoder(fs->L, -1);
+ lua_pop(fs->L, 1);
+
+ return r;
+}
- if (hash_lookup(encoder_hash, &e)) {
+void frrscript_register_type_codec(struct frrscript_codec *codec)
+{
+ struct frrscript_codec c = *codec;
+
+ zlog_debug("Registering codec for '%s'", codec->typename);
+
+ if (hash_lookup(codec_hash, &c)) {
zlog_backtrace(LOG_ERR);
- assert(!"Type encoder double-registered.");
+ assert(!"Type codec double-registered.");
}
- assert(hash_get(encoder_hash, &e, encoder_alloc));
+ assert(hash_get(codec_hash, &c, codec_alloc));
}
+void frrscript_register_type_codecs(struct frrscript_codec *codecs)
+{
+ for (int i = 0; codecs[i].typename != NULL; i++)
+ frrscript_register_type_codec(&codecs[i]);
+}
struct frrscript *frrscript_load(const char *name,
int (*load_cb)(struct frrscript *))
void frrscript_init()
{
- encoder_hash = hash_create(encoder_hash_key, encoder_hash_cmp,
- "Lua type encoders");
+ codec_hash = hash_create(codec_hash_key, codec_hash_cmp,
+ "Lua type encoders");
/* Register core library types */
- frrscript_register_type_encoder("integer", (encoder_func) lua_pushintegerp);
- frrscript_register_type_encoder("string", (encoder_func) lua_pushstring);
- frrscript_register_type_encoder("prefix", (encoder_func)lua_pushprefix);
- frrscript_register_type_encoder("interface",
- (encoder_func)lua_pushinterface);
- frrscript_register_type_encoder("sockunion", (encoder_func) lua_pushsockunion);
- frrscript_register_type_encoder("in_addr", (encoder_func) lua_pushinaddr);
- frrscript_register_type_encoder("in6_addr", (encoder_func) lua_pushin6addr);
- frrscript_register_type_encoder("time_t", (encoder_func) lua_pushtimet);
+ frrscript_register_type_codecs(frrscript_codecs_lib);
}
#define FRRSCRIPT_PATH "/etc/frr/scripts"
typedef void (*encoder_func)(lua_State *, const void *);
+typedef void *(*decoder_func)(lua_State *, int);
+
+struct frrscript_codec {
+ const char *typename;
+ encoder_func encoder;
+ decoder_func decoder;
+};
struct frrscript {
/* Script name */
struct lua_State *L;
};
+struct frrscript_env {
+ /* Value type */
+ const char *typename;
+
+ /* Binding name */
+ const char *name;
+
+ /* Value */
+ const void *val;
+};
/*
* Create new FRR script.
void frrscript_unload(struct frrscript *fs);
/*
- * Register a Lua encoder for a type.
+ * Register a Lua codec for a type.
*
* tname
* Name of type; e.g., "peer", "ospf_interface", etc. Chosen at will.
*
- * encoder
- * Function pointer to encoder function. Encoder function should push a Lua
+ * codec(s)
+ * Function pointer to codec struct. Encoder function should push a Lua
* table representing the passed argument - which will have the C type
- * associated with the chosen 'tname' to the provided stack.
+ * associated with the chosen 'tname' to the provided stack. The decoder
+ * function should pop a value from the top of the stack and return a heap
+ * chunk containing that value. Allocations should be made with MTYPE_TMP.
+ *
+ * If using the plural function variant, pass a NULL-terminated array.
*
*/
-void frrscript_register_type_encoder(const char *tname, encoder_func encoder);
+void frrscript_register_type_codec(struct frrscript_codec *codec);
+void frrscript_register_type_codecs(struct frrscript_codec *codecs);
/*
* Initialize scripting subsystem. Call this before anything else.
*/
void frrscript_init(void);
-/*
- * Forward decl for frrscript_lua_call
- */
-int frrscript_lua_call(struct frrscript *fs, ...);
/*
- * Call FRR script.
- *
- * Call it like this:
+ * Call script.
*
- * frrscript_call(fs, FRRSCRIPT_ARGS("cool_prefix", "prefix", p),
- * FRRSCRIPT_RESULTS("result1", "result2"))
- */
-#define frrscript_call(fs, ...) frrscript_lua_call((fs), __VA_ARGS__)
-
-/*
- * Macro that defines the arguments to a script.
+ * fs
+ * The script to call; this is obtained from frrscript_load().
*
- * For each argument you want to pass to a script, pass *three* arguments to
- * this function. The first should be name of the variable to bind the argument
- * to in the script's environment. The second should be the type, as registered
- * by frrscript_register_type_encoder(). The third should be the argument
- * itself.
+ * env
+ * The script's environment. Specify this as an array of frrscript_env.
*
- * This macro itself should be used as the second argument to frrscript_call().
+ * Returns:
+ * 0 if the script ran successfully, nonzero otherwise.
*/
-#define FRRSCRIPT_ARGS(...) PP_NARG(__VA_ARGS__), ##__VA_ARGS__
+int frrscript_call(struct frrscript *fs, struct frrscript_env *env);
+
/*
- * Macro that defines the results from a script.
+ * Get result from finished script.
*
- * Similar to FRRSCRIPT_ARGS, except this defines the results from a script.
+ * fs
+ * The script. This script must have been run already.
*
- * The first argument should be the name to bind the first result to and will
- * be used after the script finishes to get that particular result value.
+ * 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.
*
- * This macro itself should be used as the third argument to frrscript_call().
- * It may not be omitted.
+ * Returns:
+ * The script result of the specified name and type, or NULL.
*/
-#define FRRSCRIPT_RESULTS(...) PP_NARG(__VA_ARGS__), ##__VA_ARGS__
+void *frrscript_get_result(struct frrscript *fs,
+ const struct frrscript_env *result);
#ifdef __cplusplus
}