diff options
author | Quentin Young <qlyoung@nvidia.com> | 2020-11-29 01:02:39 +0100 |
---|---|---|
committer | Quentin Young <qlyoung@nvidia.com> | 2020-12-02 00:37:14 +0100 |
commit | 5f98c815b6c75115963955a75d1dd2c047d589c9 (patch) | |
tree | 556d2f4e4e87f0cd463eb5bcabfeb4ed86a8fad8 /lib/frrscript.c | |
parent | lib: add Lua stack dumper (diff) | |
download | frr-5f98c815b6c75115963955a75d1dd2c047d589c9.tar.xz frr-5f98c815b6c75115963955a75d1dd2c047d589c9.zip |
lib: start adding generic scripting stuff
Rather than let Luaisms propagate from the start, this is some generic
wrapper stuff that defines some semantics for interacting with scripts
that aren't specific to the underlying language.
The concept I have in mind for FRR's idea of a script is:
- has a name
- has some inputs, which have types
- has some outputs, which have types
I don't want to even say they have to be files; maybe we can embed
scripts in frr.conf, for example. Similarly the types of inputs and
outputs are probably going to end up being some language-specific setup.
For now, we will stick to this simple model, but the plan is to add full
object support (ie calling back into C).
This shouldn't be misconstrued as prepping for multilingual scripting
support, which is a bad idea for the following reasons:
- Each language would require different FFI methods, and specifically
different object encoders; a lot of code
- Languages have different capabilities that would have to be brought to
parity with each other; a lot of work
- Languages have *vastly* different performance characteristics; bad
impressions, lots of issues we can't do anything about
- Each language would need a dedicated maintainer for the above reasons;
pragmatically difficult
- Supporting multiple languages fractures the community and limits the
audience with which a given script can be shared
The only pro for multilingual support would be ease of use for users not
familiar with Lua but familiar with one of the other supported
languages. This is not enough to outweigh the cons.
In order to get rich scripting capabilities, we need to be able to pass
representations of internal objects to the scripts. For example, a
script that performs some computation based on information about a peer
needs access to some equivalent of `struct peer` for the peer in
question. To transfer these objects from C-space into Lua-space we need
to encode them onto the Lua stack. This patch adds a mapping from
arbitrary type names to the functions that encode objects of that type.
For example, the function that encodes `struct peer` into a Lua table
could be registered with:
bgp_peer_encoder_func(struct frrscript *fs, struct peer *peer)
{
// encode peer to Lua table, push to stack in fs->scriptinfo->L
}
frrscript_register_type_encoder("peer", bgp_peer_encoder_func);
Later on when calling a script that wants a peer, the plan is to be able
to specify the type name like so:
frrscript_call(script, "peer", peer);
Using C-style types for the type names would have been nice, it might be
possible to do this with preprocessor magic or possibly python
preprocessing later on.
Signed-off-by: Quentin Young <qlyoung@nvidia.com>
mergeme no stdlib
Signed-off-by: Quentin Young <qlyoung@nvidia.com>
Diffstat (limited to 'lib/frrscript.c')
-rw-r--r-- | lib/frrscript.c | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/lib/frrscript.c b/lib/frrscript.c new file mode 100644 index 000000000..9b27ca745 --- /dev/null +++ b/lib/frrscript.c @@ -0,0 +1,133 @@ +/* Scripting foo + * Copyright (C) 2020 NVIDIA Corporation + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <zebra.h> + +#include "frrscript.h" +#include "memory.h" +#include "hash.h" +#include "log.h" + +#include "frrlua.h" + +/* Type encoders */ + +struct encoder { + char *typename; + int (*encoder)(struct lua_State *, const void *); +}; + +struct hash *encoder_hash; + +static unsigned int encoder_hash_key(const void *data) +{ + const struct encoder *e = data; + + return string_hash_make(e->typename); +} + +static bool encoder_hash_cmp(const void *d1, const void *d2) +{ + return !strcmp(d1, d2); +} + +static void *encoder_alloc(void *arg) +{ + struct encoder *tmp = arg; + + struct encoder *e = XCALLOC(MTYPE_TMP, sizeof(struct encoder)); + e->typename = XSTRDUP(MTYPE_TMP, tmp->typename); + + return e; +} + +#if 0 +static void encoder_free(struct encoder *e) +{ + XFREE(MTYPE_TMP, e->typename); + XFREE(MTYPE_TMP, e); +} +#endif + +/* Generic script APIs */ + +int frrscript_lua_call(struct frrscript *fs, ...) +{ + /* Process arguments according to argspec in fs */ + /* ... */ + + int ret = lua_pcall(fs->L, 0, 0, 0); + + /* Process stack result according to argspec in fs */ + /* ... */ + + /* LUA_OK is 0, so we can just return lua_pcall's result directly */ + return ret; +} + +void frrscript_register_type_encoder(const char *typename, + int (*encoder)(lua_State *L, void *)) +{ + struct encoder e = { + .typename = (char *) typename, + .encoder = NULL + }; + + if (hash_lookup(encoder_hash, &e)) { + zlog_backtrace(LOG_ERR); + assert(!"Type encoder double-registered."); + } + + hash_get(encoder_hash, &e, encoder_alloc); +} + + +struct frrscript *frrscript_load(const char *name, + int (*load_cb)(struct frrscript *)) +{ + struct frrscript *fs = XCALLOC(MTYPE_TMP, sizeof(struct frrscript)); + + fs->name = XSTRDUP(MTYPE_TMP, name); + fs->L = luaL_newstate(); + + char fname[MAXPATHLEN]; + snprintf(fname, sizeof(fname), FRRSCRIPT_PATH "/%s.lua", fs->name); + + if (luaL_loadfile(fs->L, fname) != LUA_OK) + goto fail; + + if (load_cb && (*load_cb)(fs) != 0) + goto fail; + + return fs; +fail: + frrscript_unload(fs); + return NULL; +} + +void frrscript_unload(struct frrscript *fs) +{ + XFREE(MTYPE_TMP, fs->name); + XFREE(MTYPE_TMP, fs); +} + +void frrscript_init() +{ + encoder_hash = hash_create(encoder_hash_key, encoder_hash_cmp, "Lua type encoders"); +} |