blob: 4b1c1cf1c691cce1d7ba4e32d43e31f5f4dfe146 [file] [log] [blame]
/**
* @file schema_mount.c
* @author Tadeas Vintrlik <xvintr04@stud.fit.vutbr.cz>
* @brief libyang extension plugin - Schema Mount (RFC 8528)
*
* Copyright (c) 2021 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
*/
#define _GNU_SOURCE
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "dict.h"
#include "libyang.h"
#include "log.h"
#include "plugins_exts.h"
#include "tree_data.h"
#include "tree_schema.h"
/**
* @brief Check if given mount point is unique among its' siblings
*
* @param cctx Compilation context.
* @param c_ext Compiled extension instance for checking uniqueness.
* @param p_ext Extension instance of the mount-point for comparison.
*
* @return LY_SUCCESS if is unique. LY_EINVAL otherwise.
*/
static LY_ERR
schema_mount_unique_mount_point(struct lysc_ctx *cctx, const struct lysc_ext_instance *c_ext,
const struct lysp_ext_instance *p_ext)
{
struct lysp_module *pmod;
struct lysp_ext_instance *exts;
LY_ARRAY_COUNT_TYPE u, v;
struct lysp_node *parent;
struct lysp_import *module;
char *ext_prefix, *ext_name;
/* Check if it is the only instance of the mount-point among its' siblings */
parent = (struct lysp_node *) c_ext->parent;
exts = parent->exts;
pmod = lysc_ctx_get_pmod(cctx);
LY_ARRAY_FOR(exts, u) {
/* Extract prefix and name of the extension */
ext_prefix = strdup(exts[u].name);
ext_name = strstr(exts[u].name, ":");
ext_name++;
ext_prefix[strstr(ext_prefix, ":") - ext_prefix] = '\0';
module = NULL;
LY_ARRAY_FOR(pmod->imports, v) {
if (!strcmp(pmod->imports[v].prefix, ext_prefix)) {
/* Found the matching module */
module = &pmod->imports[v];
break;
}
}
free(ext_prefix);
if ((&exts[u] != p_ext) && module && (!strcmp(module->name, "ietf-yang-schema-mount")) &&
(!strcmp(exts[u].name, "mount-point"))) {
/* Found another instance of mount-point only one allowed per node */
return LY_EINVAL;
}
}
return LY_SUCCESS;
}
/**
* @brief Schema mount compile.
*
* Checks if it can be a valid extension instance for yang schema mount.
*
* Implementation of ::lyplg_ext_compile_clb callback set as lyext_plugin::compile.
*/
static LY_ERR
schema_mount_compile(struct lysc_ctx *cctx, const struct lysp_ext_instance *p_ext,
struct lysc_ext_instance *c_ext)
{
const struct lys_module *cur_mod;
/* Check if processing right callback */
assert(!strcmp(p_ext->name, "yangmnt:mount-point"));
/* Check if mount point was found in YANG version 1.1 module */
cur_mod = lysc_ctx_get_cur_mod(cctx);
if (cur_mod->parsed->version != LYS_VERSION_1_1) {
return LY_EINVAL;
}
/* Check if its' parent is a container or a list */
if ((p_ext->parent_stmt != LY_STMT_CONTAINER) && (p_ext->parent_stmt != LY_STMT_LIST)) {
return LY_EINVAL;
}
/* Check if the only mount-point among siblings */
if (schema_mount_unique_mount_point(cctx, c_ext, p_ext)) {
return LY_EINVAL;
}
(void)c_ext;
return LY_SUCCESS;
}
/**
* @brief Parse callback for schema mount.
*
* Check if data is valid for schema mount and inserts it to the parent.
*/
static LY_ERR
schema_mount_parse(struct ly_in *in, LYD_FORMAT format, struct lysc_ext_instance *ext,
struct lyd_node *parent, uint32_t parse_opts, uint32_t val_opts)
{
LY_ERR ret = LY_SUCCESS;
const struct ly_ctx *ctx;
struct lyd_node *subtree, *yanglib, *mount_point, *final = NULL;
struct ly_err_item *err;
ly_bool found_yanglib = 0, found_mount_point = 0;
uint32_t old_log_opts;
ctx = LYD_CTX(parent);
old_log_opts = ly_log_options(LY_LOSTORE_LAST);
/* Check if intended for schema-mount - had both required data nodes */
while (1) {
/* Parse by sub-trees */
if (lyd_parse_data(ctx, NULL, in, 0, parse_opts, val_opts, &subtree)) {
/* Either end or error - must check */
err = ly_err_first(ctx);
if (err->vecode == LYVE_SYNTAX_XML) {
/* Could just be EOF - check */
/* TODO: Search in error message if EOF then break */
lyd_insert_sibling(final, subtree, NULL);
break;
} else {
/* Other parsing error encountered */
ret = LY_EINVAL;
goto cleanup;
}
}
if (!final) {
/* If there was nothing inserted yet this subtree becomes the one to insert into */
final = subtree;
}
lyd_find_path(subtree, "/ietf-yang-library:yang-library", 0, &yanglib);
if (yanglib && !(yanglib->flags & LYD_DEFAULT)) {
/* Found and not created by flags */
found_yanglib = 1;
lyd_insert_sibling(final, yanglib, NULL);
continue;
}
lyd_find_path(subtree, "/ietf-yang-schema-mount:mount-points", 0, &mount_point);
if (mount_point && !(mount_point->flags & LYD_DEFAULT)) {
/* Was found and not created by flags */
found_mount_point = 1;
lyd_insert_sibling(final, mount_point, NULL);
continue;
}
}
if (found_mount_point && found_yanglib) {
/* It is valid data and can be inserted into the parent */
lyd_insert_child(parent, final);
} else {
/* It was not data for schema mount */
lyd_free_tree(final);
ret = LY_ENOT;
}
cleanup:
ly_log_options(old_log_opts);
return ret;
}
/**
* @brief Plugin descriptions for the Yang Schema Mount extension.
*
* Note that external plugins are supposed to use:
*
* LYPLG_EXTENSIONS = {
*/
const struct lyplg_ext_record plugins_schema_mount[] = {
{
.module = "ietf-yang-schema-mount",
.revision = "2019-01-14",
.name = "mount-point",
.plugin.id = "libyang 2 - Schema Mount, version 1",
.plugin.compile = &schema_mount_compile,
.plugin.parse = &schema_mount_parse,
.plugin.validate = NULL,
.plugin.sprinter = NULL,
.plugin.free = NULL
},
{0} /* terminating zeroed item */
};