blob: 958c342d6125e6e581f80e85e18c3dd64403b7e6 [file] [log] [blame]
Radek Krejci3f5e3db2018-10-11 15:57:47 +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 */
Radek Krejcib7db73a2018-10-24 14:18:40 +020014
15#include "common.h"
Radek Krejci86d106e2018-10-18 09:53:19 +020016
Radek Krejcie7b95092019-05-15 11:03:07 +020017#include <assert.h>
Radek Krejcid33273d2018-10-25 14:55:52 +020018#include <dirent.h>
Radek Krejci86d106e2018-10-18 09:53:19 +020019#include <errno.h>
20#include <fcntl.h>
Radek Krejcie7b95092019-05-15 11:03:07 +020021#include <limits.h>
22#include <stdint.h>
Radek Krejci86d106e2018-10-18 09:53:19 +020023#include <stdio.h>
Radek Krejcie7b95092019-05-15 11:03:07 +020024#include <stdlib.h>
25#include <string.h>
Radek Krejci86d106e2018-10-18 09:53:19 +020026#include <sys/stat.h>
Radek Krejci86d106e2018-10-18 09:53:19 +020027#include <unistd.h>
Radek Krejci3f5e3db2018-10-11 15:57:47 +020028
Radek Krejci86d106e2018-10-18 09:53:19 +020029#include "context.h"
Radek Krejcie7b95092019-05-15 11:03:07 +020030#include "dict.h"
31#include "log.h"
32#include "set.h"
Michal Vasko519fd602020-05-26 12:17:39 +020033#include "xpath.h"
Radek Krejcie7b95092019-05-15 11:03:07 +020034#include "tree.h"
35#include "tree_schema.h"
Radek Krejci70853c52018-10-15 14:46:16 +020036#include "tree_schema_internal.h"
Radek Krejci3f5e3db2018-10-11 15:57:47 +020037
Radek Krejcia3045382018-11-22 14:30:31 +010038API const struct lysc_node *
39lys_getnext(const struct lysc_node *last, const struct lysc_node *parent, const struct lysc_module *module, int options)
40{
Radek Krejci6eeb58f2019-02-22 16:29:37 +010041 const struct lysc_node *next = NULL;
Radek Krejcia3045382018-11-22 14:30:31 +010042 struct lysc_node **snode;
Radek Krejci6eeb58f2019-02-22 16:29:37 +010043 int action_flag = 0, notif_flag = 0;
44 const struct lysc_action *actions;
45 const struct lysc_notif *notifs;
Radek Krejci7eb54ba2020-05-18 16:30:04 +020046 LY_ARRAY_SIZE_TYPE u;
Radek Krejcia3045382018-11-22 14:30:31 +010047
48 LY_CHECK_ARG_RET(NULL, parent || module, NULL);
49
Radek Krejcid5a2b9d2019-04-12 10:39:30 +020050next:
Radek Krejcia3045382018-11-22 14:30:31 +010051 if (!last) {
52 /* first call */
53
54 /* get know where to start */
55 if (parent) {
56 /* schema subtree */
Radek Krejci056d0a82018-12-06 16:57:25 +010057 if (parent->nodetype == LYS_CHOICE && (options & LYS_GETNEXT_WITHCASE)) {
Radek Krejcid5a2b9d2019-04-12 10:39:30 +020058 if (((struct lysc_node_choice*)parent)->cases) {
59 next = last = (const struct lysc_node*)&((struct lysc_node_choice*)parent)->cases[0];
Radek Krejci056d0a82018-12-06 16:57:25 +010060 }
Radek Krejci056d0a82018-12-06 16:57:25 +010061 } else {
Radek Krejci6eeb58f2019-02-22 16:29:37 +010062 snode = lysc_node_children_p(parent, (options & LYS_GETNEXT_OUTPUT) ? LYS_CONFIG_R : LYS_CONFIG_W);
Radek Krejci05b774b2019-02-25 13:26:18 +010063 /* do not return anything if the node does not have any children */
Radek Krejcid5a2b9d2019-04-12 10:39:30 +020064 if (snode && *snode) {
65 next = last = *snode;
Radek Krejci056d0a82018-12-06 16:57:25 +010066 }
Radek Krejcia3045382018-11-22 14:30:31 +010067 }
Radek Krejcia3045382018-11-22 14:30:31 +010068 } else {
69 /* top level data */
70 next = last = module->data;
71 }
72 if (!next) {
Radek Krejci6eeb58f2019-02-22 16:29:37 +010073 /* try to get action or notification */
74 goto repeat;
Radek Krejcia3045382018-11-22 14:30:31 +010075 }
Radek Krejci05b774b2019-02-25 13:26:18 +010076 /* test if the next can be returned */
77 goto check;
78
Michal Vasko1bf09392020-03-27 12:38:10 +010079 } else if (last->nodetype & (LYS_RPC | LYS_ACTION)) {
Radek Krejci05b774b2019-02-25 13:26:18 +010080 action_flag = 1;
Radek Krejci6eeb58f2019-02-22 16:29:37 +010081 if (last->parent) {
82 actions = lysc_node_actions(last->parent);
83 } else {
84 actions = module->rpcs;
85 }
86 LY_ARRAY_FOR(actions, u) {
87 if (&actions[u] == (struct lysc_action*)last) {
88 break;
89 }
90 }
91 if (u + 1 < LY_ARRAY_SIZE(actions)) {
92 next = (struct lysc_node*)(&actions[u + 1]);
93 }
94 goto repeat;
95 } else if (last->nodetype == LYS_NOTIF) {
Radek Krejci05b774b2019-02-25 13:26:18 +010096 action_flag = notif_flag = 1;
Radek Krejci6eeb58f2019-02-22 16:29:37 +010097 if (last->parent) {
98 notifs = lysc_node_notifs(last->parent);
99 } else {
100 notifs = module->notifs;
101 }
102 LY_ARRAY_FOR(notifs, u) {
103 if (&notifs[u] == (struct lysc_notif*)last) {
104 break;
105 }
106 }
107 if (u + 1 < LY_ARRAY_SIZE(notifs)) {
108 next = (struct lysc_node*)(&notifs[u + 1]);
109 }
110 goto repeat;
Radek Krejcia3045382018-11-22 14:30:31 +0100111 }
112
113 next = last->next;
114repeat:
Radek Krejci01342af2019-01-03 15:18:08 +0100115 if (next && parent && parent->nodetype == LYS_CASE && next->parent != parent) {
116 /* inside case (as an explicit parent, not when diving into it from choice),
117 * limit the list of children only to the specific case */
118 next = NULL;
119 }
Radek Krejcia3045382018-11-22 14:30:31 +0100120 if (!next) {
Radek Krejcia9026eb2018-12-12 16:04:47 +0100121 /* possibly go back to parent */
Radek Krejci05b774b2019-02-25 13:26:18 +0100122 if (last && last->parent != parent) {
Radek Krejcia9026eb2018-12-12 16:04:47 +0100123 last = last->parent;
Radek Krejcid5a2b9d2019-04-12 10:39:30 +0200124 goto next;
Radek Krejci6eeb58f2019-02-22 16:29:37 +0100125 } else if (!action_flag) {
126 action_flag = 1;
127 next = parent ? (struct lysc_node*)lysc_node_actions(parent) : (struct lysc_node*)module->rpcs;
128 } else if (!notif_flag) {
129 notif_flag = 1;
130 next = parent ? (struct lysc_node*)lysc_node_notifs(parent) : (struct lysc_node*)module->notifs;
131 } else {
132 return NULL;
Radek Krejcia9026eb2018-12-12 16:04:47 +0100133 }
Radek Krejci6eeb58f2019-02-22 16:29:37 +0100134 goto repeat;
Radek Krejcia3045382018-11-22 14:30:31 +0100135 }
Radek Krejci05b774b2019-02-25 13:26:18 +0100136check:
Radek Krejcia3045382018-11-22 14:30:31 +0100137 switch (next->nodetype) {
Michal Vasko1bf09392020-03-27 12:38:10 +0100138 case LYS_RPC:
Radek Krejcia3045382018-11-22 14:30:31 +0100139 case LYS_ACTION:
140 case LYS_NOTIF:
141 case LYS_LEAF:
142 case LYS_ANYXML:
143 case LYS_ANYDATA:
144 case LYS_LIST:
145 case LYS_LEAFLIST:
Radek Krejcia9026eb2018-12-12 16:04:47 +0100146 case LYS_CASE:
Radek Krejcia3045382018-11-22 14:30:31 +0100147 break;
148 case LYS_CONTAINER:
149 if (!(((struct lysc_node_container *)next)->flags & LYS_PRESENCE) && (options & LYS_GETNEXT_INTONPCONT)) {
150 if (((struct lysc_node_container *)next)->child) {
151 /* go into */
152 next = ((struct lysc_node_container *)next)->child;
153 } else {
154 next = next->next;
155 }
156 goto repeat;
157 }
158 break;
159 case LYS_CHOICE:
160 if (options & LYS_GETNEXT_WITHCHOICE) {
161 return next;
Radek Krejci9bb94eb2018-12-04 16:48:35 +0100162 } else if ((options & LYS_GETNEXT_NOCHOICE) || !((struct lysc_node_choice *)next)->cases) {
163 next = next->next;
164 } else {
Radek Krejcia3045382018-11-22 14:30:31 +0100165 /* go into */
Radek Krejcia9026eb2018-12-12 16:04:47 +0100166 if (options & LYS_GETNEXT_WITHCASE) {
Radek Krejci05b774b2019-02-25 13:26:18 +0100167 next = (struct lysc_node*)((struct lysc_node_choice *)next)->cases;
Radek Krejcia9026eb2018-12-12 16:04:47 +0100168 } else {
169 next = ((struct lysc_node_choice *)next)->cases->child;
170 }
Radek Krejcia3045382018-11-22 14:30:31 +0100171 }
172 goto repeat;
173 default:
174 /* we should not be here */
Radek Krejcib07b5c92019-04-08 10:56:37 +0200175 LOGINT(module ? module->mod->ctx : parent->module->ctx);
Radek Krejcia3045382018-11-22 14:30:31 +0100176 return NULL;
177 }
178
179 if (!(options & LYS_GETNEXT_NOSTATECHECK)) {
180 /* check if the node is disabled by if-feature */
Radek Krejcifab954b2019-09-11 11:25:14 +0200181 if (lysc_node_is_disabled(next, 0)) {
Radek Krejcia3045382018-11-22 14:30:31 +0100182 next = next->next;
183 goto repeat;
184 }
185 }
186 return next;
187}
188
189API const struct lysc_node *
Radek Krejci09e8d0a2019-11-17 12:14:15 +0800190lys_find_node(struct ly_ctx *ctx, const struct lysc_node *context_node, const char *qpath)
191{
192 const char *id = qpath;
193 const char *prefix, *name;
194 size_t prefix_len, name_len;
195 unsigned int u;
196 const struct lysc_node *node = context_node;
197 struct lys_module *mod = NULL;
198
199 LY_CHECK_ARG_RET(ctx, qpath, NULL);
200
201 while(*id) {
202 if (id[0] == '/') {
203 ++id;
204 }
205 /* precess ".." in relative paths */
206 while (!strncmp("../", id, 3)) {
207 id += 3;
208 if (!node) {
209 LOGERR(ctx, LY_EINVAL, "Invalid qpath \"%s\" - too many \"..\" in the path.", qpath);
210 return NULL;
211 }
212 node = node->parent;
213 }
214
215 if (ly_parse_nodeid(&id, &prefix, &prefix_len, &name, &name_len) != LY_SUCCESS) {
216 LOGERR(ctx, LY_EINVAL, "Invalid qpath \"%s\" - invalid nodeid \"%.*s\".", qpath, id- qpath, qpath);
217 return NULL;
218 }
219 if (prefix) {
220 if (context_node) {
221 mod = lys_module_find_prefix(context_node->module, prefix, prefix_len);
222 } else {
223 for (u = 0; u < ctx->list.count; ++u) {
224 if (!ly_strncmp(((struct lys_module *)ctx->list.objs[u])->name, prefix, prefix_len)) {
225 struct lys_module *m = (struct lys_module *)ctx->list.objs[u];
226 if (mod) {
227 if (m->implemented) {
228 mod = m;
229 break;
230 } else if (m->latest_revision) {
231 mod = m;
232 }
233 } else {
234 mod = m;
235 }
236 }
237 }
238 }
239 }
240 if (!mod) {
241 LOGERR(ctx, LY_EINVAL, "Invalid qpath - unable to find module connected with the prefix of the node \"%.*s\".",
242 id - qpath, qpath);
243 return NULL;
244 }
245
246 node = lys_find_child(node, mod, name, name_len, 0, LYS_GETNEXT_NOSTATECHECK);
247 if (!node) {
248 LOGERR(ctx, LY_EINVAL, "Invalid qpath - unable to find \"%.*s\".", id - qpath, qpath);
249 return NULL;
250 }
251 }
252
253 return node;
254}
255
256API const struct lysc_node *
Michal Vaskoe444f752020-02-10 12:20:06 +0100257lys_find_child(const struct lysc_node *parent, const struct lys_module *module, const char *name, size_t name_len,
258 uint16_t nodetype, int options)
Radek Krejcia3045382018-11-22 14:30:31 +0100259{
260 const struct lysc_node *node = NULL;
261
262 LY_CHECK_ARG_RET(NULL, module, name, NULL);
263 if (!nodetype) {
264 nodetype = 0xffff;
265 }
266
267 while ((node = lys_getnext(node, parent, module->compiled, options))) {
268 if (!(node->nodetype & nodetype)) {
269 continue;
270 }
271 if (node->module != module) {
272 continue;
273 }
274
275 if (name_len) {
Radek Krejci7f9b6512019-09-18 13:11:09 +0200276 if (!ly_strncmp(node->name, name, name_len)) {
Radek Krejcia3045382018-11-22 14:30:31 +0100277 return node;
278 }
279 } else {
280 if (!strcmp(node->name, name)) {
281 return node;
282 }
283 }
284 }
285 return NULL;
286}
287
Michal Vasko519fd602020-05-26 12:17:39 +0200288API LY_ERR
289lys_atomize_xpath(const struct lysc_node *ctx_node, const char *xpath, int options, struct ly_set **set)
290{
291 LY_ERR ret = LY_SUCCESS;
292 struct lyxp_set xp_set;
293 struct lyxp_expr *exp;
294 uint32_t i;
295
296 LY_CHECK_ARG_RET(NULL, ctx_node, xpath, set, LY_EINVAL);
297 if (!(options & LYXP_SCNODE_ALL)) {
298 options = LYXP_SCNODE;
299 }
300
301 memset(&xp_set, 0, sizeof xp_set);
302
303 /* compile expression */
304 exp = lyxp_expr_parse(ctx_node->module->ctx, xpath);
305 LY_CHECK_ERR_GOTO(!exp, ret = LY_EINVAL, cleanup);
306
307 /* atomize expression */
308 ret = lyxp_atomize(exp, LYD_JSON, ctx_node->module, ctx_node, LYXP_NODE_ELEM, &xp_set, options);
309 LY_CHECK_GOTO(ret, cleanup);
310
311 /* allocate return set */
312 *set = ly_set_new();
313 LY_CHECK_ERR_GOTO(!*set, LOGMEM(ctx_node->module->ctx); ret = LY_EMEM, cleanup);
314
315 /* transform into ly_set */
316 (*set)->objs = malloc(xp_set.used * sizeof *(*set)->objs);
317 LY_CHECK_ERR_GOTO(!(*set)->objs, LOGMEM(ctx_node->module->ctx); ret = LY_EMEM, cleanup);
318 (*set)->size = xp_set.used;
319
320 for (i = 0; i < xp_set.used; ++i) {
321 if (xp_set.val.nodes[i].type == LYXP_NODE_ELEM) {
322 ly_set_add(*set, xp_set.val.scnodes[i].scnode, LY_SET_OPT_USEASLIST);
323 }
324 }
325
326cleanup:
327 lyxp_set_free_content(&xp_set);
328 lyxp_expr_free(ctx_node->module->ctx, exp);
329 return ret;
330}
331
Michal Vasko14654712020-02-06 08:35:21 +0100332char *
333lysc_path_until(const struct lysc_node *node, const struct lysc_node *parent, LYSC_PATH_TYPE pathtype, char *buffer,
334 size_t buflen)
Radek Krejci327de162019-06-14 12:52:07 +0200335{
Michal Vasko03ff5a72019-09-11 13:49:33 +0200336 const struct lysc_node *iter;
Radek Krejci327de162019-06-14 12:52:07 +0200337 char *path = NULL;
338 int len = 0;
339
Radek Krejci3bbd93e2019-07-24 09:57:23 +0200340 LY_CHECK_ARG_RET(NULL, node, NULL);
341 if (buffer) {
342 LY_CHECK_ARG_RET(node->module->ctx, buflen > 1, NULL);
343 }
344
Radek Krejci327de162019-06-14 12:52:07 +0200345 switch (pathtype) {
Michal Vasko03ff5a72019-09-11 13:49:33 +0200346 case LYSC_PATH_LOG:
Michal Vasko90932a92020-02-12 14:33:03 +0100347 for (iter = node; iter && (iter != parent) && (len >= 0); iter = iter->parent) {
Radek Krejci1c0c3442019-07-23 16:08:47 +0200348 char *s = buffer ? strdup(buffer) : path;
Radek Krejci327de162019-06-14 12:52:07 +0200349 char *id;
Michal Vasko14654712020-02-06 08:35:21 +0100350 const char *slash;
Radek Krejci327de162019-06-14 12:52:07 +0200351
Michal Vasko03ff5a72019-09-11 13:49:33 +0200352 id = strdup(iter->name);
Michal Vasko14654712020-02-06 08:35:21 +0100353 if (parent && (iter->parent == parent)) {
354 slash = "";
355 } else {
356 slash = "/";
357 }
Radek Krejci327de162019-06-14 12:52:07 +0200358 if (!iter->parent || iter->parent->module != iter->module) {
359 /* print prefix */
Radek Krejci1c0c3442019-07-23 16:08:47 +0200360 if (buffer) {
Michal Vasko14654712020-02-06 08:35:21 +0100361 len = snprintf(buffer, buflen, "%s%s:%s%s", slash, iter->module->name, id, s ? s : "");
Radek Krejci1c0c3442019-07-23 16:08:47 +0200362 } else {
Michal Vasko14654712020-02-06 08:35:21 +0100363 len = asprintf(&path, "%s%s:%s%s", slash, iter->module->name, id, s ? s : "");
Radek Krejci1c0c3442019-07-23 16:08:47 +0200364 }
Radek Krejci327de162019-06-14 12:52:07 +0200365 } else {
366 /* prefix is the same as in parent */
Radek Krejci1c0c3442019-07-23 16:08:47 +0200367 if (buffer) {
Michal Vasko14654712020-02-06 08:35:21 +0100368 len = snprintf(buffer, buflen, "%s%s%s", slash, id, s ? s : "");
Radek Krejci1c0c3442019-07-23 16:08:47 +0200369 } else {
Michal Vasko14654712020-02-06 08:35:21 +0100370 len = asprintf(&path, "%s%s%s", slash, id, s ? s : "");
Radek Krejci1c0c3442019-07-23 16:08:47 +0200371 }
Radek Krejci327de162019-06-14 12:52:07 +0200372 }
373 free(s);
374 free(id);
Radek Krejci1c0c3442019-07-23 16:08:47 +0200375
376 if (buffer && buflen <= (size_t)len) {
377 /* not enough space in buffer */
378 break;
379 }
Radek Krejci327de162019-06-14 12:52:07 +0200380 }
381
382 if (len < 0) {
383 free(path);
384 path = NULL;
385 } else if (len == 0) {
Radek Krejci3bbd93e2019-07-24 09:57:23 +0200386 if (buffer) {
387 strcpy(buffer, "/");
388 } else {
389 path = strdup("/");
390 }
Radek Krejci327de162019-06-14 12:52:07 +0200391 }
392 break;
393 }
394
Radek Krejci1c0c3442019-07-23 16:08:47 +0200395 if (buffer) {
396 return buffer;
397 } else {
398 return path;
399 }
Radek Krejci327de162019-06-14 12:52:07 +0200400}
401
Michal Vasko14654712020-02-06 08:35:21 +0100402API char *
403lysc_path(const struct lysc_node *node, LYSC_PATH_TYPE pathtype, char *buffer, size_t buflen)
404{
405 return lysc_path_until(node, NULL, pathtype, buffer, buflen);
406}
407
Michal Vasko28d78432020-05-26 13:10:53 +0200408API LY_ERR
Radek Krejci19a96102018-11-15 13:38:09 +0100409lysc_feature_value(const struct lysc_feature *feature)
Radek Krejci6f7feb62018-10-12 15:23:02 +0200410{
Michal Vasko28d78432020-05-26 13:10:53 +0200411 LY_CHECK_ARG_RET(NULL, feature, LY_EINVAL);
412 return feature->flags & LYS_FENABLED ? LY_SUCCESS : LY_ENOT;
Radek Krejci151a5b72018-10-19 14:21:44 +0200413}
414
Radek Krejci693262f2019-04-29 15:23:20 +0200415uint8_t
416lysc_iff_getop(uint8_t *list, int pos)
Radek Krejci151a5b72018-10-19 14:21:44 +0200417{
418 uint8_t *item;
419 uint8_t mask = 3, result;
420
421 assert(pos >= 0);
422
423 item = &list[pos / 4];
424 result = (*item) & (mask << 2 * (pos % 4));
425 return result >> 2 * (pos % 4);
426}
427
Michal Vasko28d78432020-05-26 13:10:53 +0200428static LY_ERR
Radek Krejci151a5b72018-10-19 14:21:44 +0200429lysc_iffeature_value_(const struct lysc_iffeature *iff, int *index_e, int *index_f)
430{
431 uint8_t op;
Michal Vasko28d78432020-05-26 13:10:53 +0200432 LY_ERR a, b;
Radek Krejci151a5b72018-10-19 14:21:44 +0200433
Radek Krejci693262f2019-04-29 15:23:20 +0200434 op = lysc_iff_getop(iff->expr, *index_e);
Radek Krejci151a5b72018-10-19 14:21:44 +0200435 (*index_e)++;
436
437 switch (op) {
438 case LYS_IFF_F:
439 /* resolve feature */
Radek Krejci2c4e7172018-10-19 15:56:26 +0200440 return lysc_feature_value(iff->features[(*index_f)++]);
Radek Krejci151a5b72018-10-19 14:21:44 +0200441 case LYS_IFF_NOT:
442 /* invert result */
Michal Vasko28d78432020-05-26 13:10:53 +0200443 return lysc_iffeature_value_(iff, index_e, index_f) == LY_SUCCESS ? LY_ENOT : LY_SUCCESS;
Radek Krejci151a5b72018-10-19 14:21:44 +0200444 case LYS_IFF_AND:
445 case LYS_IFF_OR:
446 a = lysc_iffeature_value_(iff, index_e, index_f);
447 b = lysc_iffeature_value_(iff, index_e, index_f);
448 if (op == LYS_IFF_AND) {
Michal Vasko28d78432020-05-26 13:10:53 +0200449 if ((a == LY_SUCCESS) && (b == LY_SUCCESS)) {
450 return LY_SUCCESS;
451 } else {
452 return LY_ENOT;
453 }
Radek Krejci151a5b72018-10-19 14:21:44 +0200454 } else { /* LYS_IFF_OR */
Michal Vasko28d78432020-05-26 13:10:53 +0200455 if ((a == LY_SUCCESS) || (b == LY_SUCCESS)) {
456 return LY_SUCCESS;
457 } else {
458 return LY_ENOT;
459 }
Radek Krejci151a5b72018-10-19 14:21:44 +0200460 }
461 }
462
463 return 0;
464}
465
Michal Vasko28d78432020-05-26 13:10:53 +0200466API LY_ERR
Radek Krejci151a5b72018-10-19 14:21:44 +0200467lysc_iffeature_value(const struct lysc_iffeature *iff)
468{
469 int index_e = 0, index_f = 0;
470
471 LY_CHECK_ARG_RET(NULL, iff, -1);
472
473 if (iff->expr) {
474 return lysc_iffeature_value_(iff, &index_e, &index_f);
475 }
476 return 0;
477}
478
Radek Krejci151a5b72018-10-19 14:21:44 +0200479/**
480 * @brief Enable/Disable the specified feature in the module.
481 *
482 * If the feature is already set to the desired value, LY_SUCCESS is returned.
483 * By changing the feature, also all the feature which depends on it via their
484 * if-feature statements are again evaluated (disabled if a if-feature statemen
485 * evaluates to false).
486 *
Radek Krejci0af46292019-01-11 16:02:31 +0100487 * @param[in] mod Module where to set (search for) the feature.
Radek Krejci151a5b72018-10-19 14:21:44 +0200488 * @param[in] name Name of the feature to set. Asterisk ('*') can be used to
489 * set all the features in the module.
490 * @param[in] value Desired value of the feature: 1 (enable) or 0 (disable).
491 * @return LY_ERR value.
492 */
493static LY_ERR
Radek Krejci0af46292019-01-11 16:02:31 +0100494lys_feature_change(const struct lys_module *mod, const char *name, int value)
Radek Krejci151a5b72018-10-19 14:21:44 +0200495{
496 int all = 0;
Radek Krejci7eb54ba2020-05-18 16:30:04 +0200497 LY_ARRAY_SIZE_TYPE u, disabled_count;
498 uint32_t changed_count;
Radek Krejci151a5b72018-10-19 14:21:44 +0200499 struct lysc_feature *f, **df;
500 struct lysc_iffeature *iff;
501 struct ly_set *changed;
Radek Krejci0af46292019-01-11 16:02:31 +0100502 struct ly_ctx *ctx = mod->ctx; /* shortcut */
Radek Krejci151a5b72018-10-19 14:21:44 +0200503
Radek Krejci6e67c402019-05-02 09:55:39 +0200504 if (!strcmp(name, "*")) {
505 /* enable all */
506 all = 1;
507 }
508
Radek Krejci0af46292019-01-11 16:02:31 +0100509 if (!mod->compiled) {
510 LOGERR(ctx, LY_EINVAL, "Module \"%s\" is not implemented so all its features are permanently disabled without a chance to change it.",
511 mod->name);
512 return LY_EINVAL;
513 }
514 if (!mod->compiled->features) {
Radek Krejci6e67c402019-05-02 09:55:39 +0200515 if (all) {
516 /* no feature to enable */
517 return LY_SUCCESS;
518 }
Radek Krejci0af46292019-01-11 16:02:31 +0100519 LOGERR(ctx, LY_EINVAL, "Unable to switch feature since the module \"%s\" has no features.", mod->name);
Radek Krejci151a5b72018-10-19 14:21:44 +0200520 return LY_EINVAL;
521 }
522
Radek Krejci151a5b72018-10-19 14:21:44 +0200523 changed = ly_set_new();
Radek Krejcica3db002018-11-01 10:31:01 +0100524 changed_count = 0;
Radek Krejci151a5b72018-10-19 14:21:44 +0200525
Radek Krejcica3db002018-11-01 10:31:01 +0100526run:
Radek Krejci0af46292019-01-11 16:02:31 +0100527 for (disabled_count = u = 0; u < LY_ARRAY_SIZE(mod->compiled->features); ++u) {
528 f = &mod->compiled->features[u];
Radek Krejci151a5b72018-10-19 14:21:44 +0200529 if (all || !strcmp(f->name, name)) {
530 if ((value && (f->flags & LYS_FENABLED)) || (!value && !(f->flags & LYS_FENABLED))) {
531 if (all) {
532 /* skip already set features */
533 continue;
534 } else {
535 /* feature already set correctly */
536 ly_set_free(changed, NULL);
537 return LY_SUCCESS;
538 }
539 }
540
541 if (value) { /* enable */
542 /* check referenced features if they are enabled */
543 LY_ARRAY_FOR(f->iffeatures, struct lysc_iffeature, iff) {
Michal Vasko28d78432020-05-26 13:10:53 +0200544 if (lysc_iffeature_value(iff) == LY_ENOT) {
Radek Krejci151a5b72018-10-19 14:21:44 +0200545 if (all) {
Radek Krejcica3db002018-11-01 10:31:01 +0100546 ++disabled_count;
Radek Krejci151a5b72018-10-19 14:21:44 +0200547 goto next;
548 } else {
Radek Krejci0bcdaed2019-01-10 10:21:34 +0100549 LOGERR(ctx, LY_EDENIED,
Radek Krejci151a5b72018-10-19 14:21:44 +0200550 "Feature \"%s\" cannot be enabled since it is disabled by its if-feature condition(s).",
551 f->name);
552 ly_set_free(changed, NULL);
553 return LY_EDENIED;
554 }
555 }
556 }
557 /* enable the feature */
558 f->flags |= LYS_FENABLED;
559 } else { /* disable */
560 /* disable the feature */
561 f->flags &= ~LYS_FENABLED;
562 }
563
564 /* remember the changed feature */
565 ly_set_add(changed, f, LY_SET_OPT_USEASLIST);
566
567 if (!all) {
568 /* stop in case changing a single feature */
569 break;
570 }
571 }
572next:
573 ;
574 }
575
576 if (!all && !changed->count) {
Radek Krejci0af46292019-01-11 16:02:31 +0100577 LOGERR(ctx, LY_EINVAL, "Feature \"%s\" not found in module \"%s\".", name, mod->name);
Radek Krejci151a5b72018-10-19 14:21:44 +0200578 ly_set_free(changed, NULL);
579 return LY_EINVAL;
580 }
581
Radek Krejcica3db002018-11-01 10:31:01 +0100582 if (value && all && disabled_count) {
583 if (changed_count == changed->count) {
584 /* no change in last run -> not able to enable all ... */
585 /* ... print errors */
Radek Krejci0af46292019-01-11 16:02:31 +0100586 for (u = 0; disabled_count && u < LY_ARRAY_SIZE(mod->compiled->features); ++u) {
587 if (!(mod->compiled->features[u].flags & LYS_FENABLED)) {
Radek Krejci0bcdaed2019-01-10 10:21:34 +0100588 LOGERR(ctx, LY_EDENIED,
Radek Krejcica3db002018-11-01 10:31:01 +0100589 "Feature \"%s\" cannot be enabled since it is disabled by its if-feature condition(s).",
Radek Krejci0af46292019-01-11 16:02:31 +0100590 mod->compiled->features[u].name);
Radek Krejcica3db002018-11-01 10:31:01 +0100591 --disabled_count;
592 }
593 }
594 /* ... restore the original state */
595 for (u = 0; u < changed->count; ++u) {
596 f = changed->objs[u];
597 /* re-disable the feature */
598 f->flags &= ~LYS_FENABLED;
599 }
600
601 ly_set_free(changed, NULL);
602 return LY_EDENIED;
603 } else {
604 /* we did some change in last run, try it again */
605 changed_count = changed->count;
606 goto run;
607 }
608 }
609
Radek Krejci151a5b72018-10-19 14:21:44 +0200610 /* reflect change(s) in the dependent features */
611 for (u = 0; u < changed->count; ++u) {
612 /* If a dependent feature is enabled, it can be now changed by the change (to false) of the value of
613 * its if-feature statements. The reverse logic, automatically enable feature when its feature is enabled
614 * is not done - by default, features are disabled and must be explicitely enabled. */
615 f = changed->objs[u];
616 LY_ARRAY_FOR(f->depfeatures, struct lysc_feature*, df) {
617 if (!((*df)->flags & LYS_FENABLED)) {
618 /* not enabled, nothing to do */
619 continue;
620 }
621 /* check the feature's if-features which could change by the previous change of our feature */
622 LY_ARRAY_FOR((*df)->iffeatures, struct lysc_iffeature, iff) {
Michal Vasko28d78432020-05-26 13:10:53 +0200623 if (lysc_iffeature_value(iff) == LY_ENOT) {
Radek Krejci151a5b72018-10-19 14:21:44 +0200624 /* the feature must be disabled now */
625 (*df)->flags &= ~LYS_FENABLED;
626 /* add the feature into the list of changed features */
627 ly_set_add(changed, *df, LY_SET_OPT_USEASLIST);
628 break;
629 }
630 }
631 }
632 }
633
634 ly_set_free(changed, NULL);
635 return LY_SUCCESS;
636}
637
638API LY_ERR
Radek Krejcied5acc52019-04-25 15:57:04 +0200639lys_feature_enable(const struct lys_module *module, const char *feature)
Radek Krejci151a5b72018-10-19 14:21:44 +0200640{
Radek Krejci0af46292019-01-11 16:02:31 +0100641 LY_CHECK_ARG_RET(NULL, module, feature, LY_EINVAL);
Radek Krejci151a5b72018-10-19 14:21:44 +0200642
Radek Krejcied5acc52019-04-25 15:57:04 +0200643 return lys_feature_change((struct lys_module*)module, feature, 1);
Radek Krejci151a5b72018-10-19 14:21:44 +0200644}
645
646API LY_ERR
Radek Krejcied5acc52019-04-25 15:57:04 +0200647lys_feature_disable(const struct lys_module *module, const char *feature)
Radek Krejci151a5b72018-10-19 14:21:44 +0200648{
Radek Krejci0af46292019-01-11 16:02:31 +0100649 LY_CHECK_ARG_RET(NULL, module, feature, LY_EINVAL);
Radek Krejci151a5b72018-10-19 14:21:44 +0200650
Radek Krejcied5acc52019-04-25 15:57:04 +0200651 return lys_feature_change((struct lys_module*)module, feature, 0);
Radek Krejci151a5b72018-10-19 14:21:44 +0200652}
653
654API int
655lys_feature_value(const struct lys_module *module, const char *feature)
656{
657 struct lysc_feature *f;
658 struct lysc_module *mod;
Radek Krejci7eb54ba2020-05-18 16:30:04 +0200659 LY_ARRAY_SIZE_TYPE u;
Radek Krejci151a5b72018-10-19 14:21:44 +0200660
661 LY_CHECK_ARG_RET(NULL, module, module->compiled, feature, -1);
662 mod = module->compiled;
663
664 /* search for the specified feature */
665 for (u = 0; u < LY_ARRAY_SIZE(mod->features); ++u) {
Radek Krejci2c4e7172018-10-19 15:56:26 +0200666 f = &mod->features[u];
Radek Krejci151a5b72018-10-19 14:21:44 +0200667 if (!strcmp(f->name, feature)) {
668 if (f->flags & LYS_FENABLED) {
669 return 1;
670 } else {
671 return 0;
672 }
673 }
674 }
675
676 /* feature definition not found */
677 return -1;
678}
679
Michal Vaskoc193ce92020-03-06 11:04:48 +0100680API const struct lysc_node *
Radek Krejcifab954b2019-09-11 11:25:14 +0200681lysc_node_is_disabled(const struct lysc_node *node, int recursive)
Radek Krejcia3045382018-11-22 14:30:31 +0100682{
Radek Krejci7eb54ba2020-05-18 16:30:04 +0200683 LY_ARRAY_SIZE_TYPE u;
Radek Krejcia3045382018-11-22 14:30:31 +0100684
685 LY_CHECK_ARG_RET(NULL, node, NULL);
686
Michal Vaskoc193ce92020-03-06 11:04:48 +0100687 do {
Radek Krejci056d0a82018-12-06 16:57:25 +0100688 if (node->iffeatures) {
Radek Krejcia3045382018-11-22 14:30:31 +0100689 /* check local if-features */
Radek Krejci056d0a82018-12-06 16:57:25 +0100690 LY_ARRAY_FOR(node->iffeatures, u) {
Michal Vasko28d78432020-05-26 13:10:53 +0200691 if (lysc_iffeature_value(&node->iffeatures[u]) == LY_ENOT) {
Michal Vaskoc193ce92020-03-06 11:04:48 +0100692 return node;
Radek Krejcia3045382018-11-22 14:30:31 +0100693 }
694 }
695 }
696
697 if (!recursive) {
698 return NULL;
699 }
700
Michal Vaskoc193ce92020-03-06 11:04:48 +0100701 /* go through schema-only parents */
Radek Krejcia3045382018-11-22 14:30:31 +0100702 node = node->parent;
Michal Vaskoc193ce92020-03-06 11:04:48 +0100703 } while (node && (node->nodetype & (LYS_CASE | LYS_CHOICE)));
704
Radek Krejcia3045382018-11-22 14:30:31 +0100705 return NULL;
706}
707
Radek Krejci77a8bcd2019-09-11 11:20:02 +0200708LY_ERR
709lys_set_implemented_internal(struct lys_module *mod, uint8_t value)
710{
711 struct lys_module *m;
712
713 LY_CHECK_ARG_RET(NULL, mod, LY_EINVAL);
714
715 if (mod->implemented) {
716 return LY_SUCCESS;
717 }
718
719 /* we have module from the current context */
720 m = ly_ctx_get_module_implemented(mod->ctx, mod->name);
721 if (m) {
722 if (m != mod) {
723 /* check collision with other implemented revision */
724 LOGERR(mod->ctx, LY_EDENIED, "Module \"%s\" is present in the context in other implemented revision (%s).",
725 mod->name, mod->revision ? mod->revision : "module without revision");
726 return LY_EDENIED;
727 } else {
728 /* mod is already implemented */
729 return LY_SUCCESS;
730 }
731 }
732
733 /* mark the module implemented, check for collision was already done */
734 mod->implemented = value;
735
736 /* compile the schema */
737 LY_CHECK_RET(lys_compile(mod, LYSC_OPT_INTERNAL));
738
739 return LY_SUCCESS;
740}
741
742API LY_ERR
743lys_set_implemented(struct lys_module *mod)
744{
745 return lys_set_implemented_internal(mod, 1);
746}
747
Radek Krejci0bcdaed2019-01-10 10:21:34 +0100748struct lysp_submodule *
Radek Krejcie7b95092019-05-15 11:03:07 +0200749lys_parse_mem_submodule(struct ly_ctx *ctx, const char *data, LYS_INFORMAT format, struct lys_parser_ctx *main_ctx,
Michal Vaskob36053d2020-03-26 15:49:30 +0100750 LY_ERR (*custom_check)(const struct ly_ctx*, struct lysp_module*, struct lysp_submodule*, void*), void *check_data)
Radek Krejci9f5e6fb2018-10-25 09:26:12 +0200751{
Radek Krejci0bcdaed2019-01-10 10:21:34 +0100752 LY_ERR ret = LY_EINVAL;
753 struct lysp_submodule *submod = NULL, *latest_sp;
Michal Vaskob36053d2020-03-26 15:49:30 +0100754 struct lys_yang_parser_ctx *yangctx = NULL;
755 struct lys_yin_parser_ctx *yinctx = NULL;
756 struct lys_parser_ctx *pctx;
Radek Krejci0bcdaed2019-01-10 10:21:34 +0100757
758 LY_CHECK_ARG_RET(ctx, ctx, data, NULL);
759
Radek Krejci0bcdaed2019-01-10 10:21:34 +0100760 switch (format) {
761 case LYS_IN_YIN:
Michal Vaskob36053d2020-03-26 15:49:30 +0100762 ret = yin_parse_submodule(&yinctx, ctx, main_ctx, data, &submod);
763 pctx = (struct lys_parser_ctx *)yinctx;
Radek Krejci0bcdaed2019-01-10 10:21:34 +0100764 break;
765 case LYS_IN_YANG:
Michal Vaskob36053d2020-03-26 15:49:30 +0100766 ret = yang_parse_submodule(&yangctx, ctx, main_ctx, data, &submod);
767 pctx = (struct lys_parser_ctx *)yangctx;
Radek Krejci0bcdaed2019-01-10 10:21:34 +0100768 break;
769 default:
David Sedlák4f2f5ba2019-08-15 13:18:48 +0200770 LOGERR(ctx, LY_EINVAL, "Invalid schema input format.");
Radek Krejci0bcdaed2019-01-10 10:21:34 +0100771 break;
Radek Krejci9f5e6fb2018-10-25 09:26:12 +0200772 }
Radek Krejci0bcdaed2019-01-10 10:21:34 +0100773 LY_CHECK_RET(ret, NULL);
774
775 /* make sure that the newest revision is at position 0 */
776 lysp_sort_revisions(submod->revs);
777
Radek Krejci0bcdaed2019-01-10 10:21:34 +0100778 /* decide the latest revision */
Michal Vaskob36053d2020-03-26 15:49:30 +0100779 latest_sp = ly_ctx_get_submodule(PARSER_CTX(pctx), submod->belongsto, submod->name, NULL);
Radek Krejci0bcdaed2019-01-10 10:21:34 +0100780 if (latest_sp) {
781 if (submod->revs) {
782 if (!latest_sp->revs) {
783 /* latest has no revision, so mod is anyway newer */
784 submod->latest_revision = latest_sp->latest_revision;
Radek Krejcib3289d62019-09-18 12:21:39 +0200785 /* the latest_sp is zeroed later when the new module is being inserted into the context */
786 } else if (strcmp(submod->revs[0].date, latest_sp->revs[0].date) > 0) {
787 submod->latest_revision = latest_sp->latest_revision;
788 /* the latest_sp is zeroed later when the new module is being inserted into the context */
Radek Krejci0bcdaed2019-01-10 10:21:34 +0100789 } else {
Radek Krejcib3289d62019-09-18 12:21:39 +0200790 latest_sp = NULL;
Radek Krejci0bcdaed2019-01-10 10:21:34 +0100791 }
Radek Krejcib3289d62019-09-18 12:21:39 +0200792 } else {
793 latest_sp = NULL;
Radek Krejci0bcdaed2019-01-10 10:21:34 +0100794 }
795 } else {
796 submod->latest_revision = 1;
797 }
798
Radek Krejcib3289d62019-09-18 12:21:39 +0200799 if (custom_check) {
Michal Vaskob36053d2020-03-26 15:49:30 +0100800 LY_CHECK_GOTO(custom_check(PARSER_CTX(pctx), NULL, submod, check_data), error);
Radek Krejcib3289d62019-09-18 12:21:39 +0200801 }
802
803 if (latest_sp) {
804 latest_sp->latest_revision = 0;
805 }
806
Radek Krejci0bcdaed2019-01-10 10:21:34 +0100807 /* remap possibly changed and reallocated typedefs and groupings list back to the main context */
Michal Vaskob36053d2020-03-26 15:49:30 +0100808 memcpy(&main_ctx->tpdfs_nodes, &pctx->tpdfs_nodes, sizeof main_ctx->tpdfs_nodes);
809 memcpy(&main_ctx->grps_nodes, &pctx->grps_nodes, sizeof main_ctx->grps_nodes);
Radek Krejci0bcdaed2019-01-10 10:21:34 +0100810
David Sedlák1b623122019-08-05 15:27:49 +0200811 if (format == LYS_IN_YANG) {
Michal Vaskob36053d2020-03-26 15:49:30 +0100812 yang_parser_ctx_free(yangctx);
David Sedlák1b623122019-08-05 15:27:49 +0200813 } else {
Michal Vaskob36053d2020-03-26 15:49:30 +0100814 yin_parser_ctx_free(yinctx);
David Sedlák1b623122019-08-05 15:27:49 +0200815 }
Radek Krejci0bcdaed2019-01-10 10:21:34 +0100816 return submod;
David Sedlák1b623122019-08-05 15:27:49 +0200817
Radek Krejci0bcdaed2019-01-10 10:21:34 +0100818error:
819 lysp_submodule_free(ctx, submod);
David Sedlák1b623122019-08-05 15:27:49 +0200820 if (format == LYS_IN_YANG) {
Michal Vaskob36053d2020-03-26 15:49:30 +0100821 yang_parser_ctx_free(yangctx);
David Sedlák1b623122019-08-05 15:27:49 +0200822 } else {
Michal Vaskob36053d2020-03-26 15:49:30 +0100823 yin_parser_ctx_free(yinctx);
David Sedlák1b623122019-08-05 15:27:49 +0200824 }
Radek Krejci0bcdaed2019-01-10 10:21:34 +0100825 return NULL;
Radek Krejci9f5e6fb2018-10-25 09:26:12 +0200826}
827
Radek Krejcid33273d2018-10-25 14:55:52 +0200828struct lys_module *
Radek Krejci0bcdaed2019-01-10 10:21:34 +0100829lys_parse_mem_module(struct ly_ctx *ctx, const char *data, LYS_INFORMAT format, int implement,
Michal Vaskob36053d2020-03-26 15:49:30 +0100830 LY_ERR (*custom_check)(const struct ly_ctx *ctx, struct lysp_module *mod, struct lysp_submodule *submod, void *data),
Radek Krejci0bcdaed2019-01-10 10:21:34 +0100831 void *check_data)
Radek Krejci86d106e2018-10-18 09:53:19 +0200832{
Radek Krejci6d6e4e42018-10-29 13:28:19 +0100833 struct lys_module *mod = NULL, *latest, *mod_dup;
Radek Krejci086c7132018-10-26 15:29:04 +0200834 struct lysp_import *imp;
835 struct lysp_include *inc;
Radek Krejci9ed7a192018-10-31 16:23:51 +0100836 LY_ERR ret = LY_EINVAL;
Radek Krejci7eb54ba2020-05-18 16:30:04 +0200837 LY_ARRAY_SIZE_TYPE u, v;
Michal Vaskob36053d2020-03-26 15:49:30 +0100838 struct lys_yang_parser_ctx *yangctx = NULL;
839 struct lys_yin_parser_ctx *yinctx = NULL;
840 struct lys_parser_ctx *pctx;
Radek Krejci86d106e2018-10-18 09:53:19 +0200841
842 LY_CHECK_ARG_RET(ctx, ctx, data, NULL);
843
844 mod = calloc(1, sizeof *mod);
845 LY_CHECK_ERR_RET(!mod, LOGMEM(ctx), NULL);
Radek Krejci0bcdaed2019-01-10 10:21:34 +0100846 mod->ctx = ctx;
Radek Krejci86d106e2018-10-18 09:53:19 +0200847
848 switch (format) {
849 case LYS_IN_YIN:
Michal Vaskob36053d2020-03-26 15:49:30 +0100850 ret = yin_parse_module(&yinctx, data, mod);
851 pctx = (struct lys_parser_ctx *)yinctx;
Radek Krejci86d106e2018-10-18 09:53:19 +0200852 break;
853 case LYS_IN_YANG:
Michal Vaskob36053d2020-03-26 15:49:30 +0100854 ret = yang_parse_module(&yangctx, data, mod);
855 pctx = (struct lys_parser_ctx *)yangctx;
Radek Krejci86d106e2018-10-18 09:53:19 +0200856 break;
857 default:
858 LOGERR(ctx, LY_EINVAL, "Invalid schema input format.");
859 break;
860 }
Radek Krejci0bcdaed2019-01-10 10:21:34 +0100861 LY_CHECK_ERR_RET(ret, lys_module_free(mod, NULL), NULL);
Radek Krejci9f5e6fb2018-10-25 09:26:12 +0200862
863 /* make sure that the newest revision is at position 0 */
864 lysp_sort_revisions(mod->parsed->revs);
Radek Krejci0af46292019-01-11 16:02:31 +0100865 if (mod->parsed->revs) {
866 mod->revision = lydict_insert(ctx, mod->parsed->revs[0].date, 0);
867 }
Radek Krejci86d106e2018-10-18 09:53:19 +0200868
Radek Krejcib3289d62019-09-18 12:21:39 +0200869 /* decide the latest revision */
870 latest = (struct lys_module*)ly_ctx_get_module_latest(ctx, mod->name);
871 if (latest) {
872 if (mod->revision) {
873 if (!latest->revision) {
874 /* latest has no revision, so mod is anyway newer */
875 mod->latest_revision = latest->latest_revision;
876 /* the latest is zeroed later when the new module is being inserted into the context */
877 } else if (strcmp(mod->revision, latest->revision) > 0) {
878 mod->latest_revision = latest->latest_revision;
879 /* the latest is zeroed later when the new module is being inserted into the context */
880 } else {
881 latest = NULL;
882 }
883 } else {
884 latest = NULL;
885 }
886 } else {
887 mod->latest_revision = 1;
888 }
889
Radek Krejci0bcdaed2019-01-10 10:21:34 +0100890 if (custom_check) {
891 LY_CHECK_GOTO(custom_check(ctx, mod->parsed, NULL, check_data), error);
892 }
893
Radek Krejci86d106e2018-10-18 09:53:19 +0200894 if (implement) {
Radek Krejci9f5e6fb2018-10-25 09:26:12 +0200895 /* mark the loaded module implemented */
Radek Krejci0bcdaed2019-01-10 10:21:34 +0100896 if (ly_ctx_get_module_implemented(ctx, mod->name)) {
897 LOGERR(ctx, LY_EDENIED, "Module \"%s\" is already implemented in the context.", mod->name);
Radek Krejcibbe09a92018-11-08 09:36:54 +0100898 goto error;
Radek Krejcib7db73a2018-10-24 14:18:40 +0200899 }
Radek Krejci0bcdaed2019-01-10 10:21:34 +0100900 mod->implemented = 1;
Radek Krejci86d106e2018-10-18 09:53:19 +0200901 }
902
Radek Krejci0bcdaed2019-01-10 10:21:34 +0100903 /* check for duplicity in the context */
Radek Krejci0af46292019-01-11 16:02:31 +0100904 mod_dup = (struct lys_module*)ly_ctx_get_module(ctx, mod->name, mod->revision);
Radek Krejci0bcdaed2019-01-10 10:21:34 +0100905 if (mod_dup) {
906 if (mod_dup->parsed) {
907 /* error */
Radek Krejcid33273d2018-10-25 14:55:52 +0200908 if (mod->parsed->revs) {
Radek Krejci0bcdaed2019-01-10 10:21:34 +0100909 LOGERR(ctx, LY_EEXIST, "Module \"%s\" of revision \"%s\" is already present in the context.",
910 mod->name, mod->parsed->revs[0].date);
Radek Krejcid33273d2018-10-25 14:55:52 +0200911 } else {
Radek Krejci0bcdaed2019-01-10 10:21:34 +0100912 LOGERR(ctx, LY_EEXIST, "Module \"%s\" with no revision is already present in the context.",
913 mod->name);
Radek Krejcid33273d2018-10-25 14:55:52 +0200914 }
Radek Krejci0bcdaed2019-01-10 10:21:34 +0100915 goto error;
916 } else {
917 /* add the parsed data to the currently compiled-only module in the context */
918 mod_dup->parsed = mod->parsed;
919 mod_dup->parsed->mod = mod_dup;
920 mod->parsed = NULL;
921 lys_module_free(mod, NULL);
922 mod = mod_dup;
923 goto finish_parsing;
Radek Krejcid33273d2018-10-25 14:55:52 +0200924 }
Radek Krejci0bcdaed2019-01-10 10:21:34 +0100925 }
Radek Krejci86d106e2018-10-18 09:53:19 +0200926
Radek Krejci0af46292019-01-11 16:02:31 +0100927 if (!mod->implemented) {
Michal Vasko5fe75f12020-03-02 13:52:37 +0100928 /* pre-compile features of the module */
Radek Krejci327de162019-06-14 12:52:07 +0200929 LY_CHECK_GOTO(lys_feature_precompile(NULL, ctx, mod, mod->parsed->features, &mod->off_features), error);
Radek Krejci0af46292019-01-11 16:02:31 +0100930 }
931
Radek Krejci0bcdaed2019-01-10 10:21:34 +0100932 if (latest) {
Radek Krejcib3289d62019-09-18 12:21:39 +0200933 latest->latest_revision = 0;
Radek Krejci0bcdaed2019-01-10 10:21:34 +0100934 }
Radek Krejcid33273d2018-10-25 14:55:52 +0200935
Radek Krejci0bcdaed2019-01-10 10:21:34 +0100936 /* add into context */
937 ly_set_add(&ctx->list, mod, LY_SET_OPT_USEASLIST);
Radek Krejcid33273d2018-10-25 14:55:52 +0200938
Radek Krejci6d6e4e42018-10-29 13:28:19 +0100939finish_parsing:
Radek Krejci0bcdaed2019-01-10 10:21:34 +0100940 /* resolve imports */
941 mod->parsed->parsing = 1;
942 LY_ARRAY_FOR(mod->parsed->imports, u) {
943 imp = &mod->parsed->imports[u];
944 if (!imp->module && lysp_load_module(ctx, imp->name, imp->rev[0] ? imp->rev : NULL, 0, 0, &imp->module)) {
945 goto error_ctx;
Radek Krejci086c7132018-10-26 15:29:04 +0200946 }
Radek Krejci0bcdaed2019-01-10 10:21:34 +0100947 /* check for importing the same module twice */
Radek Krejci7eb54ba2020-05-18 16:30:04 +0200948 for (v = 0; v < u; ++v) {
949 if (imp->module == mod->parsed->imports[v].module) {
Radek Krejci0bcdaed2019-01-10 10:21:34 +0100950 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE, "Single revision of the module \"%s\" referred twice.", imp->name);
Radek Krejcibbe09a92018-11-08 09:36:54 +0100951 goto error_ctx;
Radek Krejci086c7132018-10-26 15:29:04 +0200952 }
953 }
Radek Krejcid33273d2018-10-25 14:55:52 +0200954 }
Radek Krejci0bcdaed2019-01-10 10:21:34 +0100955 LY_ARRAY_FOR(mod->parsed->includes, u) {
956 inc = &mod->parsed->includes[u];
Michal Vaskob36053d2020-03-26 15:49:30 +0100957 if (!inc->submodule && lysp_load_submodule(pctx, mod->parsed, inc)) {
Radek Krejci0bcdaed2019-01-10 10:21:34 +0100958 goto error_ctx;
959 }
Radek Krejci0af46292019-01-11 16:02:31 +0100960 if (!mod->implemented) {
Michal Vasko5fe75f12020-03-02 13:52:37 +0100961 /* pre-compile features of the submodule */
Radek Krejci327de162019-06-14 12:52:07 +0200962 LY_CHECK_GOTO(lys_feature_precompile(NULL, ctx, mod, inc->submodule->features, &mod->off_features), error);
Radek Krejci0af46292019-01-11 16:02:31 +0100963 }
Radek Krejci0bcdaed2019-01-10 10:21:34 +0100964 }
965 mod->parsed->parsing = 0;
966
Radek Krejci7fc68292019-06-12 13:51:09 +0200967 /* check name collisions - typedefs and TODO groupings */
Michal Vaskob36053d2020-03-26 15:49:30 +0100968 LY_CHECK_GOTO(lysp_check_typedefs(pctx, mod->parsed), error_ctx);
Radek Krejcid33273d2018-10-25 14:55:52 +0200969
David Sedlák1b623122019-08-05 15:27:49 +0200970 if (format == LYS_IN_YANG) {
Michal Vaskob36053d2020-03-26 15:49:30 +0100971 yang_parser_ctx_free(yangctx);
David Sedlák1b623122019-08-05 15:27:49 +0200972 } else {
Michal Vaskob36053d2020-03-26 15:49:30 +0100973 yin_parser_ctx_free(yinctx);
David Sedlák1b623122019-08-05 15:27:49 +0200974 }
Radek Krejci86d106e2018-10-18 09:53:19 +0200975 return mod;
Radek Krejcibbe09a92018-11-08 09:36:54 +0100976
977error_ctx:
978 ly_set_rm(&ctx->list, mod, NULL);
979error:
980 lys_module_free(mod, NULL);
Michal Vaskob36053d2020-03-26 15:49:30 +0100981 ly_set_erase(&pctx->tpdfs_nodes, NULL);
David Sedlák1b623122019-08-05 15:27:49 +0200982 if (format == LYS_IN_YANG) {
Michal Vaskob36053d2020-03-26 15:49:30 +0100983 yang_parser_ctx_free(yangctx);
David Sedlák1b623122019-08-05 15:27:49 +0200984 } else {
Michal Vaskob36053d2020-03-26 15:49:30 +0100985 yin_parser_ctx_free(yinctx);
David Sedlák1b623122019-08-05 15:27:49 +0200986 }
987
Radek Krejcibbe09a92018-11-08 09:36:54 +0100988 return NULL;
Radek Krejci86d106e2018-10-18 09:53:19 +0200989}
990
Radek Krejcid14e9692018-11-01 11:00:37 +0100991API struct lys_module *
Radek Krejci86d106e2018-10-18 09:53:19 +0200992lys_parse_mem(struct ly_ctx *ctx, const char *data, LYS_INFORMAT format)
993{
Radek Krejci096235c2019-01-11 11:12:19 +0100994 struct lys_module *mod;
995
996 mod = lys_parse_mem_module(ctx, data, format, 1, NULL, NULL);
997 LY_CHECK_RET(!mod, NULL);
998
999 if (lys_compile(mod, 0)) {
1000 ly_set_rm(&ctx->list, mod, NULL);
1001 lys_module_free(mod, NULL);
1002 return NULL;
1003 }
1004 return mod;
Radek Krejci86d106e2018-10-18 09:53:19 +02001005}
1006
1007static void
1008lys_parse_set_filename(struct ly_ctx *ctx, const char **filename, int fd)
1009{
Radek Krejci65639b92018-11-27 10:51:37 +01001010 char path[PATH_MAX];
Radek Krejci86d106e2018-10-18 09:53:19 +02001011
1012#ifdef __APPLE__
1013 if (fcntl(fd, F_GETPATH, path) != -1) {
1014 *filename = lydict_insert(ctx, path, 0);
1015 }
1016#else
Radek Krejci65639b92018-11-27 10:51:37 +01001017 int len;
1018 char proc_path[32];
1019
Radek Krejci86d106e2018-10-18 09:53:19 +02001020 /* get URI if there is /proc */
1021 sprintf(proc_path, "/proc/self/fd/%d", fd);
1022 if ((len = readlink(proc_path, path, PATH_MAX - 1)) > 0) {
1023 *filename = lydict_insert(ctx, path, len);
1024 }
1025#endif
1026}
1027
Radek Krejci0bcdaed2019-01-10 10:21:34 +01001028void *
Radek Krejcie7b95092019-05-15 11:03:07 +02001029lys_parse_fd_(struct ly_ctx *ctx, int fd, LYS_INFORMAT format, int implement, struct lys_parser_ctx *main_ctx,
Michal Vaskob36053d2020-03-26 15:49:30 +01001030 lys_custom_check custom_check, void *check_data)
Radek Krejci86d106e2018-10-18 09:53:19 +02001031{
Radek Krejci0bcdaed2019-01-10 10:21:34 +01001032 void *result;
1033 struct lys_module *mod = NULL;
1034 struct lysp_submodule *submod = NULL;
Radek Krejci86d106e2018-10-18 09:53:19 +02001035 size_t length;
1036 char *addr;
1037
1038 LY_CHECK_ARG_RET(ctx, ctx, NULL);
1039 if (fd < 0) {
1040 LOGARG(ctx, fd);
1041 return NULL;
1042 }
1043
1044 LY_CHECK_RET(ly_mmap(ctx, fd, &length, (void **)&addr), NULL);
1045 if (!addr) {
1046 LOGERR(ctx, LY_EINVAL, "Empty schema file.");
1047 return NULL;
1048 }
1049
Radek Krejci0bcdaed2019-01-10 10:21:34 +01001050 if (main_ctx) {
1051 result = submod = lys_parse_mem_submodule(ctx, addr, format, main_ctx, custom_check, check_data);
1052 } else {
1053 result = mod = lys_parse_mem_module(ctx, addr, format, implement, custom_check, check_data);
Radek Krejci096235c2019-01-11 11:12:19 +01001054 if (mod && implement && lys_compile(mod, 0)) {
1055 ly_set_rm(&ctx->list, mod, NULL);
1056 lys_module_free(mod, NULL);
1057 result = mod = NULL;
1058 }
Radek Krejci0bcdaed2019-01-10 10:21:34 +01001059 }
Radek Krejci86d106e2018-10-18 09:53:19 +02001060 ly_munmap(addr, length);
1061
Radek Krejci0bcdaed2019-01-10 10:21:34 +01001062 if (mod && !mod->filepath) {
1063 lys_parse_set_filename(ctx, &mod->filepath, fd);
1064 } else if (submod && !submod->filepath) {
1065 lys_parse_set_filename(ctx, &submod->filepath, fd);
Radek Krejci86d106e2018-10-18 09:53:19 +02001066 }
1067
Radek Krejci0bcdaed2019-01-10 10:21:34 +01001068 return result;
1069}
1070
1071struct lys_module *
Michal Vaskob36053d2020-03-26 15:49:30 +01001072lys_parse_fd_module(struct ly_ctx *ctx, int fd, LYS_INFORMAT format, int implement, lys_custom_check custom_check,
Radek Krejci0bcdaed2019-01-10 10:21:34 +01001073 void *check_data)
1074{
1075 return (struct lys_module*)lys_parse_fd_(ctx, fd, format, implement, NULL, custom_check, check_data);
1076}
1077
1078struct lysp_submodule *
Radek Krejcie7b95092019-05-15 11:03:07 +02001079lys_parse_fd_submodule(struct ly_ctx *ctx, int fd, LYS_INFORMAT format, struct lys_parser_ctx *main_ctx,
Michal Vaskob36053d2020-03-26 15:49:30 +01001080 lys_custom_check custom_check, void *check_data)
Radek Krejci0bcdaed2019-01-10 10:21:34 +01001081{
1082 assert(main_ctx);
1083 return (struct lysp_submodule*)lys_parse_fd_(ctx, fd, format, 0, main_ctx, custom_check, check_data);
Radek Krejci86d106e2018-10-18 09:53:19 +02001084}
1085
Radek Krejcid14e9692018-11-01 11:00:37 +01001086API struct lys_module *
Radek Krejci86d106e2018-10-18 09:53:19 +02001087lys_parse_fd(struct ly_ctx *ctx, int fd, LYS_INFORMAT format)
1088{
Radek Krejci0bcdaed2019-01-10 10:21:34 +01001089 return lys_parse_fd_module(ctx, fd, format, 1, NULL, NULL);
Radek Krejci86d106e2018-10-18 09:53:19 +02001090}
1091
Radek Krejcid33273d2018-10-25 14:55:52 +02001092struct lys_module *
Radek Krejci0bcdaed2019-01-10 10:21:34 +01001093lys_parse_path_(struct ly_ctx *ctx, const char *path, LYS_INFORMAT format, int implement,
Michal Vaskob36053d2020-03-26 15:49:30 +01001094 lys_custom_check custom_check, void *check_data)
Radek Krejci86d106e2018-10-18 09:53:19 +02001095{
1096 int fd;
Radek Krejcid33273d2018-10-25 14:55:52 +02001097 struct lys_module *mod;
Radek Krejci86d106e2018-10-18 09:53:19 +02001098 const char *rev, *dot, *filename;
1099 size_t len;
1100
1101 LY_CHECK_ARG_RET(ctx, ctx, path, NULL);
1102
1103 fd = open(path, O_RDONLY);
1104 LY_CHECK_ERR_RET(fd == -1, LOGERR(ctx, LY_ESYS, "Opening file \"%s\" failed (%s).", path, strerror(errno)), NULL);
1105
Radek Krejci0bcdaed2019-01-10 10:21:34 +01001106 mod = lys_parse_fd_module(ctx, fd, format, implement, custom_check, check_data);
Radek Krejci86d106e2018-10-18 09:53:19 +02001107 close(fd);
1108 LY_CHECK_RET(!mod, NULL);
1109
1110 /* check that name and revision match filename */
1111 filename = strrchr(path, '/');
1112 if (!filename) {
1113 filename = path;
1114 } else {
1115 filename++;
1116 }
1117 rev = strchr(filename, '@');
1118 dot = strrchr(filename, '.');
1119
1120 /* name */
Radek Krejci0bcdaed2019-01-10 10:21:34 +01001121 len = strlen(mod->name);
1122 if (strncmp(filename, mod->name, len) ||
Radek Krejci86d106e2018-10-18 09:53:19 +02001123 ((rev && rev != &filename[len]) || (!rev && dot != &filename[len]))) {
Radek Krejci0bcdaed2019-01-10 10:21:34 +01001124 LOGWRN(ctx, "File name \"%s\" does not match module name \"%s\".", filename, mod->name);
Radek Krejci86d106e2018-10-18 09:53:19 +02001125 }
1126 if (rev) {
1127 len = dot - ++rev;
Radek Krejcib7db73a2018-10-24 14:18:40 +02001128 if (!mod->parsed->revs || len != 10 || strncmp(mod->parsed->revs[0].date, rev, len)) {
Radek Krejci86d106e2018-10-18 09:53:19 +02001129 LOGWRN(ctx, "File name \"%s\" does not match module revision \"%s\".", filename,
Radek Krejcib7db73a2018-10-24 14:18:40 +02001130 mod->parsed->revs ? mod->parsed->revs[0].date : "none");
Radek Krejci86d106e2018-10-18 09:53:19 +02001131 }
1132 }
1133
Radek Krejci0bcdaed2019-01-10 10:21:34 +01001134 if (!mod->filepath) {
Radek Krejci86d106e2018-10-18 09:53:19 +02001135 /* store URI */
1136 char rpath[PATH_MAX];
1137 if (realpath(path, rpath) != NULL) {
Radek Krejci0bcdaed2019-01-10 10:21:34 +01001138 mod->filepath = lydict_insert(ctx, rpath, 0);
Radek Krejci86d106e2018-10-18 09:53:19 +02001139 } else {
Radek Krejci0bcdaed2019-01-10 10:21:34 +01001140 mod->filepath = lydict_insert(ctx, path, 0);
Radek Krejci86d106e2018-10-18 09:53:19 +02001141 }
1142 }
1143
1144 return mod;
1145}
1146
Radek Krejcid14e9692018-11-01 11:00:37 +01001147API struct lys_module *
Radek Krejcid33273d2018-10-25 14:55:52 +02001148lys_parse_path(struct ly_ctx *ctx, const char *path, LYS_INFORMAT format)
1149{
Radek Krejci0bcdaed2019-01-10 10:21:34 +01001150 return lys_parse_path_(ctx, path, format, 1, NULL, NULL);
Radek Krejcid33273d2018-10-25 14:55:52 +02001151}
1152
1153API LY_ERR
1154lys_search_localfile(const char * const *searchpaths, int cwd, const char *name, const char *revision,
1155 char **localfile, LYS_INFORMAT *format)
1156{
1157 size_t len, flen, match_len = 0, dir_len;
1158 int i, implicit_cwd = 0, ret = EXIT_FAILURE;
1159 char *wd, *wn = NULL;
1160 DIR *dir = NULL;
1161 struct dirent *file;
1162 char *match_name = NULL;
1163 LYS_INFORMAT format_aux, match_format = 0;
1164 struct ly_set *dirs;
1165 struct stat st;
1166
1167 LY_CHECK_ARG_RET(NULL, localfile, LY_EINVAL);
1168
1169 /* start to fill the dir fifo with the context's search path (if set)
1170 * and the current working directory */
1171 dirs = ly_set_new();
1172 if (!dirs) {
1173 LOGMEM(NULL);
1174 return EXIT_FAILURE;
1175 }
1176
1177 len = strlen(name);
1178 if (cwd) {
1179 wd = get_current_dir_name();
1180 if (!wd) {
1181 LOGMEM(NULL);
1182 goto cleanup;
1183 } else {
1184 /* add implicit current working directory (./) to be searched,
1185 * this directory is not searched recursively */
1186 if (ly_set_add(dirs, wd, 0) == -1) {
1187 goto cleanup;
1188 }
1189 implicit_cwd = 1;
1190 }
1191 }
1192 if (searchpaths) {
1193 for (i = 0; searchpaths[i]; i++) {
1194 /* check for duplicities with the implicit current working directory */
1195 if (implicit_cwd && !strcmp(dirs->objs[0], searchpaths[i])) {
1196 implicit_cwd = 0;
1197 continue;
1198 }
1199 wd = strdup(searchpaths[i]);
1200 if (!wd) {
1201 LOGMEM(NULL);
1202 goto cleanup;
1203 } else if (ly_set_add(dirs, wd, 0) == -1) {
1204 goto cleanup;
1205 }
1206 }
1207 }
1208 wd = NULL;
1209
1210 /* start searching */
1211 while (dirs->count) {
1212 free(wd);
1213 free(wn); wn = NULL;
1214
1215 dirs->count--;
1216 wd = (char *)dirs->objs[dirs->count];
1217 dirs->objs[dirs->count] = NULL;
1218 LOGVRB("Searching for \"%s\" in %s.", name, wd);
1219
1220 if (dir) {
1221 closedir(dir);
1222 }
1223 dir = opendir(wd);
1224 dir_len = strlen(wd);
1225 if (!dir) {
1226 LOGWRN(NULL, "Unable to open directory \"%s\" for searching (sub)modules (%s).", wd, strerror(errno));
1227 } else {
1228 while ((file = readdir(dir))) {
1229 if (!strcmp(".", file->d_name) || !strcmp("..", file->d_name)) {
1230 /* skip . and .. */
1231 continue;
1232 }
1233 free(wn);
1234 if (asprintf(&wn, "%s/%s", wd, file->d_name) == -1) {
1235 LOGMEM(NULL);
1236 goto cleanup;
1237 }
1238 if (stat(wn, &st) == -1) {
1239 LOGWRN(NULL, "Unable to get information about \"%s\" file in \"%s\" when searching for (sub)modules (%s)",
1240 file->d_name, wd, strerror(errno));
1241 continue;
1242 }
1243 if (S_ISDIR(st.st_mode) && (dirs->count || !implicit_cwd)) {
1244 /* we have another subdirectory in searchpath to explore,
1245 * subdirectories are not taken into account in current working dir (dirs->set.g[0]) */
1246 if (ly_set_add(dirs, wn, 0) == -1) {
1247 goto cleanup;
1248 }
1249 /* continue with the next item in current directory */
1250 wn = NULL;
1251 continue;
1252 } else if (!S_ISREG(st.st_mode)) {
1253 /* not a regular file (note that we see the target of symlinks instead of symlinks */
1254 continue;
1255 }
1256
1257 /* here we know that the item is a file which can contain a module */
1258 if (strncmp(name, file->d_name, len) ||
1259 (file->d_name[len] != '.' && file->d_name[len] != '@')) {
1260 /* different filename than the module we search for */
1261 continue;
1262 }
1263
1264 /* get type according to filename suffix */
1265 flen = strlen(file->d_name);
Radek Krejcied5acc52019-04-25 15:57:04 +02001266 if (!strcmp(&file->d_name[flen - 5], ".yang")) {
Radek Krejcid33273d2018-10-25 14:55:52 +02001267 format_aux = LYS_IN_YANG;
Radek Krejcied5acc52019-04-25 15:57:04 +02001268 /* TODO YIN parser
1269 } else if (!strcmp(&file->d_name[flen - 4], ".yin")) {
1270 format_aux = LYS_IN_YIN;
1271 */
Radek Krejcid33273d2018-10-25 14:55:52 +02001272 } else {
1273 /* not supportde suffix/file format */
1274 continue;
1275 }
1276
1277 if (revision) {
1278 /* we look for the specific revision, try to get it from the filename */
1279 if (file->d_name[len] == '@') {
1280 /* check revision from the filename */
1281 if (strncmp(revision, &file->d_name[len + 1], strlen(revision))) {
1282 /* another revision */
1283 continue;
1284 } else {
1285 /* exact revision */
1286 free(match_name);
1287 match_name = wn;
1288 wn = NULL;
1289 match_len = dir_len + 1 + len;
1290 match_format = format_aux;
1291 goto success;
1292 }
1293 } else {
1294 /* continue trying to find exact revision match, use this only if not found */
1295 free(match_name);
1296 match_name = wn;
1297 wn = NULL;
1298 match_len = dir_len + 1 +len;
1299 match_format = format_aux;
1300 continue;
1301 }
1302 } else {
1303 /* remember the revision and try to find the newest one */
1304 if (match_name) {
1305 if (file->d_name[len] != '@' ||
1306 lysp_check_date(NULL, &file->d_name[len + 1], flen - (format_aux == LYS_IN_YANG ? 5 : 4) - len - 1, NULL)) {
1307 continue;
1308 } else if (match_name[match_len] == '@' &&
1309 (strncmp(&match_name[match_len + 1], &file->d_name[len + 1], LY_REV_SIZE - 1) >= 0)) {
1310 continue;
1311 }
1312 free(match_name);
1313 }
1314
1315 match_name = wn;
1316 wn = NULL;
1317 match_len = dir_len + 1 + len;
1318 match_format = format_aux;
1319 continue;
1320 }
1321 }
1322 }
1323 }
1324
1325success:
1326 (*localfile) = match_name;
1327 match_name = NULL;
1328 if (format) {
1329 (*format) = match_format;
1330 }
1331 ret = EXIT_SUCCESS;
1332
1333cleanup:
1334 free(wn);
1335 free(wd);
1336 if (dir) {
1337 closedir(dir);
1338 }
1339 free(match_name);
1340 ly_set_free(dirs, free);
1341
1342 return ret;
1343}
1344