blob: d66cea6cb050ff5dd9bc4c7327bf234b63fa46a6 [file] [log] [blame]
/**
* @file yangdata.c
* @author Pavol Vican <vican.pavol@gmail.com>
* @brief libyang extension plugin - YANG DATA (RFC 8040)
*
* Copyright (c) 2018 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
*/
#ifdef __GNUC__
# define UNUSED(x) UNUSED_ ## x __attribute__((__unused__))
#else
# define UNUSED(x) UNUSED_ ## x
#endif
#include <stdlib.h>
#include "../extensions.h"
int check_node(struct lys_node *node);
/**
* @brief Callback to check that the yang-data can be instantiated inside the provided node
*
* @param[in] parent The parent of the instantiated extension.
* @param[in] parent_type The type of the structure provided as \p parent.
* @param[in] substmt_type libyang does not store all the extension instances in the structures where they are
* instantiated in the module. In some cases (see #LYEXT_SUBSTMT) they are stored in parent
* structure and marked with flag to know in which substatement of the parent the extension
* was originally instantiated.
* @return 0 - ok
* 1 - error
*/
int yang_data_position(const void * UNUSED(parent), LYEXT_PAR parent_type, LYEXT_SUBSTMT UNUSED(substmt_type))
{
/* yang-data can appear only at the top level of a YANG module or submodule */
if (parent_type == LYEXT_PAR_MODULE) {
return 0;
} else {
return 1;
}
}
/* return values - 0 - OK
* 1 - Something wrong
* -1 - Absolute wrong
*/
int check_choice(struct lys_node *root) {
struct lys_node *node, *next;
int result = 1, tmp_result;
LY_TREE_FOR_SAFE(root->child, next, node) {
tmp_result = (node->nodetype == LYS_CASE) ? check_node(node->child) : check_node(node);
if (tmp_result == -1) {
return -1;
} else if (tmp_result == 0) {
result = 0;
}
}
return result;
}
/* return values - 0 - OK
* 1 - Something wrong
* -1 - Absolute wrong
*/
int check_node(struct lys_node *node) {
int result = 0;
if (node == NULL) {
return 1;
}
/* check nodes and find only one container */
if (node->nodetype == LYS_CHOICE) {
result = check_choice(node);
} else if (node->nodetype == LYS_USES) {
result = check_node(((struct lys_node_uses*)node)->grp->child);
} else if (node->nodetype != LYS_CONTAINER || (node->next != NULL || node->prev != node)) {
result = -1;
}
return result;
}
void remove_iffeature(struct lys_iffeature **iffeature, uint8_t *iffeature_size, struct ly_ctx *ctx) {
lys_iffeature_free(ctx, *iffeature, *iffeature_size, 0, NULL);
*iffeature_size = 0;
*iffeature = NULL;
}
void remove_iffeature_type(struct lys_type *type, struct ly_ctx *ctx) {
unsigned int i;
if (type->base == LY_TYPE_ENUM) {
for (i = 0; i < type->info.enums.count; ++i) {
remove_iffeature(&type->info.enums.enm[i].iffeature, &type->info.enums.enm[i].iffeature_size, ctx);
}
} else if (type->base == LY_TYPE_BITS) {
for (i = 0; i < type->info.bits.count; ++i) {
remove_iffeature(&type->info.bits.bit[i].iffeature, &type->info.bits.bit[i].iffeature_size, ctx);
}
}
}
/* fix schema - ignore config flag, iffeature */
void fix_schema(struct lys_node *root, struct ly_ctx *ctx) {
struct lys_node *node, *next;
struct lys_node_container *cont;
struct lys_node_rpc_action *action;
struct lys_node_grp *grp;
struct lys_node_uses *uses;
int i;
LY_TREE_DFS_BEGIN(root, next, node) {
/* ignore config flag */
node->flags = node->flags & (~(LYS_CONFIG_MASK | LYS_CONFIG_SET));
remove_iffeature(&node->iffeature, &node->iffeature_size, ctx);
switch (node->nodetype) {
case LYS_CONTAINER:
cont = (struct lys_node_container *)node;
for (i = 0; i < cont->tpdf_size; ++i) {
remove_iffeature_type(&cont->tpdf[i].type, ctx);
}
break;
case LYS_LEAF:
remove_iffeature_type(&((struct lys_node_leaf *)node)->type, ctx);
break;
case LYS_LEAFLIST:
remove_iffeature_type(&((struct lys_node_leaflist *)node)->type, ctx);
break;
case LYS_ACTION:
case LYS_NOTIF:
action = (struct lys_node_rpc_action *)node;
for (i = 0; i < action->tpdf_size; ++i) {
remove_iffeature_type(&action->tpdf[i].type, ctx);
}
break;
case LYS_GROUPING:
grp = (struct lys_node_grp *)node;
for (i = 0; i < grp->tpdf_size; ++i) {
remove_iffeature_type(&grp->tpdf[i].type, ctx);
}
break;
case LYS_USES:
uses = (struct lys_node_uses *)node;
for (i = 0; i < uses->augment_size; ++i) {
remove_iffeature(&uses->augment[i].iffeature, &uses->augment[i].iffeature_size, ctx);
fix_schema(uses->augment[i].child, ctx);
}
for (i = 0; i < uses->refine_size; ++i) {
remove_iffeature(&uses->refine[i].iffeature, &uses->refine[i].iffeature_size, ctx);
}
break;
default:
break;
}
LY_TREE_DFS_END(root, next, node)
}
}
int yang_data_result(struct lys_ext_instance *ext) {
struct lys_node **root;
root = lys_ext_complex_get_substmt(LY_STMT_CONTAINER, (struct lys_ext_instance_complex *)ext, NULL);
if (!root || !(*root) || (*root)->next != NULL || check_node(*root)) {
return 1;
}
fix_schema(*root, ext->def->module->ctx);
return 0;
}
struct lyext_substmt yang_data_substmt[] = {
{LY_STMT_USES, 0, LY_STMT_CARD_OPT},
{LY_STMT_CONTAINER, 0, LY_STMT_CARD_OPT},
{LY_STMT_CHOICE, 0, LY_STMT_CARD_OPT},
{0, 0, 0} /* terminating item */
};
/**
* @brief Plugin for the RFC 8040 restconf extension
*/
struct lyext_plugin_complex yang_data = {
.type = LYEXT_COMPLEX,
.flags = 0,
.check_position = &yang_data_position,
.check_result = &yang_data_result,
.check_inherit = NULL,
.valid_data = NULL,
/* specification of allowed substatements of the extension instance */
.substmt = yang_data_substmt,
/* final size of the extension instance structure with the space for storing the substatements */
.instance_size = (sizeof(struct lys_ext_instance_complex) - 1) + 2 * sizeof(void*)
};
/**
* @brief list of all extension plugins implemented here
*
* MANDATORY object for all libyang extension plugins, the name must match the <name>.so
*/
struct lyext_plugin_list yangdata[] = {
{"ietf-restconf", "2017-01-26", "yang-data", (struct lyext_plugin*)&yang_data},
{NULL, NULL, NULL, NULL} /* terminating item */
};