blob: 02059b99d743a3a133d254c28b32d15ab60669e7 [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
92lyd_value_parse(struct lyd_node_term *node, const char *value, size_t value_len, int dynamic,
93 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;
99 void *priv = NULL;
100 int options = LY_TYPE_OPTS_VALIDATE | LY_TYPE_OPTS_CANONIZE | LY_TYPE_OPTS_STORE |
101 (dynamic ? LY_TYPE_OPTS_DYNAMIC : 0) | (trees ? 0 : LY_TYPE_OPTS_INCOMPLETE_DATA);
102 assert(node);
103
104 ctx = node->schema->module->ctx;
105 type = ((struct lysc_node_leaf*)node->schema)->type;
106 if (type->plugin->validate) {
107 rc = type->plugin->validate(ctx, type, value, value_len, options, get_prefix, parser, format,
108 trees ? (void*)node : (void*)node->schema, trees,
109 &node->value.canonized, &err, &priv);
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);
119 }
120 goto error;
121 }
122 } else if (dynamic) {
123 node->value.canonized = lydict_insert_zc(ctx, (char*)value);
124 } else {
125 node->value.canonized = lydict_insert(ctx, value, value_len);
126 }
127 node->value.plugin = type->plugin;
128
129 if (type->plugin->store) {
130 rc = type->plugin->store(ctx, type, options, &node->value, &err, &priv);
131 if (rc) {
132 ret = rc;
133 if (err) {
134 ly_err_print(err);
135 LOGVAL(ctx, LY_VLOG_STR, err->path, err->vecode, err->msg);
136 ly_err_free(err);
137 }
138 goto error;
139 }
140 }
141
142error:
143 return ret;
144}
145
146API LY_ERR
147lys_value_validate(struct ly_ctx *ctx, const struct lysc_node *node, const char *value, size_t value_len,
148 ly_clb_resolve_prefix get_prefix, void *get_prefix_data, LYD_FORMAT format)
149{
150 LY_ERR rc = LY_SUCCESS;
151 struct ly_err_item *err = NULL;
152 struct lysc_type *type;
153 void *priv = NULL;
154 int options = LY_TYPE_OPTS_VALIDATE | LY_TYPE_OPTS_INCOMPLETE_DATA;
155
156 LY_CHECK_ARG_RET(ctx, node, value, LY_EINVAL);
157
158 if (!(node->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
159 LOGARG(ctx, node);
160 return LY_EINVAL;
161 }
162
163 type = ((struct lysc_node_leaf*)node)->type;
164
165 if (type->plugin->validate) {
166 rc = type->plugin->validate(ctx ? ctx : node->module->ctx, type, value, value_len, options,
167 get_prefix, get_prefix_data, format, node, NULL, NULL, &err, &priv);
168 if (rc == LY_EINCOMPLETE) {
169 /* actually success since we do not provide the context tree and call validation with
170 * LY_TYPE_OPTS_INCOMPLETE_DATA */
171 rc = LY_SUCCESS;
172 } else if (rc && err) {
173 if (ctx) {
174 /* log only in case the ctx was provided as input parameter */
175 ly_err_print(err);
176 LOGVAL(ctx, LY_VLOG_STR, err->path, err->vecode, err->msg);
177 }
178 ly_err_free(err);
179 }
180 }
181
182 return rc;
183}
184
185API LY_ERR
186lyd_value_validate(struct ly_ctx *ctx, const struct lyd_node_term *node, const char *value, size_t value_len,
187 ly_clb_resolve_prefix get_prefix, void *get_prefix_data, LYD_FORMAT format, struct lyd_node **trees)
188{
189 LY_ERR rc;
190 struct ly_err_item *err = NULL;
191 struct lysc_type *type;
192 void *priv = NULL;
193 int options = LY_TYPE_OPTS_VALIDATE | LY_TYPE_OPTS_STORE | (trees ? 0 : LY_TYPE_OPTS_INCOMPLETE_DATA);
194
195 LY_CHECK_ARG_RET(ctx, node, value, LY_EINVAL);
196
197 type = ((struct lysc_node_leaf*)node->schema)->type;
198
199 if (type->plugin->validate) {
200 rc = type->plugin->validate(ctx ? ctx : node->schema->module->ctx, type, value, value_len, options,
201 get_prefix, get_prefix_data, format, trees ? (void*)node : (void*)node->schema, trees,
202 NULL, &err, &priv);
203 if (rc == LY_EINCOMPLETE) {
204 return rc;
205 } else if (rc) {
206 if (err) {
207 if (ctx) {
208 ly_err_print(err);
209 LOGVAL(ctx, LY_VLOG_STR, err->path, err->vecode, err->msg);
210 }
211 ly_err_free(err);
212 }
213 return rc;
214 }
215 }
216
217 return LY_SUCCESS;
218}
219
220API LY_ERR
221lyd_value_compare(const struct lyd_node_term *node, const char *value, size_t value_len,
222 ly_clb_resolve_prefix get_prefix, void *get_prefix_data, LYD_FORMAT format, struct lyd_node **trees)
223{
224 LY_ERR ret = LY_SUCCESS, rc;
225 struct ly_err_item *err = NULL;
226 struct ly_ctx *ctx;
227 struct lysc_type *type;
228 void *priv = NULL;
229 struct lyd_value data = {0};
230 int options = LY_TYPE_OPTS_VALIDATE | LY_TYPE_OPTS_CANONIZE | LY_TYPE_OPTS_STORE | (trees ? 0 : LY_TYPE_OPTS_INCOMPLETE_DATA);
231
232 LY_CHECK_ARG_RET(node ? node->schema->module->ctx : NULL, node, value, LY_EINVAL);
233
234 ctx = node->schema->module->ctx;
235 type = ((struct lysc_node_leaf*)node->schema)->type;
236 if (type->plugin->validate) {
237 rc = type->plugin->validate(ctx, type, value, value_len, options,
238 get_prefix, get_prefix_data, format, (struct lyd_node*)node, trees,
239 &data.canonized, &err, &priv);
240 if (rc == LY_EINCOMPLETE) {
241 ret = rc;
242 /* continue with comparing, just remember what to return if storing is ok */
243 } else if (rc) {
244 /* value to compare is invalid */
245 ret = LY_EINVAL;
246 if (err) {
247 ly_err_free(err);
248 }
249 goto cleanup;
250 }
251 } else {
252 data.canonized = lydict_insert(ctx, value, value_len);
253 }
254
255 /* store the value to compare into a dummy storage to do a comparison */
256 if (type->plugin->store) {
257 if (type->plugin->store(ctx, type, options, &data, &err, &priv)) {
258 ret = LY_EINVAL;
259 if (err) {
260 ly_err_free(err);
261 }
262 goto cleanup;
263 }
264 }
265
266 /* compare data */
267 if (type->plugin->compare) {
268 ret = type->plugin->compare(&node->value, &data);
269 } else if (data.canonized != node->value.canonized) {
270 ret = LY_EVALID;
271 }
272
273cleanup:
274 if (type->plugin->free) {
275 type->plugin->free(ctx, type, &data);
276 }
277 lydict_remove(ctx, data.canonized);
278
279 return ret;
280}
281
Radek Krejcie7b95092019-05-15 11:03:07 +0200282static struct lyd_node *
283lyd_parse_mem_(struct ly_ctx *ctx, const char *data, LYD_FORMAT format, int options, va_list ap)
284{
Radek Krejcie7b95092019-05-15 11:03:07 +0200285 struct lyd_node *result = NULL;
286 const struct lyd_node *rpc_act = NULL, *data_tree = NULL, *iter;
Radek Krejcie92210c2019-05-17 15:53:35 +0200287#if 0
Radek Krejcie7b95092019-05-15 11:03:07 +0200288 const char *yang_data_name = NULL;
Radek Krejcie92210c2019-05-17 15:53:35 +0200289#endif
Radek Krejcie7b95092019-05-15 11:03:07 +0200290
291 if (lyd_parse_check_options(ctx, options, __func__)) {
292 return NULL;
293 }
294
295 if (options & LYD_OPT_RPCREPLY) {
296 rpc_act = va_arg(ap, const struct lyd_node *);
297 if (!rpc_act || rpc_act->parent || !(rpc_act->schema->nodetype & (LYS_ACTION | LYS_LIST | LYS_CONTAINER))) {
298 LOGERR(ctx, LY_EINVAL, "Data parser invalid variable parameter (const struct lyd_node *rpc_act).");
299 return NULL;
300 }
301 }
302 if (options & (LYD_OPT_RPC | LYD_OPT_NOTIF | LYD_OPT_RPCREPLY)) {
303 data_tree = va_arg(ap, const struct lyd_node *);
304 if (data_tree) {
305 if (options & LYD_OPT_NOEXTDEPS) {
306 LOGERR(ctx, LY_EINVAL, "%s: invalid parameter (variable arg const struct lyd_node *data_tree and LYD_OPT_NOEXTDEPS set).",
307 __func__);
308 return NULL;
309 }
310
311 LY_LIST_FOR(data_tree, iter) {
312 if (iter->parent) {
313 /* a sibling is not top-level */
314 LOGERR(ctx, LY_EINVAL, "%s: invalid variable parameter (const struct lyd_node *data_tree).", __func__);
315 return NULL;
316 }
317 }
318
319 /* move it to the beginning */
320 for (; data_tree->prev->next; data_tree = data_tree->prev);
321
322 /* LYD_OPT_NOSIBLINGS cannot be set in this case */
323 if (options & LYD_OPT_NOSIBLINGS) {
324 LOGERR(ctx, LY_EINVAL, "%s: invalid parameter (variable arg const struct lyd_node *data_tree with LYD_OPT_NOSIBLINGS).", __func__);
325 return NULL;
326 }
327 }
328 }
Radek Krejcie92210c2019-05-17 15:53:35 +0200329#if 0
Radek Krejcie7b95092019-05-15 11:03:07 +0200330 if (options & LYD_OPT_DATA_TEMPLATE) {
331 yang_data_name = va_arg(ap, const char *);
332 }
Radek Krejcie92210c2019-05-17 15:53:35 +0200333#endif
Radek Krejcie7b95092019-05-15 11:03:07 +0200334
335 if (!format) {
336 /* TODO try to detect format from the content */
337 }
338
339 switch (format) {
340 case LYD_XML:
Radek Krejcie92210c2019-05-17 15:53:35 +0200341 lyd_parse_xml(ctx, data, options, &result);
Radek Krejcie7b95092019-05-15 11:03:07 +0200342 break;
343#if 0
344 case LYD_JSON:
Radek Krejcie92210c2019-05-17 15:53:35 +0200345 lyd_parse_json(ctx, data, options, rpc_act, data_tree, yang_data_name);
Radek Krejcie7b95092019-05-15 11:03:07 +0200346 break;
347 case LYD_LYB:
Radek Krejcie92210c2019-05-17 15:53:35 +0200348 lyd_parse_lyb(ctx, data, options, data_tree, yang_data_name, NULL);
Radek Krejcie7b95092019-05-15 11:03:07 +0200349 break;
350#endif
351 case LYD_UNKNOWN:
352 LOGINT(ctx);
353 break;
354 }
355
Radek Krejcie7b95092019-05-15 11:03:07 +0200356 return result;
357}
358
359API struct lyd_node *
360lyd_parse_mem(struct ly_ctx *ctx, const char *data, LYD_FORMAT format, int options, ...)
361{
362 va_list ap;
363 struct lyd_node *result;
364
365 va_start(ap, options);
366 result = lyd_parse_mem_(ctx, data, format, options, ap);
367 va_end(ap);
368
369 return result;
370}
371
372static struct lyd_node *
373lyd_parse_fd_(struct ly_ctx *ctx, int fd, LYD_FORMAT format, int options, va_list ap)
374{
375 struct lyd_node *result;
376 size_t length;
377 char *addr;
378
379 LY_CHECK_ARG_RET(ctx, ctx, NULL);
380 if (fd < 0) {
381 LOGARG(ctx, fd);
382 return NULL;
383 }
384
385 LY_CHECK_RET(ly_mmap(ctx, fd, &length, (void **)&addr), NULL);
386 result = lyd_parse_mem_(ctx, addr ? addr : "", format, options, ap);
Radek Krejcidf3da792019-05-17 10:32:24 +0200387 if (addr) {
388 ly_munmap(addr, length);
389 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200390
391 return result;
392}
393
394API struct lyd_node *
395lyd_parse_fd(struct ly_ctx *ctx, int fd, LYD_FORMAT format, int options, ...)
396{
397 struct lyd_node *ret;
398 va_list ap;
399
400 va_start(ap, options);
401 ret = lyd_parse_fd_(ctx, fd, format, options, ap);
402 va_end(ap);
403
404 return ret;
405}
406
407API struct lyd_node *
408lyd_parse_path(struct ly_ctx *ctx, const char *path, LYD_FORMAT format, int options, ...)
409{
410 int fd;
411 struct lyd_node *result;
412 size_t len;
413 va_list ap;
414
415 LY_CHECK_ARG_RET(ctx, ctx, path, NULL);
416
417 fd = open(path, O_RDONLY);
418 LY_CHECK_ERR_RET(fd == -1, LOGERR(ctx, LY_ESYS, "Opening file \"%s\" failed (%s).", path, strerror(errno)), NULL);
419
420 if (!format) {
421 /* unknown format - try to detect it from filename's suffix */
422 len = strlen(path);
423
424 /* ignore trailing whitespaces */
425 for (; len > 0 && isspace(path[len - 1]); len--);
426
427 if (len >= 5 && !strncmp(&path[len - 4], ".xml", 4)) {
428 format = LYD_XML;
429#if 0
430 } else if (len >= 6 && !strncmp(&path[len - 5], ".json", 5)) {
431 format = LYD_JSON;
432 } else if (len >= 5 && !strncmp(&path[len - 4], ".lyb", 4)) {
433 format = LYD_LYB;
434#endif
435 } /* else still unknown, try later to detect it from the content */
436 }
437
438 va_start(ap, options);
439 result = lyd_parse_fd_(ctx, fd, format, options, ap);
440
441 va_end(ap);
442 close(fd);
443
444 return result;
445}
Radek Krejci084289f2019-07-09 17:35:30 +0200446
447API const struct lyd_node_term *
448lyd_target(struct lyd_value_path *path, struct lyd_node **trees)
449{
450 unsigned int u, v, x;
451 const struct lyd_node *node = NULL, *parent = NULL, *start_search;
452 uint64_t pos = 1;
453
454 LY_CHECK_ARG_RET(NULL, path, trees, NULL);
455
456 LY_ARRAY_FOR(path, u) {
457 if (parent) {
458 start_search = lyd_node_children(parent);
459search_inner:
460 node = lyd_search(start_search, path[u].node->module, path[u].node->name, strlen(path[u].node->name), path[u].node->nodetype, NULL, 0);
461 } else {
462 LY_ARRAY_FOR(trees, v) {
463 start_search = trees[v];
464search_toplevel:
465 /* WARNING! to use search_toplevel label correctly, variable v must be preserved and not changed! */
466 node = lyd_search(start_search, path[u].node->module, path[u].node->name, strlen(path[u].node->name), path[u].node->nodetype, NULL, 0);
467 if (node) {
468 break;
469 }
470 }
471 }
472 if (!node) {
473 return NULL;
474 }
475
476 /* check predicate if any */
477 LY_ARRAY_FOR(path[u].predicates, x) {
478 if (path[u].predicates[x].type == 0) {
479 /* position predicate */
480 if (pos != path[u].predicates[x].position) {
481 pos++;
482 goto search_repeat;
483 }
484 /* done, no more predicates are allowed here */
485 break;
486 } else if (path[u].predicates[x].type == 1) {
487 /* key-predicate */
488 struct lysc_type *type = ((struct lysc_node_leaf*)path[u].predicates[x].key)->type;
489 const struct lyd_node *key = lyd_search(lyd_node_children(node), path[u].predicates[x].key->module,
490 path[u].predicates[x].key->name, strlen(path[u].predicates[x].key->name),
491 LYS_LEAF, NULL, 0);
492 if (!key) {
493 /* probably error and we shouldn't be here due to previous checks when creating path */
494 goto search_repeat;
495 }
496 if (type->plugin->compare(&((struct lyd_node_term*)key)->value, path[u].predicates[x].value)) {
497 goto search_repeat;
498 }
499 } else if (path[u].predicates[x].type == 2) {
500 /* leaf-list-predicate */
501 struct lysc_type *type = ((struct lysc_node_leaf*)path[u].node)->type;
502 if (type->plugin->compare(&((struct lyd_node_term*)node)->value, path[u].predicates[x].value)) {
503 goto search_repeat;
504 }
505 } else {
506 LOGINT(NULL);
507 }
508 }
509
510 parent = node;
511 }
512
513 return (const struct lyd_node_term*)node;
514
515search_repeat:
516 start_search = node->next;
517 if (parent) {
518 goto search_inner;
519 } else {
520 goto search_toplevel;
521 }
522}
523
524