blob: 87c15697a1ca5a4abf4d9f254b306ce0f5e1de64 [file] [log] [blame]
Radek Krejcie7b95092019-05-15 11:03:07 +02001/**
2 * @file tree_schema.c
3 * @author Radek Krejci <rkrejci@cesnet.cz>
4 * @brief Schema tree implementation
5 *
6 * Copyright (c) 2015 - 2018 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#include "common.h"
16
Radek Krejci084289f2019-07-09 17:35:30 +020017#include <assert.h>
Radek Krejcie7b95092019-05-15 11:03:07 +020018#include <ctype.h>
19#include <errno.h>
20#include <fcntl.h>
21#include <stdarg.h>
22#include <stdint.h>
23#include <string.h>
24#include <unistd.h>
25
26#include "log.h"
27#include "tree.h"
28#include "tree_data.h"
29#include "tree_data_internal.h"
30#include "tree_schema.h"
31
32static int
Radek Krejci357398c2019-05-27 14:15:01 +020033cmp_str(const char *refstr, const char *str, size_t str_len)
Radek Krejcie7b95092019-05-15 11:03:07 +020034{
35
Radek Krejci357398c2019-05-27 14:15:01 +020036 if (str_len) {
37 int r = strncmp(refstr, str, str_len);
38 if (!r && !refstr[str_len]) {
Radek Krejcie7b95092019-05-15 11:03:07 +020039 return 0;
40 } else {
41 return 1;
42 }
43 } else {
44 return strcmp(refstr, str);
45 }
46}
47
48API const struct lyd_node *
49lyd_search(const struct lyd_node *first, const struct lys_module *module,
50 const char *name, size_t name_len, uint16_t nodetype, const char *value, size_t value_len)
51{
52 const struct lyd_node *node = NULL;
53 const struct lysc_node *snode;
54
55 LY_CHECK_ARG_RET(NULL, module, name, NULL);
56 if (!nodetype) {
57 nodetype = 0xffff;
58 }
59
60 LY_LIST_FOR(first, node) {
61 snode = node->schema;
62 if (!(snode->nodetype & nodetype)) {
63 continue;
64 }
65 if (snode->module != module) {
66 continue;
67 }
68
69 if (cmp_str(snode->name, name, name_len)) {
70 continue;
71 }
72
73 if (value) {
74 if (snode->nodetype == LYS_LIST) {
75 /* TODO handle value as keys of the list instance */
76 } else if (snode->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
77 if (cmp_str(((struct lyd_node_term*)node)->value.canonized, value, value_len)) {
78 continue;
79 }
80 } else {
81 continue;
82 }
83 }
84
85 /* all criteria passed */
86 return node;
87 }
88 return NULL;
89}
90
Radek Krejci084289f2019-07-09 17:35:30 +020091LY_ERR
Radek Krejci3c9758d2019-07-11 16:49:10 +020092lyd_value_parse(struct lyd_node_term *node, const char *value, size_t value_len, int dynamic, int second,
Radek Krejci084289f2019-07-09 17:35:30 +020093 ly_clb_resolve_prefix get_prefix, void *parser, LYD_FORMAT format, struct lyd_node **trees)
94{
95 LY_ERR ret = LY_SUCCESS, rc;
96 struct ly_err_item *err = NULL;
97 struct ly_ctx *ctx;
98 struct lysc_type *type;
Radek Krejci3c9758d2019-07-11 16:49:10 +020099 int options = LY_TYPE_OPTS_STORE | (second ? LY_TYPE_OPTS_SECOND_CALL : 0) |
100 (dynamic ? LY_TYPE_OPTS_DYNAMIC : 0) | (trees ? 0 : LY_TYPE_OPTS_INCOMPLETE_DATA);
Radek Krejci084289f2019-07-09 17:35:30 +0200101 assert(node);
102
103 ctx = node->schema->module->ctx;
Radek Krejci084289f2019-07-09 17:35:30 +0200104
Radek Krejci73dead22019-07-11 16:46:16 +0200105 type = ((struct lysc_node_leaf*)node->schema)->type;
106 node->value.plugin = type->plugin;
107 rc = type->plugin->store(ctx, type, value, value_len, options, get_prefix, parser, format,
108 trees ? (void*)node : (void*)node->schema, trees,
109 &node->value, NULL, &err);
110 if (rc == LY_EINCOMPLETE) {
111 ret = rc;
112 /* continue with storing, just remember what to return if storing is ok */
113 } else if (rc) {
114 ret = rc;
115 if (err) {
116 ly_err_print(err);
117 LOGVAL(ctx, LY_VLOG_STR, err->path, err->vecode, err->msg);
118 ly_err_free(err);
Radek Krejci084289f2019-07-09 17:35:30 +0200119 }
Radek Krejci73dead22019-07-11 16:46:16 +0200120 goto error;
Radek Krejci084289f2019-07-09 17:35:30 +0200121 }
122
123error:
124 return ret;
125}
126
127API LY_ERR
128lys_value_validate(struct ly_ctx *ctx, const struct lysc_node *node, const char *value, size_t value_len,
129 ly_clb_resolve_prefix get_prefix, void *get_prefix_data, LYD_FORMAT format)
130{
131 LY_ERR rc = LY_SUCCESS;
132 struct ly_err_item *err = NULL;
133 struct lysc_type *type;
Radek Krejci084289f2019-07-09 17:35:30 +0200134
135 LY_CHECK_ARG_RET(ctx, node, value, LY_EINVAL);
136
137 if (!(node->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
138 LOGARG(ctx, node);
139 return LY_EINVAL;
140 }
141
142 type = ((struct lysc_node_leaf*)node)->type;
Radek Krejci73dead22019-07-11 16:46:16 +0200143 /* just validate, no storing of enything */
144 rc = type->plugin->store(ctx ? ctx : node->module->ctx, type, value, value_len, LY_TYPE_OPTS_INCOMPLETE_DATA,
145 get_prefix, get_prefix_data, format, node, NULL, NULL, NULL, &err);
146 if (rc == LY_EINCOMPLETE) {
147 /* actually success since we do not provide the context tree and call validation with
148 * LY_TYPE_OPTS_INCOMPLETE_DATA */
149 rc = LY_SUCCESS;
150 } else if (rc && err) {
151 if (ctx) {
152 /* log only in case the ctx was provided as input parameter */
153 ly_err_print(err);
154 LOGVAL(ctx, LY_VLOG_STR, err->path, err->vecode, err->msg);
Radek Krejci084289f2019-07-09 17:35:30 +0200155 }
Radek Krejci73dead22019-07-11 16:46:16 +0200156 ly_err_free(err);
Radek Krejci084289f2019-07-09 17:35:30 +0200157 }
158
159 return rc;
160}
161
162API LY_ERR
163lyd_value_validate(struct ly_ctx *ctx, const struct lyd_node_term *node, const char *value, size_t value_len,
164 ly_clb_resolve_prefix get_prefix, void *get_prefix_data, LYD_FORMAT format, struct lyd_node **trees)
165{
166 LY_ERR rc;
167 struct ly_err_item *err = NULL;
168 struct lysc_type *type;
Radek Krejci73dead22019-07-11 16:46:16 +0200169 int options = (trees ? 0 : LY_TYPE_OPTS_INCOMPLETE_DATA);
Radek Krejci084289f2019-07-09 17:35:30 +0200170
171 LY_CHECK_ARG_RET(ctx, node, value, LY_EINVAL);
172
173 type = ((struct lysc_node_leaf*)node->schema)->type;
Radek Krejci73dead22019-07-11 16:46:16 +0200174 rc = type->plugin->store(ctx ? ctx : node->schema->module->ctx, type, value, value_len, options,
175 get_prefix, get_prefix_data, format, trees ? (void*)node : (void*)node->schema, trees,
176 NULL, NULL, &err);
177 if (rc == LY_EINCOMPLETE) {
178 return rc;
179 } else if (rc) {
180 if (err) {
181 if (ctx) {
182 ly_err_print(err);
183 LOGVAL(ctx, LY_VLOG_STR, err->path, err->vecode, err->msg);
Radek Krejci084289f2019-07-09 17:35:30 +0200184 }
Radek Krejci73dead22019-07-11 16:46:16 +0200185 ly_err_free(err);
Radek Krejci084289f2019-07-09 17:35:30 +0200186 }
Radek Krejci73dead22019-07-11 16:46:16 +0200187 return rc;
Radek Krejci084289f2019-07-09 17:35:30 +0200188 }
189
190 return LY_SUCCESS;
191}
192
193API LY_ERR
194lyd_value_compare(const struct lyd_node_term *node, const char *value, size_t value_len,
195 ly_clb_resolve_prefix get_prefix, void *get_prefix_data, LYD_FORMAT format, struct lyd_node **trees)
196{
197 LY_ERR ret = LY_SUCCESS, rc;
198 struct ly_err_item *err = NULL;
199 struct ly_ctx *ctx;
200 struct lysc_type *type;
Radek Krejci084289f2019-07-09 17:35:30 +0200201 struct lyd_value data = {0};
Radek Krejci73dead22019-07-11 16:46:16 +0200202 int options = LY_TYPE_OPTS_STORE | (trees ? 0 : LY_TYPE_OPTS_INCOMPLETE_DATA);
Radek Krejci084289f2019-07-09 17:35:30 +0200203
204 LY_CHECK_ARG_RET(node ? node->schema->module->ctx : NULL, node, value, LY_EINVAL);
205
206 ctx = node->schema->module->ctx;
207 type = ((struct lysc_node_leaf*)node->schema)->type;
Radek Krejci73dead22019-07-11 16:46:16 +0200208 rc = type->plugin->store(ctx, type, value, value_len, options, get_prefix, get_prefix_data, format, (struct lyd_node*)node,
209 trees, &data, NULL, &err);
210 if (rc == LY_EINCOMPLETE) {
211 ret = rc;
212 /* continue with comparing, just remember what to return if storing is ok */
213 } else if (rc) {
214 /* value to compare is invalid */
215 ret = LY_EINVAL;
216 if (err) {
217 ly_err_free(err);
Radek Krejci084289f2019-07-09 17:35:30 +0200218 }
Radek Krejci73dead22019-07-11 16:46:16 +0200219 goto cleanup;
Radek Krejci084289f2019-07-09 17:35:30 +0200220 }
221
222 /* compare data */
Radek Krejci5af04842019-07-12 11:32:07 +0200223 if (type->plugin->compare(&node->value, &data)) {
224 /* do not assign it directly from the compare callback to keep possible LY_EINCOMPLETE from validation */
225 ret = LY_EVALID;
226 }
Radek Krejci084289f2019-07-09 17:35:30 +0200227
228cleanup:
Radek Krejci73dead22019-07-11 16:46:16 +0200229 type->plugin->free(ctx, type, &data);
Radek Krejci084289f2019-07-09 17:35:30 +0200230
231 return ret;
232}
233
Radek Krejcie7b95092019-05-15 11:03:07 +0200234static struct lyd_node *
235lyd_parse_mem_(struct ly_ctx *ctx, const char *data, LYD_FORMAT format, int options, va_list ap)
236{
Radek Krejcie7b95092019-05-15 11:03:07 +0200237 struct lyd_node *result = NULL;
238 const struct lyd_node *rpc_act = NULL, *data_tree = NULL, *iter;
Radek Krejcie92210c2019-05-17 15:53:35 +0200239#if 0
Radek Krejcie7b95092019-05-15 11:03:07 +0200240 const char *yang_data_name = NULL;
Radek Krejcie92210c2019-05-17 15:53:35 +0200241#endif
Radek Krejcie7b95092019-05-15 11:03:07 +0200242
243 if (lyd_parse_check_options(ctx, options, __func__)) {
244 return NULL;
245 }
246
247 if (options & LYD_OPT_RPCREPLY) {
248 rpc_act = va_arg(ap, const struct lyd_node *);
249 if (!rpc_act || rpc_act->parent || !(rpc_act->schema->nodetype & (LYS_ACTION | LYS_LIST | LYS_CONTAINER))) {
250 LOGERR(ctx, LY_EINVAL, "Data parser invalid variable parameter (const struct lyd_node *rpc_act).");
251 return NULL;
252 }
253 }
254 if (options & (LYD_OPT_RPC | LYD_OPT_NOTIF | LYD_OPT_RPCREPLY)) {
255 data_tree = va_arg(ap, const struct lyd_node *);
256 if (data_tree) {
257 if (options & LYD_OPT_NOEXTDEPS) {
258 LOGERR(ctx, LY_EINVAL, "%s: invalid parameter (variable arg const struct lyd_node *data_tree and LYD_OPT_NOEXTDEPS set).",
259 __func__);
260 return NULL;
261 }
262
263 LY_LIST_FOR(data_tree, iter) {
264 if (iter->parent) {
265 /* a sibling is not top-level */
266 LOGERR(ctx, LY_EINVAL, "%s: invalid variable parameter (const struct lyd_node *data_tree).", __func__);
267 return NULL;
268 }
269 }
270
271 /* move it to the beginning */
272 for (; data_tree->prev->next; data_tree = data_tree->prev);
273
274 /* LYD_OPT_NOSIBLINGS cannot be set in this case */
275 if (options & LYD_OPT_NOSIBLINGS) {
276 LOGERR(ctx, LY_EINVAL, "%s: invalid parameter (variable arg const struct lyd_node *data_tree with LYD_OPT_NOSIBLINGS).", __func__);
277 return NULL;
278 }
279 }
280 }
Radek Krejcie92210c2019-05-17 15:53:35 +0200281#if 0
Radek Krejcie7b95092019-05-15 11:03:07 +0200282 if (options & LYD_OPT_DATA_TEMPLATE) {
283 yang_data_name = va_arg(ap, const char *);
284 }
Radek Krejcie92210c2019-05-17 15:53:35 +0200285#endif
Radek Krejcie7b95092019-05-15 11:03:07 +0200286
287 if (!format) {
288 /* TODO try to detect format from the content */
289 }
290
291 switch (format) {
292 case LYD_XML:
Radek Krejcie92210c2019-05-17 15:53:35 +0200293 lyd_parse_xml(ctx, data, options, &result);
Radek Krejcie7b95092019-05-15 11:03:07 +0200294 break;
295#if 0
296 case LYD_JSON:
Radek Krejcie92210c2019-05-17 15:53:35 +0200297 lyd_parse_json(ctx, data, options, rpc_act, data_tree, yang_data_name);
Radek Krejcie7b95092019-05-15 11:03:07 +0200298 break;
299 case LYD_LYB:
Radek Krejcie92210c2019-05-17 15:53:35 +0200300 lyd_parse_lyb(ctx, data, options, data_tree, yang_data_name, NULL);
Radek Krejcie7b95092019-05-15 11:03:07 +0200301 break;
302#endif
303 case LYD_UNKNOWN:
304 LOGINT(ctx);
305 break;
306 }
307
Radek Krejcie7b95092019-05-15 11:03:07 +0200308 return result;
309}
310
311API struct lyd_node *
312lyd_parse_mem(struct ly_ctx *ctx, const char *data, LYD_FORMAT format, int options, ...)
313{
314 va_list ap;
315 struct lyd_node *result;
316
317 va_start(ap, options);
318 result = lyd_parse_mem_(ctx, data, format, options, ap);
319 va_end(ap);
320
321 return result;
322}
323
324static struct lyd_node *
325lyd_parse_fd_(struct ly_ctx *ctx, int fd, LYD_FORMAT format, int options, va_list ap)
326{
327 struct lyd_node *result;
328 size_t length;
329 char *addr;
330
331 LY_CHECK_ARG_RET(ctx, ctx, NULL);
332 if (fd < 0) {
333 LOGARG(ctx, fd);
334 return NULL;
335 }
336
337 LY_CHECK_RET(ly_mmap(ctx, fd, &length, (void **)&addr), NULL);
338 result = lyd_parse_mem_(ctx, addr ? addr : "", format, options, ap);
Radek Krejcidf3da792019-05-17 10:32:24 +0200339 if (addr) {
340 ly_munmap(addr, length);
341 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200342
343 return result;
344}
345
346API struct lyd_node *
347lyd_parse_fd(struct ly_ctx *ctx, int fd, LYD_FORMAT format, int options, ...)
348{
349 struct lyd_node *ret;
350 va_list ap;
351
352 va_start(ap, options);
353 ret = lyd_parse_fd_(ctx, fd, format, options, ap);
354 va_end(ap);
355
356 return ret;
357}
358
359API struct lyd_node *
360lyd_parse_path(struct ly_ctx *ctx, const char *path, LYD_FORMAT format, int options, ...)
361{
362 int fd;
363 struct lyd_node *result;
364 size_t len;
365 va_list ap;
366
367 LY_CHECK_ARG_RET(ctx, ctx, path, NULL);
368
369 fd = open(path, O_RDONLY);
370 LY_CHECK_ERR_RET(fd == -1, LOGERR(ctx, LY_ESYS, "Opening file \"%s\" failed (%s).", path, strerror(errno)), NULL);
371
372 if (!format) {
373 /* unknown format - try to detect it from filename's suffix */
374 len = strlen(path);
375
376 /* ignore trailing whitespaces */
377 for (; len > 0 && isspace(path[len - 1]); len--);
378
379 if (len >= 5 && !strncmp(&path[len - 4], ".xml", 4)) {
380 format = LYD_XML;
381#if 0
382 } else if (len >= 6 && !strncmp(&path[len - 5], ".json", 5)) {
383 format = LYD_JSON;
384 } else if (len >= 5 && !strncmp(&path[len - 4], ".lyb", 4)) {
385 format = LYD_LYB;
386#endif
387 } /* else still unknown, try later to detect it from the content */
388 }
389
390 va_start(ap, options);
391 result = lyd_parse_fd_(ctx, fd, format, options, ap);
392
393 va_end(ap);
394 close(fd);
395
396 return result;
397}
Radek Krejci084289f2019-07-09 17:35:30 +0200398
399API const struct lyd_node_term *
400lyd_target(struct lyd_value_path *path, struct lyd_node **trees)
401{
402 unsigned int u, v, x;
403 const struct lyd_node *node = NULL, *parent = NULL, *start_search;
404 uint64_t pos = 1;
405
406 LY_CHECK_ARG_RET(NULL, path, trees, NULL);
407
408 LY_ARRAY_FOR(path, u) {
409 if (parent) {
410 start_search = lyd_node_children(parent);
411search_inner:
412 node = lyd_search(start_search, path[u].node->module, path[u].node->name, strlen(path[u].node->name), path[u].node->nodetype, NULL, 0);
413 } else {
414 LY_ARRAY_FOR(trees, v) {
415 start_search = trees[v];
416search_toplevel:
417 /* WARNING! to use search_toplevel label correctly, variable v must be preserved and not changed! */
418 node = lyd_search(start_search, path[u].node->module, path[u].node->name, strlen(path[u].node->name), path[u].node->nodetype, NULL, 0);
419 if (node) {
420 break;
421 }
422 }
423 }
424 if (!node) {
425 return NULL;
426 }
427
428 /* check predicate if any */
429 LY_ARRAY_FOR(path[u].predicates, x) {
430 if (path[u].predicates[x].type == 0) {
431 /* position predicate */
432 if (pos != path[u].predicates[x].position) {
433 pos++;
434 goto search_repeat;
435 }
436 /* done, no more predicates are allowed here */
437 break;
438 } else if (path[u].predicates[x].type == 1) {
439 /* key-predicate */
440 struct lysc_type *type = ((struct lysc_node_leaf*)path[u].predicates[x].key)->type;
441 const struct lyd_node *key = lyd_search(lyd_node_children(node), path[u].predicates[x].key->module,
442 path[u].predicates[x].key->name, strlen(path[u].predicates[x].key->name),
443 LYS_LEAF, NULL, 0);
444 if (!key) {
445 /* probably error and we shouldn't be here due to previous checks when creating path */
446 goto search_repeat;
447 }
448 if (type->plugin->compare(&((struct lyd_node_term*)key)->value, path[u].predicates[x].value)) {
449 goto search_repeat;
450 }
451 } else if (path[u].predicates[x].type == 2) {
452 /* leaf-list-predicate */
453 struct lysc_type *type = ((struct lysc_node_leaf*)path[u].node)->type;
454 if (type->plugin->compare(&((struct lyd_node_term*)node)->value, path[u].predicates[x].value)) {
455 goto search_repeat;
456 }
457 } else {
458 LOGINT(NULL);
459 }
460 }
461
462 parent = node;
463 }
464
465 return (const struct lyd_node_term*)node;
466
467search_repeat:
468 start_search = node->next;
469 if (parent) {
470 goto search_inner;
471 } else {
472 goto search_toplevel;
473 }
474}
475
476