/src/libyang/src/plugins_exts/schema_mount.c
Line | Count | Source (jump to first uncovered line) |
1 | | /** |
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 <pthread.h> |
19 | | #include <stdint.h> |
20 | | #include <stdlib.h> |
21 | | #include <string.h> |
22 | | |
23 | | #include "compat.h" |
24 | | #include "dict.h" |
25 | | #include "libyang.h" |
26 | | #include "log.h" |
27 | | #include "ly_common.h" |
28 | | #include "parser_data.h" |
29 | | #include "plugins_exts.h" |
30 | | #include "plugins_types.h" |
31 | | #include "tree_data.h" |
32 | | #include "tree_schema.h" |
33 | | |
34 | | /** |
35 | | * @brief Internal schema mount data structure for holding all the contexts of parsed data. |
36 | | */ |
37 | | struct lyplg_ext_sm { |
38 | | pthread_mutex_t lock; /**< lock for accessing this shared structure */ |
39 | | |
40 | | struct lyplg_ext_sm_shared { |
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 inline schema data, may be reused if possible */ |
55 | | } *schemas; /**< array of inline schemas */ |
56 | | uint32_t schema_count; /**< length of schemas array */ |
57 | | } inln; /**< inline mount points */ |
58 | | }; |
59 | | |
60 | | struct sprinter_tree_priv { |
61 | | struct ly_ctx *ext_ctx; |
62 | | struct ly_set *refs; |
63 | | }; |
64 | | |
65 | | #define EXT_LOGERR_MEM_RET(cctx, ext) \ |
66 | 0 | lyplg_ext_compile_log(cctx, ext, LY_LLERR, LY_EMEM, "Memory allocation failed (%s:%d).", __FILE__, __LINE__); \ |
67 | 0 | return LY_EMEM |
68 | | |
69 | | #define EXT_LOGERR_MEM_GOTO(cctx, ext, rc, label) \ |
70 | 0 | lyplg_ext_compile_log(cctx, ext, LY_LLERR, LY_EMEM, "Memory allocation failed (%s:%d).", __FILE__, __LINE__); \ |
71 | 0 | rc = LY_EMEM; \ |
72 | 0 | goto label |
73 | | |
74 | | #define EXT_LOGERR_INT_RET(cctx, ext) \ |
75 | 0 | lyplg_ext_compile_log(cctx, ext, LY_LLERR, LY_EINT, "Internal error (%s:%d).", __FILE__, __LINE__); \ |
76 | 0 | return LY_EINT |
77 | | |
78 | | /** |
79 | | * @brief Check if given mount point is unique among its siblings |
80 | | * |
81 | | * @param[in] pctx Parse context. |
82 | | * @param[in] ext Parsed extension instance. |
83 | | * @return LY_SUCCESS if is unique; |
84 | | * @return LY_EINVAL otherwise. |
85 | | */ |
86 | | static LY_ERR |
87 | | schema_mount_parse_unique_mp(struct lysp_ctx *pctx, const struct lysp_ext_instance *ext) |
88 | 0 | { |
89 | 0 | struct lysp_ext_instance *exts; |
90 | 0 | LY_ARRAY_COUNT_TYPE u; |
91 | 0 | struct lysp_node *parent; |
92 | | |
93 | | /* check if it is the only instance of the mount-point among its siblings */ |
94 | 0 | parent = ext->parent; |
95 | 0 | exts = parent->exts; |
96 | 0 | LY_ARRAY_FOR(exts, u) { |
97 | 0 | if (&exts[u] == ext) { |
98 | 0 | continue; |
99 | 0 | } |
100 | | |
101 | 0 | if (!strcmp(exts[u].name, ext->name)) { |
102 | 0 | lyplg_ext_parse_log(pctx, ext, LY_LLERR, LY_EVALID, "Multiple extension \"%s\" instances.", ext->name); |
103 | 0 | return LY_EINVAL; |
104 | 0 | } |
105 | 0 | } |
106 | 0 | return LY_SUCCESS; |
107 | 0 | } |
108 | | |
109 | | /** |
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 | | */ |
115 | | static LY_ERR |
116 | | schema_mount_parse(struct lysp_ctx *pctx, struct lysp_ext_instance *ext) |
117 | 0 | { |
118 | | /* check YANG version 1.1 */ |
119 | 0 | if (lyplg_ext_parse_get_cur_pmod(pctx)->version != LYS_VERSION_1_1) { |
120 | 0 | lyplg_ext_parse_log(pctx, ext, LY_LLERR, LY_EVALID, "Extension \"%s\" instance not allowed in YANG version 1 module.", |
121 | 0 | ext->name); |
122 | 0 | return LY_EINVAL; |
123 | 0 | } |
124 | | |
125 | | /* check parent nodetype */ |
126 | 0 | if ((ext->parent_stmt != LY_STMT_CONTAINER) && (ext->parent_stmt != LY_STMT_LIST)) { |
127 | 0 | lyplg_ext_parse_log(pctx, ext, LY_LLERR, LY_EVALID, "Extension \"%s\" instance allowed only in container or list statement.", |
128 | 0 | ext->name); |
129 | 0 | return LY_EINVAL; |
130 | 0 | } |
131 | | |
132 | | /* check uniqueness */ |
133 | 0 | if (schema_mount_parse_unique_mp(pctx, ext)) { |
134 | 0 | return LY_EINVAL; |
135 | 0 | } |
136 | | |
137 | | /* nothing to actually parse */ |
138 | 0 | return LY_SUCCESS; |
139 | 0 | } |
140 | | |
141 | | struct lyplg_ext_sm_shared_cb_data { |
142 | | const struct lysc_ext_instance *ext; |
143 | | struct lyplg_ext_sm_shared *sm_shared; |
144 | | }; |
145 | | |
146 | | static LY_ERR |
147 | | schema_mount_compile_mod_dfs_cb(struct lysc_node *node, void *data, ly_bool *UNUSED(dfs_continue)) |
148 | 0 | { |
149 | 0 | struct lyplg_ext_sm_shared_cb_data *cb_data = data; |
150 | 0 | struct lyplg_ext_sm *sm_data; |
151 | 0 | struct lysc_ext_instance *exts; |
152 | 0 | LY_ARRAY_COUNT_TYPE u; |
153 | |
|
154 | 0 | if (node == cb_data->ext->parent) { |
155 | | /* parent of the current compiled extension, skip */ |
156 | 0 | return LY_SUCCESS; |
157 | 0 | } |
158 | | |
159 | | /* find the same mount point */ |
160 | 0 | exts = node->exts; |
161 | 0 | LY_ARRAY_FOR(exts, u) { |
162 | 0 | if (!strcmp(exts[u].def->module->name, "ietf-yang-schema-mount") && !strcmp(exts[u].def->name, "mount-point") && |
163 | 0 | (exts[u].argument == cb_data->ext->argument)) { |
164 | | /* same mount point, break the DFS search */ |
165 | 0 | sm_data = exts[u].compiled; |
166 | 0 | cb_data->sm_shared = sm_data->shared; |
167 | 0 | return LY_EEXIST; |
168 | 0 | } |
169 | 0 | } |
170 | | |
171 | | /* not found, continue search */ |
172 | 0 | return LY_SUCCESS; |
173 | 0 | } |
174 | | |
175 | | static struct lyplg_ext_sm_shared * |
176 | | schema_mount_compile_find_shared(const struct lys_module *mod, const struct lysc_ext_instance *ext) |
177 | 0 | { |
178 | 0 | struct lyplg_ext_sm_shared_cb_data cb_data; |
179 | 0 | LY_ERR r; |
180 | | |
181 | | /* prepare cb_data */ |
182 | 0 | cb_data.ext = ext; |
183 | 0 | cb_data.sm_shared = NULL; |
184 | | |
185 | | /* try to find the same mount point */ |
186 | 0 | r = lysc_module_dfs_full(mod, schema_mount_compile_mod_dfs_cb, &cb_data); |
187 | 0 | (void)r; |
188 | 0 | assert((!r && !cb_data.sm_shared) || ((r == LY_EEXIST) && cb_data.sm_shared)); |
189 | | |
190 | 0 | return cb_data.sm_shared; |
191 | 0 | } |
192 | | |
193 | | /** |
194 | | * @brief Schema mount compile. |
195 | | * 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 | | */ |
199 | | static LY_ERR |
200 | | schema_mount_compile(struct lysc_ctx *cctx, const struct lysp_ext_instance *UNUSED(extp), struct lysc_ext_instance *ext) |
201 | 0 | { |
202 | 0 | const struct lysc_node *node; |
203 | 0 | struct lyplg_ext_sm *sm_data; |
204 | | |
205 | | /* init internal data */ |
206 | 0 | sm_data = calloc(1, sizeof *sm_data); |
207 | 0 | if (!sm_data) { |
208 | 0 | EXT_LOGERR_MEM_RET(cctx, ext); |
209 | 0 | } |
210 | 0 | pthread_mutex_init(&sm_data->lock, NULL); |
211 | 0 | ext->compiled = sm_data; |
212 | | |
213 | | /* find the owner module */ |
214 | 0 | node = ext->parent; |
215 | 0 | while (node->parent) { |
216 | 0 | node = node->parent; |
217 | 0 | } |
218 | | |
219 | | /* reuse/init shared schema */ |
220 | 0 | sm_data->shared = schema_mount_compile_find_shared(node->module, ext); |
221 | 0 | if (sm_data->shared) { |
222 | 0 | ++sm_data->shared->ref_count; |
223 | 0 | } else { |
224 | 0 | sm_data->shared = calloc(1, sizeof *sm_data->shared); |
225 | 0 | if (!sm_data->shared) { |
226 | 0 | free(sm_data); |
227 | 0 | EXT_LOGERR_MEM_RET(cctx, ext); |
228 | 0 | } |
229 | 0 | sm_data->shared->ref_count = 1; |
230 | 0 | } |
231 | | |
232 | 0 | return LY_SUCCESS; |
233 | 0 | } |
234 | | |
235 | | /** |
236 | | * @brief Learn details about the current mount point. |
237 | | * |
238 | | * @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 Optional flag whether the schema is shared or inline. |
242 | | * @return LY_ERR value. |
243 | | */ |
244 | | static LY_ERR |
245 | | schema_mount_get_smount(const struct lysc_ext_instance *ext, const struct lyd_node *ext_data, ly_bool *config, |
246 | | ly_bool *shared) |
247 | 0 | { |
248 | 0 | struct lyd_node *mpoint, *node; |
249 | 0 | char *path = NULL; |
250 | 0 | LY_ERR r; |
251 | | |
252 | | /* find the mount point */ |
253 | 0 | if (asprintf(&path, "/ietf-yang-schema-mount:schema-mounts/mount-point[module='%s'][label='%s']", ext->module->name, |
254 | 0 | ext->argument) == -1) { |
255 | 0 | EXT_LOGERR_MEM_RET(NULL, ext); |
256 | 0 | } |
257 | 0 | r = ext_data ? lyd_find_path(ext_data, path, 0, &mpoint) : LY_ENOTFOUND; |
258 | 0 | free(path); |
259 | 0 | if (r) { |
260 | | /* missing mount-point, cannot be data for this extension (https://datatracker.ietf.org/doc/html/rfc8528#page-10) */ |
261 | 0 | return LY_ENOT; |
262 | 0 | } |
263 | | |
264 | | /* check config */ |
265 | 0 | *config = 1; |
266 | 0 | if (!lyd_find_path(mpoint, "config", 0, &node) && !strcmp(lyd_get_value(node), "false")) { |
267 | 0 | *config = 0; |
268 | 0 | } |
269 | 0 | assert((ext->parent_stmt == LY_STMT_CONTAINER) || (ext->parent_stmt == LY_STMT_LIST)); |
270 | 0 | if (((struct lysc_node *)ext->parent)->flags & LYS_CONFIG_R) { |
271 | 0 | *config = 0; |
272 | 0 | } |
273 | |
|
274 | 0 | if (shared) { |
275 | | /* check schema-ref */ |
276 | 0 | if (lyd_find_path(mpoint, "shared-schema", 0, NULL)) { |
277 | 0 | if (lyd_find_path(mpoint, "inline", 0, NULL)) { |
278 | 0 | EXT_LOGERR_INT_RET(NULL, ext); |
279 | 0 | } |
280 | 0 | *shared = 0; |
281 | 0 | } else { |
282 | 0 | *shared = 1; |
283 | 0 | } |
284 | 0 | } |
285 | | |
286 | 0 | return LY_SUCCESS; |
287 | 0 | } |
288 | | |
289 | | /** |
290 | | * @brief Create schema (context) based on retrieved extension data. |
291 | | * |
292 | | * @param[in] ext Compiled extension instance. |
293 | | * @param[in] ext_data Extension data retrieved by the callback. |
294 | | * @param[in] config Whether the whole schema should keep its config or be set to false. |
295 | | * @param[out] ext_ctx Schema to use for parsing the data. |
296 | | * @return LY_ERR value. |
297 | | */ |
298 | | static LY_ERR |
299 | | schema_mount_create_ctx(const struct lysc_ext_instance *ext, const struct lyd_node *ext_data, ly_bool config, |
300 | | struct ly_ctx **ext_ctx) |
301 | 0 | { |
302 | 0 | LY_ERR rc = LY_SUCCESS; |
303 | 0 | const char * const *searchdirs; |
304 | 0 | char *sdirs = NULL; |
305 | 0 | const struct lys_module *mod; |
306 | 0 | struct lysc_node *root, *node; |
307 | 0 | uint32_t i, idx = 0; |
308 | | |
309 | | /* get searchdirs from the current context */ |
310 | 0 | searchdirs = ly_ctx_get_searchdirs(ext->module->ctx); |
311 | |
|
312 | 0 | if (searchdirs) { |
313 | | /* append them all into a single string */ |
314 | 0 | for (i = 0; searchdirs[i]; ++i) { |
315 | 0 | if ((rc = ly_strcat(&sdirs, "%s" PATH_SEPARATOR, searchdirs[i]))) { |
316 | 0 | goto cleanup; |
317 | 0 | } |
318 | 0 | } |
319 | 0 | } |
320 | | |
321 | | /* create the context based on the data */ |
322 | 0 | if ((rc = ly_ctx_new_yldata(sdirs, ext_data, ly_ctx_get_options(ext->module->ctx), ext_ctx))) { |
323 | 0 | lyplg_ext_compile_log(NULL, ext, LY_LLERR, rc, "Failed to create context for the schema-mount data."); |
324 | 0 | goto cleanup; |
325 | 0 | } |
326 | | |
327 | 0 | if (!config) { |
328 | | /* manually change the config of all schema nodes in all the modules */ |
329 | 0 | while ((mod = ly_ctx_get_module_iter(*ext_ctx, &idx))) { |
330 | 0 | if (!mod->implemented) { |
331 | 0 | continue; |
332 | 0 | } |
333 | | |
334 | 0 | LY_LIST_FOR(mod->compiled->data, root) { |
335 | 0 | LYSC_TREE_DFS_BEGIN(root, node) { |
336 | 0 | node->flags &= ~LYS_CONFIG_W; |
337 | 0 | node->flags |= LYS_CONFIG_R; |
338 | |
|
339 | 0 | LYSC_TREE_DFS_END(root, node); |
340 | 0 | } |
341 | 0 | } |
342 | 0 | } |
343 | 0 | } |
344 | |
|
345 | 0 | cleanup: |
346 | 0 | free(sdirs); |
347 | 0 | return rc; |
348 | 0 | } |
349 | | |
350 | | /** |
351 | | * @brief Get ietf-yang-library context-id from its data. |
352 | | * |
353 | | * @param[in] ext Compiled extension instance for logging. |
354 | | * @param[in] ext_data Extension data retrieved by the callback with the yang-library data. |
355 | | * @param[out] content_id Content ID in @p ext_data. |
356 | | * @return LY_ERR value. |
357 | | */ |
358 | | static LY_ERR |
359 | | schema_mount_get_content_id(struct lysc_ext_instance *ext, const struct lyd_node *ext_data, const char **content_id) |
360 | 0 | { |
361 | 0 | struct lyd_node *node = NULL; |
362 | |
|
363 | 0 | *content_id = NULL; |
364 | | |
365 | | /* get yang-library content-id or module-set-id */ |
366 | 0 | if (ext_data) { |
367 | 0 | lyd_find_path(ext_data, "/ietf-yang-library:yang-library/content-id", 0, &node); |
368 | 0 | if (!node) { |
369 | 0 | lyd_find_path(ext_data, "/ietf-yang-library:modules-state/module-set-id", 0, &node); |
370 | 0 | } |
371 | 0 | if (node) { |
372 | 0 | *content_id = lyd_get_value(node); |
373 | 0 | } |
374 | 0 | } |
375 | |
|
376 | 0 | if (!*content_id) { |
377 | 0 | lyplg_ext_compile_log(NULL, ext, LY_LLERR, LY_EVALID, |
378 | 0 | "Missing \"content-id\" or \"module-set-id\" in ietf-yang-library data."); |
379 | 0 | return LY_EVALID; |
380 | 0 | } |
381 | 0 | return LY_SUCCESS; |
382 | 0 | } |
383 | | |
384 | | /** |
385 | | * @brief Get schema (context) for a shared-schema mount point. |
386 | | * |
387 | | * @param[in] ext Compiled extension instance. |
388 | | * @param[in] ext_data Extension data retrieved by the callback. |
389 | | * @param[in] config Whether the whole schema should keep its config or be set to false. |
390 | | * @param[out] ext_ctx Schema to use for parsing the data. |
391 | | * @return LY_ERR value. |
392 | | */ |
393 | | static LY_ERR |
394 | | schema_mount_get_ctx_shared(struct lysc_ext_instance *ext, const struct lyd_node *ext_data, ly_bool config, |
395 | | const struct ly_ctx **ext_ctx) |
396 | 0 | { |
397 | 0 | struct lyplg_ext_sm *sm_data = ext->compiled; |
398 | 0 | LY_ERR rc = LY_SUCCESS, r; |
399 | 0 | struct ly_ctx *new_ctx = NULL; |
400 | 0 | uint32_t i; |
401 | 0 | const char *content_id; |
402 | 0 | void *mem; |
403 | |
|
404 | 0 | assert(sm_data && sm_data->shared); |
405 | | |
406 | | /* get yang-library content-id or module-set-id */ |
407 | 0 | if ((r = schema_mount_get_content_id(ext, ext_data, &content_id))) { |
408 | 0 | return r; |
409 | 0 | } |
410 | | |
411 | | /* LOCK */ |
412 | 0 | if ((r = pthread_mutex_lock(&sm_data->lock))) { |
413 | 0 | lyplg_ext_compile_log(NULL, ext, LY_LLERR, LY_ESYS, "Mutex lock failed (%s).", strerror(r)); |
414 | 0 | return LY_ESYS; |
415 | 0 | } |
416 | | |
417 | | /* try to find this mount point */ |
418 | 0 | for (i = 0; i < sm_data->shared->schema_count; ++i) { |
419 | 0 | if (ext->argument == sm_data->shared->schemas[i].mount_point) { |
420 | 0 | break; |
421 | 0 | } |
422 | 0 | } |
423 | |
|
424 | 0 | if (i < sm_data->shared->schema_count) { |
425 | | /* schema exists already */ |
426 | 0 | if (strcmp(content_id, sm_data->shared->schemas[i].content_id)) { |
427 | 0 | lyplg_ext_compile_log_path("/ietf-yang-library:yang-library/content-id", ext, LY_LLERR, LY_EVALID, |
428 | 0 | "Shared-schema yang-library content-id \"%s\" differs from \"%s\" used previously.", |
429 | 0 | content_id, sm_data->shared->schemas[i].content_id); |
430 | 0 | rc = LY_EVALID; |
431 | 0 | goto cleanup; |
432 | 0 | } |
433 | 0 | } else { |
434 | | /* no schema found, create it */ |
435 | 0 | if ((r = schema_mount_create_ctx(ext, ext_data, config, &new_ctx))) { |
436 | 0 | rc = r; |
437 | 0 | goto cleanup; |
438 | 0 | } |
439 | | |
440 | | /* new entry */ |
441 | 0 | mem = realloc(sm_data->shared->schemas, (i + 1) * sizeof *sm_data->shared->schemas); |
442 | 0 | if (!mem) { |
443 | 0 | ly_ctx_destroy(new_ctx); |
444 | 0 | EXT_LOGERR_MEM_GOTO(NULL, ext, rc, cleanup); |
445 | 0 | } |
446 | 0 | sm_data->shared->schemas = mem; |
447 | 0 | ++sm_data->shared->schema_count; |
448 | | |
449 | | /* fill entry */ |
450 | 0 | sm_data->shared->schemas[i].ctx = new_ctx; |
451 | 0 | sm_data->shared->schemas[i].mount_point = ext->argument; |
452 | 0 | lydict_insert(ext->module->ctx, content_id, 0, &sm_data->shared->schemas[i].content_id); |
453 | 0 | } |
454 | | |
455 | | /* use the context */ |
456 | 0 | *ext_ctx = sm_data->shared->schemas[i].ctx; |
457 | |
|
458 | 0 | cleanup: |
459 | | /* UNLOCK */ |
460 | 0 | pthread_mutex_unlock(&sm_data->lock); |
461 | |
|
462 | 0 | return rc; |
463 | 0 | } |
464 | | |
465 | | /** |
466 | | * @brief Check whether ietf-yang-library data describe an existing context meaning whether it includes |
467 | | * at least exactly all the mentioned modules. |
468 | | * |
469 | | * @param[in] ext Compiled extension instance for logging. |
470 | | * @param[in] ext_data Extension data retrieved by the callback with the yang-library data. |
471 | | * @param[in] ctx Context to consider. |
472 | | * @return LY_SUCCESS if the context matches. |
473 | | * @return LY_ENOT if the context differs. |
474 | | * @return LY_ERR on error. |
475 | | */ |
476 | | static LY_ERR |
477 | | schema_mount_ctx_match(struct lysc_ext_instance *ext, const struct lyd_node *ext_data, const struct ly_ctx *ctx) |
478 | 0 | { |
479 | 0 | struct ly_set *impl_mods = NULL, *imp_mods = NULL; |
480 | 0 | struct lyd_node *node; |
481 | 0 | const struct lys_module *mod; |
482 | 0 | const char *name, *revision; |
483 | 0 | LY_ERR rc = LY_ENOT, r; |
484 | 0 | uint32_t i; |
485 | | |
486 | | /* collect all the implemented and imported modules, we do not really care about content-id */ |
487 | 0 | if (!lyd_find_path(ext_data, "/ietf-yang-library:yang-library/content-id", 0, NULL)) { |
488 | 0 | if ((r = lyd_find_xpath(ext_data, "/ietf-yang-library:yang-library/module-set[1]/module", &impl_mods))) { |
489 | 0 | rc = r; |
490 | 0 | goto cleanup; |
491 | 0 | } |
492 | 0 | if ((r = lyd_find_xpath(ext_data, "/ietf-yang-library:yang-library/module-set[1]/import-only-module", &imp_mods))) { |
493 | 0 | rc = r; |
494 | 0 | goto cleanup; |
495 | 0 | } |
496 | 0 | } else { |
497 | 0 | if ((r = lyd_find_xpath(ext_data, "/ietf-yang-library:modules-state/module[conformance-type='implement']", &impl_mods))) { |
498 | 0 | rc = r; |
499 | 0 | goto cleanup; |
500 | 0 | } |
501 | 0 | if ((r = lyd_find_xpath(ext_data, "/ietf-yang-library:modules-state/module[conformance-type='import']", &imp_mods))) { |
502 | 0 | rc = r; |
503 | 0 | goto cleanup; |
504 | 0 | } |
505 | 0 | } |
506 | | |
507 | 0 | if (!impl_mods->count) { |
508 | 0 | lyplg_ext_compile_log(NULL, ext, LY_LLERR, LY_EVALID, "No implemented modules included in ietf-yang-library data."); |
509 | 0 | rc = LY_EVALID; |
510 | 0 | goto cleanup; |
511 | 0 | } |
512 | | |
513 | | /* check all the implemented modules */ |
514 | 0 | for (i = 0; i < impl_mods->count; ++i) { |
515 | 0 | lyd_find_path(impl_mods->dnodes[i], "name", 0, &node); |
516 | 0 | name = lyd_get_value(node); |
517 | |
|
518 | 0 | lyd_find_path(impl_mods->dnodes[i], "revision", 0, &node); |
519 | 0 | if (node && strlen(lyd_get_value(node))) { |
520 | 0 | revision = lyd_get_value(node); |
521 | 0 | } else { |
522 | 0 | revision = NULL; |
523 | 0 | } |
524 | |
|
525 | 0 | if (!(mod = ly_ctx_get_module(ctx, name, revision)) || !mod->implemented) { |
526 | | /* unsuitable module */ |
527 | 0 | goto cleanup; |
528 | 0 | } |
529 | 0 | } |
530 | | |
531 | | /* check all the imported modules */ |
532 | 0 | for (i = 0; i < imp_mods->count; ++i) { |
533 | 0 | lyd_find_path(imp_mods->dnodes[i], "name", 0, &node); |
534 | 0 | name = lyd_get_value(node); |
535 | |
|
536 | 0 | lyd_find_path(imp_mods->dnodes[i], "revision", 0, &node); |
537 | 0 | if (node && strlen(lyd_get_value(node))) { |
538 | 0 | revision = lyd_get_value(node); |
539 | 0 | } else { |
540 | 0 | revision = NULL; |
541 | 0 | } |
542 | |
|
543 | 0 | if (!ly_ctx_get_module(ctx, name, revision)) { |
544 | | /* unsuitable module */ |
545 | 0 | goto cleanup; |
546 | 0 | } |
547 | 0 | } |
548 | | |
549 | | /* context matches and can be reused */ |
550 | 0 | rc = LY_SUCCESS; |
551 | |
|
552 | 0 | cleanup: |
553 | 0 | ly_set_free(impl_mods, NULL); |
554 | 0 | ly_set_free(imp_mods, NULL); |
555 | 0 | return rc; |
556 | 0 | } |
557 | | |
558 | | /** |
559 | | * @brief Get schema (context) for an inline mount point. |
560 | | * |
561 | | * @param[in] ext Compiled extension instance. |
562 | | * @param[in] ext_data Extension data retrieved by the callback. |
563 | | * @param[in] config Whether the whole schema should keep its config or be set to false. |
564 | | * @param[out] ext_ctx Schema to use for parsing the data. |
565 | | * @return LY_ERR value. |
566 | | */ |
567 | | static LY_ERR |
568 | | schema_mount_get_ctx_inline(struct lysc_ext_instance *ext, const struct lyd_node *ext_data, ly_bool config, |
569 | | const struct ly_ctx **ext_ctx) |
570 | 0 | { |
571 | 0 | struct lyplg_ext_sm *sm_data = ext->compiled; |
572 | 0 | struct ly_ctx *new_ctx = NULL; |
573 | 0 | uint32_t i; |
574 | 0 | void *mem; |
575 | 0 | LY_ERR rc = LY_SUCCESS, r; |
576 | |
|
577 | 0 | assert(sm_data && sm_data->shared); |
578 | | |
579 | | /* LOCK */ |
580 | 0 | if ((r = pthread_mutex_lock(&sm_data->lock))) { |
581 | 0 | lyplg_ext_compile_log(NULL, ext, LY_LLERR, LY_ESYS, "Mutex lock failed (%s).", strerror(r)); |
582 | 0 | return LY_ESYS; |
583 | 0 | } |
584 | | |
585 | | /* try to find a context we can reuse */ |
586 | 0 | for (i = 0; i < sm_data->inln.schema_count; ++i) { |
587 | 0 | r = schema_mount_ctx_match(ext, ext_data, sm_data->inln.schemas[i].ctx); |
588 | 0 | if (!r) { |
589 | | /* match */ |
590 | 0 | *ext_ctx = sm_data->inln.schemas[i].ctx; |
591 | 0 | goto cleanup; |
592 | 0 | } else if (r != LY_ENOT) { |
593 | | /* error */ |
594 | 0 | rc = r; |
595 | 0 | goto cleanup; |
596 | 0 | } |
597 | 0 | } |
598 | | |
599 | | /* new schema required, create context */ |
600 | 0 | if ((r = schema_mount_create_ctx(ext, ext_data, config, &new_ctx))) { |
601 | 0 | rc = r; |
602 | 0 | goto cleanup; |
603 | 0 | } |
604 | | |
605 | | /* new entry */ |
606 | 0 | mem = realloc(sm_data->inln.schemas, (i + 1) * sizeof *sm_data->inln.schemas); |
607 | 0 | if (!mem) { |
608 | 0 | ly_ctx_destroy(new_ctx); |
609 | 0 | EXT_LOGERR_MEM_GOTO(NULL, ext, rc, cleanup); |
610 | 0 | } |
611 | 0 | sm_data->inln.schemas = mem; |
612 | 0 | ++sm_data->inln.schema_count; |
613 | | |
614 | | /* fill entry */ |
615 | 0 | sm_data->inln.schemas[i].ctx = new_ctx; |
616 | | |
617 | | /* use the context */ |
618 | 0 | *ext_ctx = sm_data->inln.schemas[i].ctx; |
619 | |
|
620 | 0 | cleanup: |
621 | | /* UNLOCK */ |
622 | 0 | pthread_mutex_unlock(&sm_data->lock); |
623 | |
|
624 | 0 | return rc; |
625 | 0 | } |
626 | | |
627 | | LY_ERR |
628 | | lyplg_ext_schema_mount_get_ctx(struct lysc_ext_instance *ext, const struct ly_ctx **ext_ctx) |
629 | 0 | { |
630 | 0 | LY_ERR ret = LY_SUCCESS, r; |
631 | 0 | struct lyd_node *iter, *ext_data = NULL; |
632 | 0 | ly_bool ext_data_free = 0, config, shared; |
633 | |
|
634 | 0 | *ext_ctx = NULL; |
635 | | |
636 | | /* get operational data with ietf-yang-library and ietf-yang-schema-mount data */ |
637 | 0 | if ((r = lyplg_ext_get_data(ext->module->ctx, ext, (void **)&ext_data, &ext_data_free))) { |
638 | 0 | ret = r; |
639 | 0 | goto cleanup; |
640 | 0 | } |
641 | | |
642 | 0 | LY_LIST_FOR(ext_data, iter) { |
643 | 0 | if (iter->flags & LYD_NEW) { |
644 | | /* must be validated for the parent-reference prefix data to be stored */ |
645 | 0 | lyplg_ext_compile_log(NULL, ext, LY_LLERR, LY_EINVAL, "Provided ext data have not been validated."); |
646 | 0 | ret = LY_EINVAL; |
647 | 0 | goto cleanup; |
648 | 0 | } |
649 | 0 | } |
650 | | |
651 | | /* learn about this mount point */ |
652 | 0 | if ((r = schema_mount_get_smount(ext, ext_data, &config, &shared))) { |
653 | 0 | ret = r; |
654 | 0 | goto cleanup; |
655 | 0 | } |
656 | | |
657 | | /* create/get the context for parsing the data */ |
658 | 0 | if (shared) { |
659 | 0 | r = schema_mount_get_ctx_shared(ext, ext_data, config, ext_ctx); |
660 | 0 | } else { |
661 | 0 | r = schema_mount_get_ctx_inline(ext, ext_data, config, ext_ctx); |
662 | 0 | } |
663 | 0 | if (r) { |
664 | 0 | ret = r; |
665 | 0 | goto cleanup; |
666 | 0 | } |
667 | | |
668 | 0 | cleanup: |
669 | 0 | if (ext_data_free) { |
670 | 0 | lyd_free_all(ext_data); |
671 | 0 | } |
672 | 0 | return ret; |
673 | 0 | } |
674 | | |
675 | | /** |
676 | | * @brief Snode callback for schema mount. |
677 | | * Check if data are valid for schema mount and returns their schema node. |
678 | | */ |
679 | | static LY_ERR |
680 | | schema_mount_snode(struct lysc_ext_instance *ext, const struct lyd_node *parent, const struct lysc_node *sparent, |
681 | | const char *prefix, size_t prefix_len, LY_VALUE_FORMAT format, void *prefix_data, const char *name, size_t name_len, |
682 | | const struct lysc_node **snode) |
683 | 0 | { |
684 | 0 | LY_ERR r; |
685 | 0 | const struct lys_module *mod; |
686 | 0 | const struct ly_ctx *ext_ctx = NULL; |
687 | | |
688 | | /* get context based on ietf-yang-library data */ |
689 | 0 | if ((r = lyplg_ext_schema_mount_get_ctx(ext, &ext_ctx))) { |
690 | 0 | return r; |
691 | 0 | } |
692 | | |
693 | | /* get the module */ |
694 | 0 | mod = lyplg_type_identity_module(ext_ctx, parent ? parent->schema : sparent, prefix, prefix_len, format, prefix_data); |
695 | 0 | if (!mod) { |
696 | 0 | return LY_ENOT; |
697 | 0 | } |
698 | | |
699 | | /* get the top-level schema node */ |
700 | 0 | *snode = lys_find_child(NULL, mod, name, name_len, 0, 0); |
701 | 0 | return *snode ? LY_SUCCESS : LY_ENOT; |
702 | 0 | } |
703 | | |
704 | | static LY_ERR |
705 | | schema_mount_get_parent_ref(const struct lysc_ext_instance *ext, const struct lyd_node *ext_data, |
706 | | struct ly_set **set) |
707 | 0 | { |
708 | 0 | LY_ERR ret = LY_SUCCESS; |
709 | 0 | char *path = NULL; |
710 | | |
711 | | /* get all parent references of this mount point */ |
712 | 0 | if (asprintf(&path, "/ietf-yang-schema-mount:schema-mounts/mount-point[module='%s'][label='%s']" |
713 | 0 | "/shared-schema/parent-reference", ext->module->name, ext->argument) == -1) { |
714 | 0 | EXT_LOGERR_MEM_GOTO(NULL, ext, ret, cleanup); |
715 | 0 | } |
716 | 0 | if ((ret = lyd_find_xpath(ext_data, path, set))) { |
717 | 0 | goto cleanup; |
718 | 0 | } |
719 | | |
720 | 0 | cleanup: |
721 | 0 | free(path); |
722 | 0 | return ret; |
723 | 0 | } |
724 | | |
725 | | /** |
726 | | * @brief Duplicate all accessible parent references for a shared-schema mount point. |
727 | | * |
728 | | * @param[in] ext Compiled extension instance. |
729 | | * @param[in] ctx_node Context node for evaluating the parent-reference XPath expressions. |
730 | | * @param[in] ext_data Extension data retrieved by the callback. |
731 | | * @param[in] trg_ctx Mounted data context to use for duplication. |
732 | | * @param[out] ref_set Set of all top-level parent-ref subtrees connected to each other, may be empty. |
733 | | * @return LY_ERR value. |
734 | | */ |
735 | | static LY_ERR |
736 | | schema_mount_dup_parent_ref(const struct lysc_ext_instance *ext, const struct lyd_node *ctx_node, |
737 | | const struct lyd_node *ext_data, const struct ly_ctx *trg_ctx, struct ly_set **ref_set) |
738 | 0 | { |
739 | 0 | LY_ERR ret = LY_SUCCESS; |
740 | 0 | char *path = NULL; |
741 | 0 | struct ly_set *set = NULL, *par_set = NULL; |
742 | 0 | struct lyd_node_term *term; |
743 | 0 | struct lyd_node *dup = NULL, *top_node, *first; |
744 | 0 | struct lyd_value_xpath10 *xp_val; |
745 | 0 | uint32_t i, j; |
746 | |
|
747 | 0 | *ref_set = NULL; |
748 | |
|
749 | 0 | if (!ext_data) { |
750 | | /* we expect the same ext data as before and there must be some for data to be parsed */ |
751 | 0 | lyplg_ext_compile_log(NULL, ext, LY_LLERR, LY_EINVAL, "No ext data provided."); |
752 | 0 | ret = LY_EINVAL; |
753 | 0 | goto cleanup; |
754 | 0 | } |
755 | | |
756 | 0 | if ((ret = schema_mount_get_parent_ref(ext, ext_data, &set))) { |
757 | 0 | goto cleanup; |
758 | 0 | } |
759 | | |
760 | | /* prepare result set */ |
761 | 0 | if ((ret = ly_set_new(ref_set))) { |
762 | 0 | goto cleanup; |
763 | 0 | } |
764 | | |
765 | 0 | first = NULL; |
766 | 0 | for (i = 0; i < set->count; ++i) { |
767 | 0 | term = set->objs[i]; |
768 | | |
769 | | /* get the referenced nodes (subtrees) */ |
770 | 0 | LYD_VALUE_GET(&term->value, xp_val); |
771 | 0 | if ((ret = lyd_find_xpath3(ctx_node, ctx_node, lyxp_get_expr(xp_val->exp), xp_val->format, xp_val->prefix_data, |
772 | 0 | NULL, &par_set))) { |
773 | 0 | lyplg_ext_compile_log(NULL, ext, LY_LLERR, ret, "Parent reference \"%s\" evaluation failed.", |
774 | 0 | lyxp_get_expr(xp_val->exp)); |
775 | 0 | goto cleanup; |
776 | 0 | } |
777 | | |
778 | 0 | for (j = 0; j < par_set->count; ++j) { |
779 | | /* duplicate with parents in the context of the mounted data */ |
780 | 0 | if ((ret = lyd_dup_single_to_ctx(par_set->dnodes[j], trg_ctx, NULL, |
781 | 0 | LYD_DUP_RECURSIVE | LYD_DUP_WITH_PARENTS | LYD_DUP_WITH_FLAGS | LYD_DUP_NO_EXT, &dup))) { |
782 | 0 | goto cleanup; |
783 | 0 | } |
784 | | |
785 | | /* go top-level */ |
786 | 0 | while (dup->parent) { |
787 | 0 | dup = lyd_parent(dup); |
788 | 0 | } |
789 | | |
790 | | /* check whether the top-level node exists */ |
791 | 0 | if (first) { |
792 | 0 | if ((ret = lyd_find_sibling_first(first, dup, &top_node)) && (ret != LY_ENOTFOUND)) { |
793 | 0 | goto cleanup; |
794 | 0 | } |
795 | 0 | } else { |
796 | 0 | top_node = NULL; |
797 | 0 | } |
798 | | |
799 | 0 | if (top_node) { |
800 | | /* merge */ |
801 | 0 | ret = lyd_merge_tree(&first, dup, LYD_MERGE_DESTRUCT); |
802 | 0 | dup = NULL; |
803 | 0 | if (ret) { |
804 | 0 | goto cleanup; |
805 | 0 | } |
806 | 0 | } else { |
807 | | /* insert */ |
808 | 0 | if ((ret = lyd_insert_sibling(first, dup, &first))) { |
809 | 0 | goto cleanup; |
810 | 0 | } |
811 | | |
812 | | /* add into the result set because a new top-level node was added */ |
813 | 0 | if ((ret = ly_set_add(*ref_set, dup, 1, NULL))) { |
814 | 0 | goto cleanup; |
815 | 0 | } |
816 | 0 | dup = NULL; |
817 | 0 | } |
818 | 0 | } |
819 | 0 | } |
820 | | |
821 | 0 | cleanup: |
822 | 0 | free(path); |
823 | 0 | ly_set_free(set, NULL); |
824 | 0 | ly_set_free(par_set, NULL); |
825 | 0 | lyd_free_tree(dup); |
826 | 0 | if (ret && *ref_set) { |
827 | 0 | if ((*ref_set)->count) { |
828 | 0 | lyd_free_siblings((*ref_set)->dnodes[0]); |
829 | 0 | } |
830 | 0 | ly_set_free(*ref_set, NULL); |
831 | 0 | *ref_set = NULL; |
832 | 0 | } |
833 | 0 | return ret; |
834 | 0 | } |
835 | | |
836 | | LIBYANG_API_DEF LY_ERR |
837 | | lyplg_ext_schema_mount_get_parent_ref(const struct lysc_ext_instance *ext, struct ly_set **refs) |
838 | 0 | { |
839 | 0 | LY_ERR rc; |
840 | 0 | struct ly_set *pref_set = NULL; |
841 | 0 | struct ly_set *snode_set = NULL; |
842 | 0 | struct ly_set *results_set = NULL; |
843 | 0 | struct lyd_node *ext_data; |
844 | 0 | ly_bool ext_data_free; |
845 | | |
846 | | /* get operational data with ietf-yang-library and ietf-yang-schema-mount data */ |
847 | 0 | if ((rc = lyplg_ext_get_data(ext->module->ctx, ext, (void **)&ext_data, &ext_data_free))) { |
848 | 0 | return rc; |
849 | 0 | } |
850 | | |
851 | 0 | LY_CHECK_GOTO(rc = schema_mount_get_parent_ref(ext, ext_data, &pref_set), cleanup); |
852 | 0 | if (pref_set->count == 0) { |
853 | 0 | goto cleanup; |
854 | 0 | } |
855 | | |
856 | 0 | LY_CHECK_GOTO(rc = ly_set_new(&results_set), cleanup); |
857 | |
|
858 | 0 | for (uint32_t i = 0; i < pref_set->count; ++i) { |
859 | 0 | struct lyd_node_term *term; |
860 | 0 | struct lyd_value_xpath10 *xp_val; |
861 | 0 | char *value; |
862 | 0 | struct ly_err_item *err; |
863 | |
|
864 | 0 | term = (struct lyd_node_term *)pref_set->dnodes[i]; |
865 | 0 | LYD_VALUE_GET(&term->value, xp_val); |
866 | 0 | LY_CHECK_GOTO(rc = lyplg_type_print_xpath10_value(xp_val, LY_VALUE_JSON, NULL, &value, &err), cleanup); |
867 | 0 | LY_CHECK_ERR_GOTO(rc = lys_find_xpath(ext->module->ctx, NULL, value, 0, &snode_set), free(value), cleanup); |
868 | 0 | free(value); |
869 | 0 | for (uint32_t sn = 0; sn < snode_set->count; sn++) { |
870 | 0 | LY_CHECK_GOTO(rc = ly_set_add(results_set, snode_set->snodes[sn], 0, NULL), cleanup); |
871 | 0 | } |
872 | 0 | ly_set_free(snode_set, NULL); |
873 | 0 | snode_set = NULL; |
874 | 0 | } |
875 | | |
876 | 0 | *refs = results_set; |
877 | |
|
878 | 0 | cleanup: |
879 | 0 | if (rc) { |
880 | 0 | ly_set_free(results_set, NULL); |
881 | 0 | } |
882 | 0 | ly_set_free(snode_set, NULL); |
883 | 0 | if (ext_data_free) { |
884 | 0 | lyd_free_all(ext_data); |
885 | 0 | } |
886 | 0 | ly_set_free(pref_set, NULL); |
887 | |
|
888 | 0 | return rc; |
889 | 0 | } |
890 | | |
891 | | /** |
892 | | * @brief Validate callback for schema mount. |
893 | | */ |
894 | | static LY_ERR |
895 | | schema_mount_validate(struct lysc_ext_instance *ext, struct lyd_node *sibling, const struct lyd_node *dep_tree, |
896 | | enum lyd_type data_type, uint32_t val_opts, struct lyd_node **diff) |
897 | 0 | { |
898 | 0 | LY_ERR ret = LY_SUCCESS; |
899 | 0 | uint32_t *prev_lo, temp_lo = LY_LOSTORE_LAST, i; |
900 | 0 | const struct ly_err_item *err; |
901 | 0 | struct lyd_node *iter, *ext_data = NULL, *ref_first = NULL, *orig_parent = lyd_parent(sibling), *op_tree; |
902 | 0 | struct lyd_node *ext_diff = NULL, *diff_parent = NULL; |
903 | 0 | ly_bool ext_data_free = 0; |
904 | 0 | struct ly_set *ref_set = NULL; |
905 | |
|
906 | 0 | if (!sibling) { |
907 | | /* some data had to be parsed for this callback to be called */ |
908 | 0 | EXT_LOGERR_INT_RET(NULL, ext); |
909 | 0 | } |
910 | | |
911 | | /* get operational data with ietf-yang-library and ietf-yang-schema-mount data */ |
912 | 0 | if ((ret = lyplg_ext_get_data(ext->module->ctx, ext, (void **)&ext_data, &ext_data_free))) { |
913 | 0 | goto cleanup; |
914 | 0 | } |
915 | | |
916 | 0 | LY_LIST_FOR(ext_data, iter) { |
917 | 0 | if (iter->flags & LYD_NEW) { |
918 | | /* must be validated for the parent-reference prefix data to be stored */ |
919 | 0 | lyplg_ext_compile_log(NULL, ext, LY_LLERR, LY_EINVAL, "Provided ext data have not been validated."); |
920 | 0 | ret = LY_EINVAL; |
921 | 0 | goto cleanup; |
922 | 0 | } |
923 | 0 | } |
924 | | |
925 | | /* duplicate the referenced parent nodes into ext context */ |
926 | 0 | if ((ret = schema_mount_dup_parent_ref(ext, orig_parent, ext_data, LYD_CTX(sibling), &ref_set))) { |
927 | 0 | goto cleanup; |
928 | 0 | } |
929 | | |
930 | 0 | if (data_type != LYD_TYPE_DATA_YANG) { |
931 | | /* remember the operation data tree, it may be moved */ |
932 | 0 | op_tree = sibling; |
933 | 0 | } |
934 | | |
935 | | /* create accessible tree, remove LYD_EXT to not call this callback recursively */ |
936 | 0 | LY_CHECK_GOTO(lyd_unlink_siblings(sibling), cleanup); |
937 | 0 | LY_LIST_FOR(sibling, iter) { |
938 | 0 | iter->flags &= ~LYD_EXT; |
939 | 0 | } |
940 | 0 | if (ref_set->count) { |
941 | 0 | if ((ret = lyd_insert_sibling(sibling, ref_set->dnodes[0], &sibling))) { |
942 | 0 | goto cleanup; |
943 | 0 | } |
944 | 0 | } |
945 | | |
946 | | /* only store messages in the context, log as an extension */ |
947 | 0 | prev_lo = ly_temp_log_options(&temp_lo); |
948 | |
|
949 | 0 | if (data_type == LYD_TYPE_DATA_YANG) { |
950 | | /* validate all the modules with data */ |
951 | 0 | ret = lyd_validate_all(&sibling, NULL, val_opts | LYD_VALIDATE_PRESENT, diff ? &ext_diff : NULL); |
952 | 0 | } else { |
953 | | /* validate the operation */ |
954 | 0 | ret = lyd_validate_op(op_tree, dep_tree, data_type, diff ? &ext_diff : NULL); |
955 | 0 | } |
956 | | |
957 | | /* restore logging */ |
958 | 0 | ly_temp_log_options(prev_lo); |
959 | | |
960 | | /* restore sibling tree */ |
961 | 0 | for (i = 0; i < ref_set->count; ++i) { |
962 | 0 | if (ref_set->dnodes[i] == sibling) { |
963 | 0 | sibling = sibling->next; |
964 | 0 | } |
965 | 0 | lyd_free_tree(ref_set->dnodes[i]); |
966 | 0 | } |
967 | 0 | LY_LIST_FOR(sibling, iter) { |
968 | 0 | iter->flags |= LYD_EXT; |
969 | 0 | } |
970 | 0 | lyplg_ext_insert(orig_parent, sibling); |
971 | |
|
972 | 0 | if (ret) { |
973 | | /* log the error in the original context */ |
974 | 0 | err = ly_err_first(LYD_CTX(sibling)); |
975 | 0 | if (!err) { |
976 | 0 | lyplg_ext_compile_log(NULL, ext, LY_LLERR, ret, "Unknown validation error (err code %d).", ret); |
977 | 0 | } else { |
978 | 0 | lyplg_ext_compile_log_err(err, ext); |
979 | 0 | } |
980 | 0 | goto cleanup; |
981 | 0 | } |
982 | | |
983 | | /* create proper diff */ |
984 | 0 | if (diff && ext_diff) { |
985 | | /* diff nodes from an extension instance */ |
986 | 0 | LY_LIST_FOR(ext_diff, iter) { |
987 | 0 | iter->flags |= LYD_EXT; |
988 | 0 | } |
989 | | |
990 | | /* create the parent and insert the diff */ |
991 | 0 | if ((ret = lyd_dup_single(lyd_parent(sibling), NULL, LYD_DUP_WITH_PARENTS | LYD_DUP_NO_META, &diff_parent))) { |
992 | 0 | goto cleanup; |
993 | 0 | } |
994 | 0 | if ((ret = lyplg_ext_insert(diff_parent, ext_diff))) { |
995 | 0 | goto cleanup; |
996 | 0 | } |
997 | 0 | ext_diff = NULL; |
998 | | |
999 | | /* go top-level and set the operation */ |
1000 | 0 | while (lyd_parent(diff_parent)) { |
1001 | 0 | diff_parent = lyd_parent(diff_parent); |
1002 | 0 | } |
1003 | 0 | if ((ret = lyd_new_meta(LYD_CTX(diff_parent), diff_parent, NULL, "yang:operation", "none", 0, NULL))) { |
1004 | 0 | goto cleanup; |
1005 | 0 | } |
1006 | | |
1007 | | /* finally merge into the global diff */ |
1008 | 0 | if ((ret = lyd_diff_merge_all(diff, diff_parent, LYD_DIFF_MERGE_DEFAULTS))) { |
1009 | 0 | goto cleanup; |
1010 | 0 | } |
1011 | 0 | } |
1012 | | |
1013 | 0 | cleanup: |
1014 | 0 | ly_set_free(ref_set, NULL); |
1015 | 0 | lyd_free_siblings(ref_first); |
1016 | 0 | lyd_free_tree(ext_diff); |
1017 | 0 | lyd_free_all(diff_parent); |
1018 | 0 | if (ext_data_free) { |
1019 | 0 | lyd_free_all(ext_data); |
1020 | 0 | } |
1021 | 0 | return ret; |
1022 | 0 | } |
1023 | | |
1024 | | /** |
1025 | | * @brief Schema mount compile free. |
1026 | | * |
1027 | | * Implementation of ::lyplg_ext_compile_free_clb callback set as ::lyext_plugin::cfree. |
1028 | | */ |
1029 | | static void |
1030 | | schema_mount_cfree(const struct ly_ctx *ctx, struct lysc_ext_instance *ext) |
1031 | 0 | { |
1032 | 0 | struct lyplg_ext_sm *sm_data = ext->compiled; |
1033 | 0 | uint32_t i; |
1034 | |
|
1035 | 0 | if (!sm_data) { |
1036 | 0 | return; |
1037 | 0 | } |
1038 | | |
1039 | 0 | if (!--sm_data->shared->ref_count) { |
1040 | 0 | for (i = 0; i < sm_data->shared->schema_count; ++i) { |
1041 | 0 | ly_ctx_destroy(sm_data->shared->schemas[i].ctx); |
1042 | 0 | lydict_remove(ctx, sm_data->shared->schemas[i].content_id); |
1043 | 0 | } |
1044 | 0 | free(sm_data->shared->schemas); |
1045 | 0 | free(sm_data->shared); |
1046 | 0 | } |
1047 | |
|
1048 | 0 | for (i = 0; i < sm_data->inln.schema_count; ++i) { |
1049 | 0 | ly_ctx_destroy(sm_data->inln.schemas[i].ctx); |
1050 | 0 | } |
1051 | 0 | free(sm_data->inln.schemas); |
1052 | |
|
1053 | 0 | pthread_mutex_destroy(&sm_data->lock); |
1054 | 0 | free(sm_data); |
1055 | 0 | } |
1056 | | |
1057 | | LIBYANG_API_DEF LY_ERR |
1058 | | lyplg_ext_schema_mount_create_context(const struct lysc_ext_instance *ext, struct ly_ctx **ctx) |
1059 | 0 | { |
1060 | 0 | struct lyd_node *ext_data = NULL; |
1061 | 0 | ly_bool ext_data_free = 0, config; |
1062 | 0 | LY_ERR rc = LY_SUCCESS; |
1063 | |
|
1064 | 0 | if (!ext->module->ctx->ext_clb) { |
1065 | 0 | return LY_EINVAL; |
1066 | 0 | } |
1067 | | |
1068 | 0 | if (strcmp(ext->def->module->name, "ietf-yang-schema-mount") || strcmp(ext->def->name, "mount-point")) { |
1069 | 0 | return LY_EINVAL; |
1070 | 0 | } |
1071 | | |
1072 | | /* get operational data with ietf-yang-library and ietf-yang-schema-mount data */ |
1073 | 0 | if ((rc = lyplg_ext_get_data(ext->module->ctx, ext, (void **)&ext_data, &ext_data_free))) { |
1074 | 0 | return rc; |
1075 | 0 | } |
1076 | | |
1077 | | /* learn about this mount point */ |
1078 | 0 | if ((rc = schema_mount_get_smount(ext, ext_data, &config, NULL))) { |
1079 | 0 | goto cleanup; |
1080 | 0 | } |
1081 | | |
1082 | | /* create the context */ |
1083 | 0 | rc = schema_mount_create_ctx(ext, ext_data, config, ctx); |
1084 | |
|
1085 | 0 | cleanup: |
1086 | 0 | if (ext_data_free) { |
1087 | 0 | lyd_free_all(ext_data); |
1088 | 0 | } |
1089 | 0 | return rc; |
1090 | 0 | } |
1091 | | |
1092 | | static void |
1093 | | schema_mount_spriter_tree_free(void *priv) |
1094 | 0 | { |
1095 | 0 | struct sprinter_tree_priv *st_priv; |
1096 | |
|
1097 | 0 | st_priv = priv; |
1098 | 0 | ly_set_free(st_priv->refs, NULL); |
1099 | 0 | ly_ctx_destroy(st_priv->ext_ctx); |
1100 | 0 | free(st_priv); |
1101 | 0 | } |
1102 | | |
1103 | | static LY_ERR |
1104 | | schema_mount_sprinter_tree_cnode_override_mounted(const struct lysc_node *node, const void *UNUSED(plugin_priv), |
1105 | | ly_bool *UNUSED(skip), const char **UNUSED(flags), const char **add_opts) |
1106 | 0 | { |
1107 | 0 | if (!node->parent) { |
1108 | 0 | *add_opts = "/"; |
1109 | 0 | } |
1110 | |
|
1111 | 0 | return LY_SUCCESS; |
1112 | 0 | } |
1113 | | |
1114 | | static LY_ERR |
1115 | | schema_mount_sprinter_tree_pnode_override_mounted(const struct lysp_node *node, const void *UNUSED(plugin_priv), |
1116 | | ly_bool *UNUSED(skip), const char **UNUSED(flags), const char **add_opts) |
1117 | 0 | { |
1118 | 0 | if (!node->parent) { |
1119 | 0 | *add_opts = "/"; |
1120 | 0 | } |
1121 | |
|
1122 | 0 | return LY_SUCCESS; |
1123 | 0 | } |
1124 | | |
1125 | | static LY_ERR |
1126 | | schema_mount_sprinter_tree_node_override_parent_refs(const struct lysc_node *node, const void *plugin_priv, |
1127 | | ly_bool *skip, const char **UNUSED(flags), const char **add_opts) |
1128 | 0 | { |
1129 | 0 | uint32_t i; |
1130 | 0 | const struct ly_set *refs; |
1131 | 0 | const struct lysc_module *mod; |
1132 | 0 | struct lysc_node *ref, *iter; |
1133 | |
|
1134 | 0 | refs = ((struct sprinter_tree_priv *)plugin_priv)->refs; |
1135 | 0 | mod = node->module->compiled; |
1136 | | |
1137 | | /* Assume the @p node will be skipped. */ |
1138 | 0 | *skip = 1; |
1139 | 0 | for (i = 0; (i < refs->count) && *skip; i++) { |
1140 | 0 | ref = refs->snodes[i]; |
1141 | 0 | if (ref->module->compiled != mod) { |
1142 | | /* parent-reference points to different module */ |
1143 | 0 | continue; |
1144 | 0 | } |
1145 | | |
1146 | 0 | for (iter = ref; iter; iter = iter->parent) { |
1147 | 0 | if (iter == node) { |
1148 | | /* @p node is not skipped because it is parent-rererence node or his parent */ |
1149 | 0 | *skip = 0; |
1150 | 0 | break; |
1151 | 0 | } |
1152 | 0 | } |
1153 | 0 | } |
1154 | |
|
1155 | 0 | if (!*skip && !node->parent) { |
1156 | | /* top-node has additional opts */ |
1157 | 0 | *add_opts = "@"; |
1158 | 0 | } |
1159 | |
|
1160 | 0 | return LY_SUCCESS; |
1161 | 0 | } |
1162 | | |
1163 | | /** |
1164 | | * @brief Schema mount schema parsed tree printer. |
1165 | | * |
1166 | | * Implementation of ::lyplg_ext_sprinter_ptree_clb callback set as lyext_plugin::printer_ptree. |
1167 | | */ |
1168 | | static LY_ERR |
1169 | | schema_mount_sprinter_ptree(struct lysp_ext_instance *UNUSED(ext), const struct lyspr_tree_ctx *ctx, |
1170 | | const char **flags, const char **UNUSED(add_opts)) |
1171 | 0 | { |
1172 | 0 | if (!ctx) { |
1173 | 0 | *flags = "mp"; |
1174 | 0 | } |
1175 | |
|
1176 | 0 | return LY_SUCCESS; |
1177 | 0 | } |
1178 | | |
1179 | | /** |
1180 | | * @brief Schema mount schema compiled tree printer. |
1181 | | * |
1182 | | * Implementation of ::lyplg_ext_sprinter_ctree_clb callback set as lyext_plugin::printer_ctree. |
1183 | | */ |
1184 | | static LY_ERR |
1185 | | schema_mount_sprinter_ctree(struct lysc_ext_instance *ext, const struct lyspr_tree_ctx *ctx, |
1186 | | const char **flags, const char **UNUSED(add_opts)) |
1187 | 0 | { |
1188 | 0 | LY_ERR rc = LY_SUCCESS; |
1189 | 0 | struct ly_ctx *ext_ctx = NULL; |
1190 | 0 | const struct lys_module *mod; |
1191 | 0 | struct ly_set *refs = NULL; |
1192 | 0 | struct lysc_node *tree1, *tree2; |
1193 | 0 | uint32_t i, j; |
1194 | 0 | ly_bool from_parent_ref, is_first; |
1195 | 0 | struct sprinter_tree_priv *st_priv; |
1196 | |
|
1197 | 0 | if (!ctx) { |
1198 | 0 | *flags = "mp"; |
1199 | 0 | return LY_SUCCESS; |
1200 | 0 | } |
1201 | | |
1202 | 0 | if (lyplg_ext_schema_mount_create_context(ext, &ext_ctx)) { |
1203 | | /* Void mount point */ |
1204 | 0 | return LY_SUCCESS; |
1205 | 0 | } |
1206 | | |
1207 | 0 | rc = lyplg_ext_schema_mount_get_parent_ref(ext, &refs); |
1208 | 0 | LY_CHECK_GOTO(rc, cleanup); |
1209 | | |
1210 | | /* build new list of modules to print. This list will omit internal |
1211 | | * modules, modules with no nodes (e.g., iana-if-types) and modules |
1212 | | * that were loaded as the result of a parent-reference. |
1213 | | */ |
1214 | 0 | i = ly_ctx_internal_modules_count(ext_ctx); |
1215 | 0 | while ((mod = ly_ctx_get_module_iter(ext_ctx, &i))) { |
1216 | 0 | from_parent_ref = 0; |
1217 | |
|
1218 | 0 | for (j = 0; refs && j < refs->count; j++) { |
1219 | 0 | if (!strcmp(mod->ns, refs->snodes[j]->module->ns)) { |
1220 | 0 | from_parent_ref = 1; |
1221 | 0 | break; |
1222 | 0 | } |
1223 | 0 | } |
1224 | 0 | if (from_parent_ref) { |
1225 | | /* Modules loaded as the result of a parent-reference are added later. */ |
1226 | 0 | continue; |
1227 | 0 | } |
1228 | | |
1229 | | /* Add data nodes, rpcs and notifications. */ |
1230 | 0 | if ((ext_ctx->flags & LY_CTX_SET_PRIV_PARSED) && mod->compiled) { |
1231 | | /* For compiled module. */ |
1232 | 0 | rc = lyplg_ext_sprinter_ctree_add_nodes(ctx, mod->compiled->data, |
1233 | 0 | schema_mount_sprinter_tree_cnode_override_mounted); |
1234 | 0 | LY_CHECK_GOTO(rc, cleanup); |
1235 | 0 | if (mod->compiled->rpcs) { |
1236 | 0 | rc = lyplg_ext_sprinter_ctree_add_nodes(ctx, &mod->compiled->rpcs->node, |
1237 | 0 | schema_mount_sprinter_tree_cnode_override_mounted); |
1238 | 0 | } |
1239 | 0 | LY_CHECK_GOTO(rc, cleanup); |
1240 | 0 | if (mod->compiled->notifs) { |
1241 | 0 | rc = lyplg_ext_sprinter_ctree_add_nodes(ctx, &mod->compiled->notifs->node, |
1242 | 0 | schema_mount_sprinter_tree_cnode_override_mounted); |
1243 | 0 | } |
1244 | 0 | LY_CHECK_GOTO(rc, cleanup); |
1245 | 0 | } else { |
1246 | | /* For parsed module. */ |
1247 | 0 | rc = lyplg_ext_sprinter_ptree_add_nodes(ctx, mod->parsed->data, |
1248 | 0 | schema_mount_sprinter_tree_pnode_override_mounted); |
1249 | 0 | LY_CHECK_GOTO(rc, cleanup); |
1250 | 0 | if (mod->parsed->rpcs) { |
1251 | 0 | rc = lyplg_ext_sprinter_ptree_add_nodes(ctx, &mod->parsed->rpcs->node, |
1252 | 0 | schema_mount_sprinter_tree_pnode_override_mounted); |
1253 | 0 | } |
1254 | 0 | LY_CHECK_GOTO(rc, cleanup); |
1255 | 0 | if (mod->parsed->notifs) { |
1256 | 0 | rc = lyplg_ext_sprinter_ptree_add_nodes(ctx, &mod->parsed->notifs->node, |
1257 | 0 | schema_mount_sprinter_tree_pnode_override_mounted); |
1258 | 0 | } |
1259 | 0 | LY_CHECK_GOTO(rc, cleanup); |
1260 | 0 | } |
1261 | 0 | } |
1262 | | |
1263 | | /* Add modules loaded as the result of a parent-reference. */ |
1264 | 0 | for (i = 0; refs && (i < refs->count); i++) { |
1265 | 0 | tree1 = refs->snodes[i]->module->compiled->data; |
1266 | | |
1267 | | /* Add data nodes from the module only once. */ |
1268 | 0 | is_first = 1; |
1269 | 0 | for (j = 0; j < i; j++) { |
1270 | 0 | tree2 = refs->snodes[j]->module->compiled->data; |
1271 | 0 | if (tree1 == tree2) { |
1272 | 0 | is_first = 0; |
1273 | 0 | break; |
1274 | 0 | } |
1275 | 0 | } |
1276 | 0 | if (is_first) { |
1277 | | /* Add all data nodes but unavailable nodes are skipped in the callback. */ |
1278 | 0 | rc = lyplg_ext_sprinter_ctree_add_nodes(ctx, tree1, schema_mount_sprinter_tree_node_override_parent_refs); |
1279 | 0 | LY_CHECK_GOTO(rc, cleanup); |
1280 | 0 | } |
1281 | 0 | } |
1282 | | |
1283 | | /* add private plugin data */ |
1284 | 0 | st_priv = calloc(1, sizeof(*st_priv)); |
1285 | 0 | LY_CHECK_ERR_GOTO(!st_priv, rc = LY_EMEM, cleanup); |
1286 | 0 | st_priv->ext_ctx = ext_ctx; |
1287 | 0 | st_priv->refs = refs; |
1288 | 0 | rc = lyplg_ext_sprinter_tree_set_priv(ctx, st_priv, schema_mount_spriter_tree_free); |
1289 | |
|
1290 | 0 | cleanup: |
1291 | 0 | if (rc) { |
1292 | 0 | ly_set_free(refs, NULL); |
1293 | 0 | ly_ctx_destroy(ext_ctx); |
1294 | 0 | } |
1295 | |
|
1296 | 0 | return rc; |
1297 | 0 | } |
1298 | | |
1299 | | /** |
1300 | | * @brief Plugin descriptions for the Yang Schema Mount extension. |
1301 | | * |
1302 | | * Note that external plugins are supposed to use: |
1303 | | * |
1304 | | * LYPLG_EXTENSIONS = { |
1305 | | */ |
1306 | | const struct lyplg_ext_record plugins_schema_mount[] = { |
1307 | | { |
1308 | | .module = "ietf-yang-schema-mount", |
1309 | | .revision = "2019-01-14", |
1310 | | .name = "mount-point", |
1311 | | |
1312 | | .plugin.id = "ly2 schema mount v1", |
1313 | | .plugin.parse = schema_mount_parse, |
1314 | | .plugin.compile = schema_mount_compile, |
1315 | | .plugin.printer_info = NULL, |
1316 | | .plugin.printer_ctree = schema_mount_sprinter_ctree, |
1317 | | .plugin.printer_ptree = schema_mount_sprinter_ptree, |
1318 | | .plugin.node = NULL, |
1319 | | .plugin.snode = schema_mount_snode, |
1320 | | .plugin.validate = schema_mount_validate, |
1321 | | .plugin.pfree = NULL, |
1322 | | .plugin.cfree = schema_mount_cfree |
1323 | | }, |
1324 | | {0} /* terminating zeroed item */ |
1325 | | }; |