diff options
Diffstat (limited to '')
-rw-r--r-- | src/plugins_exts.c | 680 |
1 files changed, 680 insertions, 0 deletions
diff --git a/src/plugins_exts.c b/src/plugins_exts.c new file mode 100644 index 0000000..00970fa --- /dev/null +++ b/src/plugins_exts.c @@ -0,0 +1,680 @@ +/** + * @file plugins_exts.c + * @author Radek Krejci <rkrejci@cesnet.cz> + * @author Michal Vasko <mvasko@cesnet.cz> + * @brief helper functions for extension plugins + * + * Copyright (c) 2019 - 2022 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 "plugins_exts.h" + +#include <assert.h> +#include <stdint.h> +#include <stdlib.h> + +#include "common.h" +#include "dict.h" +#include "parser_internal.h" +#include "printer_internal.h" +#include "schema_compile.h" +#include "schema_compile_amend.h" +#include "schema_compile_node.h" +#include "schema_features.h" +#include "tree_schema_internal.h" + +LIBYANG_API_DEF const struct lysp_module * +lyplg_ext_parse_get_cur_pmod(const struct lysp_ctx *pctx) +{ + return PARSER_CUR_PMOD(pctx); +} + +LIBYANG_API_DEF LY_ERR +lyplg_ext_parse_extension_instance(struct lysp_ctx *pctx, struct lysp_ext_instance *ext) +{ + LY_ERR rc = LY_SUCCESS; + LY_ARRAY_COUNT_TYPE u; + struct lysp_stmt *stmt; + + /* check for invalid substatements */ + LY_LIST_FOR(ext->child, stmt) { + if (stmt->flags & (LYS_YIN_ATTR | LYS_YIN_ARGUMENT)) { + continue; + } + LY_ARRAY_FOR(ext->substmts, u) { + if (ext->substmts[u].stmt == stmt->kw) { + break; + } + } + if (u == LY_ARRAY_COUNT(ext->substmts)) { + LOGVAL(PARSER_CTX(pctx), LYVE_SYNTAX_YANG, "Invalid keyword \"%s\" as a child of \"%s%s%s\" extension instance.", + stmt->stmt, ext->name, ext->argument ? " " : "", ext->argument ? ext->argument : ""); + rc = LY_EVALID; + goto cleanup; + } + } + + /* parse all the known statements */ + LY_ARRAY_FOR(ext->substmts, u) { + LY_LIST_FOR(ext->child, stmt) { + if (ext->substmts[u].stmt != stmt->kw) { + continue; + } + + if ((rc = lys_parse_ext_instance_stmt(pctx, &ext->substmts[u], stmt))) { + goto cleanup; + } + } + } + +cleanup: + return rc; +} + +/** + * @brief Compile an instance extension statement. + * + * @param[in] ctx Compile context. + * @param[in] parsed Parsed ext instance substatement structure. + * @param[in] ext Compiled ext instance. + * @param[in] substmt Compled ext instance substatement info. + * @return LY_ERR value. + */ +static LY_ERR +lys_compile_ext_instance_stmt(struct lysc_ctx *ctx, const void *parsed, struct lysc_ext_instance *ext, + struct lysc_ext_substmt *substmt) +{ + LY_ERR rc = LY_SUCCESS; + ly_bool length_restr = 0; + LY_DATA_TYPE basetype; + + /* compilation wthout any storage */ + if (substmt->stmt == LY_STMT_IF_FEATURE) { + ly_bool enabled; + + /* evaluate */ + LY_CHECK_GOTO(rc = lys_eval_iffeatures(ctx->ctx, parsed, &enabled), cleanup); + if (!enabled) { + /* it is disabled, remove the whole extension instance */ + rc = LY_ENOT; + } + } + + if (!substmt->storage || !parsed) { + /* nothing to store or nothing parsed to compile */ + goto cleanup; + } + + switch (substmt->stmt) { + case LY_STMT_NOTIFICATION: + case LY_STMT_INPUT: + case LY_STMT_OUTPUT: + case LY_STMT_ACTION: + case LY_STMT_RPC: + case LY_STMT_ANYDATA: + case LY_STMT_ANYXML: + case LY_STMT_CASE: + case LY_STMT_CHOICE: + case LY_STMT_CONTAINER: + case LY_STMT_LEAF: + case LY_STMT_LEAF_LIST: + case LY_STMT_LIST: + case LY_STMT_USES: { + const uint16_t flags; + struct lysp_node *pnodes, *pnode; + struct lysc_node *node; + + lyplg_ext_get_storage(ext, LY_STMT_STATUS, sizeof flags, (const void **)&flags); + pnodes = (struct lysp_node *)parsed; + + /* compile nodes */ + LY_LIST_FOR(pnodes, pnode) { + if (pnode->nodetype & (LYS_INPUT | LYS_OUTPUT)) { + /* manual compile */ + node = calloc(1, sizeof(struct lysc_node_action_inout)); + LY_CHECK_ERR_GOTO(!node, LOGMEM(ctx->ctx); rc = LY_EMEM, cleanup); + LY_CHECK_GOTO(rc = lys_compile_node_action_inout(ctx, pnode, node), cleanup); + LY_CHECK_GOTO(rc = lys_compile_node_connect(ctx, NULL, node), cleanup); + } else { + /* ctx->ext substatement storage is used as the document root */ + LY_CHECK_GOTO(rc = lys_compile_node(ctx, pnode, NULL, flags, NULL), cleanup); + } + } + break; + } + case LY_STMT_ARGUMENT: + case LY_STMT_CONTACT: + case LY_STMT_DESCRIPTION: + case LY_STMT_ERROR_APP_TAG: + case LY_STMT_ERROR_MESSAGE: + case LY_STMT_KEY: + case LY_STMT_MODIFIER: + case LY_STMT_NAMESPACE: + case LY_STMT_ORGANIZATION: + case LY_STMT_PRESENCE: + case LY_STMT_REFERENCE: + case LY_STMT_UNITS: + /* just make a copy */ + LY_CHECK_GOTO(rc = lydict_insert(ctx->ctx, parsed, 0, substmt->storage), cleanup); + break; + + case LY_STMT_BIT: + basetype = LY_TYPE_BITS; + /* fallthrough */ + case LY_STMT_ENUM: + if (substmt->stmt == LY_STMT_ENUM) { + basetype = LY_TYPE_ENUM; + } + + /* compile */ + LY_CHECK_GOTO(rc = lys_compile_type_enums(ctx, parsed, basetype, NULL, substmt->storage), cleanup); + break; + + case LY_STMT_CONFIG: { + uint16_t flags; + + if (!(ctx->compile_opts & LYS_COMPILE_NO_CONFIG)) { + memcpy(&flags, &parsed, 2); + if (flags & LYS_CONFIG_MASK) { + /* explicitly set */ + flags |= LYS_SET_CONFIG; + } else if (ext->parent_stmt & LY_STMT_DATA_NODE_MASK) { + /* inherit */ + flags = ((struct lysc_node *)ext->parent)->flags & LYS_CONFIG_MASK; + } else { + /* default config */ + flags = LYS_CONFIG_W; + } + memcpy(substmt->storage, &flags, 2); + } /* else leave zero */ + break; + } + case LY_STMT_MUST: { + const struct lysp_restr *restrs = parsed; + + /* sized array */ + COMPILE_ARRAY_GOTO(ctx, restrs, *(struct lysc_must **)substmt->storage, lys_compile_must, rc, cleanup); + break; + } + case LY_STMT_WHEN: { + const uint16_t flags; + const struct lysp_when *when = parsed; + + /* read compiled status */ + lyplg_ext_get_storage(ext, LY_STMT_STATUS, sizeof flags, (const void **)&flags); + + /* compile */ + LY_CHECK_GOTO(rc = lys_compile_when(ctx, when, flags, NULL, NULL, NULL, substmt->storage), cleanup); + break; + } + case LY_STMT_FRACTION_DIGITS: + case LY_STMT_REQUIRE_INSTANCE: + /* just make a copy */ + memcpy(substmt->storage, &parsed, 1); + break; + + case LY_STMT_MANDATORY: + case LY_STMT_ORDERED_BY: + case LY_STMT_STATUS: + /* just make a copy */ + memcpy(substmt->storage, &parsed, 2); + break; + + case LY_STMT_MAX_ELEMENTS: + case LY_STMT_MIN_ELEMENTS: + /* just make a copy */ + memcpy(substmt->storage, &parsed, 4); + break; + + case LY_STMT_POSITION: + case LY_STMT_VALUE: + /* just make a copy */ + memcpy(substmt->storage, &parsed, 8); + break; + + case LY_STMT_IDENTITY: + /* compile */ + LY_CHECK_GOTO(rc = lys_identity_precompile(ctx, NULL, NULL, parsed, substmt->storage), cleanup); + break; + + case LY_STMT_LENGTH: + length_restr = 1; + /* fallthrough */ + case LY_STMT_RANGE: + /* compile, use uint64 default range */ + LY_CHECK_GOTO(rc = lys_compile_type_range(ctx, parsed, LY_TYPE_UINT64, length_restr, 0, NULL, substmt->storage), + cleanup); + break; + + case LY_STMT_PATTERN: + /* compile */ + LY_CHECK_GOTO(rc = lys_compile_type_patterns(ctx, parsed, NULL, substmt->storage), cleanup); + break; + + case LY_STMT_TYPE: { + const uint16_t flags; + const char *units; + const struct lysp_type *ptype = parsed; + + /* read compiled info */ + lyplg_ext_get_storage(ext, LY_STMT_STATUS, sizeof flags, (const void **)&flags); + lyplg_ext_get_storage(ext, LY_STMT_UNITS, sizeof units, (const void **)&units); + + /* compile */ + LY_CHECK_GOTO(rc = lys_compile_type(ctx, NULL, flags, ext->def->name, ptype, substmt->storage, &units, NULL), cleanup); + break; + } + case LY_STMT_EXTENSION_INSTANCE: { + struct lysp_ext_instance *extps = (struct lysp_ext_instance *)parsed; + + /* compile sized array */ + COMPILE_EXTS_GOTO(ctx, extps, *(struct lysc_ext_instance **)substmt->storage, ext, rc, cleanup); + break; + } + case LY_STMT_AUGMENT: + case LY_STMT_GROUPING: + case LY_STMT_BASE: + case LY_STMT_BELONGS_TO: + case LY_STMT_DEFAULT: + case LY_STMT_DEVIATE: + case LY_STMT_DEVIATION: + case LY_STMT_EXTENSION: + case LY_STMT_FEATURE: + case LY_STMT_IF_FEATURE: + case LY_STMT_IMPORT: + case LY_STMT_INCLUDE: + case LY_STMT_MODULE: + case LY_STMT_PATH: + case LY_STMT_PREFIX: + case LY_STMT_REFINE: + case LY_STMT_REVISION: + case LY_STMT_REVISION_DATE: + case LY_STMT_SUBMODULE: + case LY_STMT_TYPEDEF: + case LY_STMT_UNIQUE: + case LY_STMT_YANG_VERSION: + case LY_STMT_YIN_ELEMENT: + LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG, "Statement \"%s\" compilation is not supported.", lyplg_ext_stmt2str(substmt->stmt)); + rc = LY_EVALID; + goto cleanup; + + default: + LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG, "Statement \"%s\" is not supported as an extension " + "(found in \"%s%s%s\") substatement.", lyplg_ext_stmt2str(substmt->stmt), ext->def->name, + ext->argument ? " " : "", ext->argument ? ext->argument : ""); + rc = LY_EVALID; + goto cleanup; + } + +cleanup: + return rc; +} + +LIBYANG_API_DEF LY_ERR +lyplg_ext_compile_extension_instance(struct lysc_ctx *ctx, const struct lysp_ext_instance *extp, + struct lysc_ext_instance *ext) +{ + LY_ERR rc = LY_SUCCESS; + LY_ARRAY_COUNT_TYPE u, v; + enum ly_stmt stmtp; + const void *storagep; + struct ly_set storagep_compiled = {0}; + + LY_CHECK_ARG_RET(ctx ? ctx->ctx : NULL, ctx, extp, ext, LY_EINVAL); + + /* note into the compile context that we are processing extension now */ + ctx->ext = ext; + + LY_ARRAY_FOR(extp->substmts, u) { + stmtp = extp->substmts[u].stmt; + storagep = *(void **)extp->substmts[u].storage; + + if (ly_set_contains(&storagep_compiled, storagep, NULL)) { + /* this parsed statement has already been compiled (for example, if it is a linked list of parsed nodes) */ + continue; + } + + LY_ARRAY_FOR(ext->substmts, v) { + if (stmtp != ext->substmts[v].stmt) { + continue; + } + + if ((rc = lys_compile_ext_instance_stmt(ctx, storagep, ext, &ext->substmts[v]))) { + goto cleanup; + } + + /* parsed substatement compiled */ + break; + } + + /* compiled */ + ly_set_add(&storagep_compiled, storagep, 1, NULL); + } + +cleanup: + ctx->ext = NULL; + ly_set_erase(&storagep_compiled, NULL); + return rc; +} + +LIBYANG_API_DEF struct ly_ctx * +lyplg_ext_compile_get_ctx(const struct lysc_ctx *ctx) +{ + return ctx->ctx; +} + +LIBYANG_API_DEF uint32_t * +lyplg_ext_compile_get_options(const struct lysc_ctx *ctx) +{ + return &((struct lysc_ctx *)ctx)->compile_opts; +} + +LIBYANG_API_DEF const struct lys_module * +lyplg_ext_compile_get_cur_mod(const struct lysc_ctx *ctx) +{ + return ctx->cur_mod; +} + +LIBYANG_API_DEF struct lysp_module * +lyplg_ext_compile_get_pmod(const struct lysc_ctx *ctx) +{ + return ctx->pmod; +} + +LIBYANG_API_DEF struct ly_out ** +lyplg_ext_print_get_out(const struct lyspr_ctx *ctx) +{ + return &((struct lyspr_ctx *)ctx)->out; +} + +LIBYANG_API_DEF uint32_t * +lyplg_ext_print_get_options(const struct lyspr_ctx *ctx) +{ + return &((struct lyspr_ctx *)ctx)->options; +} + +LIBYANG_API_DEF uint16_t * +lyplg_ext_print_get_level(const struct lyspr_ctx *ctx) +{ + return &((struct lyspr_ctx *)ctx)->level; +} + +LIBYANG_API_DECL LY_ERR +lyplg_ext_sprinter_ctree_add_ext_nodes(const struct lyspr_tree_ctx *ctx, struct lysc_ext_instance *ext, + lyplg_ext_sprinter_ctree_override_clb clb) +{ + LY_ERR rc = LY_SUCCESS; + uint32_t i; + struct lysc_node *schema; + + LY_CHECK_ARG_RET2(NULL, ctx, ext, LY_EINVAL); + + LY_ARRAY_FOR(ext->substmts, i) { + switch (ext->substmts[i].stmt) { + case LY_STMT_NOTIFICATION: + case LY_STMT_INPUT: + case LY_STMT_OUTPUT: + case LY_STMT_ACTION: + case LY_STMT_RPC: + case LY_STMT_ANYDATA: + case LY_STMT_ANYXML: + case LY_STMT_CASE: + case LY_STMT_CHOICE: + case LY_STMT_CONTAINER: + case LY_STMT_LEAF: + case LY_STMT_LEAF_LIST: + case LY_STMT_LIST: + schema = *((struct lysc_node **)ext->substmts[i].storage); + if (schema) { + rc = lyplg_ext_sprinter_ctree_add_nodes(ctx, schema, clb); + return rc; + } + default: + break; + } + } + + return rc; +} + +LIBYANG_API_DECL LY_ERR +lyplg_ext_sprinter_ptree_add_ext_nodes(const struct lyspr_tree_ctx *ctx, struct lysp_ext_instance *ext, + lyplg_ext_sprinter_ptree_override_clb clb) +{ + LY_ERR rc = LY_SUCCESS; + uint32_t i; + struct lysp_node *schema; + + LY_CHECK_ARG_RET2(NULL, ctx, ext, LY_EINVAL); + + LY_ARRAY_FOR(ext->substmts, i) { + switch (ext->substmts[i].stmt) { + case LY_STMT_NOTIFICATION: + case LY_STMT_INPUT: + case LY_STMT_OUTPUT: + case LY_STMT_ACTION: + case LY_STMT_RPC: + case LY_STMT_ANYDATA: + case LY_STMT_ANYXML: + case LY_STMT_CASE: + case LY_STMT_CHOICE: + case LY_STMT_CONTAINER: + case LY_STMT_LEAF: + case LY_STMT_LEAF_LIST: + case LY_STMT_LIST: + schema = *((struct lysp_node **)ext->substmts[i].storage); + if (schema) { + rc = lyplg_ext_sprinter_ptree_add_nodes(ctx, schema, clb); + return rc; + } + default: + break; + } + } + + return rc; +} + +LIBYANG_API_DECL LY_ERR +lyplg_ext_sprinter_ctree_add_nodes(const struct lyspr_tree_ctx *ctx, struct lysc_node *nodes, + lyplg_ext_sprinter_ctree_override_clb clb) +{ + struct lyspr_tree_schema *new; + + LY_CHECK_ARG_RET1(NULL, ctx, LY_EINVAL); + + if (!nodes) { + return LY_SUCCESS; + } + + LY_ARRAY_NEW_RET(NULL, ((struct lyspr_tree_ctx *)ctx)->schemas, new, LY_EMEM); + new->compiled = 1; + new->ctree = nodes; + new->cn_overr = clb; + + return LY_SUCCESS; +} + +LIBYANG_API_DECL LY_ERR +lyplg_ext_sprinter_ptree_add_nodes(const struct lyspr_tree_ctx *ctx, struct lysp_node *nodes, + lyplg_ext_sprinter_ptree_override_clb clb) +{ + struct lyspr_tree_schema *new; + + LY_CHECK_ARG_RET1(NULL, ctx, LY_EINVAL); + + if (!nodes) { + return LY_SUCCESS; + } + + LY_ARRAY_NEW_RET(NULL, ((struct lyspr_tree_ctx *)ctx)->schemas, new, LY_EMEM); + new->compiled = 0; + new->ptree = nodes; + new->pn_overr = clb; + + return LY_SUCCESS; +} + +LIBYANG_API_DECL LY_ERR +lyplg_ext_sprinter_tree_set_priv(const struct lyspr_tree_ctx *ctx, void *plugin_priv, void (*free_clb)(void *plugin_priv)) +{ + LY_CHECK_ARG_RET1(NULL, ctx, LY_EINVAL); + + ((struct lyspr_tree_ctx *)ctx)->plugin_priv = plugin_priv; + ((struct lyspr_tree_ctx *)ctx)->free_plugin_priv = free_clb; + + return LY_SUCCESS; +} + +LIBYANG_API_DEF const char * +lyplg_ext_stmt2str(enum ly_stmt stmt) +{ + if (stmt == LY_STMT_EXTENSION_INSTANCE) { + return "extension instance"; + } else { + return lys_stmt_str(stmt); + } +} + +LIBYANG_API_DEF enum ly_stmt +lyplg_ext_nodetype2stmt(uint16_t nodetype) +{ + switch (nodetype) { + case LYS_CONTAINER: + return LY_STMT_CONTAINER; + case LYS_CHOICE: + return LY_STMT_CHOICE; + case LYS_LEAF: + return LY_STMT_LEAF; + case LYS_LEAFLIST: + return LY_STMT_LEAF_LIST; + case LYS_LIST: + return LY_STMT_LIST; + case LYS_ANYXML: + return LY_STMT_ANYXML; + case LYS_ANYDATA: + return LY_STMT_ANYDATA; + case LYS_CASE: + return LY_STMT_CASE; + case LYS_RPC: + return LY_STMT_RPC; + case LYS_ACTION: + return LY_STMT_ACTION; + case LYS_NOTIF: + return LY_STMT_NOTIFICATION; + case LYS_USES: + return LY_STMT_USES; + case LYS_INPUT: + return LY_STMT_INPUT; + case LYS_OUTPUT: + return LY_STMT_OUTPUT; + default: + return LY_STMT_NONE; + } +} + +LY_ERR +lyplg_ext_get_storage_p(const struct lysc_ext_instance *ext, int stmt, const void ***storage_p) +{ + LY_ARRAY_COUNT_TYPE u; + enum ly_stmt match = 0; + + *storage_p = NULL; + + if (!(stmt & LY_STMT_NODE_MASK)) { + /* matching a non-node statement */ + match = stmt; + } + + LY_ARRAY_FOR(ext->substmts, u) { + if ((match && (ext->substmts[u].stmt == match)) || (!match && (ext->substmts[u].stmt & stmt))) { + *storage_p = ext->substmts[u].storage; + return LY_SUCCESS; + } + } + + return LY_ENOT; +} + +LIBYANG_API_DEF LY_ERR +lyplg_ext_get_storage(const struct lysc_ext_instance *ext, int stmt, uint32_t storage_size, const void **storage) +{ + LY_ERR rc = LY_SUCCESS; + const void **s; + + /* get pointer to the storage, is set even on error */ + rc = lyplg_ext_get_storage_p(ext, stmt, &s); + + /* assign */ + if (s) { + memcpy(storage, s, storage_size); + } else { + memset(storage, 0, storage_size); + } + + return rc; +} + +LIBYANG_API_DEF LY_ERR +lyplg_ext_parsed_get_storage(const struct lysc_ext_instance *ext, int stmt, uint32_t storage_size, const void **storage) +{ + LY_ARRAY_COUNT_TYPE u; + const struct lysp_ext_instance *extp = NULL; + enum ly_stmt match = 0; + const void **s = NULL; + + /* find the parsed ext instance */ + LY_ARRAY_FOR(ext->module->parsed->exts, u) { + extp = &ext->module->parsed->exts[u]; + + if (ext->def == extp->def->compiled) { + break; + } + extp = NULL; + } + assert(extp); + + if (!(stmt & LY_STMT_NODE_MASK)) { + /* matching a non-node statement */ + match = stmt; + } + + /* get the substatement */ + LY_ARRAY_FOR(extp->substmts, u) { + if ((match && (extp->substmts[u].stmt == match)) || (!match && (extp->substmts[u].stmt & stmt))) { + s = extp->substmts[u].storage; + break; + } + } + + /* assign */ + if (s) { + memcpy(storage, s, storage_size); + } else { + memset(storage, 0, storage_size); + } + + return LY_SUCCESS; +} + +LIBYANG_API_DEF LY_ERR +lyplg_ext_get_data(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, void **ext_data, ly_bool *ext_data_free) +{ + LY_ERR rc; + + if (!ctx->ext_clb) { + lyplg_ext_compile_log(NULL, ext, LY_LLERR, LY_EINVAL, "Failed to get extension data, no callback set."); + return LY_EINVAL; + } + + if ((rc = ctx->ext_clb(ext, ctx->ext_clb_data, ext_data, ext_data_free))) { + lyplg_ext_compile_log(NULL, ext, LY_LLERR, rc, "Callback for getting ext data failed."); + } + return rc; +} |