/** * @file plugins_exts.c * @author Radek Krejci * @author Michal Vasko * @brief helper functions for extension plugins * * Copyright (c) 2019 - 2024 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 #include #include #include "dict.h" #include "ly_common.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_p 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, void **parsed_p, struct lysc_ext_instance *ext, struct lysc_ext_substmt *substmt) { LY_ERR rc = LY_SUCCESS; ly_bool length_restr = 0; LY_DATA_TYPE basetype; assert(*parsed_p); /* 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_p, &enabled), cleanup); if (!enabled) { /* it is disabled, remove the whole extension instance */ rc = LY_ENOT; } } if (!substmt->storage_p) { /* nothing to store */ 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 = *parsed_p; /* 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_p, 0, (const char **)substmt->storage_p), 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 */ rc = lys_compile_type_enums(ctx, *parsed_p, basetype, NULL, (struct lysc_type_bitenum_item **)substmt->storage_p); LY_CHECK_GOTO(rc, cleanup); break; case LY_STMT_CONFIG: { uint16_t flags; if (!(ctx->compile_opts & LYS_COMPILE_NO_CONFIG)) { memcpy(&flags, parsed_p, 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_p, &flags, 2); } /* else leave zero */ break; } case LY_STMT_MUST: { const struct lysp_restr *restrs = *parsed_p; /* sized array */ COMPILE_ARRAY_GOTO(ctx, restrs, *(struct lysc_must **)substmt->storage_p, lys_compile_must, rc, cleanup); break; } case LY_STMT_WHEN: { const uint16_t flags; const struct lysp_when *when = *parsed_p; /* 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, (struct lysc_when **)substmt->storage_p), cleanup); break; } case LY_STMT_FRACTION_DIGITS: case LY_STMT_REQUIRE_INSTANCE: /* just make a copy */ memcpy(substmt->storage_p, parsed_p, 1); break; case LY_STMT_MANDATORY: case LY_STMT_ORDERED_BY: case LY_STMT_STATUS: /* just make a copy */ memcpy(substmt->storage_p, parsed_p, 2); break; case LY_STMT_MAX_ELEMENTS: case LY_STMT_MIN_ELEMENTS: /* just make a copy */ memcpy(substmt->storage_p, parsed_p, 4); break; case LY_STMT_POSITION: case LY_STMT_VALUE: /* just make a copy */ memcpy(substmt->storage_p, parsed_p, 8); break; case LY_STMT_IDENTITY: /* compile */ rc = lys_identity_precompile(ctx, NULL, NULL, *parsed_p, (struct lysc_ident **)substmt->storage_p); LY_CHECK_GOTO(rc, cleanup); break; case LY_STMT_LENGTH: length_restr = 1; /* fallthrough */ case LY_STMT_RANGE: /* compile, use uint64 default range */ rc = lys_compile_type_range(ctx, *parsed_p, LY_TYPE_UINT64, length_restr, 0, NULL, (struct lysc_range **)substmt->storage_p); LY_CHECK_GOTO(rc, cleanup); break; case LY_STMT_PATTERN: /* compile */ rc = lys_compile_type_patterns(ctx, *parsed_p, NULL, (struct lysc_pattern ***)substmt->storage_p); LY_CHECK_GOTO(rc, cleanup); break; case LY_STMT_TYPE: { const uint16_t flags; const char *units; const struct lysp_type *ptype = *parsed_p; /* 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 */ rc = lys_compile_type(ctx, NULL, flags, ext->def->name, ptype, (struct lysc_type **)substmt->storage_p, &units, NULL); LY_CHECK_GOTO(rc, cleanup); LY_ATOMIC_INC_BARRIER((*(struct lysc_type **)substmt->storage_p)->refcount); break; } case LY_STMT_EXTENSION_INSTANCE: { struct lysp_ext_instance *extps = *parsed_p; /* compile sized array */ COMPILE_EXTS_GOTO(ctx, extps, *(struct lysc_ext_instance **)substmt->storage_p, 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; 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 = extp->substmts[u].storage_p; if (!*storagep || ly_set_contains(&storagep_compiled, storagep, NULL)) { /* nothing parsed or already 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_RET(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 = *ext->substmts[i].storage_p; 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_RET(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 = *ext->substmts[i].storage_p; 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_RET(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_RET(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_RET(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, void ***storage_pp) { LY_ARRAY_COUNT_TYPE u; enum ly_stmt match = 0; *storage_pp = 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_pp = ext->substmts[u].storage_p; 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; void **s_p; /* get pointer to the storage, is set even on error */ rc = lyplg_ext_get_storage_p(ext, stmt, &s_p); /* assign */ if (s_p) { memcpy(storage, s_p, 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; void **s_p = NULL; LY_CHECK_ARG_RET(NULL, ext, ext->module->parsed, LY_EINVAL); /* 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_p = extp->substmts[u].storage_p; break; } } /* assign */ if (s_p) { memcpy(storage, s_p, 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; }