]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
sysrepo-lua: migrated module code from sysrepo-module-lua branch
authorVasek Sraier <git@vakabus.cz>
Sun, 26 Jan 2020 14:48:08 +0000 (15:48 +0100)
committerAleš <ales.mrazek@nic.cz>
Mon, 20 Jul 2020 14:54:40 +0000 (16:54 +0200)
Because this is not a merge commit, line history was lost. But
it does not matter that much, because pretty much all the commited
code was written by me and it's mainly a proof of concept. Nothing
important should have been lost.

meson.build
modules/sysrepo-lua/cbindings/sysrepo_clib.c [new file with mode: 0644]
modules/sysrepo-lua/cbindings/sysrepo_clib.h [new file with mode: 0644]
modules/sysrepo-lua/common [new symlink]
modules/sysrepo-lua/ffi.lua [new file with mode: 0644]
modules/sysrepo-lua/init.lua [new file with mode: 0644]
modules/sysrepo-lua/meson.build

index 948880ee1c1fe18f2e0026701fea92e7fa42dc04..a3c2f695097429a8ca6a743860c0f8ac5ffc930a 100644 (file)
@@ -114,7 +114,7 @@ message('---------------------------')
 
 ### Sysrepo
 message('--- sysrepo dependencies ---')
-libsysrepo = dependency('sysrepo', version: '>=1', required: false)
+libsysrepo = dependency('libsysrepo', version: '>=1', required: false)
 libyang = dependency('libyang', version: '>=1', required: false)
 sysrepo = get_option('sysrepo')
 if (sysrepo == 'auto' or sysrepo == 'enabled') and libsysrepo.found() and libyang.found()
diff --git a/modules/sysrepo-lua/cbindings/sysrepo_clib.c b/modules/sysrepo-lua/cbindings/sysrepo_clib.c
new file mode 100644 (file)
index 0000000..2220130
--- /dev/null
@@ -0,0 +1,205 @@
+/*  Copyright (C) 2020 CZ.NIC, z.s.p.o. <knot-resolver@labs.nic.cz>
+
+    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 3 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.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "sysrepo_clib.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sysrepo.h>
+#include <uv.h>
+
+#include "lib/module.h"
+#include "common/sysrepo_conf.h"
+
+EXPORT_STRDEF_TO_LUA_IMPL(YM_COMMON)
+EXPORT_STRDEF_TO_LUA_IMPL(XPATH_BASE)
+
+struct el_subscription_ctx {
+       sr_conn_ctx_t *connection;
+       sr_session_ctx_t *session;
+       sr_subscription_ctx_t *subscription;
+       el_subsription_cb callback;
+       uv_poll_t uv_handle;
+};
+
+/** Callback to Lua used for applying configuration  */
+static set_leaf_conf_t apply_conf_f = NULL;
+static el_subscription_ctx_t *el_subscription_ctx = NULL;
+
+/**
+ * Change callback getting called by sysrepo. Iterates over changed options and passes
+ * them over to Lua.
+ */
+static int sysrepo_conf_change_callback(sr_session_ctx_t *session,
+                                       const char *module_name,
+                                       const char *xpath, sr_event_t event,
+                                       uint32_t request_id, void *private_data)
+{
+       if (event == SR_EV_CHANGE) {
+               // gets called before the actual change of configuration is commited. If we
+               // return an error, the change is aborted can be used for configuration
+               // verification. Must have no sideeffects.
+
+               // TODO
+       } else if (event == SR_EV_ABORT) {
+               // Gets called when the transaction gets aborted. Because we have no
+               // sideeffects during verification, we don't care and and there is nothing
+               // to do
+       } else if (event == SR_EV_DONE) {
+               // after configuration change commit. We should apply the configuration.
+               // Will not hurt if we verify the changes again, but we have no way of
+               // declining the change now
+
+               int sr_err = SR_ERR_OK;
+               sr_change_iter_t *it = NULL;
+               sr_change_oper_t oper;
+               sr_val_t *old_value = NULL;
+               sr_val_t *new_value = NULL;
+
+               // get all changes
+               sr_err = sr_get_changes_iter(session, XPATH_BASE "//.", &it);
+               if (sr_err != SR_ERR_OK)
+                       goto cleanup;
+
+               while ((sr_get_change_next(session, it, &oper, &old_value,
+                                          &new_value)) == SR_ERR_OK) {
+                       apply_conf_f(new_value);
+               }
+       cleanup:
+               if (sr_err != SR_ERR_OK && sr_err != SR_ERR_NOT_FOUND)
+                       kr_log_error("Sysrepo module error: %s\n",
+                                    sr_strerror(sr_err));
+               sr_free_val(old_value);
+               sr_free_val(new_value);
+               sr_free_change_iter(it);
+       }
+       return SR_ERR_OK;
+}
+
+void el_subscr_finish_closing(uv_handle_t *handle)
+{
+       el_subscription_ctx_t *el_subscr = handle->data;
+       assert(el_subscr != NULL);
+       free(el_subscr);
+}
+
+/** Free a event loop subscription. */
+void el_subscription_free(el_subscription_ctx_t *el_subscr)
+{
+       sr_disconnect(el_subscr->connection);
+       uv_close((uv_handle_t *)&el_subscr->uv_handle,
+                el_subscr_finish_closing);
+}
+
+static void el_subscr_cb_tramp(uv_poll_t *handle, int status, int events)
+{
+       el_subscription_ctx_t *el_subscr = handle->data;
+       el_subscr->callback(el_subscr, status);
+}
+
+/** Start a new event loop subscription.  */
+static el_subscription_ctx_t *
+el_subscription_new(sr_subscription_ctx_t *sr_subscr,
+                   el_subsription_cb el_callback)
+{
+       int fd;
+       int err = sr_get_event_pipe(sr_subscr, &fd);
+       if (err != SR_ERR_OK)
+               return NULL;
+       el_subscription_ctx_t *el_subscr = malloc(sizeof(*el_subscr));
+       if (el_subscr == NULL)
+               return NULL;
+       err = uv_poll_init(uv_default_loop(), &el_subscr->uv_handle, fd);
+       if (err != 0) {
+               free(el_subscr);
+               return NULL;
+       }
+       el_subscr->subscription = sr_subscr;
+       el_subscr->callback = el_callback;
+       el_subscr->uv_handle.data = el_subscr;
+       err = uv_poll_start(&el_subscr->uv_handle, UV_READABLE,
+                           el_subscr_cb_tramp);
+       if (err != 0) {
+               el_subscription_free(el_subscr);
+               return NULL;
+       }
+       return el_subscr;
+}
+
+static void el_subscr_cb(el_subscription_ctx_t *el_subscr, int status)
+{
+       if (status) {
+               /* some error */
+               return;
+       }
+       /* normal state */
+       sr_process_events(el_subscr->subscription, el_subscr->session, NULL);
+}
+
+int sysrepo_init(set_leaf_conf_t apply_conf_callback)
+{
+       // store callback to Lua
+       apply_conf_f = apply_conf_callback;
+
+       int sr_err = SR_ERR_OK;
+       sr_conn_ctx_t *sr_connection = NULL;
+       sr_session_ctx_t *sr_session = NULL;
+       sr_subscription_ctx_t *sr_subscription = NULL;
+
+       sr_err = sr_connect(0, &sr_connection);
+       if (sr_err != SR_ERR_OK)
+               goto cleanup;
+
+       sr_err = sr_connection_recover(sr_connection);
+       if (sr_err != SR_ERR_OK)
+               goto cleanup;
+
+       sr_err = sr_session_start(sr_connection, SR_DS_RUNNING, &sr_session);
+       if (sr_err != SR_ERR_OK)
+               goto cleanup;
+
+       /* register sysrepo subscriptions and callbacks
+               SR_SUBSCR_NO_THREAD - don't create a thread for handling them
+               SR_SUBSCR_ENABLED - send us current configuration in a callback just after subscribing
+        */
+       sr_err = sr_module_change_subscribe(
+               sr_session, YM_COMMON, XPATH_BASE, sysrepo_conf_change_callback,
+               NULL, 0, SR_SUBSCR_NO_THREAD | SR_SUBSCR_ENABLED,
+               &sr_subscription);
+       if (sr_err != SR_ERR_OK)
+               goto cleanup;
+
+       /* add subscriptions to kres event loop */
+       el_subscription_ctx =
+               el_subscription_new(sr_subscription, el_subscr_cb);
+       el_subscription_ctx->connection = sr_connection;
+       el_subscription_ctx->session = sr_session;
+
+       return kr_ok();
+
+cleanup:
+       sr_disconnect(sr_connection);
+       kr_log_error("Error (%s)\n", sr_strerror(sr_err));
+       return kr_error(sr_err);
+}
+
+int sysrepo_deinit()
+{
+       el_subscription_free(el_subscription_ctx);
+       // remove reference to Lua callback so that it can be free'd safely
+       apply_conf_f = NULL;
+       return kr_ok();
+}
diff --git a/modules/sysrepo-lua/cbindings/sysrepo_clib.h b/modules/sysrepo-lua/cbindings/sysrepo_clib.h
new file mode 100644 (file)
index 0000000..9ac1082
--- /dev/null
@@ -0,0 +1,49 @@
+/*  Copyright (C) 2020 CZ.NIC, z.s.p.o. <knot-resolver@labs.nic.cz>
+
+    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 3 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.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+/**
+ * This header file defines an interface between sysrepo_clib and the rest
+ * of the sysrepo module written in Lua. Functions and structs defined here
+ * might be used through LuaJIT's FFI.
+ */
+
+#include "lib/defines.h"
+#include "lib/utils.h"
+
+#include "common/sysrepo_conf.h"
+
+/** Generates prototype for functions exporting string constants from C to Lua */
+#define EXPORT_STRDEF_TO_LUA(name) char *get_strdef_##name(void);
+/** Generates function to be used from within Lua to get access to string constants. */
+#define EXPORT_STRDEF_TO_LUA_IMPL(name) \
+       char *get_strdef_##name()       \
+       {                               \
+               return name;            \
+       }
+
+EXPORT_STRDEF_TO_LUA(YM_COMMON)
+EXPORT_STRDEF_TO_LUA(XPATH_BASE)
+
+typedef struct el_subscription_ctx el_subscription_ctx_t;
+/** Callback for our sysrepo subscriptions */
+typedef void (*el_subsription_cb)(el_subscription_ctx_t *el_subscr, int status);
+/** Callback to Lua for applying configuration */
+typedef void (*set_leaf_conf_t)(sr_val_t *val);
+
+KR_EXPORT int sysrepo_init(set_leaf_conf_t set_leaf_conf_cb);
+KR_EXPORT int sysrepo_deinit(void);
diff --git a/modules/sysrepo-lua/common b/modules/sysrepo-lua/common
new file mode 120000 (symlink)
index 0000000..2fd86f2
--- /dev/null
@@ -0,0 +1 @@
+../sysrepo/common
\ No newline at end of file
diff --git a/modules/sysrepo-lua/ffi.lua b/modules/sysrepo-lua/ffi.lua
new file mode 100644 (file)
index 0000000..355dcb1
--- /dev/null
@@ -0,0 +1,246 @@
+local ffi = require("ffi")
+
+-- FIXME remove absolute path
+-- the load opens the file relative to CWD. That could be anywhere. So we need to know, where is knot installed.
+local clib = ffi.load("/tmp/kr/lib/knot-resolver/kres_modules/sysrepo-lua/cbindings.so")
+
+-------------------------------------------------------------------------------
+--                      FFI initialization
+-------------------------------------------------------------------------------
+
+local function initialize_ffi()
+    --- Definition of `sr_val_t` copied from sysrepo.h on 2020-01-01.
+    ffi.cdef[[
+    /**
+     * @brief Possible types of a data element stored in the sysrepo datastore.
+     */
+     typedef enum sr_type_e {
+        /* special types that does not contain any data */
+        SR_UNKNOWN_T,              /**< Element unknown to sysrepo (unsupported element). */
+
+        SR_LIST_T,                 /**< List instance. ([RFC 7950 sec 7.8](http://tools.ietf.org/html/rfc7950#section-7.8)) */
+        SR_CONTAINER_T,            /**< Non-presence container. ([RFC 7950 sec 7.5](http://tools.ietf.org/html/rfc7950#section-7.5)) */
+        SR_CONTAINER_PRESENCE_T,   /**< Presence container. ([RFC 7950 sec 7.5.1](http://tools.ietf.org/html/rfc7950#section-7.5.1)) */
+        SR_LEAF_EMPTY_T,           /**< A leaf that does not hold any value ([RFC 7950 sec 9.11](http://tools.ietf.org/html/rfc7950#section-9.11)) */
+        SR_NOTIFICATION_T,         /**< Notification instance ([RFC 7095 sec 7.16](https://tools.ietf.org/html/rfc7950#section-7.16)) */
+
+        /* types containing some data */
+        SR_BINARY_T,       /**< Base64-encoded binary data ([RFC 7950 sec 9.8](http://tools.ietf.org/html/rfc7950#section-9.8)) */
+        SR_BITS_T,         /**< A set of bits or flags ([RFC 7950 sec 9.7](http://tools.ietf.org/html/rfc7950#section-9.7)) */
+        SR_BOOL_T,         /**< A boolean value ([RFC 7950 sec 9.5](http://tools.ietf.org/html/rfc7950#section-9.5)) */
+        SR_DECIMAL64_T,    /**< 64-bit signed decimal number ([RFC 7950 sec 9.3](http://tools.ietf.org/html/rfc7950#section-9.3)) */
+        SR_ENUM_T,         /**< A string from enumerated strings list ([RFC 7950 sec 9.6](http://tools.ietf.org/html/rfc7950#section-9.6)) */
+        SR_IDENTITYREF_T,  /**< A reference to an abstract identity ([RFC 7950 sec 9.10](http://tools.ietf.org/html/rfc7950#section-9.10)) */
+        SR_INSTANCEID_T,   /**< References a data tree node ([RFC 7950 sec 9.13](http://tools.ietf.org/html/rfc7950#section-9.13)) */
+        SR_INT8_T,         /**< 8-bit signed integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
+        SR_INT16_T,        /**< 16-bit signed integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
+        SR_INT32_T,        /**< 32-bit signed integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
+        SR_INT64_T,        /**< 64-bit signed integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
+        SR_STRING_T,       /**< Human-readable string ([RFC 7950 sec 9.4](http://tools.ietf.org/html/rfc7950#section-9.4)) */
+        SR_UINT8_T,        /**< 8-bit unsigned integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
+        SR_UINT16_T,       /**< 16-bit unsigned integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
+        SR_UINT32_T,       /**< 32-bit unsigned integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
+        SR_UINT64_T,       /**< 64-bit unsigned integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
+        SR_ANYXML_T,       /**< Unknown chunk of XML ([RFC 7950 sec 7.10](https://tools.ietf.org/html/rfc7950#section-7.10)) */
+        SR_ANYDATA_T,      /**< Unknown set of nodes, encoded in XML ([RFC 7950 sec 7.10](https://tools.ietf.org/html/rfc7950#section-7.10)) */
+    } sr_type_t;
+
+    /**
+     * @brief Data of an element (if applicable), properly set according to the type.
+     */
+    typedef union sr_data_u {
+        char *binary_val;       /**< Base64-encoded binary data ([RFC 7950 sec 9.8](http://tools.ietf.org/html/rfc7950#section-9.8)) */
+        char *bits_val;         /**< A set of bits or flags ([RFC 7950 sec 9.7](http://tools.ietf.org/html/rfc7950#section-9.7)) */
+        bool bool_val;          /**< A boolean value ([RFC 7950 sec 9.5](http://tools.ietf.org/html/rfc7950#section-9.5)) */
+        double decimal64_val;   /**< 64-bit signed decimal number ([RFC 7950 sec 9.3](http://tools.ietf.org/html/rfc7950#section-9.3)) */
+        char *enum_val;         /**< A string from enumerated strings list ([RFC 7950 sec 9.6](http://tools.ietf.org/html/rfc7950#section-9.6)) */
+        char *identityref_val;  /**< A reference to an abstract identity ([RFC 7950 sec 9.10](http://tools.ietf.org/html/rfc7950#section-9.10)) */
+        char *instanceid_val;   /**< References a data tree node ([RFC 7950 sec 9.13](http://tools.ietf.org/html/rfc7950#section-9.13)) */
+        int8_t int8_val;        /**< 8-bit signed integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
+        int16_t int16_val;      /**< 16-bit signed integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
+        int32_t int32_val;      /**< 32-bit signed integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
+        int64_t int64_val;      /**< 64-bit signed integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
+        char *string_val;       /**< Human-readable string ([RFC 7950 sec 9.4](http://tools.ietf.org/html/rfc7950#section-9.4)) */
+        uint8_t uint8_val;      /**< 8-bit unsigned integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
+        uint16_t uint16_val;    /**< 16-bit unsigned integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
+        uint32_t uint32_val;    /**< 32-bit unsigned integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
+        uint64_t uint64_val;    /**< 64-bit unsigned integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
+        char *anyxml_val;       /**< Unknown chunk of XML ([RFC 7950 sec 7.10](https://tools.ietf.org/html/rfc7950#section-7.10)) */
+        char *anydata_val;      /**< Unknown set of nodes, encoded in XML ([RFC 7950 sec 7.10](https://tools.ietf.org/html/rfc7950#section-7.10)) */
+    } sr_data_t;
+
+    /**
+     * @brief Structure that contains value of an data element stored in the sysrepo datastore.
+     */
+    typedef struct sr_val_s {
+        /** [XPath](@ref paths) (or rather path) identifier of the data element. */
+        char *xpath;
+
+        /** Type of an element. */
+        sr_type_t type;
+
+        /**
+         * Flag for node with default value (applicable only for leaves).
+         * It is set to TRUE only if the value was *implicitly* set by the datastore as per
+         * module schema. Explicitly set/modified data element (through the sysrepo API) always
+         * has this flag unset regardless of the entered value.
+         */
+        bool dflt;
+
+        /** [Origin](@ref oper_ds) of the value. */
+        char *origin;
+
+        /** Data of an element (if applicable), properly set according to the type. */
+        sr_data_t data;
+
+    } sr_val_t;
+
+    typedef void (*set_leaf_conf_t)(sr_val_t *val);
+    int sysrepo_init(set_leaf_conf_t set_leaf_conf_cb);
+    int sysrepo_deinit(void);
+    ]]
+end
+
+-- make sure this module runs just once
+-- LuaJIT's FFI can't be initialized multiple times for the same types
+if (_KNOT_SYSREPO_FFI_INITIALIZED == true) then
+    -- nothing to initialize then
+else
+    initialize_ffi()
+    _KNOT_SYSREPO_FFI_INITIALIZED = true
+end
+
+-- TODO version check so that we cant load new module into an old Knot
+
+-------------------------------------------------------------------------------
+--                      Data convertsion helpers
+-------------------------------------------------------------------------------
+
+--- Helper table for converting sr_data_t union type to string based on the provided type
+local _value_to_str_conversion_table = {
+    ["SR_UNKNOWN_T"] = function(_) return "unknown value" end,
+    ["SR_LIST_T"] = function(_) return "no value (list)" end,
+    ["SR_CONTAINER_T"] = function(_) return "no value (container)" end,
+    ["SR_CONTAINER_PRESENCE_T"] = function(_) return "container (container presence)" end,
+    ["SR_LEAF_EMPTY_T"] = function(_) return "empty (empty leaf)" end,
+    ["SR_NOTIFICATION_T"] = function(_) return "no value (notification)" end,
+    ["SR_BINARY_T"] = function(val) return ffi.string(val.binary_val) .. " (binary)" end,
+    ["SR_BITS_T"] = function(_) return "??? (bits)" end,
+    ["SR_BOOL_T"] = function(val) return tostring(val.bool_val) .. " (bool)" end,
+    ["SR_DECIMAL64_T"] = function(val) return tostring(val.decimal64_val) .. "(decimal64)" end,
+    ["SR_ENUM_T"] = function(val) return ffi.string(val.enum_val) .. " (enum)" end,
+    ["SR_IDENTITYREF_T"] = function(val) return ffi.string(val.enum_val) .. " (indentityref)" end,
+    ["SR_INSTANCEID_T"] = function(val) return ffi.string(val.enum_val) .. " (instanceid)" end,
+    ["SR_INT8_T"] = function(val) return tostring(val.int8_val) .. " (int8)" end,
+    ["SR_INT16_T"] = function(val) return tostring(val.int16_val) .. " (int16)" end,
+    ["SR_INT32_T"] = function(val) return tostring(val.int32_val) .. " (int32)" end,
+    ["SR_INT64_T"] = function(val) return tostring(val.int64_val) .. " (int64)" end,
+    ["SR_STRING_T"] = function(val) return ffi.string(val.string_val) .. " (string)" end,
+    ["SR_UINT8_T"] = function(val) return tostring(val.uint8_val) .. " (int8)" end,
+    ["SR_UINT16_T"] = function(val) return tostring(val.uint16_val) .. " (uint16)" end,
+    ["SR_UINT32_T"] = function(val) return tostring(val.uint32_val) .. " (uint32)" end,
+    ["SR_UINT64_T"] = function(val) return tostring(val.uint64_val) .. " (uint64)" end,
+    ["SR_ANYXML_T"] = function(val) return ffi.string(val.anyxml_val) .. " (anyxml)" end,
+    ["SR_ANYDATA_T"] = function(val) return ffi.string(val.anydata_val) .. " (anydata)" end,
+}
+
+--- Convert from type sr_type_t into a uppercase string.
+-- @param sr_type_t value. In case of wrong type, the function crashes the whole runtime.
+-- @return string value of the enum
+local function type_to_str(tp)
+    if (tp == "SR_UNKNOWN_T") then return "SR_UNKNOWN_T"
+    elseif (tp == "SR_LIST_T") then return "SR_LIST_T"
+    elseif (tp == "SR_CONTAINER_T") then return "SR_CONTAINER_T"
+    elseif (tp == "SR_CONTAINER_PRESENCE_T") then return "SR_CONTAINER_PRESENCE_T"
+    elseif (tp == "SR_LEAF_EMPTY_T") then return "SR_LEAF_EMPTY_T"
+    elseif (tp == "SR_NOTIFICATION_T") then return "SR_NOTIFICATION_T"
+    elseif (tp == "SR_BINARY_T") then return "SR_BINARY_T"
+    elseif (tp == "SR_BITS_T") then return "SR_BITS_T"
+    elseif (tp == "SR_BOOL_T") then return "SR_BOOL_T"
+    elseif (tp == "SR_DECIMAL64_T") then return "SR_DECIMAL64_T"
+    elseif (tp == "SR_ENUM_T") then return "SR_ENUM_T"
+    elseif (tp == "SR_IDENTITYREF_T") then return "SR_IDENTITYREF_T"
+    elseif (tp == "SR_INSTANCEID_T") then return "SR_INSTANCEID_T"
+    elseif (tp == "SR_INT8_T") then return "SR_INT8_T"
+    elseif (tp == "SR_INT16_T") then return "SR_INT16_T"
+    elseif (tp == "SR_INT32_T") then return "SR_INT32_T"
+    elseif (tp == "SR_INT64_T") then return "SR_INT64_T"
+    elseif (tp == "SR_STRING_T") then return "SR_STRING_T"
+    elseif (tp == "SR_UINT8_T") then return "SR_UINT8_T"
+    elseif (tp == "SR_UINT16_T") then return "SR_UINT16_T"
+    elseif (tp == "SR_UINT32_T") then return "SR_UINT32_T"
+    elseif (tp == "SR_UINT64_T") then return "SR_UINT64_T"
+    elseif (tp == "SR_ANYXML_T") then return "SR_ANYXML_T"
+    elseif (tp == "SR_ANYDATA_T") then return "SR_ANYDATA_T"
+    else
+        error("unexpected value of sr_type_t enum")
+    end
+end
+
+
+-------------------------------------------------------------------------------
+--                      Callback management
+-------------------------------------------------------------------------------
+
+local callbacks = {}
+local function create_callback(ctype ,func)
+    assert(type(ctype) == "string")
+    assert(type(func) == "function")
+
+    local cb = ffi.cast(ctype, func)
+    table.insert(callbacks, cb)
+    return cb
+end
+
+local function free_callbacks()
+    for _, cb in ipairs(callbacks) do
+        cb:free()
+    end
+end
+
+
+-------------------------------------------------------------------------------
+--                      Exported functionality
+-------------------------------------------------------------------------------
+
+local sysrepo_ffi = {}
+
+function sysrepo_ffi.init(apply_conf_func)
+    local cb = create_callback("set_leaf_conf_t", apply_conf_func)
+    local res = clib.sysrepo_init(cb)
+    if res ~= 0 then
+        error("Initialization failed with error code " .. tostring(res))
+    end
+end
+
+function sysrepo_ffi.deinit()
+    local res = clib.sysrepo_deinit()
+    free_callbacks()
+    if res ~= 0 then
+        error("Deinitialization failed with error code " .. tostring(res))
+    end
+end
+
+--- Converts from cdata sr_val_t to table (using table is slower, but safer)
+function sysrepo_ffi.sr_val_to_table(val)
+    local tbl = {
+        dflt = val.dflt,
+        xpath = ffi.string(val.xpath),
+        type = type_to_str(val.type),
+        _value = val.data,
+    }
+    setmetatable(tbl, {
+        __tostring = function(slf)
+            return string.format(
+                "{\n\txpath = '%s',\n\ttype = '%s',\n\tdflt = '%s',\n\tdata = '%s'\n}",
+                slf.xpath,
+                slf.type,
+                tostring(slf.dflt),
+                _value_to_str_conversion_table[type_to_str(slf.type)](slf._value)
+            )
+        end
+    })
+    return tbl
+end
+
+return sysrepo_ffi
diff --git a/modules/sysrepo-lua/init.lua b/modules/sysrepo-lua/init.lua
new file mode 100644 (file)
index 0000000..8ba7dae
--- /dev/null
@@ -0,0 +1,20 @@
+local sysrepo_ffi = require("kres_modules/sysrepo-lua/ffi")
+
+local sysrepo = {}
+
+local function apply_configuration(sr_val)
+    local sr_val_table = sysrepo_ffi.sr_val_to_table(sr_val)
+
+    print("Configuration change")
+    print(tostring(sr_val_table))
+end
+
+function sysrepo.init()
+    sysrepo_ffi.init(apply_configuration)
+end
+
+function sysrepo.deinit()
+    sysrepo_ffi.deinit()
+end
+
+return sysrepo
index bbe88c48307ea1e07a2cd518d30351f38d48819d..e8ad92316c71396a9538cf4e13aae8b559cb1f05 100644 (file)
@@ -1,8 +1,43 @@
 # lua module: sysrepo-lua
 
 sysrepo_lua_src = files([
+  'ffi.lua',
+  'init.lua',
 ])
 
+sysrepo_src = files([
+  'cbindings/sysrepo_clib.h',
+  'cbindings/sysrepo_clib.c',
+])
+c_src_lint += sysrepo_src
+
+sysrepo_common_src = files([
+       'common/sysrepo_conf.c',
+       'common/sysrepo_conf.h',
+       'common/string_helper.h',
+       'common/string_helper.c',
+])
+c_src_lint += sysrepo_common_src
+
 if build_sysrepo
+  install_data(
+    sysrepo_lua_src,
+    install_dir: modules_dir + '/sysrepo-lua',
+  )
 
+  sysrepo_mod = shared_module(
+       'cbindings',
+       sysrepo_src,
+       dependencies: [
+         luajit_inc,
+      libyang,
+         libsysrepo,
+       ],
+       include_directories: mod_inc_dir,
+       name_prefix: '',
+       install: true,
+       install_dir: modules_dir + '/sysrepo-lua',
+  )
 endif
+
+