blob: 6a3c86e62bd792e9ed38527ff614069191e3c1e9 [file] [log] [blame]
tadeas-vintrlik2aa36b42021-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>
Michal Vaskoddd76592022-01-17 13:34:48 +010018#include <pthread.h>
tadeas-vintrlik2aa36b42021-11-03 13:07:34 +010019#include <stdint.h>
20#include <stdlib.h>
21#include <string.h>
22
Michal Vaskofbbea932022-06-07 11:00:55 +020023#include "common.h"
Michal Vasko193dacd2022-10-13 08:43:05 +020024#include "compat.h"
tadeas-vintrlik2aa36b42021-11-03 13:07:34 +010025#include "dict.h"
26#include "libyang.h"
27#include "log.h"
Michal Vaskofbbea932022-06-07 11:00:55 +020028#include "parser_data.h"
tadeas-vintrlik2aa36b42021-11-03 13:07:34 +010029#include "plugins_exts.h"
Michal Vasko8cc3f662022-03-29 11:25:51 +020030#include "plugins_types.h"
tadeas-vintrlik2aa36b42021-11-03 13:07:34 +010031#include "tree_data.h"
32#include "tree_schema.h"
33
34/**
Michal Vaskoddd76592022-01-17 13:34:48 +010035 * @brief Internal schema mount data structure for holding all the contexts of parsed data.
36 */
37struct lyplg_ext_sm {
38 struct lyplg_ext_sm_shared {
39 pthread_mutex_t lock; /**< lock for accessing this shared structure */
40
41 struct {
42 struct ly_ctx *ctx; /**< context shared between all data of this mount point */
43 const char *mount_point; /**< mount point name */
44 const char *content_id; /**< yang-library content-id (alternatively module-set-id),
45 stored in the dictionary of the ext instance context */
46 } *schemas; /**< array of shared schema schemas */
47 uint32_t schema_count; /**< length of schemas array */
48 uint32_t ref_count; /**< number of references to this structure (mount-points with the same name
49 in the module) */
50 } *shared; /**< shared schema mount points */
51
52 struct lyplg_ext_sm_inln {
53 struct {
54 struct ly_ctx *ctx; /**< context created for single inline schema data */
55 } *schemas; /**< array of inline schemas */
56 uint32_t schema_count; /**< length of schemas array */
57 } inln; /**< inline mount points */
58};
59
aPiecek03cb4872022-10-24 10:31:51 +020060struct sprinter_tree_priv {
61 struct ly_ctx *ext_ctx;
62 struct ly_set *refs;
63};
64
Michal Vasko193dacd2022-10-13 08:43:05 +020065#define EXT_LOGERR_MEM_RET(cctx, ext) \
66 lyplg_ext_compile_log(cctx, ext, LY_LLERR, LY_EMEM, "Memory allocation failed (%s:%d).", __FILE__, __LINE__); \
Michal Vaskoddd76592022-01-17 13:34:48 +010067 return LY_EMEM
68
Michal Vasko193dacd2022-10-13 08:43:05 +020069#define EXT_LOGERR_MEM_GOTO(cctx, ext, rc, label) \
70 lyplg_ext_compile_log(cctx, ext, LY_LLERR, LY_EMEM, "Memory allocation failed (%s:%d).", __FILE__, __LINE__); \
Michal Vaskoddd76592022-01-17 13:34:48 +010071 rc = LY_EMEM; \
72 goto label
73
Michal Vasko193dacd2022-10-13 08:43:05 +020074#define EXT_LOGERR_INT_RET(cctx, ext) \
75 lyplg_ext_compile_log(cctx, ext, LY_LLERR, LY_EINT, "Internal error (%s:%d).", __FILE__, __LINE__); \
Michal Vaskoddd76592022-01-17 13:34:48 +010076 return LY_EINT
77
78/**
Michal Vasko193dacd2022-10-13 08:43:05 +020079 * @brief Check if given mount point is unique among its siblings
tadeas-vintrlik2aa36b42021-11-03 13:07:34 +010080 *
Michal Vasko193dacd2022-10-13 08:43:05 +020081 * @param[in] pctx Parse context.
82 * @param[in] ext Parsed extension instance.
Michal Vaskoddd76592022-01-17 13:34:48 +010083 * @return LY_SUCCESS if is unique;
84 * @return LY_EINVAL otherwise.
tadeas-vintrlik2aa36b42021-11-03 13:07:34 +010085 */
86static LY_ERR
Michal Vasko193dacd2022-10-13 08:43:05 +020087schema_mount_parse_unique_mp(struct lysp_ctx *pctx, const struct lysp_ext_instance *ext)
tadeas-vintrlik2aa36b42021-11-03 13:07:34 +010088{
Michal Vasko193dacd2022-10-13 08:43:05 +020089 struct lysp_ext_instance *exts;
Michal Vaskoddd76592022-01-17 13:34:48 +010090 LY_ARRAY_COUNT_TYPE u;
Michal Vasko193dacd2022-10-13 08:43:05 +020091 struct lysp_node *parent;
tadeas-vintrlik2aa36b42021-11-03 13:07:34 +010092
Michal Vasko193dacd2022-10-13 08:43:05 +020093 /* check if it is the only instance of the mount-point among its siblings */
94 parent = ext->parent;
tadeas-vintrlik2aa36b42021-11-03 13:07:34 +010095 exts = parent->exts;
tadeas-vintrlik2aa36b42021-11-03 13:07:34 +010096 LY_ARRAY_FOR(exts, u) {
Michal Vasko193dacd2022-10-13 08:43:05 +020097 if (&exts[u] == ext) {
Michal Vaskoddd76592022-01-17 13:34:48 +010098 continue;
tadeas-vintrlik2aa36b42021-11-03 13:07:34 +010099 }
Michal Vaskoddd76592022-01-17 13:34:48 +0100100
Michal Vasko193dacd2022-10-13 08:43:05 +0200101 if (!strcmp(exts[u].name, ext->name)) {
102 lyplg_ext_parse_log(pctx, ext, LY_LLERR, LY_EVALID, "Multiple extension \"%s\" instances.", ext->name);
tadeas-vintrlik2aa36b42021-11-03 13:07:34 +0100103 return LY_EINVAL;
104 }
105 }
106 return LY_SUCCESS;
107}
108
Michal Vasko193dacd2022-10-13 08:43:05 +0200109/**
110 * @brief Schema mount parse.
111 * Checks if it can be a valid extension instance for yang schema mount.
112 *
113 * Implementation of ::lyplg_ext_parse_clb callback set as lyext_plugin::parse.
114 */
115static LY_ERR
116schema_mount_parse(struct lysp_ctx *pctx, struct lysp_ext_instance *ext)
117{
118 /* check YANG version 1.1 */
119 if (lyplg_ext_parse_get_cur_pmod(pctx)->version != LYS_VERSION_1_1) {
120 lyplg_ext_parse_log(pctx, ext, LY_LLERR, LY_EVALID, "Extension \"%s\" instance not allowed in YANG version 1 module.",
121 ext->name);
122 return LY_EINVAL;
123 }
124
125 /* check parent nodetype */
126 if ((ext->parent_stmt != LY_STMT_CONTAINER) && (ext->parent_stmt != LY_STMT_LIST)) {
127 lyplg_ext_parse_log(pctx, ext, LY_LLERR, LY_EVALID, "Extension \"%s\" instance allowed only in container or list statement.",
128 ext->name);
129 return LY_EINVAL;
130 }
131
132 /* check uniqueness */
133 if (schema_mount_parse_unique_mp(pctx, ext)) {
134 return LY_EINVAL;
135 }
136
137 /* nothing to actually parse */
138 return LY_SUCCESS;
139}
140
Michal Vaskoddd76592022-01-17 13:34:48 +0100141struct lyplg_ext_sm_shared_cb_data {
142 const struct lysc_ext_instance *ext;
143 struct lyplg_ext_sm_shared *sm_shared;
144};
145
146static LY_ERR
Michal Vasko193dacd2022-10-13 08:43:05 +0200147schema_mount_compile_mod_dfs_cb(struct lysc_node *node, void *data, ly_bool *UNUSED(dfs_continue))
Michal Vaskoddd76592022-01-17 13:34:48 +0100148{
149 struct lyplg_ext_sm_shared_cb_data *cb_data = data;
150 struct lyplg_ext_sm *sm_data;
151 struct lysc_ext_instance *exts;
152 LY_ARRAY_COUNT_TYPE u;
153
Michal Vaskoddd76592022-01-17 13:34:48 +0100154 if (node == cb_data->ext->parent) {
155 /* parent of the current compiled extension, skip */
156 return LY_SUCCESS;
157 }
158
159 /* find the same mount point */
160 exts = node->exts;
161 LY_ARRAY_FOR(exts, u) {
162 if (!strcmp(exts[u].def->module->name, "ietf-yang-schema-mount") && !strcmp(exts[u].def->name, "mount-point") &&
163 (exts[u].argument == cb_data->ext->argument)) {
164 /* same mount point, break the DFS search */
Michal Vasko193dacd2022-10-13 08:43:05 +0200165 sm_data = exts[u].compiled;
Michal Vaskoddd76592022-01-17 13:34:48 +0100166 cb_data->sm_shared = sm_data->shared;
167 return LY_EEXIST;
168 }
169 }
170
171 /* not found, continue search */
172 return LY_SUCCESS;
173}
174
175static struct lyplg_ext_sm_shared *
176schema_mount_compile_find_shared(const struct lys_module *mod, const struct lysc_ext_instance *ext)
177{
178 struct lyplg_ext_sm_shared_cb_data cb_data;
179 LY_ERR r;
180
181 /* prepare cb_data */
182 cb_data.ext = ext;
183 cb_data.sm_shared = NULL;
184
185 /* try to find the same mount point */
186 r = lysc_module_dfs_full(mod, schema_mount_compile_mod_dfs_cb, &cb_data);
Michal Vasko2a12cd92022-02-14 09:34:54 +0100187 (void)r;
Michal Vaskoddd76592022-01-17 13:34:48 +0100188 assert((!r && !cb_data.sm_shared) || ((r == LY_EEXIST) && cb_data.sm_shared));
189
190 return cb_data.sm_shared;
191}
192
tadeas-vintrlik2aa36b42021-11-03 13:07:34 +0100193/**
194 * @brief Schema mount compile.
tadeas-vintrlik2aa36b42021-11-03 13:07:34 +0100195 * Checks if it can be a valid extension instance for yang schema mount.
196 *
197 * Implementation of ::lyplg_ext_compile_clb callback set as lyext_plugin::compile.
198 */
199static LY_ERR
Michal Vasko193dacd2022-10-13 08:43:05 +0200200schema_mount_compile(struct lysc_ctx *cctx, const struct lysp_ext_instance *UNUSED(extp), struct lysc_ext_instance *ext)
tadeas-vintrlik2aa36b42021-11-03 13:07:34 +0100201{
Michal Vaskodb641792022-03-21 10:04:54 +0100202 const struct lysc_node *node;
Michal Vaskoddd76592022-01-17 13:34:48 +0100203 struct lyplg_ext_sm *sm_data;
tadeas-vintrlik2aa36b42021-11-03 13:07:34 +0100204
Michal Vaskoddd76592022-01-17 13:34:48 +0100205 /* init internal data */
206 sm_data = calloc(1, sizeof *sm_data);
207 if (!sm_data) {
Michal Vasko193dacd2022-10-13 08:43:05 +0200208 EXT_LOGERR_MEM_RET(cctx, ext);
Michal Vaskoddd76592022-01-17 13:34:48 +0100209 }
Michal Vasko193dacd2022-10-13 08:43:05 +0200210 ext->compiled = sm_data;
Michal Vaskoddd76592022-01-17 13:34:48 +0100211
Michal Vaskodb641792022-03-21 10:04:54 +0100212 /* find the owner module */
Michal Vasko193dacd2022-10-13 08:43:05 +0200213 node = ext->parent;
Michal Vaskodb641792022-03-21 10:04:54 +0100214 while (node->parent) {
215 node = node->parent;
216 }
217
Michal Vaskoddd76592022-01-17 13:34:48 +0100218 /* reuse/init shared schema */
Michal Vasko193dacd2022-10-13 08:43:05 +0200219 sm_data->shared = schema_mount_compile_find_shared(node->module, ext);
Michal Vaskoddd76592022-01-17 13:34:48 +0100220 if (sm_data->shared) {
221 ++sm_data->shared->ref_count;
222 } else {
223 sm_data->shared = calloc(1, sizeof *sm_data->shared);
224 if (!sm_data->shared) {
225 free(sm_data);
Michal Vasko193dacd2022-10-13 08:43:05 +0200226 EXT_LOGERR_MEM_RET(cctx, ext);
Michal Vaskoddd76592022-01-17 13:34:48 +0100227 }
228 pthread_mutex_init(&sm_data->shared->lock, NULL);
229 sm_data->shared->ref_count = 1;
230 }
tadeas-vintrlik2aa36b42021-11-03 13:07:34 +0100231
232 return LY_SUCCESS;
233}
234
235/**
Michal Vaskoddd76592022-01-17 13:34:48 +0100236 * @brief Learn details about the current mount point.
tadeas-vintrlik2aa36b42021-11-03 13:07:34 +0100237 *
Michal Vaskoddd76592022-01-17 13:34:48 +0100238 * @param[in] ext Compiled extension instance.
239 * @param[in] ext_data Extension data retrieved by the callback.
240 * @param[out] config Whether the whole schema should keep its config or be set to false.
241 * @param[out] shared Whether the schema is shared or inline.
242 * @return LY_ERR value.
tadeas-vintrlik2aa36b42021-11-03 13:07:34 +0100243 */
244static LY_ERR
Michal Vaskoddd76592022-01-17 13:34:48 +0100245schema_mount_get_smount(const struct lysc_ext_instance *ext, const struct lyd_node *ext_data, ly_bool *config,
246 ly_bool *shared)
tadeas-vintrlik2aa36b42021-11-03 13:07:34 +0100247{
Michal Vaskoddd76592022-01-17 13:34:48 +0100248 struct lyd_node *mpoint, *node;
249 char *path = NULL;
250 LY_ERR r;
tadeas-vintrlik2aa36b42021-11-03 13:07:34 +0100251
Michal Vaskoddd76592022-01-17 13:34:48 +0100252 /* find the mount point */
253 if (asprintf(&path, "/ietf-yang-schema-mount:schema-mounts/mount-point[module='%s'][label='%s']", ext->module->name,
254 ext->argument) == -1) {
Michal Vasko193dacd2022-10-13 08:43:05 +0200255 EXT_LOGERR_MEM_RET(NULL, ext);
Michal Vaskoddd76592022-01-17 13:34:48 +0100256 }
257 r = ext_data ? lyd_find_path(ext_data, path, 0, &mpoint) : LY_ENOTFOUND;
258 free(path);
259 if (r) {
260 /* missing mount-point, cannot be data for this extension (https://datatracker.ietf.org/doc/html/rfc8528#page-10) */
261 return LY_ENOT;
262 }
tadeas-vintrlik2aa36b42021-11-03 13:07:34 +0100263
Michal Vaskoddd76592022-01-17 13:34:48 +0100264 /* check config */
Michal Vasko9e12ffe2022-09-21 15:11:30 +0200265 *config = 1;
Michal Vaskoddd76592022-01-17 13:34:48 +0100266 if (!lyd_find_path(mpoint, "config", 0, &node) && !strcmp(lyd_get_value(node), "false")) {
267 *config = 0;
Michal Vasko9e12ffe2022-09-21 15:11:30 +0200268 }
269 assert((ext->parent_stmt == LY_STMT_CONTAINER) || (ext->parent_stmt == LY_STMT_LIST));
270 if (((struct lysc_node *)ext->parent)->flags & LYS_CONFIG_R) {
271 *config = 0;
Michal Vaskoddd76592022-01-17 13:34:48 +0100272 }
273
274 /* check schema-ref */
275 if (lyd_find_path(mpoint, "shared-schema", 0, NULL)) {
276 if (lyd_find_path(mpoint, "inline", 0, NULL)) {
Michal Vasko193dacd2022-10-13 08:43:05 +0200277 EXT_LOGERR_INT_RET(NULL, ext);
Michal Vaskoddd76592022-01-17 13:34:48 +0100278 }
279 *shared = 0;
280 } else {
281 *shared = 1;
282 }
283
284 return LY_SUCCESS;
285}
286
287/**
288 * @brief Create schema (context) based on retrieved extension data.
289 *
290 * @param[in] ext Compiled extension instance.
291 * @param[in] ext_data Extension data retrieved by the callback.
292 * @param[in] config Whether the whole schema should keep its config or be set to false.
293 * @param[out] ext_ctx Schema to use for parsing the data.
294 * @return LY_ERR value.
295 */
296static LY_ERR
297schema_mount_create_ctx(const struct lysc_ext_instance *ext, const struct lyd_node *ext_data, ly_bool config,
298 struct ly_ctx **ext_ctx)
299{
Michal Vasko0fca45c2022-06-07 10:57:32 +0200300 LY_ERR rc = LY_SUCCESS;
Michal Vaskoddd76592022-01-17 13:34:48 +0100301 const char * const *searchdirs;
Michal Vasko0fca45c2022-06-07 10:57:32 +0200302 char *sdirs = NULL;
Michal Vaskoddd76592022-01-17 13:34:48 +0100303 const struct lys_module *mod;
304 struct lysc_node *root, *node;
Michal Vasko0fca45c2022-06-07 10:57:32 +0200305 uint32_t i, idx = 0;
Michal Vaskoddd76592022-01-17 13:34:48 +0100306
307 /* get searchdirs from the current context */
308 searchdirs = ly_ctx_get_searchdirs(ext->module->ctx);
309
Michal Vasko0fca45c2022-06-07 10:57:32 +0200310 if (searchdirs) {
311 /* append them all into a single string */
312 for (i = 0; searchdirs[i]; ++i) {
Jan Kundrát1fbc90d2022-06-07 21:27:53 +0200313 if ((rc = ly_strcat(&sdirs, "%s" PATH_SEPARATOR, searchdirs[i]))) {
Michal Vasko0fca45c2022-06-07 10:57:32 +0200314 goto cleanup;
315 }
316 }
317 }
318
Michal Vaskoddd76592022-01-17 13:34:48 +0100319 /* create the context based on the data */
Michal Vasko0fca45c2022-06-07 10:57:32 +0200320 if ((rc = ly_ctx_new_yldata(sdirs, ext_data, ly_ctx_get_options(ext->module->ctx), ext_ctx))) {
Michal Vasko193dacd2022-10-13 08:43:05 +0200321 lyplg_ext_compile_log(NULL, ext, LY_LLERR, rc, "Failed to create context for the schema-mount data.");
Michal Vasko0fca45c2022-06-07 10:57:32 +0200322 goto cleanup;
Michal Vaskoddd76592022-01-17 13:34:48 +0100323 }
324
325 if (!config) {
326 /* manually change the config of all schema nodes in all the modules */
327 while ((mod = ly_ctx_get_module_iter(*ext_ctx, &idx))) {
328 if (!mod->implemented) {
329 continue;
tadeas-vintrlik2aa36b42021-11-03 13:07:34 +0100330 }
tadeas-vintrlik2aa36b42021-11-03 13:07:34 +0100331
Michal Vaskoddd76592022-01-17 13:34:48 +0100332 LY_LIST_FOR(mod->compiled->data, root) {
333 LYSC_TREE_DFS_BEGIN(root, node) {
334 node->flags &= ~LYS_CONFIG_W;
335 node->flags |= LYS_CONFIG_R;
tadeas-vintrlik2aa36b42021-11-03 13:07:34 +0100336
Michal Vaskoddd76592022-01-17 13:34:48 +0100337 LYSC_TREE_DFS_END(root, node);
338 }
339 }
tadeas-vintrlik2aa36b42021-11-03 13:07:34 +0100340 }
341 }
342
Michal Vasko0fca45c2022-06-07 10:57:32 +0200343cleanup:
344 free(sdirs);
345 return rc;
Michal Vaskoddd76592022-01-17 13:34:48 +0100346}
347
348/**
349 * @brief Get schema (context) for a shared-schema mount point.
350 *
351 * @param[in] ext Compiled extension instance.
352 * @param[in] ext_data Extension data retrieved by the callback.
353 * @param[in] config Whether the whole schema should keep its config or be set to false.
354 * @param[out] ext_ctx Schema to use for parsing the data.
355 * @return LY_ERR value.
356 */
357static LY_ERR
358schema_mount_get_ctx_shared(struct lysc_ext_instance *ext, const struct lyd_node *ext_data, ly_bool config,
359 const struct ly_ctx **ext_ctx)
360{
Michal Vasko193dacd2022-10-13 08:43:05 +0200361 struct lyplg_ext_sm *sm_data = ext->compiled;
Michal Vaskoddd76592022-01-17 13:34:48 +0100362 LY_ERR ret = LY_SUCCESS, r;
363 struct lyd_node *node = NULL;
ekinzie0ab8b302022-10-10 03:03:57 -0400364 struct ly_ctx *new_ctx = NULL;
Michal Vaskoddd76592022-01-17 13:34:48 +0100365 uint32_t i;
366 const char *content_id = NULL;
367 void *mem;
368
369 assert(sm_data && sm_data->shared);
370
371 /* get yang-library content-id or module-set-id */
372 if (ext_data) {
373 lyd_find_path(ext_data, "/ietf-yang-library:yang-library/content-id", 0, &node);
374 if (!node) {
375 lyd_find_path(ext_data, "/ietf-yang-library:modules-state/module-set-id", 0, &node);
376 }
377 if (node) {
378 content_id = lyd_get_value(node);
379 }
380 }
381 if (!content_id) {
Michal Vasko193dacd2022-10-13 08:43:05 +0200382 lyplg_ext_compile_log(NULL, ext, LY_LLERR, LY_EVALID,
383 "Missing \"content-id\" or \"module-set-id\" in ietf-yang-library data.");
Michal Vaskoddd76592022-01-17 13:34:48 +0100384 return LY_EVALID;
385 }
386
387 /* LOCK */
388 if ((r = pthread_mutex_lock(&sm_data->shared->lock))) {
Michal Vasko193dacd2022-10-13 08:43:05 +0200389 lyplg_ext_compile_log(NULL, ext, LY_LLERR, LY_ESYS, "Mutex lock failed (%s).", strerror(r));
Michal Vaskoddd76592022-01-17 13:34:48 +0100390 return LY_ESYS;
391 }
392
393 /* try to find this mount point */
394 for (i = 0; i < sm_data->shared->schema_count; ++i) {
395 if (ext->argument == sm_data->shared->schemas[i].mount_point) {
396 break;
397 }
398 }
399
400 if (i < sm_data->shared->schema_count) {
401 /* schema exists already */
402 if (strcmp(content_id, sm_data->shared->schemas[i].content_id)) {
Michal Vasko193dacd2022-10-13 08:43:05 +0200403 lyplg_ext_compile_log_path("/ietf-yang-library:yang-library/content-id", ext, LY_LLERR, LY_EVALID,
Michal Vaskoddd76592022-01-17 13:34:48 +0100404 "Shared-schema yang-library content-id \"%s\" differs from \"%s\" used previously.",
405 content_id, sm_data->shared->schemas[i].content_id);
406 ret = LY_EVALID;
407 goto cleanup;
408 }
tadeas-vintrlik2aa36b42021-11-03 13:07:34 +0100409 } else {
Michal Vaskoddd76592022-01-17 13:34:48 +0100410 /* no schema found, create it */
411 if ((r = schema_mount_create_ctx(ext, ext_data, config, &new_ctx))) {
412 ret = r;
413 goto cleanup;
414 }
415
416 /* new entry */
417 mem = realloc(sm_data->shared->schemas, (i + 1) * sizeof *sm_data->shared->schemas);
418 if (!mem) {
419 ly_ctx_destroy(new_ctx);
Michal Vasko193dacd2022-10-13 08:43:05 +0200420 EXT_LOGERR_MEM_GOTO(NULL, ext, ret, cleanup);
Michal Vaskoddd76592022-01-17 13:34:48 +0100421 }
422 sm_data->shared->schemas = mem;
423 ++sm_data->shared->schema_count;
424
425 /* fill entry */
426 sm_data->shared->schemas[i].ctx = new_ctx;
427 sm_data->shared->schemas[i].mount_point = ext->argument;
428 lydict_insert(ext->module->ctx, content_id, 0, &sm_data->shared->schemas[i].content_id);
429 }
430
431 /* use the context */
432 *ext_ctx = sm_data->shared->schemas[i].ctx;
433
434cleanup:
435 /* UNLOCK */
436 pthread_mutex_unlock(&sm_data->shared->lock);
437
438 return ret;
439}
440
441/**
442 * @brief Get schema (context) for an inline mount point.
443 *
444 * @param[in] ext Compiled extension instance.
445 * @param[in] ext_data Extension data retrieved by the callback.
446 * @param[in] config Whether the whole schema should keep its config or be set to false.
447 * @param[out] ext_ctx Schema to use for parsing the data.
448 * @return LY_ERR value.
449 */
450static LY_ERR
451schema_mount_get_ctx_inline(struct lysc_ext_instance *ext, const struct lyd_node *ext_data, ly_bool config,
452 const struct ly_ctx **ext_ctx)
453{
Michal Vasko193dacd2022-10-13 08:43:05 +0200454 struct lyplg_ext_sm *sm_data = ext->compiled;
Michal Vaskoddd76592022-01-17 13:34:48 +0100455 LY_ERR r;
ekinzie0ab8b302022-10-10 03:03:57 -0400456 struct ly_ctx *new_ctx = NULL;
Michal Vaskoddd76592022-01-17 13:34:48 +0100457 uint32_t i;
458 void *mem;
459
460 assert(sm_data && sm_data->shared);
461
462 i = sm_data->inln.schema_count;
463
464 /* always new schema required, create context */
465 if ((r = schema_mount_create_ctx(ext, ext_data, config, &new_ctx))) {
466 return r;
467 }
468
469 /* new entry */
470 mem = realloc(sm_data->inln.schemas, (i + 1) * sizeof *sm_data->inln.schemas);
471 if (!mem) {
472 ly_ctx_destroy(new_ctx);
Michal Vasko193dacd2022-10-13 08:43:05 +0200473 EXT_LOGERR_MEM_RET(NULL, ext);
Michal Vaskoddd76592022-01-17 13:34:48 +0100474 }
475 sm_data->inln.schemas = mem;
476 ++sm_data->inln.schema_count;
477
478 /* fill entry */
479 sm_data->inln.schemas[i].ctx = new_ctx;
480
481 /* use the context */
482 *ext_ctx = sm_data->inln.schemas[i].ctx;
483 return LY_SUCCESS;
484}
485
486/**
487 * @brief Get schema (context) for a mount point.
488 *
489 * @param[in] ext Compiled extension instance.
490 * @param[out] ext_ctx Schema to use for parsing the data.
491 * @return LY_ERR value.
492 */
493static LY_ERR
494schema_mount_get_ctx(struct lysc_ext_instance *ext, const struct ly_ctx **ext_ctx)
495{
496 LY_ERR ret = LY_SUCCESS, r;
497 struct lyd_node *iter, *ext_data = NULL;
498 ly_bool ext_data_free = 0, config, shared;
499
500 *ext_ctx = NULL;
501
502 /* get operational data with ietf-yang-library and ietf-yang-schema-mount data */
503 if ((r = lyplg_ext_get_data(ext->module->ctx, ext, (void **)&ext_data, &ext_data_free))) {
504 ret = r;
505 goto cleanup;
506 }
507
508 LY_LIST_FOR(ext_data, iter) {
509 if (iter->flags & LYD_NEW) {
510 /* must be validated for the parent-reference prefix data to be stored */
Michal Vasko193dacd2022-10-13 08:43:05 +0200511 lyplg_ext_compile_log(NULL, ext, LY_LLERR, LY_EINVAL, "Provided ext data have not been validated.");
Michal Vaskoddd76592022-01-17 13:34:48 +0100512 ret = LY_EINVAL;
513 goto cleanup;
514 }
515 }
516
517 /* learn about this mount point */
518 if ((r = schema_mount_get_smount(ext, ext_data, &config, &shared))) {
519 ret = r;
520 goto cleanup;
521 }
522
523 /* create/get the context for parsing the data */
524 if (shared) {
525 r = schema_mount_get_ctx_shared(ext, ext_data, config, ext_ctx);
526 } else {
527 r = schema_mount_get_ctx_inline(ext, ext_data, config, ext_ctx);
528 }
529 if (r) {
530 ret = r;
531 goto cleanup;
532 }
533
534cleanup:
535 if (ext_data_free) {
536 lyd_free_all(ext_data);
537 }
538 return ret;
539}
540
541/**
Michal Vasko8cc3f662022-03-29 11:25:51 +0200542 * @brief Snode callback for schema mount.
543 * Check if data are valid for schema mount and returns their schema node.
Michal Vaskoddd76592022-01-17 13:34:48 +0100544 */
545static LY_ERR
Michal Vasko8cc3f662022-03-29 11:25:51 +0200546schema_mount_snode(struct lysc_ext_instance *ext, const struct lyd_node *parent, const struct lysc_node *sparent,
547 const char *prefix, size_t prefix_len, LY_VALUE_FORMAT format, void *prefix_data, const char *name, size_t name_len,
548 const struct lysc_node **snode)
Michal Vaskoddd76592022-01-17 13:34:48 +0100549{
Michal Vasko8cc3f662022-03-29 11:25:51 +0200550 LY_ERR r;
551 const struct lys_module *mod;
ekinzie0ab8b302022-10-10 03:03:57 -0400552 const struct ly_ctx *ext_ctx = NULL;
Michal Vaskoddd76592022-01-17 13:34:48 +0100553
554 /* get context based on ietf-yang-library data */
555 if ((r = schema_mount_get_ctx(ext, &ext_ctx))) {
556 return r;
557 }
558
Michal Vasko8cc3f662022-03-29 11:25:51 +0200559 /* get the module */
560 mod = lyplg_type_identity_module(ext_ctx, parent ? parent->schema : sparent, prefix, prefix_len, format, prefix_data);
561 if (!mod) {
562 return LY_ENOT;
tadeas-vintrlik2aa36b42021-11-03 13:07:34 +0100563 }
564
Michal Vasko8cc3f662022-03-29 11:25:51 +0200565 /* get the top-level schema node */
566 *snode = lys_find_child(NULL, mod, name, name_len, 0, 0);
567 return *snode ? LY_SUCCESS : LY_ENOT;
tadeas-vintrlik2aa36b42021-11-03 13:07:34 +0100568}
569
ekinzie0ab8b302022-10-10 03:03:57 -0400570static LY_ERR
571schema_mount_get_parent_ref(const struct lysc_ext_instance *ext, const struct lyd_node *ext_data,
572 struct ly_set **set)
573{
574 LY_ERR ret = LY_SUCCESS;
575 char *path = NULL;
576
577 /* get all parent references of this mount point */
578 if (asprintf(&path, "/ietf-yang-schema-mount:schema-mounts/mount-point[module='%s'][label='%s']"
579 "/shared-schema/parent-reference", ext->module->name, ext->argument) == -1) {
Michal Vasko193dacd2022-10-13 08:43:05 +0200580 EXT_LOGERR_MEM_GOTO(NULL, ext, ret, cleanup);
ekinzie0ab8b302022-10-10 03:03:57 -0400581 }
582 if ((ret = lyd_find_xpath(ext_data, path, set))) {
583 goto cleanup;
584 }
585
586cleanup:
587 free(path);
588 return ret;
589}
590
tadeas-vintrlik2aa36b42021-11-03 13:07:34 +0100591/**
Michal Vaskoddd76592022-01-17 13:34:48 +0100592 * @brief Duplicate all accessible parent references for a shared-schema mount point.
593 *
594 * @param[in] ext Compiled extension instance.
595 * @param[in] ctx_node Context node for evaluating the parent-reference XPath expressions.
596 * @param[in] ext_data Extension data retrieved by the callback.
597 * @param[in] trg_ctx Mounted data context to use for duplication.
598 * @param[out] ref_set Set of all top-level parent-ref subtrees connected to each other, may be empty.
599 * @return LY_ERR value.
600 */
601static LY_ERR
602schema_mount_dup_parent_ref(const struct lysc_ext_instance *ext, const struct lyd_node *ctx_node,
603 const struct lyd_node *ext_data, const struct ly_ctx *trg_ctx, struct ly_set **ref_set)
604{
605 LY_ERR ret = LY_SUCCESS;
606 char *path = NULL;
607 struct ly_set *set = NULL, *par_set = NULL;
608 struct lyd_node_term *term;
609 struct lyd_node *dup = NULL, *top_node, *first;
610 struct lyd_value_xpath10 *xp_val;
611 uint32_t i, j;
612
613 *ref_set = NULL;
614
615 if (!ext_data) {
616 /* we expect the same ext data as before and there must be some for data to be parsed */
Michal Vasko193dacd2022-10-13 08:43:05 +0200617 lyplg_ext_compile_log(NULL, ext, LY_LLERR, LY_EINVAL, "No ext data provided.");
Michal Vaskoddd76592022-01-17 13:34:48 +0100618 ret = LY_EINVAL;
619 goto cleanup;
620 }
621
ekinzie0ab8b302022-10-10 03:03:57 -0400622 if ((ret = schema_mount_get_parent_ref(ext, ext_data, &set))) {
Michal Vaskoddd76592022-01-17 13:34:48 +0100623 goto cleanup;
624 }
625
626 /* prepare result set */
627 if ((ret = ly_set_new(ref_set))) {
628 goto cleanup;
629 }
630
631 first = NULL;
632 for (i = 0; i < set->count; ++i) {
633 term = set->objs[i];
634
635 /* get the referenced nodes (subtrees) */
636 LYD_VALUE_GET(&term->value, xp_val);
637 if ((ret = lyd_find_xpath4(ctx_node, ctx_node, lyxp_get_expr(xp_val->exp), xp_val->format, xp_val->prefix_data,
638 NULL, &par_set))) {
Michal Vasko193dacd2022-10-13 08:43:05 +0200639 lyplg_ext_compile_log(NULL, ext, LY_LLERR, ret, "Parent reference \"%s\" evaluation failed.",
640 lyxp_get_expr(xp_val->exp));
Michal Vaskoddd76592022-01-17 13:34:48 +0100641 goto cleanup;
642 }
643
644 for (j = 0; j < par_set->count; ++j) {
645 /* duplicate with parents in the context of the mounted data */
646 if ((ret = lyd_dup_single_to_ctx(par_set->dnodes[j], trg_ctx, NULL,
Michal Vaskofbbea932022-06-07 11:00:55 +0200647 LYD_DUP_RECURSIVE | LYD_DUP_WITH_PARENTS | LYD_DUP_WITH_FLAGS | LYD_DUP_NO_EXT, &dup))) {
Michal Vaskoddd76592022-01-17 13:34:48 +0100648 goto cleanup;
649 }
650
651 /* go top-level */
652 while (dup->parent) {
653 dup = lyd_parent(dup);
654 }
655
656 /* check whether the top-level node exists */
657 if (first) {
658 if ((ret = lyd_find_sibling_first(first, dup, &top_node)) && (ret != LY_ENOTFOUND)) {
659 goto cleanup;
660 }
661 } else {
662 top_node = NULL;
663 }
664
665 if (top_node) {
666 /* merge */
667 ret = lyd_merge_tree(&first, dup, LYD_MERGE_DESTRUCT);
668 dup = NULL;
669 if (ret) {
670 goto cleanup;
671 }
672 } else {
673 /* insert */
674 if ((ret = lyd_insert_sibling(first, dup, &first))) {
675 goto cleanup;
676 }
677
678 /* add into the result set because a new top-level node was added */
679 if ((ret = ly_set_add(*ref_set, dup, 1, NULL))) {
680 goto cleanup;
681 }
682 dup = NULL;
683 }
684 }
685 }
686
687cleanup:
688 free(path);
689 ly_set_free(set, NULL);
690 ly_set_free(par_set, NULL);
691 lyd_free_tree(dup);
692 if (ret && *ref_set) {
693 if ((*ref_set)->count) {
694 lyd_free_siblings((*ref_set)->dnodes[0]);
695 }
696 ly_set_free(*ref_set, NULL);
697 *ref_set = NULL;
698 }
699 return ret;
700}
701
ekinzie0ab8b302022-10-10 03:03:57 -0400702LY_ERR
703lyplg_ext_schema_mount_get_parent_ref(const struct lysc_ext_instance *ext, struct ly_set **refs)
704{
aPiecekb72a6df2022-10-10 10:02:38 +0200705 LY_ERR rc;
ekinzie0ab8b302022-10-10 03:03:57 -0400706 struct ly_set *pref_set = NULL;
aPiecekb72a6df2022-10-10 10:02:38 +0200707 struct ly_set *snode_set = NULL;
ekinzie0ab8b302022-10-10 03:03:57 -0400708 struct ly_set *results_set = NULL;
709 struct lyd_node *ext_data;
710 ly_bool ext_data_free;
711
712 /* get operational data with ietf-yang-library and ietf-yang-schema-mount data */
aPiecekb72a6df2022-10-10 10:02:38 +0200713 if ((rc = lyplg_ext_get_data(ext->module->ctx, ext, (void **)&ext_data, &ext_data_free))) {
714 return rc;
ekinzie0ab8b302022-10-10 03:03:57 -0400715 }
716
aPiecekb72a6df2022-10-10 10:02:38 +0200717 LY_CHECK_GOTO(rc = schema_mount_get_parent_ref(ext, ext_data, &pref_set), cleanup);
ekinzie0ab8b302022-10-10 03:03:57 -0400718 if (pref_set->count == 0) {
aPiecekb72a6df2022-10-10 10:02:38 +0200719 goto cleanup;
ekinzie0ab8b302022-10-10 03:03:57 -0400720 }
721
aPiecekb72a6df2022-10-10 10:02:38 +0200722 LY_CHECK_GOTO(rc = ly_set_new(&results_set), cleanup);
ekinzie0ab8b302022-10-10 03:03:57 -0400723
724 for (uint32_t i = 0; i < pref_set->count; ++i) {
725 struct lyd_node_term *term;
726 struct lyd_value_xpath10 *xp_val;
727 char *value;
728 struct ly_err_item *err;
729
730 term = (struct lyd_node_term *)pref_set->dnodes[i];
731 LYD_VALUE_GET(&term->value, xp_val);
aPiecekb72a6df2022-10-10 10:02:38 +0200732 LY_CHECK_GOTO(rc = lyplg_type_print_xpath10_value(xp_val, LY_VALUE_JSON, NULL, &value, &err), cleanup);
733 LY_CHECK_ERR_GOTO(rc = lys_find_xpath(ext->module->ctx, NULL, value, 0, &snode_set), free(value), cleanup);
ekinzie0ab8b302022-10-10 03:03:57 -0400734 free(value);
735 for (uint32_t sn = 0; sn < snode_set->count; sn++) {
aPiecekb72a6df2022-10-10 10:02:38 +0200736 LY_CHECK_GOTO(rc = ly_set_add(results_set, snode_set->snodes[sn], 0, NULL), cleanup);
ekinzie0ab8b302022-10-10 03:03:57 -0400737 }
738 ly_set_free(snode_set, NULL);
aPiecekb72a6df2022-10-10 10:02:38 +0200739 snode_set = NULL;
ekinzie0ab8b302022-10-10 03:03:57 -0400740 }
741
742 *refs = results_set;
743
aPiecekb72a6df2022-10-10 10:02:38 +0200744cleanup:
745 if (rc) {
746 ly_set_free(results_set, NULL);
747 }
748 ly_set_free(snode_set, NULL);
ekinzie0ab8b302022-10-10 03:03:57 -0400749 if (ext_data_free) {
750 lyd_free_all(ext_data);
751 }
752 ly_set_free(pref_set, NULL);
aPiecekb72a6df2022-10-10 10:02:38 +0200753
754 return rc;
ekinzie0ab8b302022-10-10 03:03:57 -0400755}
756
Michal Vaskoddd76592022-01-17 13:34:48 +0100757/**
758 * @brief Validate callback for schema mount.
759 */
760static LY_ERR
Michal Vaskofbbea932022-06-07 11:00:55 +0200761schema_mount_validate(struct lysc_ext_instance *ext, struct lyd_node *sibling, const struct lyd_node *dep_tree,
762 enum lyd_type data_type, uint32_t val_opts, struct lyd_node **diff)
Michal Vaskoddd76592022-01-17 13:34:48 +0100763{
764 LY_ERR ret = LY_SUCCESS;
765 uint32_t old_log_opts, i;
766 struct ly_err_item *err;
Michal Vaskofbbea932022-06-07 11:00:55 +0200767 struct lyd_node *iter, *ext_data = NULL, *ref_first = NULL, *orig_parent = lyd_parent(sibling), *op_tree;
Michal Vaskof4c6f002022-04-01 09:12:22 +0200768 struct lyd_node *ext_diff = NULL, *diff_parent = NULL;
Michal Vaskoddd76592022-01-17 13:34:48 +0100769 ly_bool ext_data_free = 0;
770 struct ly_set *ref_set = NULL;
771
772 if (!sibling) {
773 /* some data had to be parsed for this callback to be called */
Michal Vasko193dacd2022-10-13 08:43:05 +0200774 EXT_LOGERR_INT_RET(NULL, ext);
Michal Vaskoddd76592022-01-17 13:34:48 +0100775 }
776
777 /* get operational data with ietf-yang-library and ietf-yang-schema-mount data */
778 if ((ret = lyplg_ext_get_data(ext->module->ctx, ext, (void **)&ext_data, &ext_data_free))) {
779 goto cleanup;
780 }
781
782 LY_LIST_FOR(ext_data, iter) {
783 if (iter->flags & LYD_NEW) {
784 /* must be validated for the parent-reference prefix data to be stored */
Michal Vasko193dacd2022-10-13 08:43:05 +0200785 lyplg_ext_compile_log(NULL, ext, LY_LLERR, LY_EINVAL, "Provided ext data have not been validated.");
Michal Vaskoddd76592022-01-17 13:34:48 +0100786 ret = LY_EINVAL;
787 goto cleanup;
788 }
789 }
790
791 /* duplicate the referenced parent nodes into ext context */
792 if ((ret = schema_mount_dup_parent_ref(ext, orig_parent, ext_data, LYD_CTX(sibling), &ref_set))) {
793 goto cleanup;
794 }
795
Michal Vaskofbbea932022-06-07 11:00:55 +0200796 if (data_type != LYD_TYPE_DATA_YANG) {
797 /* remember the operation data tree, it may be moved */
798 op_tree = sibling;
799 }
800
Michal Vaskoddd76592022-01-17 13:34:48 +0100801 /* create accessible tree, remove LYD_EXT to not call this callback recursively */
802 lyd_unlink_siblings(sibling);
803 LY_LIST_FOR(sibling, iter) {
804 iter->flags &= ~LYD_EXT;
805 }
806 if (ref_set->count) {
807 if ((ret = lyd_insert_sibling(sibling, ref_set->dnodes[0], &sibling))) {
808 goto cleanup;
809 }
810 }
811
812 /* only store messages in the context, log as an extension */
813 old_log_opts = ly_log_options(LY_LOSTORE_LAST);
814
Michal Vaskofbbea932022-06-07 11:00:55 +0200815 if (data_type == LYD_TYPE_DATA_YANG) {
816 /* validate all the data */
817 ret = lyd_validate_all(&sibling, NULL, val_opts, diff ? &ext_diff : NULL);
818 } else {
819 /* validate the operation */
820 ret = lyd_validate_op(op_tree, dep_tree, data_type, diff ? &ext_diff : NULL);
821 }
822
823 /* restore logging */
Michal Vaskoddd76592022-01-17 13:34:48 +0100824 ly_log_options(old_log_opts);
825
826 /* restore sibling tree */
827 for (i = 0; i < ref_set->count; ++i) {
828 if (ref_set->dnodes[i] == sibling) {
829 sibling = sibling->next;
830 }
831 lyd_free_tree(ref_set->dnodes[i]);
832 }
833 LY_LIST_FOR(sibling, iter) {
834 iter->flags |= LYD_EXT;
835 }
Michal Vasko193dacd2022-10-13 08:43:05 +0200836 lyplg_ext_insert(orig_parent, sibling);
Michal Vaskoddd76592022-01-17 13:34:48 +0100837
838 if (ret) {
839 /* log the error in the original context */
840 err = ly_err_first(LYD_CTX(sibling));
841 if (!err) {
Michal Vasko193dacd2022-10-13 08:43:05 +0200842 lyplg_ext_compile_log(NULL, ext, LY_LLERR, ret, "Unknown validation error (err code %d).", ret);
Michal Vaskoddd76592022-01-17 13:34:48 +0100843 } else {
Michal Vasko193dacd2022-10-13 08:43:05 +0200844 lyplg_ext_compile_log_path(err->path, ext, LY_LLERR, err->no, "%s", err->msg);
Michal Vaskoddd76592022-01-17 13:34:48 +0100845 }
846 goto cleanup;
847 }
848
Michal Vaskof4c6f002022-04-01 09:12:22 +0200849 /* create proper diff */
850 if (diff && ext_diff) {
851 /* diff nodes from an extension instance */
852 LY_LIST_FOR(ext_diff, iter) {
853 iter->flags |= LYD_EXT;
854 }
855
856 /* create the parent and insert the diff */
857 if ((ret = lyd_dup_single(lyd_parent(sibling), NULL, LYD_DUP_WITH_PARENTS | LYD_DUP_NO_META, &diff_parent))) {
858 goto cleanup;
859 }
Michal Vasko193dacd2022-10-13 08:43:05 +0200860 if ((ret = lyplg_ext_insert(diff_parent, ext_diff))) {
Michal Vaskof4c6f002022-04-01 09:12:22 +0200861 goto cleanup;
862 }
863 ext_diff = NULL;
864
865 /* go top-level and set the operation */
866 while (lyd_parent(diff_parent)) {
867 diff_parent = lyd_parent(diff_parent);
868 }
869 if ((ret = lyd_new_meta(LYD_CTX(diff_parent), diff_parent, NULL, "yang:operation", "none", 0, NULL))) {
870 goto cleanup;
871 }
872
873 /* finally merge into the global diff */
874 if ((ret = lyd_diff_merge_all(diff, diff_parent, LYD_DIFF_MERGE_DEFAULTS))) {
875 goto cleanup;
876 }
877 }
878
Michal Vaskoddd76592022-01-17 13:34:48 +0100879cleanup:
880 ly_set_free(ref_set, NULL);
881 lyd_free_siblings(ref_first);
Michal Vaskof4c6f002022-04-01 09:12:22 +0200882 lyd_free_tree(ext_diff);
883 lyd_free_all(diff_parent);
Michal Vaskoddd76592022-01-17 13:34:48 +0100884 if (ext_data_free) {
885 lyd_free_all(ext_data);
886 }
887 return ret;
888}
889
890/**
Michal Vasko193dacd2022-10-13 08:43:05 +0200891 * @brief Schema mount compile free.
Michal Vaskoddd76592022-01-17 13:34:48 +0100892 *
Michal Vasko193dacd2022-10-13 08:43:05 +0200893 * Implementation of ::lyplg_ext_compile_free_clb callback set as ::lyext_plugin::cfree.
Michal Vaskoddd76592022-01-17 13:34:48 +0100894 */
895static void
Michal Vasko193dacd2022-10-13 08:43:05 +0200896schema_mount_cfree(const struct ly_ctx *ctx, struct lysc_ext_instance *ext)
Michal Vaskoddd76592022-01-17 13:34:48 +0100897{
Michal Vasko193dacd2022-10-13 08:43:05 +0200898 struct lyplg_ext_sm *sm_data = ext->compiled;
Michal Vaskoddd76592022-01-17 13:34:48 +0100899 uint32_t i;
900
901 if (!sm_data) {
902 return;
903 }
904
905 if (!--sm_data->shared->ref_count) {
906 for (i = 0; i < sm_data->shared->schema_count; ++i) {
907 ly_ctx_destroy(sm_data->shared->schemas[i].ctx);
908 lydict_remove(ctx, sm_data->shared->schemas[i].content_id);
909 }
910 free(sm_data->shared->schemas);
911 pthread_mutex_destroy(&sm_data->shared->lock);
912 free(sm_data->shared);
913 }
914
915 for (i = 0; i < sm_data->inln.schema_count; ++i) {
916 ly_ctx_destroy(sm_data->inln.schemas[i].ctx);
917 }
918 free(sm_data->inln.schemas);
919 free(sm_data);
920}
921
ekinzie0ab8b302022-10-10 03:03:57 -0400922LIBYANG_API_DEF LY_ERR
923lyplg_ext_schema_mount_create_context(const struct lysc_ext_instance *ext, struct ly_ctx **ctx)
924{
925 struct lyd_node *ext_data;
926 ly_bool ext_data_free;
927 ly_bool config;
928 ly_bool shared;
929 LY_ERR res;
930
931 if (!ext->module->ctx->ext_clb) {
932 return LY_EINVAL;
933 }
934
935 if (strcmp(ext->def->module->name, "ietf-yang-schema-mount") ||
936 strcmp(ext->def->name, "mount-point")) {
937 return LY_EINVAL;
938 }
939
940 /* get operational data with ietf-yang-library and ietf-yang-schema-mount data */
941 if ((res = lyplg_ext_get_data(ext->module->ctx, ext, (void **)&ext_data, &ext_data_free))) {
942 return res;
943 }
944
945 /* learn about this mount point */
946 if ((res = schema_mount_get_smount(ext, ext_data, &config, &shared))) {
947 goto out;
948 }
949
950 res = schema_mount_create_ctx(ext, ext_data, config, ctx);
951
952out:
953 if (ext_data_free) {
954 lyd_free_all(ext_data);
955 }
956 return res;
957}
958
aPiecek03cb4872022-10-24 10:31:51 +0200959static void
960schema_mount_spriter_tree_free(void *priv)
961{
962 struct sprinter_tree_priv *st_priv;
963
964 st_priv = priv;
965 ly_set_free(st_priv->refs, NULL);
966 ly_ctx_destroy(st_priv->ext_ctx);
967 free(st_priv);
968}
969
970static LY_ERR
971schema_mount_sprinter_tree_cnode_override_mounted(const struct lysc_node *node, const void *UNUSED(plugin_priv),
972 ly_bool *UNUSED(skip), const char **UNUSED(flags), const char **add_opts)
973{
974 if (!node->parent) {
975 *add_opts = "/";
976 }
977
978 return LY_SUCCESS;
979}
980
981static LY_ERR
982schema_mount_sprinter_tree_pnode_override_mounted(const struct lysp_node *node, const void *UNUSED(plugin_priv),
983 ly_bool *UNUSED(skip), const char **UNUSED(flags), const char **add_opts)
984{
985 if (!node->parent) {
986 *add_opts = "/";
987 }
988
989 return LY_SUCCESS;
990}
991
992static LY_ERR
993schema_mount_sprinter_tree_node_override_parent_refs(const struct lysc_node *node, const void *plugin_priv,
994 ly_bool *skip, const char **UNUSED(flags), const char **add_opts)
995{
996 uint32_t i;
997 const struct ly_set *refs;
998 const struct lysc_module *mod;
999 struct lysc_node *ref, *iter;
1000
1001 refs = ((struct sprinter_tree_priv *)plugin_priv)->refs;
1002 mod = node->module->compiled;
1003
1004 /* Assume the @p node will be skipped. */
1005 *skip = 1;
1006 for (i = 0; (i < refs->count) && *skip; i++) {
1007 ref = refs->snodes[i];
1008 if (ref->module->compiled != mod) {
1009 /* parent-reference points to different module */
1010 continue;
1011 }
1012
1013 for (iter = ref; iter; iter = iter->parent) {
1014 if (iter == node) {
1015 /* @p node is not skipped because it is parent-rererence node or his parent */
1016 *skip = 0;
1017 break;
1018 }
1019 }
1020 }
1021
1022 if (!*skip && !node->parent) {
1023 /* top-node has additional opts */
1024 *add_opts = "@";
1025 }
1026
1027 return LY_SUCCESS;
1028}
1029
1030static LY_ERR
1031schema_mount_sprinter_ptree(struct lysp_ext_instance *UNUSED(ext), const struct lyspr_tree_ctx *ctx,
1032 const char **flags, const char **UNUSED(add_opts))
1033{
1034 if (!ctx) {
1035 *flags = "mp";
1036 }
1037
1038 return LY_SUCCESS;
1039}
1040
1041static LY_ERR
1042schema_mount_sprinter_ctree(struct lysc_ext_instance *ext, const struct lyspr_tree_ctx *ctx,
1043 const char **flags, const char **UNUSED(add_opts))
1044{
1045 LY_ERR rc = LY_SUCCESS;
1046 struct ly_ctx *ext_ctx = NULL;
1047 const struct lys_module *mod;
1048 struct ly_set *refs = NULL;
1049 struct lysc_node *tree1, *tree2;
1050 uint32_t i, j;
1051 ly_bool from_parent_ref, is_first;
1052 struct sprinter_tree_priv *st_priv;
1053
1054 if (!ctx) {
1055 *flags = "mp";
1056 return LY_SUCCESS;
1057 }
1058
1059 if (lyplg_ext_schema_mount_create_context(ext, &ext_ctx)) {
1060 /* Void mount point */
1061 return LY_SUCCESS;
1062 }
1063
1064 rc = lyplg_ext_schema_mount_get_parent_ref(ext, &refs);
1065 LY_CHECK_GOTO(rc, cleanup);
1066
1067 /* build new list of modules to print. This list will omit internal
1068 * modules, modules with no nodes (e.g., iana-if-types) and modules
1069 * that were loaded as the result of a parent-reference.
1070 */
1071 i = ly_ctx_internal_modules_count(ext_ctx);
1072 while ((mod = ly_ctx_get_module_iter(ext_ctx, &i))) {
1073 from_parent_ref = 0;
1074
1075 for (j = 0; refs && j < refs->count; j++) {
1076 if (!strcmp(mod->ns, refs->snodes[j]->module->ns)) {
1077 from_parent_ref = 1;
1078 break;
1079 }
1080 }
1081 if (from_parent_ref) {
1082 /* Modules loaded as the result of a parent-reference are added later. */
1083 continue;
1084 }
1085
1086 /* Add data nodes, rpcs and notifications. */
1087 if ((ext_ctx->flags & LY_CTX_SET_PRIV_PARSED) && mod->compiled) {
1088 /* For compiled module. */
1089 rc = lyplg_ext_sprinter_ctree_add_nodes(ctx, mod->compiled->data,
1090 schema_mount_sprinter_tree_cnode_override_mounted);
1091 LY_CHECK_GOTO(rc, cleanup);
1092 if (mod->compiled->rpcs) {
1093 rc = lyplg_ext_sprinter_ctree_add_nodes(ctx, &mod->compiled->rpcs->node,
1094 schema_mount_sprinter_tree_cnode_override_mounted);
1095 }
1096 LY_CHECK_GOTO(rc, cleanup);
1097 if (mod->compiled->notifs) {
1098 rc = lyplg_ext_sprinter_ctree_add_nodes(ctx, &mod->compiled->notifs->node,
1099 schema_mount_sprinter_tree_cnode_override_mounted);
1100 }
1101 LY_CHECK_GOTO(rc, cleanup);
1102 } else {
1103 /* For parsed module. */
1104 rc = lyplg_ext_sprinter_ptree_add_nodes(ctx, mod->parsed->data,
1105 schema_mount_sprinter_tree_pnode_override_mounted);
1106 LY_CHECK_GOTO(rc, cleanup);
1107 if (mod->parsed->rpcs) {
1108 rc = lyplg_ext_sprinter_ptree_add_nodes(ctx, &mod->parsed->rpcs->node,
1109 schema_mount_sprinter_tree_pnode_override_mounted);
1110 }
1111 LY_CHECK_GOTO(rc, cleanup);
1112 if (mod->parsed->notifs) {
1113 rc = lyplg_ext_sprinter_ptree_add_nodes(ctx, &mod->parsed->notifs->node,
1114 schema_mount_sprinter_tree_pnode_override_mounted);
1115 }
1116 LY_CHECK_GOTO(rc, cleanup);
1117 }
1118 }
1119
1120 /* Add modules loaded as the result of a parent-reference. */
1121 for (i = 0; refs && (i < refs->count); i++) {
1122 tree1 = refs->snodes[i]->module->compiled->data;
1123
1124 /* Add data nodes from the module only once. */
1125 is_first = 1;
1126 for (j = 0; j < i; j++) {
1127 tree2 = refs->snodes[j]->module->compiled->data;
1128 if (tree1 == tree2) {
1129 is_first = 0;
1130 break;
1131 }
1132 }
1133 if (is_first) {
1134 /* Add all data nodes but unavailable nodes are skipped in the callback. */
1135 rc = lyplg_ext_sprinter_ctree_add_nodes(ctx, tree1, schema_mount_sprinter_tree_node_override_parent_refs);
1136 LY_CHECK_GOTO(rc, cleanup);
1137 }
1138 }
1139
1140 /* Add private plugin data. */
1141 st_priv = calloc(1, sizeof(*st_priv));
1142 st_priv->ext_ctx = ext_ctx;
1143 st_priv->refs = refs;
1144 rc = lyplg_ext_sprinter_tree_set_priv(ctx, st_priv, schema_mount_spriter_tree_free);
1145
1146cleanup:
1147 if (rc) {
1148 ly_set_free(refs, NULL);
1149 ly_ctx_destroy(ext_ctx);
1150 }
1151
1152 return rc;
1153}
1154
Michal Vaskoddd76592022-01-17 13:34:48 +01001155/**
tadeas-vintrlik2aa36b42021-11-03 13:07:34 +01001156 * @brief Plugin descriptions for the Yang Schema Mount extension.
1157 *
1158 * Note that external plugins are supposed to use:
1159 *
1160 * LYPLG_EXTENSIONS = {
1161 */
1162const struct lyplg_ext_record plugins_schema_mount[] = {
1163 {
1164 .module = "ietf-yang-schema-mount",
1165 .revision = "2019-01-14",
1166 .name = "mount-point",
1167
Michal Vasko193dacd2022-10-13 08:43:05 +02001168 .plugin.id = "ly2 schema mount v1",
1169 .plugin.parse = schema_mount_parse,
1170 .plugin.compile = schema_mount_compile,
Michal Vasko941e0562022-10-18 10:35:00 +02001171 .plugin.printer_info = NULL,
aPiecek03cb4872022-10-24 10:31:51 +02001172 .plugin.printer_ctree = schema_mount_sprinter_ctree,
1173 .plugin.printer_ptree = schema_mount_sprinter_ptree,
Michal Vasko135719f2022-08-25 12:18:17 +02001174 .plugin.node = NULL,
Michal Vasko193dacd2022-10-13 08:43:05 +02001175 .plugin.snode = schema_mount_snode,
1176 .plugin.validate = schema_mount_validate,
1177 .plugin.pfree = NULL,
1178 .plugin.cfree = schema_mount_cfree
tadeas-vintrlik2aa36b42021-11-03 13:07:34 +01001179 },
1180 {0} /* terminating zeroed item */
1181};