diff --git a/lib/command.c b/lib/command.c index 56af946997..3ca5c5882b 100644 --- a/lib/command.c +++ b/lib/command.c @@ -2297,7 +2297,7 @@ DEFUN(script, 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); } diff --git a/lib/frrlua.c b/lib/frrlua.c index dd468a3def..bd1e5b00e5 100644 --- a/lib/frrlua.c +++ b/lib/frrlua.c @@ -26,7 +26,6 @@ #include "frrlua.h" #include "log.h" #include "buffer.h" -#include "frrscript.h" /* Lua stuff */ @@ -72,6 +71,17 @@ void lua_pushprefix(lua_State *L, const struct prefix *prefix) 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"); @@ -101,6 +111,47 @@ void lua_pushinterface(lua_State *L, const struct interface *ifp) 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"); @@ -115,6 +166,17 @@ void lua_pushinaddr(lua_State *L, const struct in_addr *addr) 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) { @@ -130,6 +192,17 @@ 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"); @@ -145,16 +218,53 @@ void lua_pushsockunion(lua_State *L, const union sockunion *su) 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. * diff --git a/lib/frrlua.h b/lib/frrlua.h index f9ab5509a6..a105bd069d 100644 --- a/lib/frrlua.h +++ b/lib/frrlua.h @@ -33,42 +33,104 @@ extern "C" { #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_pushintegerp(lua_State *L, const int *num); +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_tostringp(lua_State *L, int idx); /* * Retrieve an integer from table on the top of the stack. diff --git a/lib/frrscript.c b/lib/frrscript.c index 081ecd026f..c8ea946334 100644 --- a/lib/frrscript.c +++ b/lib/frrscript.c @@ -27,133 +27,171 @@ #include "frrlua.h" -/* Type encoders */ +/* Codecs */ -struct encoder { - char *typename; - encoder_func encoder; -}; +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}, + {}}; -struct hash *encoder_hash; +/* Type codecs */ -static unsigned int encoder_hash_key(const void *data) +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}; - if (hash_lookup(encoder_hash, &e)) { - zlog_backtrace(LOG_ERR); - assert(!"Type encoder double-registered."); - } + struct frrscript_codec *codec = hash_lookup(codec_hash, &c); - assert(hash_get(encoder_hash, &e, encoder_alloc)); + lua_getglobal(fs->L, result->name); + r = codec->decoder(fs->L, -1); + lua_pop(fs->L, 1); + + return r; } +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 codec double-registered."); + } + + 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 *)) @@ -216,17 +254,9 @@ void frrscript_unload(struct frrscript *fs) 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); } diff --git a/lib/frrscript.h b/lib/frrscript.h index 44067bf0b4..cbc0ca6c51 100644 --- a/lib/frrscript.h +++ b/lib/frrscript.h @@ -29,6 +29,13 @@ extern "C" { #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 */ @@ -38,6 +45,16 @@ struct frrscript { 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. @@ -51,64 +68,61 @@ struct frrscript *frrscript_load(const char *name, 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 script. * - * Call it like this: + * fs + * The script to call; this is obtained from frrscript_load(). * - * frrscript_call(fs, FRRSCRIPT_ARGS("cool_prefix", "prefix", p), - * FRRSCRIPT_RESULTS("result1", "result2")) + * env + * The script's environment. Specify this as an array of frrscript_env. + * + * Returns: + * 0 if the script ran successfully, nonzero otherwise. */ -#define frrscript_call(fs, ...) frrscript_lua_call((fs), __VA_ARGS__) +int frrscript_call(struct frrscript *fs, struct frrscript_env *env); + /* - * Macro that defines the arguments to a script. + * Get result from finished script. * - * 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. + * fs + * The script. This script must have been run already. * - * This macro itself should be used as the second argument to frrscript_call(). + * 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. + * + * Returns: + * The script result of the specified name and type, or NULL. */ -#define FRRSCRIPT_ARGS(...) PP_NARG(__VA_ARGS__), ##__VA_ARGS__ - -/* - * Macro that defines the results from a script. - * - * Similar to FRRSCRIPT_ARGS, except this defines the results from a script. - * - * 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. - * - * This macro itself should be used as the third argument to frrscript_call(). - * It may not be omitted. - */ -#define FRRSCRIPT_RESULTS(...) PP_NARG(__VA_ARGS__), ##__VA_ARGS__ +void *frrscript_get_result(struct frrscript *fs, + const struct frrscript_env *result); #ifdef __cplusplus }