extensions FEATURE YANG extension plugins support with NACM as proof-of-concept
diff --git a/src/common.c b/src/common.c
index 56a8a3e..6f8cfe9 100644
--- a/src/common.c
+++ b/src/common.c
@@ -24,7 +24,7 @@
#include <sys/stat.h>
#include <unistd.h>
-#include "extensions.h"
+#include "plugins_exts.h"
#include "tree_schema.h"
#include "tree_schema_internal.h"
diff --git a/src/log.c b/src/log.c
index b600b62..8efac1a 100644
--- a/src/log.c
+++ b/src/log.c
@@ -24,6 +24,7 @@
#include <string.h>
#include "log.h"
+#include "plugins_exts.h"
THREAD_LOCAL enum int_log_opts log_opt;
volatile uint8_t ly_log_level = LY_LLWRN;
@@ -464,6 +465,29 @@
}
API void
+lyext_log(const struct lysc_ext_instance *ext, LY_LOG_LEVEL level, LY_ERR err_no, const char *path, const char *format, ...)
+{
+ va_list ap;
+ char *plugin_msg;
+ int ret;
+
+ if (ly_log_level < level) {
+ return;
+ }
+ ret = asprintf(&plugin_msg, "Extension plugin \"%s\": %s)", ext->def->plugin->id, format);
+ if (ret == -1) {
+ LOGMEM(ext->def->module->ctx);
+ return;
+ }
+
+ va_start(ap, format);
+ log_vprintf(ext->def->module->ctx, level, (level == LY_LLERR ? LY_EPLUGIN : 0), err_no, path ? strdup(path) : NULL, plugin_msg, ap);
+ va_end(ap);
+
+ free(plugin_msg);
+}
+
+API void
ly_err_print(struct ly_err_item *eitem)
{
if (ly_log_opts & LY_LOLOG) {
diff --git a/src/parser_yang.c b/src/parser_yang.c
index 2547c99..34db6e5 100644
--- a/src/parser_yang.c
+++ b/src/parser_yang.c
@@ -24,8 +24,8 @@
#include "context.h"
#include "dict.h"
-#include "extensions.h"
#include "log.h"
+#include "plugins_exts.h"
#include "set.h"
#include "tree.h"
#include "tree_schema.h"
diff --git a/src/plugins_exts.c b/src/plugins_exts.c
new file mode 100644
index 0000000..720f828
--- /dev/null
+++ b/src/plugins_exts.c
@@ -0,0 +1,106 @@
+/**
+ * @file plugins_exts.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Internally implemented YANG extensions.
+ *
+ * Copyright (c) 2019 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#include "common.h"
+
+#include "plugins_exts.h"
+
+#include "plugins_exts_nacm.c"
+
+/**
+ * @brief list of all extension plugins implemented internally
+ */
+struct lyext_plugins_list lyext_plugins_internal[5] = {
+ {"ietf-netconf-acm", "2012-02-22", "default-deny-write", &nacm_plugin},
+ {"ietf-netconf-acm", "2018-02-14", "default-deny-write", &nacm_plugin},
+ {"ietf-netconf-acm", "2012-02-22", "default-deny-all", &nacm_plugin},
+ {"ietf-netconf-acm", "2018-02-14", "default-deny-all", &nacm_plugin},
+ {NULL, NULL, NULL, NULL} /* terminating item */
+};
+
+/* TODO support for external extension plugins */
+
+struct lyext_plugin *
+lyext_get_plugin(struct lysc_ext *ext)
+{
+ unsigned int u;
+
+ for (u = 0; lyext_plugins_internal[u].module; ++u) {
+ if (!strcmp(ext->name, lyext_plugins_internal[u].name) &&
+ !strcmp(ext->module->name, lyext_plugins_internal[u].module) &&
+ (!lyext_plugins_internal[u].revision || !strcmp(ext->module->revision, lyext_plugins_internal[u].revision))) {
+ /* we have the match */
+ return lyext_plugins_internal[u].plugin;
+ }
+ }
+
+ return NULL;
+}
+
+API const char *
+lyext_parent2str(LYEXT_PARENT type)
+{
+ switch(type) {
+ case LYEXT_PAR_MODULE:
+ return "module";
+ case LYEXT_PAR_NODE:
+ return "data node";
+ case LYEXT_PAR_INPUT:
+ return "input";
+ case LYEXT_PAR_OUTPUT:
+ return "output";
+ case LYEXT_PAR_TYPE:
+ return "type";
+ case LYEXT_PAR_TYPE_BIT:
+ return "bit";
+ case LYEXT_PAR_TYPE_ENUM:
+ return "enum";
+ case LYEXT_PAR_FEATURE:
+ return "feature";
+ case LYEXT_PAR_MUST:
+ return "must";
+ case LYEXT_PAR_PATTERN:
+ return "pattern";
+ case LYEXT_PAR_LENGTH:
+ return "length";
+ case LYEXT_PAR_RANGE:
+ return "range";
+ case LYEXT_PAR_WHEN:
+ return "when";
+ case LYEXT_PAR_IDENT:
+ return "identity";
+ case LYEXT_PAR_EXT:
+ return "extension instance";
+ case LYEXT_PAR_IMPORT:
+ return "import";
+/* YANG allows extension instances inside the following statements,
+ * but they do not have any meaning in current libyang
+ case LYEXT_PAR_TPDF:
+ return "typedef";
+ case LYEXT_PAR_EXTINST:
+ return "extension";
+ case LYEXT_PAR_REFINE:
+ return "refine";
+ case LYEXT_PAR_DEVIATION:
+ return "deviation";
+ case LYEXT_PAR_DEVIATE:
+ return "deviate";
+ case LYEXT_PAR_INCLUDE:
+ return "include";
+ case LYEXT_PAR_REVISION:
+ return "revision";
+ */
+ default:
+ return "unknown";
+ }
+}
diff --git a/src/extensions.h b/src/plugins_exts.h
similarity index 75%
rename from src/extensions.h
rename to src/plugins_exts.h
index 6b8076b..6684d6a 100644
--- a/src/extensions.h
+++ b/src/plugins_exts.h
@@ -1,9 +1,9 @@
/**
- * @file extesnions.h
+ * @file plugins_exts.h
* @author Radek Krejci <rkrejci@cesnet.cz>
* @brief libyang support for YANG extensions implementation.
*
- * Copyright (c) 2015 - 2018 CESNET, z.s.p.o.
+ * Copyright (c) 2015 - 2019 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -12,8 +12,8 @@
* https://opensource.org/licenses/BSD-3-Clause
*/
-#ifndef LY_EXTENSIONS_H_
-#define LY_EXTENSIONS_H_
+#ifndef LY_PLUGINS_EXTS_H_
+#define LY_PLUGINS_EXTS_H_
#include "set.h"
#include "tree_schema.h"
@@ -30,6 +30,17 @@
*/
/**
+ * @brief Extensions API version
+ */
+#define LYEXT_API_VERSION 1
+
+/**
+ * @brief Macro to store version of extension plugins API in the plugins.
+ * It is matched when the plugin is being loaded by libyang.
+ */
+#define LYEXT_VERSION_CHECK int lyext_api_version = LYEXT_API_VERSION;
+
+/**
* @defgroup extensionscompile YANG Extensions - Compilation Helpers
* @ingroup extensions
* @brief Helper functions to compile (via lyext_clb_compile callback) statements inside the extension instance.
@@ -124,10 +135,33 @@
lyext_clb_free free; /**< Free the extension instance specific data created by lyext_plugin::compile callback */
};
+struct lyext_plugins_list {
+ const char *module; /**< name of the module where the extension is defined */
+ const char *revision; /**< optional module revision - if not specified, the plugin applies to any revision,
+ which is not an optimal approach due to a possible future revisions of the module.
+ Instead, there should be defined multiple items in the plugins list, each with the
+ different revision, but all with the same pointer to the plugin extension. The
+ only valid use case for the NULL revision is the case the module has no revision. */
+ const char *name; /**< name of the extension */
+ struct lyext_plugin *plugin; /**< plugin for the extension */
+};
+
+
+/**
+ * @brief Provide a log message from an extension plugin.
+ *
+ * @param[in] ext Compiled extension structure providing generic information about the extension/plugin causing the message.
+ * @param[in] level Log message level (error, warning, etc.)
+ * @param[in] err_no Error type code.
+ * @param[in] path Path relevant to the message.
+ * @param[in] format Format string to print.
+ */
+void lyext_log(const struct lysc_ext_instance *ext, LY_LOG_LEVEL level, LY_ERR err_no, const char *path, const char *format, ...);
+
/** @} extensions */
#ifdef __cplusplus
}
#endif
-#endif /* LY_TREE_SCHEMA_H_ */
+#endif /* LY_PLUGINS_EXTS_H_ */
diff --git a/src/plugins_exts_internal.h b/src/plugins_exts_internal.h
new file mode 100644
index 0000000..4f0eb5f
--- /dev/null
+++ b/src/plugins_exts_internal.h
@@ -0,0 +1,27 @@
+/**
+ * @file plugins_exts_internal.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief internal functions to support extension plugins.
+ *
+ * Copyright (c) 2019 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_PLUGINS_EXTS_INTERNAL_H_
+#define LY_PLUGINS_EXTS_INTERNAL_H_
+
+#include "tree_schema.h"
+
+/**
+ * @brief Find the extension plugin for the specified extension instance.
+ *
+ * @param[in] mod YANG module where the
+ */
+struct lyext_plugin *lyext_get_plugin(struct lysc_ext *ext);
+
+#endif /* LY_PLUGINS_EXTS_INTERNAL_H_ */
diff --git a/src/plugins_exts_nacm.c b/src/plugins_exts_nacm.c
new file mode 100644
index 0000000..992aa04
--- /dev/null
+++ b/src/plugins_exts_nacm.c
@@ -0,0 +1,126 @@
+/**
+ * @file plugins_exts_nacm.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief libyang extension plugin - NACM (RFC 6536)
+ *
+ * Copyright (c) 2019 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#include "common.h"
+
+#include <stdlib.h>
+
+#include "plugins_exts.h"
+#include "tree_schema.h"
+
+/**
+ * @brief Storage for ID used to check plugin API version compatibility.
+ * Ignored here in the internal plugin.
+LYEXT_VERSION_CHECK
+ */
+
+/**
+ * @brief Compile NAMC's extension instances.
+ *
+ * Implementation of lyext_clb_compile callback set as lyext_plugin::compile.
+ */
+LY_ERR
+nacm_compile(struct lysc_ctx *cctx, const struct lysp_ext_instance *p_ext, struct lysc_ext_instance *c_ext)
+{
+ struct lysc_node *parent = NULL, *iter;
+ struct lysc_ext_instance *inherited;
+ unsigned int u;
+
+ static const uint8_t nacm_deny_all = 1;
+ static const uint8_t nacm_deny_write = 2;
+
+ /* store the NACM flag */
+ if (!strcmp(c_ext->def->name, "default-deny-write")) {
+ c_ext->data = (void*)&nacm_deny_write;
+ } else if (!strcmp(c_ext->def->name, "default-deny-all")) {
+ c_ext->data = (void*)&nacm_deny_all;
+ } else {
+ return LY_EINT;
+ }
+
+ /* check that the extension is instantiated at an allowed place - data node */
+ if (c_ext->parent_type != LYEXT_PAR_NODE) {
+ lyext_log(c_ext, LY_LLERR, LY_EVALID, cctx->path, "Extension %s is allowed only in a data nodes, but it is placed in \"%s\" statement.",
+ p_ext->name, lyext_parent2str(c_ext->parent_type));
+ return LY_EVALID;
+ } else {
+ parent = (struct lysc_node*)c_ext->parent;
+ if (!(parent->nodetype & (LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_CHOICE | LYS_ANYDATA | LYS_CASE | LYS_ACTION | LYS_NOTIF))) {
+ /* note LYS_AUGMENT and LYS_USES is not in the list since they are not present in the compiled tree. Instead, libyang
+ * passes all their extensions to their children nodes */
+invalid_parent:
+ lyext_log(c_ext, LY_LLERR, LY_EVALID, cctx->path,
+ "Extension %s is not allowed in %s statement.", p_ext->name, lys_nodetype2str(parent->nodetype));
+ return LY_EVALID;
+ }
+ if (c_ext->data == (void*)&nacm_deny_write && (parent->nodetype & (LYS_ACTION | LYS_NOTIF))) {
+ goto invalid_parent;
+ }
+ }
+
+ /* check for duplication */
+ LY_ARRAY_FOR(parent->exts, u) {
+ if (&parent->exts[u] != c_ext && parent->exts[u].def->plugin == c_ext->def->plugin) {
+ /* duplication of a NACM extension on a single node
+ * We check plugin since we want to catch even the situation that there is default-deny-all
+ * AND default-deny-write */
+ if (parent->exts[u].def == c_ext->def) {
+ lyext_log(c_ext, LY_LLERR, LY_EVALID, cctx->path, "Extension %s is instantiated multiple times.", p_ext->name);
+ } else {
+ lyext_log(c_ext, LY_LLERR, LY_EVALID, cctx->path, "Extension nacm:default-deny-write is mixed with nacm:default-deny-all.");
+ }
+ return LY_EVALID;
+ }
+ }
+
+ /* inherit the extension instance to all the children nodes */
+ LYSC_TREE_DFS_BEGIN(parent, iter) {
+ if (iter != parent) { /* ignore the parent from which we inherit */
+ /* check that the node does not have its own NACM extension instance */
+ LY_ARRAY_FOR(iter->exts, u) {
+ if (iter->exts[u].def == c_ext->def) {
+ /* the child already have its own NACM flag, so skip the subtree */
+ LYSC_TREE_DFS_continue = 1;
+ break;
+ }
+ }
+ if (!LYSC_TREE_DFS_continue) {
+ /* duplicate this one to inherit it to the child */
+ LY_ARRAY_NEW_RET(cctx->ctx, iter->exts, inherited, LY_EMEM);
+
+ inherited->def = c_ext->def;
+ inherited->parent = iter;
+ inherited->parent_type = LYEXT_PAR_NODE;
+ if (c_ext->argument) {
+ inherited->argument = lydict_insert(cctx->ctx, c_ext->argument, strlen(c_ext->argument));
+ }
+ /* TODO duplicate extension instances */
+ inherited->data = c_ext->data;
+ }
+ }
+ LYSC_TREE_DFS_END(parent, iter)
+ }
+
+ return LY_SUCCESS;
+}
+
+
+/**
+ * @brief Plugin for the NACM's default-deny-write and default-deny-all extensions
+ */
+struct lyext_plugin nacm_plugin = {
+ .id = "libyang 2 - NACM, version 1",
+ .compile = &nacm_compile,
+ .validate = NULL,
+ .free = NULL
+};
diff --git a/src/plugins_types.c b/src/plugins_types.c
index c18f9b9..9d6a339 100644
--- a/src/plugins_types.c
+++ b/src/plugins_types.c
@@ -1,5 +1,5 @@
/**
- * @file plugin_types.c
+ * @file plugins_types.c
* @author Radek Krejci <rkrejci@cesnet.cz>
* @brief Built-in types plugins and interface for user types plugins.
*
diff --git a/src/printer_yang.c b/src/printer_yang.c
index 8736639..a3242a6 100755
--- a/src/printer_yang.c
+++ b/src/printer_yang.c
@@ -20,8 +20,8 @@
#include <stdlib.h>
#include <string.h>
-#include "extensions.h"
#include "log.h"
+#include "plugins_exts.h"
#include "printer_internal.h"
#include "tree.h"
#include "tree_schema.h"
diff --git a/src/tree_data.h b/src/tree_data.h
index e9b1c32..22e9d3c 100644
--- a/src/tree_data.h
+++ b/src/tree_data.h
@@ -48,7 +48,7 @@
* 3 5 6
* </pre>
*
- * Use the same parameters for #LY_TREE_DFS_BEGIN and #LY_TREE_DFS_END. While
+ * Use the same parameters for #LYD_TREE_DFS_BEGIN and #LYD_TREE_DFS_END. While
* START can be any of the lyd_node* types, NEXT and ELEM variables are expected
* to be pointers to a generic struct lyd_node.
*
diff --git a/src/tree_schema.c b/src/tree_schema.c
index d828c67..ad8e695 100644
--- a/src/tree_schema.c
+++ b/src/tree_schema.c
@@ -751,8 +751,9 @@
#endif
if (!mod->implemented) {
- /* pre-compile features of the module */
+ /* pre-compile features and extension definitions of the module */
LY_CHECK_GOTO(lys_feature_precompile(NULL, ctx, mod, mod->parsed->features, &mod->off_features), error);
+ LY_CHECK_GOTO(lys_extension_precompile(NULL, ctx, mod, mod->parsed->extensions, &mod->off_extensions), error);
}
/* decide the latest revision */
@@ -797,8 +798,9 @@
goto error_ctx;
}
if (!mod->implemented) {
- /* pre-compile features of the module */
+ /* pre-compile features and extension definitions of the module */
LY_CHECK_GOTO(lys_feature_precompile(NULL, ctx, mod, inc->submodule->features, &mod->off_features), error);
+ LY_CHECK_GOTO(lys_extension_precompile(NULL, ctx, mod, mod->parsed->extensions, &mod->off_extensions), error);
}
}
mod->parsed->parsing = 0;
diff --git a/src/tree_schema.h b/src/tree_schema.h
index 9ba9db3..050c7be 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -32,6 +32,90 @@
#endif
/**
+ * @brief Macro to iterate via all elements in a schema tree which can be instantiated in data tree
+ * (skips cases, input, output). This is the opening part to the #LYS_TREE_DFS_END - they always have to be used together.
+ *
+ * The function follows deep-first search algorithm:
+ * <pre>
+ * 1
+ * / \
+ * 2 4
+ * / / \
+ * 3 5 6
+ * </pre>
+ *
+ * Use the same parameters for #LYSC_TREE_DFS_BEGIN and #LYSC_TREE_DFS_END. While
+ * START can be any of the lysc_node* types (including lysc_action and lysc_notif),
+ * ELEM variable must be of the struct lysc_node* type.
+ *
+ * To skip a particular subtree, instead of the continue statement, set LYSC_TREE_DFS_continue
+ * variable to non-zero value.
+ *
+ * Use with opening curly bracket '{' after the macro.
+ *
+ * @param START Pointer to the starting element processed first.
+ * @param ELEM Iterator intended for use in the block.
+ */
+#define LYSC_TREE_DFS_BEGIN(START, ELEM) \
+ { int LYSC_TREE_DFS_continue = 0; struct lysc_node *LYSC_TREE_DFS_next; \
+ for ((ELEM) = (LYSC_TREE_DFS_next) = (START); \
+ (ELEM); \
+ (ELEM) = (LYSC_TREE_DFS_next), LYSC_TREE_DFS_continue = 0)
+
+/**
+ * @brief Macro to iterate via all elements in a (sub)tree. This is the closing part
+ * to the #LYSC_TREE_DFS_BEGIN - they always have to be used together.
+ *
+ * Use the same parameters for #LYSC_TREE_DFS_BEGIN and #LYSC_TREE_DFS_END. While
+ * START can be a pointer to any of the lysc_node* types (including lysc_action and lysc_notif),
+ * ELEM variable must be pointer to the lysc_node type.
+ *
+ * Use with closing curly bracket '}' after the macro.
+ *
+ * @param START Pointer to the starting element processed first.
+ * @param ELEM Iterator intended for use in the block.
+ */
+
+#define LYSC_TREE_DFS_END(START, ELEM) \
+ /* select element for the next run - children first */ \
+ if (LYSC_TREE_DFS_continue) { \
+ (LYSC_TREE_DFS_next) = NULL; \
+ } else { \
+ (LYSC_TREE_DFS_next) = (struct lysc_node*)lysc_node_children(ELEM, 0); \
+ }\
+ if (!(LYSC_TREE_DFS_next)) { \
+ /* in case of RPC/action, get also the output children */ \
+ if (!LYSC_TREE_DFS_continue && (ELEM)->nodetype == LYS_ACTION) { \
+ (LYSC_TREE_DFS_next) = (struct lysc_node*)lysc_node_children(ELEM, LYS_CONFIG_R); \
+ } \
+ if (!(LYSC_TREE_DFS_next)) { \
+ /* no children */ \
+ if ((ELEM) == (struct lysc_node*)(START)) { \
+ /* we are done, (START) has no children */ \
+ break; \
+ } \
+ /* try siblings */ \
+ (LYSC_TREE_DFS_next) = (ELEM)->next; \
+ } \
+ } \
+ while (!(LYSC_TREE_DFS_next)) { \
+ /* parent is already processed, go to its sibling */ \
+ (ELEM) = (ELEM)->parent; \
+ /* no siblings, go back through parents */ \
+ if ((ELEM) == (struct lysc_node*)(START)) { \
+ /* we are done, no next element to process */ \
+ break; \
+ } \
+ if ((ELEM)->nodetype == LYS_ACTION) { \
+ /* there is actually next node as a child of action's output */ \
+ (LYSC_TREE_DFS_next) = (struct lysc_node*)lysc_node_children(ELEM, LYS_CONFIG_R); \
+ } \
+ if (!(LYSC_TREE_DFS_next)) { \
+ (LYSC_TREE_DFS_next) = (ELEM)->next; \
+ } \
+ } } \
+
+/**
* @brief Schema input formats accepted by libyang [parser functions](@ref howtoschemasparsers).
*/
typedef enum {
@@ -81,27 +165,39 @@
* @brief Extension instance structure parent enumeration
*/
typedef enum {
- LYEXT_PAR_MODULE, /**< ::lys_module or ::lys_submodule */
- LYEXT_PAR_NODE, /**< ::lys_node (and the derived structures) */
- LYEXT_PAR_TPDF, /**< ::lys_tpdf */
- LYEXT_PAR_TYPE, /**< ::lys_type */
- LYEXT_PAR_TYPE_BIT, /**< ::lys_type_bit */
- LYEXT_PAR_TYPE_ENUM, /**< ::lys_type_enum */
- LYEXT_PAR_FEATURE, /**< ::lys_feature */
- LYEXT_PAR_RESTR, /**< ::lys_restr - YANG's must, range, length and pattern statements */
- LYEXT_PAR_WHEN, /**< ::lys_when */
- LYEXT_PAR_IDENT, /**< ::lys_ident */
- LYEXT_PAR_EXT, /**< ::lys_ext */
- LYEXT_PAR_EXTINST, /**< ::lys_ext_instance */
- LYEXT_PAR_REFINE, /**< ::lys_refine */
- LYEXT_PAR_DEVIATION, /**< ::lys_deviation */
- LYEXT_PAR_DEVIATE, /**< ::lys_deviate */
- LYEXT_PAR_IMPORT, /**< ::lys_import */
- LYEXT_PAR_INCLUDE, /**< ::lysp_include */
- LYEXT_PAR_REVISION, /**< ::lysc_revision */
+ LYEXT_PAR_MODULE, /**< ::lysc_module */
+ LYEXT_PAR_NODE, /**< ::lysc_node (and the derived structures including ::lysc_action and ::lysc_notif) */
+ LYEXT_PAR_INPUT, /**< ::lysc_action_inout */
+ LYEXT_PAR_OUTPUT, /**< ::lysc_action_inout */
+ LYEXT_PAR_TYPE, /**< ::lysc_type */
+ LYEXT_PAR_TYPE_BIT, /**< ::lysc_type_bitenum_item */
+ LYEXT_PAR_TYPE_ENUM, /**< ::lysc_type_bitenum_item */
+ LYEXT_PAR_FEATURE, /**< ::lysc_feature */
+ LYEXT_PAR_MUST, /**< ::lysc_must */
+ LYEXT_PAR_PATTERN, /**< ::lysc_pattern */
+ LYEXT_PAR_LENGTH, /**< ::lysc_range */
+ LYEXT_PAR_RANGE, /**< ::lysc_range */
+ LYEXT_PAR_WHEN, /**< ::lysc_when */
+ LYEXT_PAR_IDENT, /**< ::lysc_ident */
+ LYEXT_PAR_EXT, /**< ::lysc_ext */
+ LYEXT_PAR_IMPORT, /**< ::lysc_import */
+// LYEXT_PAR_TPDF, /**< ::lysp_tpdf */
+// LYEXT_PAR_EXTINST, /**< ::lysp_ext_instance */
+// LYEXT_PAR_REFINE, /**< ::lysp_refine */
+// LYEXT_PAR_DEVIATION, /**< ::lysp_deviation */
+// LYEXT_PAR_DEVIATE, /**< ::lysp_deviate */
+// LYEXT_PAR_INCLUDE, /**< ::lysp_include */
+// LYEXT_PAR_REVISION, /**< ::lysp_revision */
} LYEXT_PARENT;
/**
+ * @brief Stringify extension instance parent type.
+ * @param[in] type Parent type to stringify.
+ * @return Constant string with the name of the parent statement.
+ */
+const char *lyext_parent2str(LYEXT_PARENT type);
+
+/**
* @brief Enum of substatements in which extension instances can appear.
*/
typedef enum {
@@ -915,10 +1011,9 @@
struct lysc_ext {
const char *name; /**< extension name */
const char *argument; /**< argument name, NULL if not specified */
- const char *dsc; /**< description statement */
- const char *ref; /**< reference statement */
struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
struct lyext_plugin *plugin; /**< Plugin implementing the specific extension */
+ struct lys_module *module; /**< module structure */
uint16_t flags; /**< LYS_STATUS_* value (@ref snodeflags) */
};
@@ -926,7 +1021,7 @@
* @brief YANG extension instance
*/
struct lysc_ext_instance {
- struct lysc_ext *ext; /**< pointer to the extension definition */
+ struct lysc_ext *def; /**< pointer to the extension definition */
void *parent; /**< pointer to the parent element holding the extension instance(s), use
::lysc_ext_instance#parent_type to access the schema element */
const char *argument; /**< optional value of the extension's argument */
@@ -1445,6 +1540,7 @@
struct lysc_node *data; /**< list of module's top-level data nodes (linked list) */
struct lysc_action *rpcs; /**< list of RPCs ([sized array](@ref sizedarrays)) */
struct lysc_notif *notifs; /**< list of notifications ([sized array](@ref sizedarrays)) */
+ struct lysc_ext *extensions; /**< list of the extension definitions ([sized array](@ref sizedarrays)) */
struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
};
@@ -1569,6 +1665,11 @@
from if-feature statements of the compiled schemas and their proper use in case
the module became implemented in future (no matter if implicitly via augment/deviate
or explicitly via ly_ctx_module_implement()). */
+ struct lysc_ext *off_extensions; /**< List of pre-compiled extension definitions of the module in non-implemented modules
+ ([sized array](@ref sizedarrays)). These extensions are prepared to be linked with the extension instances,
+ but they are not implemented (connected with any extension plugin). In case the module become
+ implemented, the list is moved into the compiled module structure and available extension plugins
+ are connected with the appropriate extension definision. */
uint8_t implemented; /**< flag if the module is implemented, not just imported. The module is implemented if
the flag has non-zero value. Specific values are used internally:
@@ -1759,6 +1860,13 @@
LY_ERR lys_value_validate(struct ly_ctx *ctx, const struct lysc_node *node, const char *value, size_t value_len,
ly_clb_resolve_prefix get_prefix, void *get_prefix_data, LYD_FORMAT format);
+/**
+ * @brief Stringify schema nodetype.
+ * @param[in] nodetype Nodetype to stringify.
+ * @return Constant string with the name of the node's type.
+ */
+const char *lys_nodetype2str(uint16_t nodetype);
+
/** @} */
#ifdef __cplusplus
diff --git a/src/tree_schema_compile.c b/src/tree_schema_compile.c
index 6259161..61019e2 100644
--- a/src/tree_schema_compile.c
+++ b/src/tree_schema_compile.c
@@ -24,9 +24,10 @@
#include "dict.h"
#include "log.h"
+#include "plugins_exts.h"
#include "set.h"
#include "plugins_types.h"
-#include "extensions.h"
+#include "plugins_exts_internal.h"
#include "tree.h"
#include "tree_schema.h"
#include "tree_schema_internal.h"
@@ -62,6 +63,16 @@
} \
}
+#define COMPILE_EXTS_GOTO(CTX, EXTS_P, EXT_C, PARENT, PARENT_TYPE, RET, GOTO) \
+ if (EXTS_P) { \
+ LY_ARRAY_CREATE_GOTO((CTX)->ctx, EXT_C, LY_ARRAY_SIZE(EXTS_P), RET, GOTO); \
+ for (uint32_t __exts_iter = 0, __array_offset = LY_ARRAY_SIZE(EXT_C); __exts_iter < LY_ARRAY_SIZE(EXTS_P); ++__exts_iter) { \
+ LY_ARRAY_INCREMENT(EXT_C); \
+ RET = lys_compile_ext(CTX, &(EXTS_P)[__exts_iter], &(EXT_C)[__exts_iter + __array_offset], PARENT, PARENT_TYPE); \
+ LY_CHECK_GOTO(RET != LY_SUCCESS, GOTO); \
+ } \
+ }
+
#define COMPILE_ARRAY_UNIQUE_GOTO(CTX, ARRAY_P, ARRAY_C, ITER, FUNC, RET, GOTO) \
if (ARRAY_P) { \
LY_ARRAY_CREATE_GOTO((CTX)->ctx, ARRAY_C, LY_ARRAY_SIZE(ARRAY_P), RET, GOTO); \
@@ -414,16 +425,18 @@
}
static LY_ERR
-lys_compile_ext(struct lysc_ctx *ctx, struct lysp_ext_instance *ext_p, struct lysc_ext_instance *ext)
+lys_compile_ext(struct lysc_ctx *ctx, struct lysp_ext_instance *ext_p, struct lysc_ext_instance *ext, void *parent, LYEXT_PARENT parent_type)
{
const char *name;
unsigned int u;
const struct lys_module *mod;
- struct lysp_ext *edef = NULL;
+ struct lysc_ext *elist = NULL;
DUP_STRING(ctx->ctx, ext_p->argument, ext->argument);
ext->insubstmt = ext_p->insubstmt;
ext->insubstmt_index = ext_p->insubstmt_index;
+ ext->parent = parent;
+ ext->parent_type = parent_type;
/* get module where the extension definition should be placed */
for (u = 0; ext_p->name[u] != ':'; ++u);
@@ -437,17 +450,100 @@
ext_p->name, mod->name),
LY_EVALID);
name = &ext_p->name[u + 1];
+
/* find the extension definition there */
- for (ext = NULL, u = 0; u < LY_ARRAY_SIZE(mod->parsed->extensions); ++u) {
- if (!strcmp(name, mod->parsed->extensions[u].name)) {
- edef = &mod->parsed->extensions[u];
+ if (mod->off_extensions) {
+ elist = mod->off_extensions;
+ } else {
+ elist = mod->compiled->extensions;
+ }
+ LY_ARRAY_FOR(elist, u) {
+ if (!strcmp(name, elist[u].name)) {
+ ext->def = &elist[u];
break;
}
}
- LY_CHECK_ERR_RET(!edef, LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
- "Extension definition of extension instance \"%s\" not found.", ext_p->name),
+ LY_CHECK_ERR_RET(!ext->def,
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Extension definition of extension instance \"%s\" not found.", ext_p->name),
LY_EVALID);
- /* TODO extension plugins */
+
+ if (ext->def->plugin && ext->def->plugin->compile) {
+ LY_CHECK_RET(ext->def->plugin->compile(ctx, ext_p, ext),LY_EVALID);
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Fill in the prepared compiled extensions definition structure according to the parsed extension definition.
+ */
+static LY_ERR
+lys_compile_extension(struct lysc_ctx *ctx, struct lysp_ext *ext_p, struct lysc_ext *ext)
+{
+ LY_ERR ret = LY_SUCCESS;
+
+ DUP_STRING(ctx->ctx, ext_p->name, ext->name);
+ DUP_STRING(ctx->ctx, ext_p->argument, ext->argument);
+ ext->module = ctx->mod_def;
+ COMPILE_EXTS_GOTO(ctx, ext_p->exts, ext->exts, ext, LYEXT_PAR_EXT, ret, done);
+
+done:
+ return ret;
+}
+
+/**
+ * @brief Link the extensions definitions with the available extension plugins.
+ *
+ * This is done only in the compiled (implemented) module. Extensions of a non-implemented modules
+ * are not connected with even available extension plugins.
+ *
+ * @param[in] extensions List of extensions to be processed ([sized array](@ref sizedarrays)).
+ */
+static void
+lys_compile_extension_plugins(struct lysc_ext *extensions)
+{
+ unsigned int u;
+
+ LY_ARRAY_FOR(extensions, u) {
+ extensions[u].plugin = lyext_get_plugin(&extensions[u]);
+ }
+}
+
+LY_ERR
+lys_extension_precompile(struct lysc_ctx *ctx_sc, struct ly_ctx *ctx, struct lys_module *module,
+ struct lysp_ext *extensions_p, struct lysc_ext **extensions)
+{
+ unsigned int offset = 0, u;
+ struct lysc_ctx context = {0};
+
+ assert(ctx_sc || ctx);
+
+ if (!ctx_sc) {
+ context.ctx = ctx;
+ context.mod = module;
+ context.path_len = 1;
+ context.path[0] = '/';
+ ctx_sc = &context;
+ }
+
+ if (!extensions_p) {
+ return LY_SUCCESS;
+ }
+ if (*extensions) {
+ offset = LY_ARRAY_SIZE(*extensions);
+ }
+
+ lysc_update_path(ctx_sc, NULL, "{extension}");
+ LY_ARRAY_CREATE_RET(ctx_sc->ctx, *extensions, LY_ARRAY_SIZE(extensions_p), LY_EMEM);
+ LY_ARRAY_FOR(extensions_p, u) {
+ lysc_update_path(ctx_sc, NULL, extensions_p[u].name);
+ LY_ARRAY_INCREMENT(*extensions);
+ COMPILE_CHECK_UNIQUENESS(ctx_sc, *extensions, name, &(*extensions)[offset + u], "extension", extensions_p[u].name);
+ LY_CHECK_RET(lys_compile_extension(ctx_sc, &extensions_p[u], &(*extensions)[offset + u]));
+ lysc_update_path(ctx_sc, NULL, NULL);
+ }
+ lysc_update_path(ctx_sc, NULL, NULL);
return LY_SUCCESS;
}
@@ -655,7 +751,6 @@
static LY_ERR
lys_compile_when(struct lysc_ctx *ctx, struct lysp_when *when_p, struct lysc_when **when)
{
- unsigned int u;
LY_ERR ret = LY_SUCCESS;
*when = calloc(1, sizeof **when);
@@ -664,7 +759,7 @@
DUP_STRING(ctx->ctx, when_p->dsc, (*when)->dsc);
DUP_STRING(ctx->ctx, when_p->ref, (*when)->ref);
LY_CHECK_ERR_GOTO(!(*when)->cond, ret = ly_errcode(ctx->ctx), done);
- COMPILE_ARRAY_GOTO(ctx, when_p->exts, (*when)->exts, u, lys_compile_ext, ret, done);
+ COMPILE_EXTS_GOTO(ctx, when_p->exts, (*when)->exts, (*when), LYEXT_PAR_WHEN, ret, done);
done:
return ret;
@@ -680,7 +775,6 @@
static LY_ERR
lys_compile_must(struct lysc_ctx *ctx, struct lysp_restr *must_p, struct lysc_must *must)
{
- unsigned int u;
LY_ERR ret = LY_SUCCESS;
must->cond = lyxp_expr_parse(ctx->ctx, must_p->arg);
@@ -690,7 +784,7 @@
DUP_STRING(ctx->ctx, must_p->emsg, must->emsg);
DUP_STRING(ctx->ctx, must_p->dsc, must->dsc);
DUP_STRING(ctx->ctx, must_p->ref, must->ref);
- COMPILE_ARRAY_GOTO(ctx, must_p->exts, must->exts, u, lys_compile_ext, ret, done);
+ COMPILE_EXTS_GOTO(ctx, must_p->exts, must->exts, must, LYEXT_PAR_MUST, ret, done);
done:
return ret;
@@ -706,12 +800,11 @@
static LY_ERR
lys_compile_import(struct lysc_ctx *ctx, struct lysp_import *imp_p, struct lysc_import *imp)
{
- unsigned int u;
struct lys_module *mod = NULL;
LY_ERR ret = LY_SUCCESS;
DUP_STRING(ctx->ctx, imp_p->prefix, imp->prefix);
- COMPILE_ARRAY_GOTO(ctx, imp_p->exts, imp->exts, u, lys_compile_ext, ret, done);
+ COMPILE_EXTS_GOTO(ctx, imp_p->exts, imp->exts, imp, LYEXT_PAR_IMPORT, ret, done);
imp->module = imp_p->module;
/* make sure that we have the parsed version (lysp_) of the imported module to import groupings or typedefs.
@@ -767,7 +860,7 @@
ident->module = ctx->mod;
COMPILE_ARRAY_GOTO(ctx, ident_p->iffeatures, ident->iffeatures, u, lys_compile_iffeature, ret, done);
/* backlings (derived) can be added no sooner than when all the identities in the current module are present */
- COMPILE_ARRAY_GOTO(ctx, ident_p->exts, ident->exts, u, lys_compile_ext, ret, done);
+ COMPILE_EXTS_GOTO(ctx, ident_p->exts, ident->exts, ident, LYEXT_PAR_IDENT, ret, done);
ident->flags = ident_p->flags;
lysc_update_path(ctx, NULL, NULL);
@@ -1062,7 +1155,7 @@
lysc_update_path(ctx, NULL, feature_p->name);
/* finish compilation started in lys_feature_precompile() */
- COMPILE_ARRAY_GOTO(ctx, feature_p->exts, feature->exts, u, lys_compile_ext, ret, done);
+ COMPILE_EXTS_GOTO(ctx, feature_p->exts, feature->exts, feature, LYEXT_PAR_FEATURE, ret, done);
COMPILE_ARRAY_GOTO(ctx, feature_p->iffeatures, feature->iffeatures, u, lys_compile_iffeature, ret, done);
if (feature->iffeatures) {
for (u = 0; u < LY_ARRAY_SIZE(feature->iffeatures); ++u) {
@@ -1859,7 +1952,7 @@
struct lysc_pattern **base_patterns, struct lysc_pattern ***patterns)
{
struct lysc_pattern **pattern;
- unsigned int u, v;
+ unsigned int u;
LY_ERR ret = LY_SUCCESS;
/* first, copy the patterns from the base type */
@@ -1884,7 +1977,7 @@
DUP_STRING(ctx->ctx, patterns_p[u].emsg, (*pattern)->emsg);
DUP_STRING(ctx->ctx, patterns_p[u].dsc, (*pattern)->dsc);
DUP_STRING(ctx->ctx, patterns_p[u].ref, (*pattern)->ref);
- COMPILE_ARRAY_GOTO(ctx, patterns_p[u].exts, (*pattern)->exts, v, lys_compile_ext, ret, done);
+ COMPILE_EXTS_GOTO(ctx, patterns_p[u].exts, (*pattern)->exts, (*pattern), LYEXT_PAR_PATTERN, ret, done);
}
done:
return ret;
@@ -2055,7 +2148,7 @@
}
COMPILE_ARRAY_GOTO(ctx, enums_p[u].iffeatures, e->iffeatures, v, lys_compile_iffeature, ret, done);
- COMPILE_ARRAY_GOTO(ctx, enums_p[u].exts, e->exts, v, lys_compile_ext, ret, done);
+ COMPILE_EXTS_GOTO(ctx, enums_p[u].exts, e->exts, e, basetype == LY_TYPE_ENUM ? LYEXT_PAR_TYPE_ENUM : LYEXT_PAR_TYPE_BIT, ret, done);
if (basetype == LY_TYPE_BITS) {
/* keep bits ordered by position */
@@ -2611,7 +2704,7 @@
LY_CHECK_RET(lys_compile_type_range(ctx, type_p->length, basetype, 1, 0,
base ? ((struct lysc_type_bin*)base)->length : NULL, &bin->length));
if (!tpdfname) {
- COMPILE_ARRAY_GOTO(ctx, type_p->length->exts, bin->length->exts, u, lys_compile_ext, ret, done);
+ COMPILE_EXTS_GOTO(ctx, type_p->length->exts, bin->length->exts, bin->length, LYEXT_PAR_LENGTH, ret, done);
}
}
@@ -2682,7 +2775,7 @@
LY_CHECK_RET(lys_compile_type_range(ctx, type_p->range, basetype, 0, dec->fraction_digits,
base ? ((struct lysc_type_dec*)base)->range : NULL, &dec->range));
if (!tpdfname) {
- COMPILE_ARRAY_GOTO(ctx, type_p->range->exts, dec->range->exts, u, lys_compile_ext, ret, done);
+ COMPILE_EXTS_GOTO(ctx, type_p->range->exts, dec->range->exts, dec->range, LYEXT_PAR_RANGE, ret, done);
}
}
@@ -2699,7 +2792,7 @@
LY_CHECK_RET(lys_compile_type_range(ctx, type_p->length, basetype, 1, 0,
base ? ((struct lysc_type_str*)base)->length : NULL, &str->length));
if (!tpdfname) {
- COMPILE_ARRAY_GOTO(ctx, type_p->length->exts, str->length->exts, u, lys_compile_ext, ret, done);
+ COMPILE_EXTS_GOTO(ctx, type_p->length->exts, str->length->exts, str->length, LYEXT_PAR_LENGTH, ret, done);
}
} else if (base && ((struct lysc_type_str*)base)->length) {
str->length = lysc_range_dup(ctx->ctx, ((struct lysc_type_str*)base)->length);
@@ -2759,7 +2852,7 @@
LY_CHECK_RET(lys_compile_type_range(ctx, type_p->range, basetype, 0, 0,
base ? ((struct lysc_type_num*)base)->range : NULL, &num->range));
if (!tpdfname) {
- COMPILE_ARRAY_GOTO(ctx, type_p->range->exts, num->range->exts, u, lys_compile_ext, ret, done);
+ COMPILE_EXTS_GOTO(ctx, type_p->range->exts, num->range->exts, num->range, LYEXT_PAR_RANGE, ret, done);
}
}
@@ -3206,7 +3299,7 @@
LY_CHECK_GOTO(ret, cleanup);
}
- COMPILE_ARRAY_GOTO(ctx, type_p->exts, (*type)->exts, u, lys_compile_ext, ret, cleanup);
+ COMPILE_EXTS_GOTO(ctx, type_p->exts, (*type)->exts, (*type), LYEXT_PAR_TYPE, ret, cleanup);
cleanup:
ly_set_erase(&tpdf_chain, free);
@@ -3357,12 +3450,12 @@
DUP_STRING(ctx->ctx, action_p->dsc, action->dsc);
DUP_STRING(ctx->ctx, action_p->ref, action->ref);
COMPILE_ARRAY_GOTO(ctx, action_p->iffeatures, action->iffeatures, u, lys_compile_iffeature, ret, cleanup);
- COMPILE_ARRAY_GOTO(ctx, action_p->exts, action->exts, u, lys_compile_ext, ret, cleanup);
+ COMPILE_EXTS_GOTO(ctx, action_p->exts, action->exts, action, LYEXT_PAR_NODE, ret, cleanup);
/* input */
lysc_update_path(ctx, (struct lysc_node*)action, "input");
COMPILE_ARRAY_GOTO(ctx, action_p->input.musts, action->input.musts, u, lys_compile_must, ret, cleanup);
- COMPILE_ARRAY_GOTO(ctx, action_p->input.exts, action->input_exts, u, lys_compile_ext, ret, cleanup);
+ COMPILE_EXTS_GOTO(ctx, action_p->input.exts, action->input_exts, &action->input, LYEXT_PAR_INPUT, ret, cleanup);
ctx->options |= LYSC_OPT_RPC_INPUT;
LY_LIST_FOR(action_p->input.data, child_p) {
LY_CHECK_RET(lys_compile_node(ctx, child_p, (struct lysc_node*)action, uses_status));
@@ -3373,7 +3466,7 @@
/* output */
lysc_update_path(ctx, (struct lysc_node*)action, "output");
COMPILE_ARRAY_GOTO(ctx, action_p->output.musts, action->output.musts, u, lys_compile_must, ret, cleanup);
- COMPILE_ARRAY_GOTO(ctx, action_p->output.exts, action->output_exts, u, lys_compile_ext, ret, cleanup);
+ COMPILE_EXTS_GOTO(ctx, action_p->output.exts, action->output_exts, &action->output, LYEXT_PAR_OUTPUT, ret, cleanup);
ctx->options |= LYSC_OPT_RPC_OUTPUT;
LY_LIST_FOR(action_p->output.data, child_p) {
LY_CHECK_RET(lys_compile_node(ctx, child_p, (struct lysc_node*)action, uses_status));
@@ -3437,8 +3530,8 @@
DUP_STRING(ctx->ctx, notif_p->dsc, notif->dsc);
DUP_STRING(ctx->ctx, notif_p->ref, notif->ref);
COMPILE_ARRAY_GOTO(ctx, notif_p->iffeatures, notif->iffeatures, u, lys_compile_iffeature, ret, cleanup);
- COMPILE_ARRAY_GOTO(ctx, notif_p->exts, notif->exts, u, lys_compile_ext, ret, cleanup);
COMPILE_ARRAY_GOTO(ctx, notif_p->musts, notif->musts, u, lys_compile_must, ret, cleanup);
+ COMPILE_EXTS_GOTO(ctx, notif_p->exts, notif->exts, notif, LYEXT_PAR_NODE, ret, cleanup);
ctx->options |= LYSC_OPT_NOTIFICATION;
LY_LIST_FOR(notif_p->data, child_p) {
@@ -5376,11 +5469,12 @@
(*when)->context = lysc_xpath_context(node);
}
COMPILE_ARRAY_GOTO(ctx, node_p->iffeatures, node->iffeatures, u, lys_compile_iffeature, ret, error);
- COMPILE_ARRAY_GOTO(ctx, node_p->exts, node->exts, u, lys_compile_ext, ret, error);
/* nodetype-specific part */
LY_CHECK_GOTO(node_compile_spec(ctx, node_p, node), error);
+ COMPILE_EXTS_GOTO(ctx, node_p->exts, node->exts, node, LYEXT_PAR_NODE, ret, error);
+
/* inherit LYS_MAND_TRUE in parent containers */
if (node->flags & LYS_MAND_TRUE) {
lys_compile_mandatory_parents(parent, 1);
@@ -6513,8 +6607,12 @@
/* features are compiled directly into the compiled module structure,
* but it must be done in two steps to allow forward references (via if-feature) between the features themselves.
* The features compilation is finished in the main module (lys_compile()). */
- ret = lys_feature_precompile(ctx, NULL, NULL, submod->features,
- mainmod->mod->off_features ? &mainmod->mod->off_features : &mainmod->features);
+ ret = lys_feature_precompile(ctx, NULL, NULL, submod->features, &mainmod->features);
+ LY_CHECK_GOTO(ret, error);
+ }
+ if (!mainmod->mod->off_extensions) {
+ /* extensions are compiled directly into the compiled module structure, compilation is finished in the main module (lys_compile()). */
+ ret = lys_extension_precompile(ctx, NULL, NULL, submod->extensions, &mainmod->extensions);
LY_CHECK_GOTO(ret, error);
}
@@ -6574,6 +6672,8 @@
ret = lys_compile_submodule(&ctx, &sp->includes[u]);
LY_CHECK_GOTO(ret != LY_SUCCESS, error);
}
+
+ /* features */
if (mod->off_features) {
/* there is already precompiled array of features */
mod_c->features = mod->off_features;
@@ -6602,6 +6702,20 @@
}
lysc_update_path(&ctx, NULL, NULL);
+ /* extensions */
+ /* 2-steps: a) prepare compiled structures and ... */
+ if (mod->off_extensions) {
+ /* there is already precompiled array of extension definitions */
+ mod_c->extensions = mod->off_extensions;
+ mod->off_extensions = NULL;
+ } else {
+ /* extension definitions are compiled directly into the compiled module structure */
+ ret = lys_extension_precompile(&ctx, NULL, NULL, sp->extensions, &mod_c->extensions);
+ }
+ /* ... b) connect the extension definitions with the appropriate extension plugins */
+ lys_compile_extension_plugins(mod_c->extensions);
+
+ /* identities */
lysc_update_path(&ctx, NULL, "{identity}");
COMPILE_ARRAY_UNIQUE_GOTO(&ctx, sp->identities, mod_c->identities, u, lys_compile_identity, ret, error);
if (sp->identities) {
@@ -6630,7 +6744,8 @@
ret = lys_compile_deviations(&ctx, sp);
LY_CHECK_GOTO(ret, error);
- COMPILE_ARRAY_GOTO(&ctx, sp->exts, mod_c->exts, u, lys_compile_ext, ret, error);
+ /* extension instances TODO cover extension instances from submodules */
+ COMPILE_EXTS_GOTO(&ctx, sp->exts, mod_c->exts, mod_c, LYEXT_PAR_MODULE, ret, error);
/* validate leafref's paths and when/must xpaths */
/* for leafref, we need 2 rounds - first detects circular chain by storing the first referred type (which
diff --git a/src/tree_schema_free.c b/src/tree_schema_free.c
index adbc70b..64728f2 100644
--- a/src/tree_schema_free.c
+++ b/src/tree_schema_free.c
@@ -472,6 +472,14 @@
}
void
+lysc_extension_free(struct ly_ctx *ctx, struct lysc_ext *ext)
+{
+ FREE_STRING(ctx, ext->name);
+ FREE_STRING(ctx, ext->argument);
+ FREE_ARRAY(ctx, ext->exts, lysc_ext_instance_free);
+}
+
+void
lysc_iffeature_free(struct ly_ctx *UNUSED(ctx), struct lysc_iffeature *iff)
{
LY_ARRAY_FREE(iff->features);
@@ -822,6 +830,7 @@
FREE_ARRAY(ctx, module->rpcs, lysc_action_free);
FREE_ARRAY(ctx, module->notifs, lysc_notif_free);
+ FREE_ARRAY(ctx, module->extensions, lysc_extension_free);
FREE_ARRAY(ctx, module->exts, lysc_ext_instance_free);
free(module);
@@ -847,6 +856,7 @@
lysc_module_free(module->compiled, private_destructor);
FREE_ARRAY(module->ctx, module->off_features, lysc_feature_free);
+ FREE_ARRAY(module->ctx, module->off_extensions, lysc_extension_free);
lysp_module_free(module->parsed);
FREE_STRING(module->ctx, module->name);
diff --git a/src/tree_schema_helpers.c b/src/tree_schema_helpers.c
index a3b201d..2f8a2b8 100644
--- a/src/tree_schema_helpers.c
+++ b/src/tree_schema_helpers.c
@@ -26,9 +26,9 @@
#include "context.h"
#include "dict.h"
-#include "extensions.h"
#include "hash_table.h"
#include "log.h"
+#include "plugins_exts.h"
#include "set.h"
#include "tree.h"
#include "tree_schema.h"
@@ -925,7 +925,7 @@
return NULL;
}
-const char *
+API const char *
lys_nodetype2str(uint16_t nodetype)
{
switch(nodetype) {
diff --git a/src/tree_schema_internal.h b/src/tree_schema_internal.h
index c81a9fc..8e9e029 100644
--- a/src/tree_schema_internal.h
+++ b/src/tree_schema_internal.h
@@ -17,9 +17,9 @@
#include <stdint.h>
+#include "plugins_exts.h"
#include "set.h"
#include "tree_schema.h"
-#include "extensions.h"
#define LOGVAL_YANG(CTX, ...) LOGVAL((CTX)->ctx, LY_VLOG_LINE, &(CTX)->line, __VA_ARGS__)
@@ -366,13 +366,6 @@
const char *lys_prefix_find_module(const struct lys_module *mod, const struct lys_module *import);
/**
- * @brief Stringify schema nodetype.
- * @param[in] nodetype Nodetype to stringify.
- * @return Constant string with the name of the node's type.
- */
-const char *lys_nodetype2str(uint16_t nodetype);
-
-/**
* @brief Stringify YANG built-in type.
* @param[in] basetype Built-in tyep ID to stringify.
* @return Constant string with the name of the built-in type.
@@ -530,13 +523,14 @@
* @param[in] ctx_sc Compile context - alternative to the combination of @p ctx and @p module.
* @param[in] ctx libyang context.
* @param[in] module Module of the features.
- * @param[in] features_p Array if the parsed features definitions to precompile.
+ * @param[in] features_p Array of the parsed features definitions to precompile.
* @param[in,out] features Pointer to the storage of the (pre)compiled features array where the new features are
* supposed to be added. The storage is supposed to be initiated to NULL when the first parsed features are going
* to be processed.
* @return LY_ERR value.
*/
-LY_ERR lys_feature_precompile(struct lysc_ctx *ctx_sc, struct ly_ctx *ctx, struct lys_module *module, struct lysp_feature *features_p, struct lysc_feature **features);
+LY_ERR lys_feature_precompile(struct lysc_ctx *ctx_sc, struct ly_ctx *ctx, struct lys_module *module,
+ struct lysp_feature *features_p, struct lysc_feature **features);
/**
* @brief Get the @ref ifftokens from the given position in the 2bits array
@@ -547,6 +541,21 @@
uint8_t lysc_iff_getop(uint8_t *list, int pos);
/**
+ * @brief Internal wrapper around lys_compile_extension() to be able to prepare list of compiled extension definition
+ * even for the parsed (not-implemented) module - see lys_module::off_extensions.
+ *
+ * @param[in] ctx_sc Compile context - alternative to the combination of @p ctx and @p module.
+ * @param[in] ctx libyang context.
+ * @param[in] module Module of the extensions.
+ * @param[in] extensions_p Array of the parsed extension definitions to precompile.
+ * @param[in,out] extensions Pointer to the storage of the (pre)compiled extensions array where the new extensions are
+ * supposed to be added. The storage is supposed to be initiated to NULL when the first parsed extensions are going
+ * to be processed.
+ * @return LY_ERR value.
+ */
+LY_ERR lys_extension_precompile(struct lysc_ctx *ctx_sc, struct ly_ctx *ctx, struct lys_module *module,
+ struct lysp_ext *extensions_p, struct lysc_ext **extensions);
+/**
* @brief Macro to free [sized array](@ref sizedarrays) of items using the provided free function. The ARRAY itself is also freed,
* but the memory is not sanitized.
*/