blob: 4b1c1cf1c691cce1d7ba4e32d43e31f5f4dfe146 [file] [log] [blame]
tadeas-vintrlikbf8aa6e2021-11-03 13:07:34 +01001/**
2 * @file schema_mount.c
3 * @author Tadeas Vintrlik <xvintr04@stud.fit.vutbr.cz>
4 * @brief libyang extension plugin - Schema Mount (RFC 8528)
5 *
6 * Copyright (c) 2021 CESNET, z.s.p.o.
7 *
8 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * https://opensource.org/licenses/BSD-3-Clause
13 */
14
15#define _GNU_SOURCE
16
17#include <assert.h>
18#include <stdint.h>
19#include <stdlib.h>
20#include <string.h>
21
22#include "dict.h"
23#include "libyang.h"
24#include "log.h"
25#include "plugins_exts.h"
26#include "tree_data.h"
27#include "tree_schema.h"
28
29/**
30 * @brief Check if given mount point is unique among its' siblings
31 *
32 * @param cctx Compilation context.
33 * @param c_ext Compiled extension instance for checking uniqueness.
34 * @param p_ext Extension instance of the mount-point for comparison.
35 *
36 * @return LY_SUCCESS if is unique. LY_EINVAL otherwise.
37 */
38static LY_ERR
39schema_mount_unique_mount_point(struct lysc_ctx *cctx, const struct lysc_ext_instance *c_ext,
40 const struct lysp_ext_instance *p_ext)
41{
42 struct lysp_module *pmod;
43 struct lysp_ext_instance *exts;
44 LY_ARRAY_COUNT_TYPE u, v;
45 struct lysp_node *parent;
46 struct lysp_import *module;
47 char *ext_prefix, *ext_name;
48
49 /* Check if it is the only instance of the mount-point among its' siblings */
50 parent = (struct lysp_node *) c_ext->parent;
51 exts = parent->exts;
52 pmod = lysc_ctx_get_pmod(cctx);
53 LY_ARRAY_FOR(exts, u) {
54 /* Extract prefix and name of the extension */
55 ext_prefix = strdup(exts[u].name);
56 ext_name = strstr(exts[u].name, ":");
57 ext_name++;
58 ext_prefix[strstr(ext_prefix, ":") - ext_prefix] = '\0';
59
60 module = NULL;
61 LY_ARRAY_FOR(pmod->imports, v) {
62 if (!strcmp(pmod->imports[v].prefix, ext_prefix)) {
63 /* Found the matching module */
64 module = &pmod->imports[v];
65 break;
66 }
67 }
68 free(ext_prefix);
69 if ((&exts[u] != p_ext) && module && (!strcmp(module->name, "ietf-yang-schema-mount")) &&
70 (!strcmp(exts[u].name, "mount-point"))) {
71 /* Found another instance of mount-point only one allowed per node */
72 return LY_EINVAL;
73 }
74 }
75 return LY_SUCCESS;
76}
77
78/**
79 * @brief Schema mount compile.
80 *
81 * Checks if it can be a valid extension instance for yang schema mount.
82 *
83 * Implementation of ::lyplg_ext_compile_clb callback set as lyext_plugin::compile.
84 */
85static LY_ERR
86schema_mount_compile(struct lysc_ctx *cctx, const struct lysp_ext_instance *p_ext,
87 struct lysc_ext_instance *c_ext)
88{
89 const struct lys_module *cur_mod;
90
91 /* Check if processing right callback */
92 assert(!strcmp(p_ext->name, "yangmnt:mount-point"));
93
94 /* Check if mount point was found in YANG version 1.1 module */
95 cur_mod = lysc_ctx_get_cur_mod(cctx);
96 if (cur_mod->parsed->version != LYS_VERSION_1_1) {
97 return LY_EINVAL;
98 }
99
100 /* Check if its' parent is a container or a list */
101 if ((p_ext->parent_stmt != LY_STMT_CONTAINER) && (p_ext->parent_stmt != LY_STMT_LIST)) {
102 return LY_EINVAL;
103 }
104
105 /* Check if the only mount-point among siblings */
106 if (schema_mount_unique_mount_point(cctx, c_ext, p_ext)) {
107 return LY_EINVAL;
108 }
109
110 (void)c_ext;
111
112 return LY_SUCCESS;
113}
114
115/**
116 * @brief Parse callback for schema mount.
117 *
118 * Check if data is valid for schema mount and inserts it to the parent.
119 */
120static LY_ERR
121schema_mount_parse(struct ly_in *in, LYD_FORMAT format, struct lysc_ext_instance *ext,
122 struct lyd_node *parent, uint32_t parse_opts, uint32_t val_opts)
123{
124 LY_ERR ret = LY_SUCCESS;
125 const struct ly_ctx *ctx;
126 struct lyd_node *subtree, *yanglib, *mount_point, *final = NULL;
127 struct ly_err_item *err;
128 ly_bool found_yanglib = 0, found_mount_point = 0;
129 uint32_t old_log_opts;
130
131 ctx = LYD_CTX(parent);
132
133 old_log_opts = ly_log_options(LY_LOSTORE_LAST);
134 /* Check if intended for schema-mount - had both required data nodes */
135 while (1) {
136 /* Parse by sub-trees */
137 if (lyd_parse_data(ctx, NULL, in, 0, parse_opts, val_opts, &subtree)) {
138 /* Either end or error - must check */
139 err = ly_err_first(ctx);
140 if (err->vecode == LYVE_SYNTAX_XML) {
141 /* Could just be EOF - check */
142 /* TODO: Search in error message if EOF then break */
143 lyd_insert_sibling(final, subtree, NULL);
144 break;
145 } else {
146 /* Other parsing error encountered */
147 ret = LY_EINVAL;
148 goto cleanup;
149 }
150 }
151
152 if (!final) {
153 /* If there was nothing inserted yet this subtree becomes the one to insert into */
154 final = subtree;
155 }
156
157 lyd_find_path(subtree, "/ietf-yang-library:yang-library", 0, &yanglib);
158 if (yanglib && !(yanglib->flags & LYD_DEFAULT)) {
159 /* Found and not created by flags */
160 found_yanglib = 1;
161 lyd_insert_sibling(final, yanglib, NULL);
162 continue;
163 }
164 lyd_find_path(subtree, "/ietf-yang-schema-mount:mount-points", 0, &mount_point);
165 if (mount_point && !(mount_point->flags & LYD_DEFAULT)) {
166 /* Was found and not created by flags */
167 found_mount_point = 1;
168 lyd_insert_sibling(final, mount_point, NULL);
169 continue;
170 }
171 }
172
173 if (found_mount_point && found_yanglib) {
174 /* It is valid data and can be inserted into the parent */
175 lyd_insert_child(parent, final);
176 } else {
177 /* It was not data for schema mount */
178 lyd_free_tree(final);
179 ret = LY_ENOT;
180 }
181
182cleanup:
183 ly_log_options(old_log_opts);
184 return ret;
185}
186
187/**
188 * @brief Plugin descriptions for the Yang Schema Mount extension.
189 *
190 * Note that external plugins are supposed to use:
191 *
192 * LYPLG_EXTENSIONS = {
193 */
194const struct lyplg_ext_record plugins_schema_mount[] = {
195 {
196 .module = "ietf-yang-schema-mount",
197 .revision = "2019-01-14",
198 .name = "mount-point",
199
200 .plugin.id = "libyang 2 - Schema Mount, version 1",
201 .plugin.compile = &schema_mount_compile,
202 .plugin.parse = &schema_mount_parse,
203 .plugin.validate = NULL,
204 .plugin.sprinter = NULL,
205 .plugin.free = NULL
206 },
207 {0} /* terminating zeroed item */
208};