]> git.proxmox.com Git - mirror_frr.git/blobdiff - lib/frrscript.h
Merge pull request #9083 from mobash-rasool/pim-upst-3
[mirror_frr.git] / lib / frrscript.h
index f4057f531bd4b6b73d57afac34bb886cfc59d817..4db3e6f1b2260907a001196ee6877d4546df9d9c 100644 (file)
 #ifdef HAVE_SCRIPTING
 
 #include <lua.h>
+#include <nexthop.h>
+#include <nexthop_group.h>
 #include "frrlua.h"
+#include "bgpd/bgp_script.h" // for peer and attr encoders/decoders
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+/* Forward declarations */
+extern struct zebra_dplane_ctx ctx;
+extern void lua_pushzebra_dplane_ctx(lua_State *L,
+                                    const struct zebra_dplane_ctx *ctx);
+extern void lua_decode_zebra_dplane_ctx(lua_State *L, int idx,
+                                       struct zebra_dplane_ctx *ctx);
+
+/*
+ * Script name hash
+ */
+PREDECL_HASH(frrscript_names);
+
+struct frrscript_names_entry {
+       /* Name of a Lua hook call */
+       char function_name[MAXPATHLEN];
+
+       /* Lua script in which to look for it */
+       char script_name[MAXPATHLEN];
+
+       struct frrscript_names_item item;
+};
+
+extern struct frrscript_names_head frrscript_names_hash;
+
+int frrscript_names_hash_cmp(const struct frrscript_names_entry *snhe1,
+                            const struct frrscript_names_entry *snhe2);
+uint32_t frrscript_names_hash_key(const struct frrscript_names_entry *snhe);
+
+DECLARE_HASH(frrscript_names, struct frrscript_names_entry, item,
+            frrscript_names_hash_cmp, frrscript_names_hash_key);
+
+int frrscript_names_add_function_name(const char *function_name);
+void frrscript_names_destroy(void);
+int frrscript_names_set_script_name(const char *function_name,
+                                   const char *script_name);
+char *frrscript_names_get_script_name(const char *function_name);
+
 typedef void (*encoder_func)(lua_State *, const void *);
 typedef void *(*decoder_func)(lua_State *, int);
 
@@ -39,14 +79,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;
@@ -59,15 +115,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.
@@ -96,38 +161,172 @@ void frrscript_register_type_codecs(struct frrscript_codec *codecs);
  */
 void frrscript_init(const char *scriptdir);
 
+/*
+ * 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))
 
 /*
- * Call script.
+ * 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.
  *
- * fs
- *    The script to call; this is obtained from frrscript_load().
+ * At the end, the only thing left on the stack should be the
+ * returned table.
+ */
+#define DECODE_ARGS(name, value)                                               \
+       do {                                                                   \
+               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)
+
+/*
+ * Maps the type of value to its encoder/decoder.
+ * Add new mappings here.
+ *
+ * L
+ *    Lua state
+ * scriptdir
+ *    Directory in which to look for scripts
+ */
+#define ENCODE_ARGS_WITH_STATE(L, value)                                       \
+       _Generic((value), \
+int : lua_pushinteger,                                          \
+long long * : lua_pushintegerp,                                 \
+struct prefix * : lua_pushprefix,                               \
+struct interface * : lua_pushinterface,                         \
+struct in_addr * : lua_pushinaddr,                              \
+struct in6_addr * : lua_pushin6addr,                            \
+union sockunion * : lua_pushsockunion,                          \
+time_t * : lua_pushtimet,                                       \
+char * : lua_pushstring_wrapper,                                \
+struct attr * : lua_pushattr,                                   \
+struct peer * : lua_pushpeer,                                   \
+const struct prefix * : lua_pushprefix,                         \
+const struct ipaddr * : lua_pushipaddr,                         \
+const struct ethaddr * : lua_pushethaddr,                       \
+const struct nexthop_group * : lua_pushnexthop_group,           \
+const struct nexthop * : lua_pushnexthop,                       \
+struct zebra_dplane_ctx * : lua_pushzebra_dplane_ctx            \
+)((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,                      \
+struct in_addr * : lua_decode_inaddr,                           \
+struct in6_addr * : lua_decode_in6addr,                         \
+union sockunion * : lua_decode_sockunion,                       \
+time_t * : lua_decode_timet,                                    \
+char * : lua_decode_stringp,                                    \
+struct attr * : lua_decode_attr,                                \
+struct peer * : lua_decode_noop,                                \
+const struct prefix * : lua_decode_noop,                        \
+const struct ipaddr * : lua_decode_noop,                        \
+const struct ethaddr * : lua_decode_noop,                       \
+const struct nexthop_group * : lua_decode_noop,                 \
+const struct nexthop * : lua_decode_noop,                       \
+struct zebra_dplane_ctx * : lua_decode_noop                     \
+)((L), -1, (value))
+
+/*
+ * Call Lua function state (abstraction for a single Lua function)
  *
- * env
- *    The script's environment. Specify this as an array of frrscript_env.
+ * 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, struct frrscript_env *env);
+int _frrscript_call_lua(struct lua_function_state *lfs, int nargs);
 
+/*
+ * Wrapper for calling Lua function state.
+ *
+ * The Lua function name (f) to run should have already been checked by
+ * frrscript_load. So this wrapper will:
+ * 1) Find the Lua function state, which contains the Lua state
+ * 2) Clear the Lua state (there may be leftovers items from previous call)
+ * 3) Push the Lua function (f)
+ * 4) Map frrscript_call arguments onto their encoder and decoders, push those
+ * 5) Call _frrscript_call_lua (Lua execution takes place)
+ * 6) Write back to frrscript_call arguments using their decoders
+ *
+ * This wrapper can be called multiple times (after one frrscript_load).
+ *
+ * fs
+ *    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, 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;                                                                                                                                         \
+               })                                                                                                                                                 \
+                           : ({                                                                                                                                   \
+                                     lua_settop(lfs->L, 0);                                                                                                       \
+                                     lua_getglobal(lfs->L, f);                                                                                                    \
+                                     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
 }