1 /**
2  * @file context.c
3  * @author Radek Krejci <rkrejci@cesnet.cz>
4  * @brief context implementation for libyang
5  *
6  * Copyright (c) 2015 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 #include <pthread.h>
17 #include <stddef.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <unistd.h>
23 #include <limits.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 
27 #include "common.h"
28 #include "context.h"
29 #include "hash_table.h"
30 #include "parser.h"
31 #include "tree_internal.h"
32 #include "resolve.h"
33 
34 /*
35  * counter for references to the extensions plugins (for the number of contexts)
36  * located in extensions.c
37  */
38 extern unsigned int ext_plugins_ref;
39 
40 #define IETF_YANG_METADATA_PATH "../models/ietf-yang-metadata@2016-08-05.h"
41 #define YANG_PATH "../models/yang@2017-02-20.h"
42 #define IETF_INET_TYPES_PATH "../models/ietf-inet-types@2013-07-15.h"
43 #define IETF_YANG_TYPES_PATH "../models/ietf-yang-types@2013-07-15.h"
44 #define IETF_DATASTORES "../models/ietf-datastores@2018-02-14.h"
45 #define IETF_YANG_LIB_PATH "../models/ietf-yang-library@2019-01-04.h"
46 #define IETF_YANG_LIB_REV "2019-01-04"
47 
48 #include IETF_YANG_METADATA_PATH
49 #include YANG_PATH
50 #include IETF_INET_TYPES_PATH
51 #include IETF_YANG_TYPES_PATH
52 #include IETF_DATASTORES
53 #include IETF_YANG_LIB_PATH
54 
55 #define LY_INTERNAL_MODULE_COUNT 6
56 static struct internal_modules_s {
57     const char *name;
58     const char *revision;
59     const char *data;
60     uint8_t implemented;
61     LYS_INFORMAT format;
62 } internal_modules[LY_INTERNAL_MODULE_COUNT] = {
63     {"ietf-yang-metadata", "2016-08-05", (const char*)ietf_yang_metadata_2016_08_05_yin, 0, LYS_IN_YIN},
64     {"yang", "2017-02-20", (const char*)yang_2017_02_20_yin, 1, LYS_IN_YIN},
65     {"ietf-inet-types", "2013-07-15", (const char*)ietf_inet_types_2013_07_15_yin, 0, LYS_IN_YIN},
66     {"ietf-yang-types", "2013-07-15", (const char*)ietf_yang_types_2013_07_15_yin, 0, LYS_IN_YIN},
67     /* ietf-datastores and ietf-yang-library must be right here at the end of the list! */
68     {"ietf-datastores", "2018-02-14", (const char*)ietf_datastores_2018_02_14_yin, 0, LYS_IN_YIN},
69     {"ietf-yang-library", IETF_YANG_LIB_REV, (const char*)ietf_yang_library_2019_01_04_yin, 1, LYS_IN_YIN}
70 };
71 
72 API unsigned int
ly_ctx_internal_modules_count(struct ly_ctx * ctx)73 ly_ctx_internal_modules_count(struct ly_ctx *ctx)
74 {
75     FUN_IN;
76 
77     if (!ctx) {
78         return 0;
79     }
80     return ctx->internal_module_count;
81 }
82 
83 API struct ly_ctx *
ly_ctx_new(const char * search_dir,int options)84 ly_ctx_new(const char *search_dir, int options)
85 {
86     FUN_IN;
87 
88     struct ly_ctx *ctx = NULL;
89     struct lys_module *module;
90     char *search_dir_list;
91     char *sep, *dir;
92     int rc = EXIT_SUCCESS;
93     int i;
94 
95     ctx = calloc(1, sizeof *ctx);
96     LY_CHECK_ERR_RETURN(!ctx, LOGMEM(NULL), NULL);
97 
98     /* dictionary */
99     lydict_init(&ctx->dict);
100 
101     /* plugins */
102     ly_load_plugins();
103 
104     /* initialize thread-specific key */
105     if (pthread_key_create(&ctx->errlist_key, ly_err_free) != 0) {
106         LOGERR(NULL, LY_ESYS, "pthread_key_create() in ly_ctx_new() failed");
107         goto error;
108     }
109 
110     /* models list */
111     ctx->models.list = calloc(16, sizeof *ctx->models.list);
112     LY_CHECK_ERR_RETURN(!ctx->models.list, LOGMEM(NULL); free(ctx), NULL);
113     ctx->models.flags = options;
114     ctx->models.used = 0;
115     ctx->models.size = 16;
116     if (search_dir) {
117         search_dir_list = strdup(search_dir);
118         LY_CHECK_ERR_GOTO(!search_dir_list, LOGMEM(NULL), error);
119 
120         for (dir = search_dir_list; (sep = strchr(dir, ':')) != NULL && rc == EXIT_SUCCESS; dir = sep + 1) {
121             *sep = 0;
122             rc = ly_ctx_set_searchdir(ctx, dir);
123         }
124         if (*dir && rc == EXIT_SUCCESS) {
125             rc = ly_ctx_set_searchdir(ctx, dir);
126         }
127         free(search_dir_list);
128         /* If ly_ctx_set_searchdir() failed, the error is already logged. Just exit */
129         if (rc != EXIT_SUCCESS) {
130             goto error;
131         }
132     }
133     ctx->models.module_set_id = 1;
134 
135     /* load internal modules */
136     if (options & LY_CTX_NOYANGLIBRARY) {
137         ctx->internal_module_count = LY_INTERNAL_MODULE_COUNT - 2;
138     } else {
139         ctx->internal_module_count = LY_INTERNAL_MODULE_COUNT;
140     }
141     for (i = 0; i < ctx->internal_module_count; i++) {
142         module = (struct lys_module *)lys_parse_mem(ctx, internal_modules[i].data, internal_modules[i].format);
143         if (!module) {
144             goto error;
145         }
146         module->implemented = internal_modules[i].implemented;
147     }
148 
149     return ctx;
150 
151 error:
152     /* cleanup */
153     ly_ctx_destroy(ctx, NULL);
154     return NULL;
155 }
156 
157 static int
ly_ctx_new_yl_legacy(struct ly_ctx * ctx,struct lyd_node * yltree)158 ly_ctx_new_yl_legacy(struct ly_ctx *ctx, struct lyd_node *yltree)
159 {
160     unsigned int i, u, imported;
161     struct lyd_node *module, *node;
162     struct ly_set *set;
163     const char *name, *revision;
164     struct ly_set features = {0, 0, {NULL}};
165     const struct lys_module *mod;
166 
167     set = lyd_find_path(yltree, "/ietf-yang-library:yang-library/modules-state/module");
168     if (!set) {
169         return 1;
170     }
171 
172     /* process the data tree */
173     for (i = 0; i < set->number; ++i) {
174         module = set->set.d[i];
175 
176         /* initiate */
177         name = NULL;
178         revision = NULL;
179         ly_set_clean(&features);
180         imported = 0;
181 
182         LY_TREE_FOR(module->child, node) {
183             if (!strcmp(node->schema->name, "name")) {
184                 name = ((struct lyd_node_leaf_list*)node)->value_str;
185             } else if (!strcmp(node->schema->name, "revision")) {
186                 revision = ((struct lyd_node_leaf_list*)node)->value_str;
187             } else if (!strcmp(node->schema->name, "feature")) {
188                 ly_set_add(&features, node, LY_SET_OPT_USEASLIST);
189             } else if (!strcmp(node->schema->name, "conformance-type") &&
190                     ((struct lyd_node_leaf_list*)node)->value.enm->value) {
191                 /* imported module - skip it, it will be loaded as a side effect
192                  * of loading another module */
193                 imported = 1;
194                 break;
195             }
196         }
197 
198         if (imported) {
199             continue;
200         }
201 
202         /* use the gathered data to load the module */
203         mod = ly_ctx_load_module(ctx, name, revision);
204         if (!mod) {
205             LOGERR(ctx, LY_EINVAL, "Unable to load module specified by yang library data.");
206             ly_set_free(set);
207             return 1;
208         }
209 
210         /* set features */
211         for (u = 0; u < features.number; u++) {
212             lys_features_enable(mod, ((struct lyd_node_leaf_list*)features.set.d[u])->value_str);
213         }
214     }
215 
216     ly_set_free(set);
217     return 0;
218 }
219 
220 static struct ly_ctx *
ly_ctx_new_yl_common(const char * search_dir,const char * input,LYD_FORMAT format,int options,struct lyd_node * (* parser_func)(struct ly_ctx *,const char *,LYD_FORMAT,int,...))221 ly_ctx_new_yl_common(const char *search_dir, const char *input, LYD_FORMAT format, int options,
222                      struct lyd_node* (*parser_func)(struct ly_ctx*, const char*, LYD_FORMAT, int,...))
223 {
224     unsigned int i, u;
225     struct lyd_node *module, *node;
226     const char *name, *revision;
227     struct ly_set features = {0, 0, {NULL}};
228     const struct lys_module *mod;
229     struct lyd_node *yltree = NULL;
230     struct ly_ctx *ctx = NULL;
231     struct ly_set *set = NULL;
232     int err = 0;
233 
234     /* create empty (with internal modules including ietf-yang-library) context */
235     ctx = ly_ctx_new(search_dir, options);
236     if (!ctx) {
237         goto error;
238     }
239 
240     /* parse yang library data tree */
241     yltree = parser_func(ctx, input, format, LYD_OPT_DATA, NULL);
242     if (!yltree) {
243         goto error;
244     }
245 
246     set = lyd_find_path(yltree, "/ietf-yang-library:yang-library/module-set[1]/module");
247     if (!set) {
248         goto error;
249     }
250 
251     if (set->number == 0) {
252         /* perhaps a legacy data tree? */
253         if (ly_ctx_new_yl_legacy(ctx, yltree)) {
254             goto error;
255         }
256     } else {
257         /* process the data tree */
258         for (i = 0; i < set->number; ++i) {
259             module = set->set.d[i];
260 
261             /* initiate */
262             name = NULL;
263             revision = NULL;
264             ly_set_clean(&features);
265 
266             LY_TREE_FOR(module->child, node) {
267                 if (!strcmp(node->schema->name, "name")) {
268                     name = ((struct lyd_node_leaf_list*)node)->value_str;
269                 } else if (!strcmp(node->schema->name, "revision")) {
270                     revision = ((struct lyd_node_leaf_list*)node)->value_str;
271                 } else if (!strcmp(node->schema->name, "feature")) {
272                     ly_set_add(&features, node, LY_SET_OPT_USEASLIST);
273                 }
274             }
275 
276             /* use the gathered data to load the module */
277             mod = ly_ctx_load_module(ctx, name, revision);
278             if (!mod) {
279                 LOGERR(NULL, LY_EINVAL, "Unable to load module specified by yang library data.");
280                 goto error;
281             }
282 
283             /* set features */
284             for (u = 0; u < features.number; u++) {
285                 lys_features_enable(mod, ((struct lyd_node_leaf_list*)features.set.d[u])->value_str);
286             }
287         }
288     }
289 
290     if (0) {
291         /* skip context destroy in case of success */
292 error:
293         err = 1;
294     }
295 
296     /* cleanup */
297     if (yltree) {
298         /* yang library data tree */
299         lyd_free_withsiblings(yltree);
300     }
301     if (set) {
302         ly_set_free(set);
303     }
304     if (err) {
305         ly_ctx_destroy(ctx, NULL);
306         ctx = NULL;
307     }
308 
309     return ctx;
310 }
311 
312 API struct ly_ctx *
ly_ctx_new_ylpath(const char * search_dir,const char * path,LYD_FORMAT format,int options)313 ly_ctx_new_ylpath(const char *search_dir, const char *path, LYD_FORMAT format, int options)
314 {
315     FUN_IN;
316 
317     return ly_ctx_new_yl_common(search_dir, path, format, options, lyd_parse_path);
318 }
319 
320 API struct ly_ctx *
ly_ctx_new_ylmem(const char * search_dir,const char * data,LYD_FORMAT format,int options)321 ly_ctx_new_ylmem(const char *search_dir, const char *data, LYD_FORMAT format, int options)
322 {
323     FUN_IN;
324 
325     return ly_ctx_new_yl_common(search_dir, data, format, options, lyd_parse_mem);
326 }
327 
328 static void
ly_ctx_set_option(struct ly_ctx * ctx,int options)329 ly_ctx_set_option(struct ly_ctx *ctx, int options)
330 {
331     if (!ctx) {
332         return;
333     }
334 
335     ctx->models.flags |= options;
336 }
337 
338 static void
ly_ctx_unset_option(struct ly_ctx * ctx,int options)339 ly_ctx_unset_option(struct ly_ctx *ctx, int options)
340 {
341     if (!ctx) {
342         return;
343     }
344 
345     ctx->models.flags &= ~options;
346 }
347 
348 API void
ly_ctx_set_disable_searchdirs(struct ly_ctx * ctx)349 ly_ctx_set_disable_searchdirs(struct ly_ctx *ctx)
350 {
351     FUN_IN;
352 
353     ly_ctx_set_option(ctx, LY_CTX_DISABLE_SEARCHDIRS);
354 }
355 
356 API void
ly_ctx_unset_disable_searchdirs(struct ly_ctx * ctx)357 ly_ctx_unset_disable_searchdirs(struct ly_ctx *ctx)
358 {
359     FUN_IN;
360 
361     ly_ctx_unset_option(ctx, LY_CTX_DISABLE_SEARCHDIRS);
362 }
363 
364 API void
ly_ctx_set_disable_searchdir_cwd(struct ly_ctx * ctx)365 ly_ctx_set_disable_searchdir_cwd(struct ly_ctx *ctx)
366 {
367     FUN_IN;
368 
369     ly_ctx_set_option(ctx, LY_CTX_DISABLE_SEARCHDIR_CWD);
370 }
371 
372 API void
ly_ctx_unset_disable_searchdir_cwd(struct ly_ctx * ctx)373 ly_ctx_unset_disable_searchdir_cwd(struct ly_ctx *ctx)
374 {
375     FUN_IN;
376 
377     ly_ctx_unset_option(ctx, LY_CTX_DISABLE_SEARCHDIR_CWD);
378 }
379 
380 API void
ly_ctx_set_prefer_searchdirs(struct ly_ctx * ctx)381 ly_ctx_set_prefer_searchdirs(struct ly_ctx *ctx)
382 {
383     FUN_IN;
384 
385     ly_ctx_set_option(ctx, LY_CTX_PREFER_SEARCHDIRS);
386 }
387 
388 API void
ly_ctx_unset_prefer_searchdirs(struct ly_ctx * ctx)389 ly_ctx_unset_prefer_searchdirs(struct ly_ctx *ctx)
390 {
391     FUN_IN;
392 
393     ly_ctx_unset_option(ctx, LY_CTX_PREFER_SEARCHDIRS);
394 }
395 
396 API void
ly_ctx_set_allimplemented(struct ly_ctx * ctx)397 ly_ctx_set_allimplemented(struct ly_ctx *ctx)
398 {
399     FUN_IN;
400 
401     ly_ctx_set_option(ctx, LY_CTX_ALLIMPLEMENTED);
402 }
403 
404 API void
ly_ctx_unset_allimplemented(struct ly_ctx * ctx)405 ly_ctx_unset_allimplemented(struct ly_ctx *ctx)
406 {
407     FUN_IN;
408 
409     ly_ctx_unset_option(ctx, LY_CTX_ALLIMPLEMENTED);
410 }
411 
412 API void
ly_ctx_set_trusted(struct ly_ctx * ctx)413 ly_ctx_set_trusted(struct ly_ctx *ctx)
414 {
415     FUN_IN;
416 
417     ly_ctx_set_option(ctx, LY_CTX_TRUSTED);
418 }
419 
420 API void
ly_ctx_unset_trusted(struct ly_ctx * ctx)421 ly_ctx_unset_trusted(struct ly_ctx *ctx)
422 {
423     FUN_IN;
424 
425     ly_ctx_unset_option(ctx, LY_CTX_TRUSTED);
426 }
427 
428 API int
ly_ctx_get_options(struct ly_ctx * ctx)429 ly_ctx_get_options(struct ly_ctx *ctx)
430 {
431     FUN_IN;
432 
433     return ctx->models.flags;
434 }
435 
436 API int
ly_ctx_set_searchdir(struct ly_ctx * ctx,const char * search_dir)437 ly_ctx_set_searchdir(struct ly_ctx *ctx, const char *search_dir)
438 {
439     FUN_IN;
440 
441     char *new_dir = NULL;
442     int index = 0;
443     void *r;
444     int rc = EXIT_FAILURE;
445 
446     if (!ctx) {
447         LOGARG;
448         return EXIT_FAILURE;
449     }
450 
451     if (search_dir) {
452         if (access(search_dir, R_OK | X_OK)) {
453             LOGERR(ctx, LY_ESYS, "Unable to use search directory \"%s\" (%s)",
454                    search_dir, strerror(errno));
455             return EXIT_FAILURE;
456         }
457 
458         new_dir = realpath(search_dir, NULL);
459         LY_CHECK_ERR_GOTO(!new_dir, LOGERR(ctx, LY_ESYS, "realpath() call failed (%s).", strerror(errno)), cleanup);
460         if (!ctx->models.search_paths) {
461             ctx->models.search_paths = malloc(2 * sizeof *ctx->models.search_paths);
462             LY_CHECK_ERR_GOTO(!ctx->models.search_paths, LOGMEM(ctx), cleanup);
463             index = 0;
464         } else {
465             for (index = 0; ctx->models.search_paths[index]; index++) {
466                 /* check for duplicities */
467                 if (!strcmp(new_dir, ctx->models.search_paths[index])) {
468                     /* path is already present */
469                     goto success;
470                 }
471             }
472             r = realloc(ctx->models.search_paths, (index + 2) * sizeof *ctx->models.search_paths);
473             LY_CHECK_ERR_GOTO(!r, LOGMEM(ctx), cleanup);
474             ctx->models.search_paths = r;
475         }
476         ctx->models.search_paths[index] = new_dir;
477         new_dir = NULL;
478         ctx->models.search_paths[index + 1] = NULL;
479 
480 success:
481         rc = EXIT_SUCCESS;
482     } else {
483         /* consider that no change is not actually an error */
484         return EXIT_SUCCESS;
485     }
486 
487 cleanup:
488     free(new_dir);
489     return rc;
490 }
491 
492 API const char * const *
ly_ctx_get_searchdirs(const struct ly_ctx * ctx)493 ly_ctx_get_searchdirs(const struct ly_ctx *ctx)
494 {
495     FUN_IN;
496 
497     if (!ctx) {
498         LOGARG;
499         return NULL;
500     }
501     return (const char * const *)ctx->models.search_paths;
502 }
503 
504 API void
ly_ctx_unset_searchdirs(struct ly_ctx * ctx,int index)505 ly_ctx_unset_searchdirs(struct ly_ctx *ctx, int index)
506 {
507     FUN_IN;
508 
509     int i;
510 
511     if (!ctx->models.search_paths) {
512         return;
513     }
514 
515     for (i = 0; ctx->models.search_paths[i]; i++) {
516         if (index < 0 || index == i) {
517             free(ctx->models.search_paths[i]);
518             ctx->models.search_paths[i] = NULL;
519         } else if (i > index) {
520             ctx->models.search_paths[i - 1] = ctx->models.search_paths[i];
521             ctx->models.search_paths[i] = NULL;
522         }
523     }
524     if (index < 0 || !ctx->models.search_paths[0]) {
525         free(ctx->models.search_paths);
526         ctx->models.search_paths = NULL;
527     }
528 }
529 
530 API void
ly_ctx_destroy(struct ly_ctx * ctx,void (* private_destructor)(const struct lys_node * node,void * priv))531 ly_ctx_destroy(struct ly_ctx *ctx, void (*private_destructor)(const struct lys_node *node, void *priv))
532 {
533     FUN_IN;
534 
535     int i;
536 
537     if (!ctx) {
538         return;
539     }
540 
541     /* models list */
542     for (; ctx->models.used > 0; ctx->models.used--) {
543         /* remove the applied deviations and augments */
544         lys_sub_module_remove_devs_augs(ctx->models.list[ctx->models.used - 1]);
545         /* remove the module */
546         lys_free(ctx->models.list[ctx->models.used - 1], private_destructor, 1, 0);
547     }
548     if (ctx->models.search_paths) {
549         for(i = 0; ctx->models.search_paths[i]; i++) {
550             free(ctx->models.search_paths[i]);
551         }
552         free(ctx->models.search_paths);
553     }
554     free(ctx->models.list);
555 
556     /* clean the error list */
557     ly_err_clean(ctx, 0);
558     pthread_key_delete(ctx->errlist_key);
559 
560     /* dictionary */
561     lydict_clean(&ctx->dict);
562 
563     /* plugins - will be removed only if this is the last context */
564     ly_clean_plugins();
565 
566     free(ctx);
567 }
568 
569 API const struct lys_submodule *
ly_ctx_get_submodule2(const struct lys_module * main_module,const char * submodule)570 ly_ctx_get_submodule2(const struct lys_module *main_module, const char *submodule)
571 {
572     FUN_IN;
573 
574     const struct lys_submodule *result;
575     int i;
576 
577     if (!main_module || !submodule) {
578         LOGARG;
579         return NULL;
580     }
581 
582     /* search in submodules list */
583     for (i = 0; i < main_module->inc_size; i++) {
584         result = main_module->inc[i].submodule;
585         if (ly_strequal(submodule, result->name, 0)) {
586             return result;
587         }
588 
589         /* in YANG 1.1 all the submodules must be included in the main module, so we are done.
590          * YANG 1.0 allows (is unclear about denying it) to include a submodule only in another submodule
591          * but when libyang parses such a module it adds the include into the main module so we are also done.
592          */
593     }
594 
595     return NULL;
596 }
597 
598 API const struct lys_submodule *
ly_ctx_get_submodule(const struct ly_ctx * ctx,const char * module,const char * revision,const char * submodule,const char * sub_revision)599 ly_ctx_get_submodule(const struct ly_ctx *ctx, const char *module, const char *revision, const char *submodule,
600                      const char *sub_revision)
601 {
602     FUN_IN;
603 
604     const struct lys_module *mainmod;
605     const struct lys_submodule *ret = NULL, *submod;
606     uint32_t idx = 0;
607 
608     if (!ctx || !submodule || (revision && !module)) {
609         LOGARG;
610         return NULL;
611     }
612 
613     while ((mainmod = ly_ctx_get_module_iter(ctx, &idx))) {
614         if (module && strcmp(mainmod->name, module)) {
615             /* main module name does not match */
616             continue;
617         }
618 
619         if (revision && (!mainmod->rev || strcmp(revision, mainmod->rev[0].date))) {
620             /* main module revision does not match */
621             continue;
622         }
623 
624         submod = ly_ctx_get_submodule2(mainmod, submodule);
625         if (!submod) {
626             continue;
627         }
628 
629         if (!sub_revision) {
630             /* store only if newer */
631             if (ret) {
632                 if (submod->rev && (!ret->rev || (strcmp(submod->rev[0].date, ret->rev[0].date) > 0))) {
633                     ret = submod;
634                 }
635             } else {
636                 ret = submod;
637             }
638         } else {
639             /* store only if revision matches, we are done if it does */
640             if (!submod->rev) {
641                 continue;
642             } else if (!strcmp(sub_revision, submod->rev[0].date)) {
643                 ret = submod;
644                 break;
645             }
646         }
647     }
648 
649     return ret;
650 }
651 
652 static const struct lys_module *
ly_ctx_get_module_by(const struct ly_ctx * ctx,const char * key,size_t key_len,int offset,const char * revision,int with_disabled,int implemented)653 ly_ctx_get_module_by(const struct ly_ctx *ctx, const char *key, size_t key_len, int offset, const char *revision,
654                      int with_disabled, int implemented)
655 {
656     int i;
657     char *val;
658     struct lys_module *result = NULL;
659 
660     if (!ctx || !key) {
661         LOGARG;
662         return NULL;
663     }
664 
665     for (i = 0; i < ctx->models.used; i++) {
666         if (!with_disabled && ctx->models.list[i]->disabled) {
667             /* skip the disabled modules */
668             continue;
669         }
670         /* use offset to get address of the pointer to string (char**), remember that offset is in
671          * bytes, so we have to cast the pointer to the module to (char*), finally, we want to have
672          * string not the pointer to string
673          */
674         val = *(char **)(((char *)ctx->models.list[i]) + offset);
675         if (!ctx->models.list[i] || (!key_len && strcmp(key, val)) || (key_len && (strncmp(key, val, key_len) || val[key_len]))) {
676             continue;
677         }
678 
679         if (!revision) {
680             /* compare revisons and remember the newest one */
681             if (result) {
682                 if (!ctx->models.list[i]->rev_size) {
683                     /* the current have no revision, keep the previous with some revision */
684                     continue;
685                 }
686                 if (result->rev_size && strcmp(ctx->models.list[i]->rev[0].date, result->rev[0].date) < 0) {
687                     /* the previous found matching module has a newer revision */
688                     continue;
689                 }
690             }
691             if (implemented) {
692                 if (ctx->models.list[i]->implemented) {
693                     /* we have the implemented revision */
694                     result = ctx->models.list[i];
695                     break;
696                 } else {
697                     /* do not remember the result, we are supposed to return the implemented revision
698                      * not the newest one */
699                     continue;
700                 }
701             }
702 
703             /* remember the current match and search for newer version */
704             result = ctx->models.list[i];
705         } else {
706             if (ctx->models.list[i]->rev_size && !strcmp(revision, ctx->models.list[i]->rev[0].date)) {
707                 /* matching revision */
708                 result = ctx->models.list[i];
709                 break;
710             }
711         }
712     }
713 
714     return result;
715 
716 }
717 
718 API const struct lys_module *
ly_ctx_get_module_by_ns(const struct ly_ctx * ctx,const char * ns,const char * revision,int implemented)719 ly_ctx_get_module_by_ns(const struct ly_ctx *ctx, const char *ns, const char *revision, int implemented)
720 {
721     FUN_IN;
722 
723     return ly_ctx_get_module_by(ctx, ns, 0, offsetof(struct lys_module, ns), revision, 0, implemented);
724 }
725 
726 API const struct lys_module *
ly_ctx_get_module(const struct ly_ctx * ctx,const char * name,const char * revision,int implemented)727 ly_ctx_get_module(const struct ly_ctx *ctx, const char *name, const char *revision, int implemented)
728 {
729     FUN_IN;
730 
731     return ly_ctx_get_module_by(ctx, name, 0, offsetof(struct lys_module, name), revision, 0, implemented);
732 }
733 
734 const struct lys_module *
ly_ctx_nget_module(const struct ly_ctx * ctx,const char * name,size_t name_len,const char * revision,int implemented)735 ly_ctx_nget_module(const struct ly_ctx *ctx, const char *name, size_t name_len, const char *revision, int implemented)
736 {
737     return ly_ctx_get_module_by(ctx, name, name_len, offsetof(struct lys_module, name), revision, 0, implemented);
738 }
739 
740 API const struct lys_module *
ly_ctx_get_module_older(const struct ly_ctx * ctx,const struct lys_module * module)741 ly_ctx_get_module_older(const struct ly_ctx *ctx, const struct lys_module *module)
742 {
743     FUN_IN;
744 
745     int i;
746     const struct lys_module *result = NULL, *iter;
747 
748     if (!ctx || !module || !module->rev_size) {
749         LOGARG;
750         return NULL;
751     }
752 
753 
754     for (i = 0; i < ctx->models.used; i++) {
755         iter = ctx->models.list[i];
756         if (iter->disabled) {
757             /* skip the disabled modules */
758             continue;
759         }
760         if (iter == module || !iter->rev_size) {
761             /* iter is the module itself or iter has no revision */
762             continue;
763         }
764         if (!ly_strequal(module->name, iter->name, 0)) {
765             /* different module */
766             continue;
767         }
768         if (strcmp(iter->rev[0].date, module->rev[0].date) < 0) {
769             /* iter is older than module */
770             if (result) {
771                 if (strcmp(iter->rev[0].date, result->rev[0].date) > 0) {
772                     /* iter is newer than current result */
773                     result = iter;
774                 }
775             } else {
776                 result = iter;
777             }
778         }
779     }
780 
781     return result;
782 }
783 
784 API void
ly_ctx_set_module_imp_clb(struct ly_ctx * ctx,ly_module_imp_clb clb,void * user_data)785 ly_ctx_set_module_imp_clb(struct ly_ctx *ctx, ly_module_imp_clb clb, void *user_data)
786 {
787     FUN_IN;
788 
789     if (!ctx) {
790         LOGARG;
791         return;
792     }
793 
794     ctx->imp_clb = clb;
795     ctx->imp_clb_data = user_data;
796 }
797 
798 API ly_module_imp_clb
ly_ctx_get_module_imp_clb(const struct ly_ctx * ctx,void ** user_data)799 ly_ctx_get_module_imp_clb(const struct ly_ctx *ctx, void **user_data)
800 {
801     FUN_IN;
802 
803     if (!ctx) {
804         LOGARG;
805         return NULL;
806     }
807 
808     if (user_data) {
809         *user_data = ctx->imp_clb_data;
810     }
811     return ctx->imp_clb;
812 }
813 
814 API void
ly_ctx_set_module_data_clb(struct ly_ctx * ctx,ly_module_data_clb clb,void * user_data)815 ly_ctx_set_module_data_clb(struct ly_ctx *ctx, ly_module_data_clb clb, void *user_data)
816 {
817     FUN_IN;
818 
819     if (!ctx) {
820         LOGARG;
821         return;
822     }
823 
824     ctx->data_clb = clb;
825     ctx->data_clb_data = user_data;
826 }
827 
828 API ly_module_data_clb
ly_ctx_get_module_data_clb(const struct ly_ctx * ctx,void ** user_data)829 ly_ctx_get_module_data_clb(const struct ly_ctx *ctx, void **user_data)
830 {
831     FUN_IN;
832 
833     if (!ctx) {
834         LOGARG;
835         return NULL;
836     }
837 
838     if (user_data) {
839         *user_data = ctx->data_clb_data;
840     }
841     return ctx->data_clb;
842 }
843 
844 #ifdef LY_ENABLED_LYD_PRIV
845 
846 API void
ly_ctx_set_priv_dup_clb(struct ly_ctx * ctx,void * (* priv_dup_clb)(const void * priv))847 ly_ctx_set_priv_dup_clb(struct ly_ctx *ctx, void *(*priv_dup_clb)(const void *priv))
848 {
849     FUN_IN;
850 
851     ctx->priv_dup_clb = priv_dup_clb;
852 }
853 
854 #endif
855 
856 /* if module is !NULL, then the function searches for submodule */
857 static struct lys_module *
ly_ctx_load_localfile(struct ly_ctx * ctx,struct lys_module * module,const char * name,const char * revision,int implement,struct unres_schema * unres)858 ly_ctx_load_localfile(struct ly_ctx *ctx, struct lys_module *module, const char *name, const char *revision,
859                 int implement, struct unres_schema *unres)
860 {
861     size_t len;
862     int fd, i;
863     char *filepath = NULL, *dot, *rev, *filename;
864     LYS_INFORMAT format;
865     struct lys_module *result = NULL;
866 
867     if (lys_search_localfile(ly_ctx_get_searchdirs(ctx), !(ctx->models.flags & LY_CTX_DISABLE_SEARCHDIR_CWD), name, revision,
868                              &filepath, &format)) {
869         goto cleanup;
870     } else if (!filepath) {
871         if (!module && !revision) {
872             /* otherwise the module would be already taken from the context */
873             result = (struct lys_module *)ly_ctx_get_module(ctx, name, NULL, 0);
874         }
875         if (!result) {
876             LOGERR(ctx, LY_ESYS, "Data model \"%s\" not found.", name);
877         }
878         return result;
879     }
880 
881     LOGVRB("Loading schema from \"%s\" file.", filepath);
882 
883     /* cut the format for now */
884     dot = strrchr(filepath, '.');
885     dot[1] = '\0';
886 
887     /* check that the same file was not already loaded - it make sense only in case of loading the newest revision,
888      * search also in disabled module - if the matching module is disabled, it will be enabled instead of loading it */
889     if (!revision) {
890         for (i = 0; i < ctx->models.used; ++i) {
891             if (ctx->models.list[i]->filepath && !strcmp(name, ctx->models.list[i]->name)
892                     && !strncmp(filepath, ctx->models.list[i]->filepath, strlen(filepath))) {
893                 result = ctx->models.list[i];
894                 if (implement && !result->implemented) {
895                     /* make it implemented now */
896                     if (lys_set_implemented(result)) {
897                         result = NULL;
898                     }
899                 } else if (result->disabled) {
900                     lys_set_enabled(result);
901                 }
902 
903                 goto cleanup;
904             }
905         }
906     }
907 
908     /* add the format back */
909     dot[1] = 'y';
910 
911     /* open the file */
912     fd = open(filepath, O_RDONLY);
913     if (fd < 0) {
914         LOGERR(ctx, LY_ESYS, "Unable to open data model file \"%s\" (%s).",
915                filepath, strerror(errno));
916         goto cleanup;
917     }
918 
919     if (module) {
920         result = (struct lys_module *)lys_sub_parse_fd(module, fd, format, unres);
921     } else {
922         result = (struct lys_module *)lys_parse_fd_(ctx, fd, format, revision, implement);
923     }
924     close(fd);
925 
926     if (!result) {
927         goto cleanup;
928     }
929 
930     /* check that name and revision match filename */
931     filename = strrchr(filepath, '/');
932     if (!filename) {
933         filename = filepath;
934     } else {
935         filename++;
936     }
937     rev = strchr(filename, '@');
938     /* name */
939     len = strlen(result->name);
940     if (strncmp(filename, result->name, len) ||
941             ((rev && rev != &filename[len]) || (!rev && dot != &filename[len]))) {
942         LOGWRN(ctx, "File name \"%s\" does not match module name \"%s\".", filename, result->name);
943     }
944     if (rev) {
945         len = dot - ++rev;
946         if (!result->rev_size || len != 10 || strncmp(result->rev[0].date, rev, len)) {
947             LOGWRN(ctx, "File name \"%s\" does not match module revision \"%s\".", filename,
948                    result->rev_size ? result->rev[0].date : "none");
949         }
950     }
951 
952     if (!result->filepath) {
953         char rpath[PATH_MAX];
954         if (realpath(filepath, rpath) != NULL) {
955             result->filepath = lydict_insert(ctx, rpath, 0);
956         } else {
957             result->filepath = lydict_insert(ctx, filepath, 0);
958         }
959     }
960 
961     /* success */
962 cleanup:
963     free(filepath);
964     return result;
965 }
966 
967 static struct lys_module *
ly_ctx_load_sub_module_clb(struct ly_ctx * ctx,struct lys_module * module,const char * name,const char * revision,int implement,struct unres_schema * unres)968 ly_ctx_load_sub_module_clb(struct ly_ctx *ctx, struct lys_module *module, const char *name, const char *revision,
969                            int implement, struct unres_schema *unres)
970 {
971     struct lys_module *mod = NULL;
972     const char *module_data = NULL;
973     LYS_INFORMAT format = LYS_IN_UNKNOWN;
974     void (*module_data_free)(void *module_data, void *user_data) = NULL;
975 
976     ly_errno = LY_SUCCESS;
977     if (module) {
978         mod = lys_main_module(module);
979         module_data = ctx->imp_clb(mod->name, (mod->rev_size ? mod->rev[0].date : NULL), name, revision, ctx->imp_clb_data, &format, &module_data_free);
980     } else {
981         module_data = ctx->imp_clb(name, revision, NULL, NULL, ctx->imp_clb_data, &format, &module_data_free);
982     }
983     if (!module_data && (ly_errno != LY_SUCCESS)) {
984         /* callback encountered an error, do not change it */
985         LOGERR(ctx, ly_errno, "User module retrieval callback failed!");
986         return NULL;
987     }
988 
989     if (module_data) {
990         /* we got the module from the callback */
991         if (module) {
992             mod = (struct lys_module *)lys_sub_parse_mem(module, module_data, format, unres);
993         } else {
994             mod = (struct lys_module *)lys_parse_mem_(ctx, module_data, format, NULL, 0, implement);
995         }
996 
997         if (module_data_free) {
998             module_data_free((char *)module_data, ctx->imp_clb_data);
999         }
1000     }
1001 
1002     return mod;
1003 }
1004 
1005 const struct lys_module *
ly_ctx_load_sub_module(struct ly_ctx * ctx,struct lys_module * module,const char * name,const char * revision,int implement,struct unres_schema * unres)1006 ly_ctx_load_sub_module(struct ly_ctx *ctx, struct lys_module *module, const char *name, const char *revision,
1007                        int implement, struct unres_schema *unres)
1008 {
1009     struct lys_module *mod = NULL, *latest_mod = NULL;
1010     int i;
1011 
1012     if (!module) {
1013         /* try to get the schema from the context (with or without revision),
1014          * include the disabled modules in the search to avoid their duplication,
1015          * they are enabled by the subsequent call to lys_set_implemented() */
1016         for (i = 0, mod = NULL; i < ctx->models.used; i++) {
1017             mod = ctx->models.list[i]; /* shortcut */
1018             if (ly_strequal(name, mod->name, 0)) {
1019                 /* first remember latest module if no other is found */
1020                 if (!latest_mod) {
1021                     latest_mod = mod;
1022                 } else {
1023                     if (mod->rev_size && latest_mod->rev_size && (strcmp(mod->rev[0].date, latest_mod->rev[0].date) > 0)) {
1024                         /* newer revision */
1025                         latest_mod = mod;
1026                     }
1027                 }
1028 
1029                 if (revision && mod->rev_size && !strcmp(revision, mod->rev[0].date)) {
1030                     /* the specific revision was already loaded */
1031                     break;
1032                 } else if (!revision && mod->latest_revision) {
1033                     /* the latest revision of this module was already loaded */
1034                     break;
1035                 } else if (implement && mod->implemented && !revision) {
1036                     /* we are not able to implement another module, so consider this module as the latest one */
1037                     break;
1038                 }
1039             }
1040             mod = NULL;
1041         }
1042         if (mod) {
1043             /* module must be enabled */
1044             if (mod->disabled) {
1045                 lys_set_enabled(mod);
1046             }
1047             /* module is supposed to be implemented */
1048             if (implement && lys_set_implemented(mod)) {
1049                 /* the schema cannot be implemented */
1050                 mod = NULL;
1051             }
1052             return mod;
1053         }
1054     }
1055 
1056     /* module is not yet in context, use the user callback or try to find the schema on our own */
1057     if (ctx->imp_clb && !(ctx->models.flags & LY_CTX_PREFER_SEARCHDIRS)) {
1058 search_clb:
1059         if (ctx->imp_clb) {
1060             mod = ly_ctx_load_sub_module_clb(ctx, module, name, revision, implement, unres);
1061         }
1062         if (!mod && !(ctx->models.flags & LY_CTX_PREFER_SEARCHDIRS)) {
1063             goto search_file;
1064         }
1065     } else {
1066 search_file:
1067         if (!(ctx->models.flags & LY_CTX_DISABLE_SEARCHDIRS)) {
1068             /* module was not received from the callback or there is no callback set */
1069             mod = ly_ctx_load_localfile(ctx, module, name, revision, implement, unres);
1070         }
1071         if (!mod && (ctx->models.flags & LY_CTX_PREFER_SEARCHDIRS)) {
1072             goto search_clb;
1073         }
1074     }
1075 
1076     if (mod && !revision && latest_mod && mod->rev_size && latest_mod->rev_size
1077                 && (strcmp(mod->rev[0].date, latest_mod->rev[0].date) < 0)) {
1078         /* the found module has older revision as the one already in context and we are looking for the latest one, free it */
1079         lys_free(mod, NULL, 1, 1);
1080         mod = NULL;
1081     }
1082 
1083     if (!mod && latest_mod) {
1084         /* consider the latest mod found as the latest available */
1085         mod = latest_mod;
1086     }
1087 
1088 #ifdef LY_ENABLED_LATEST_REVISIONS
1089     if (!revision && mod) {
1090         /* module is the latest revision found */
1091         mod->latest_revision = 1;
1092     }
1093 #endif
1094 
1095     return mod;
1096 }
1097 
1098 API const struct lys_module *
ly_ctx_load_module(struct ly_ctx * ctx,const char * name,const char * revision)1099 ly_ctx_load_module(struct ly_ctx *ctx, const char *name, const char *revision)
1100 {
1101     FUN_IN;
1102 
1103     if (!ctx || !name) {
1104         LOGARG;
1105         return NULL;
1106     }
1107 
1108     return ly_ctx_load_sub_module(ctx, NULL, name, revision && revision[0] ? revision : NULL, 1, NULL);
1109 }
1110 
1111 /*
1112  * mods - set of removed modules, if NULL all modules are supposed to be removed so any backlink is invalid
1113  */
1114 static void
ctx_modules_undo_backlinks(struct ly_ctx * ctx,struct ly_set * mods)1115 ctx_modules_undo_backlinks(struct ly_ctx *ctx, struct ly_set *mods)
1116 {
1117     int o;
1118     uint8_t j;
1119     unsigned int u, v;
1120     struct lys_module *mod;
1121 
1122     /* maintain backlinks (start with internal ietf-yang-library which have leafs as possible targets of leafrefs */
1123     for (o = ctx->internal_module_count - 1; o < ctx->models.used; o++) {
1124         mod = ctx->models.list[o]; /* shortcut */
1125 
1126         /* 1) features */
1127         for (j = 0; j < mod->features_size; j++) {
1128             if (!mod->features[j].depfeatures) {
1129                 continue;
1130             }
1131             for (v = 0; v < mod->features[j].depfeatures->number; v++) {
1132                 if (!mods || ly_set_contains(mods, ((struct lys_feature *)mod->features[j].depfeatures->set.g[v])->module) != -1) {
1133                     /* depending feature is in module to remove */
1134                     ly_set_rm_index(mod->features[j].depfeatures, v);
1135                     v--;
1136                 }
1137             }
1138             if (!mod->features[j].depfeatures->number) {
1139                 /* all backlinks removed */
1140                 ly_set_free(mod->features[j].depfeatures);
1141                 mod->features[j].depfeatures = NULL;
1142             }
1143         }
1144 
1145         /* 2) identities */
1146         for (u = 0; u < mod->ident_size; u++) {
1147             if (!mod->ident[u].der) {
1148                 continue;
1149             }
1150             for (v = 0; v < mod->ident[u].der->number; v++) {
1151                 if (!mods || ly_set_contains(mods, ((struct lys_ident *)mod->ident[u].der->set.g[v])->module) != -1) {
1152                     /* derived identity is in module to remove */
1153                     ly_set_rm_index(mod->ident[u].der, v);
1154                     v--;
1155                 }
1156             }
1157             if (!mod->ident[u].der->number) {
1158                 /* all backlinks removed */
1159                 ly_set_free(mod->ident[u].der);
1160                 mod->ident[u].der = NULL;
1161             }
1162         }
1163     }
1164 }
1165 
1166 static int
ctx_modules_redo_backlinks(struct ly_set * mods)1167 ctx_modules_redo_backlinks(struct ly_set *mods)
1168 {
1169     unsigned int i, j, k, s;
1170     struct lys_module *mod;
1171     struct lys_feature *feat;
1172 
1173     for (i = 0; i < mods->number; ++i) {
1174         mod = (struct lys_module *)mods->set.g[i]; /* shortcut */
1175 
1176         /* identities */
1177         if (mod->implemented) {
1178             for (j = 0; j < mod->ident_size; j++) {
1179                 for (k = 0; k < mod->ident[j].base_size; k++) {
1180                     resolve_identity_backlink_update(&mod->ident[j], mod->ident[j].base[k]);
1181                 }
1182             }
1183         }
1184 
1185         /* features */
1186         for (j = 0; j < mod->features_size; j++) {
1187             for (k = 0; k < mod->features[j].iffeature_size; k++) {
1188                 resolve_iffeature_getsizes(&mod->features[j].iffeature[k], NULL, &s);
1189                 while (s--) {
1190                     feat = mod->features[j].iffeature[k].features[s]; /* shortcut */
1191                     if (!feat->depfeatures) {
1192                         feat->depfeatures = ly_set_new();
1193                     }
1194                     ly_set_add(feat->depfeatures, &mod->features[j], LY_SET_OPT_USEASLIST);
1195                 }
1196             }
1197         }
1198     }
1199 
1200     return 0;
1201 }
1202 
1203 API int
lys_set_disabled(const struct lys_module * module)1204 lys_set_disabled(const struct lys_module *module)
1205 {
1206     FUN_IN;
1207 
1208     struct ly_ctx *ctx; /* shortcut */
1209     struct lys_module *mod;
1210     struct ly_set *mods;
1211     uint8_t j, imported;
1212     int i, o;
1213     unsigned int u, v;
1214 
1215     if (!module) {
1216         LOGARG;
1217         return EXIT_FAILURE;
1218     } else if (module->disabled) {
1219         /* already disabled module */
1220         return EXIT_SUCCESS;
1221     }
1222     mod = (struct lys_module *)module;
1223     ctx = mod->ctx;
1224 
1225     /* avoid disabling internal modules */
1226     for (i = 0; i < ctx->internal_module_count; i++) {
1227         if (mod == ctx->models.list[i]) {
1228             LOGERR(ctx, LY_EINVAL, "Internal module \"%s\" cannot be disabled.", mod->name);
1229             return EXIT_FAILURE;
1230         }
1231     }
1232 
1233     /* disable the module */
1234     mod->disabled = 1;
1235 
1236     /* get the complete list of modules to disable because of dependencies,
1237      * we are going also to disable all the imported (not implemented) modules
1238      * that are not used in any other module */
1239     mods = ly_set_new();
1240     ly_set_add(mods, mod, 0);
1241 checkdependency:
1242     for (i = ctx->internal_module_count; i < ctx->models.used; i++) {
1243         mod = ctx->models.list[i]; /* shortcut */
1244         if (mod->disabled) {
1245             /* skip the already disabled modules */
1246             continue;
1247         }
1248 
1249         /* check depndency of imported modules */
1250         for (j = 0; j < mod->imp_size; j++) {
1251             for (u = 0; u < mods->number; u++) {
1252                 if (mod->imp[j].module == mods->set.g[u]) {
1253                     /* module is importing some module to disable, so it must be also disabled */
1254                     mod->disabled = 1;
1255                     ly_set_add(mods, mod, 0);
1256                     /* we have to start again because some of the already checked modules can
1257                      * depend on the one we have just decided to disable */
1258                     goto checkdependency;
1259                 }
1260             }
1261         }
1262         /* check if the imported module is used in any module supposed to be kept */
1263         if (!mod->implemented) {
1264             imported = 0;
1265             for (o = ctx->internal_module_count; o < ctx->models.used; o++) {
1266                 if (ctx->models.list[o]->disabled) {
1267                     /* skip modules already disabled */
1268                     continue;
1269                 }
1270                 for (j = 0; j < ctx->models.list[o]->imp_size; j++) {
1271                     if (ctx->models.list[o]->imp[j].module == mod) {
1272                         /* the module is used in some other module not yet selected to be disabled */
1273                         imported = 1;
1274                         goto imported;
1275                     }
1276                 }
1277             }
1278 imported:
1279             if (!imported) {
1280                 /* module is not implemented and neither imported by any other module in context
1281                  * which is supposed to be kept enabled after this operation, so we are going to disable also
1282                  * this module */
1283                 mod->disabled = 1;
1284                 ly_set_add(mods, mod, 0);
1285                 /* we have to start again, this time not because other module can depend on this one
1286                  * (we know that there is no such module), but because the module can import module
1287                  * that could became useless. If there are no imports, we can continue */
1288                 if (mod->imp_size) {
1289                     goto checkdependency;
1290                 }
1291             }
1292         }
1293     }
1294 
1295     /* before removing applied deviations, augments and updating leafrefs, we have to enable the modules
1296      * to disable to allow all that operations */
1297     for (u = 0; u < mods->number; u++) {
1298         ((struct lys_module *)mods->set.g[u])->disabled = 0;
1299     }
1300 
1301     /* maintain backlinks (start with internal ietf-yang-library which have leafs as possible targets of leafrefs */
1302     ctx_modules_undo_backlinks(ctx, mods);
1303 
1304     /* remove the applied deviations and augments */
1305     u = mods->number;
1306     while (u--) {
1307         lys_sub_module_remove_devs_augs((struct lys_module *)mods->set.g[u]);
1308     }
1309 
1310     /* now again disable the modules to disable and disable also all its submodules */
1311     for (u = 0; u < mods->number; u++) {
1312         mod = (struct lys_module *)mods->set.g[u];
1313         mod->disabled = 1;
1314         for (v = 0; v < mod->inc_size; v++) {
1315             mod->inc[v].submodule->disabled = 1;
1316         }
1317     }
1318 
1319     /* free the set */
1320     ly_set_free(mods);
1321 
1322     /* update the module-set-id */
1323     ctx->models.module_set_id++;
1324 
1325     return EXIT_SUCCESS;
1326 }
1327 
1328 static void
lys_set_enabled_(struct ly_set * mods,struct lys_module * mod)1329 lys_set_enabled_(struct ly_set *mods, struct lys_module *mod)
1330 {
1331     unsigned int i;
1332 
1333     ly_set_add(mods, mod, 0);
1334     mod->disabled = 0;
1335 
1336     for (i = 0; i < mod->inc_size; i++) {
1337         mod->inc[i].submodule->disabled = 0;
1338     }
1339 
1340     /* go recursively */
1341     for (i = 0; i < mod->imp_size; i++) {
1342         if (!mod->imp[i].module->disabled) {
1343             continue;
1344         }
1345 
1346         lys_set_enabled_(mods, mod->imp[i].module);
1347     }
1348 }
1349 
1350 API int
lys_set_enabled(const struct lys_module * module)1351 lys_set_enabled(const struct lys_module *module)
1352 {
1353     FUN_IN;
1354 
1355     struct ly_ctx *ctx; /* shortcut */
1356     struct lys_module *mod;
1357     struct ly_set *mods, *disabled;
1358     int i;
1359     unsigned int u, v, w;
1360 
1361     if (!module) {
1362         LOGARG;
1363         return EXIT_FAILURE;
1364     } else if (!module->disabled) {
1365         /* already enabled module */
1366         return EXIT_SUCCESS;
1367     }
1368     mod = (struct lys_module *)module;
1369     ctx = mod->ctx;
1370 
1371     /* avoid disabling internal modules */
1372     for (i = 0; i < ctx->internal_module_count; i++) {
1373         if (mod == ctx->models.list[i]) {
1374             LOGERR(ctx, LY_EINVAL, "Internal module \"%s\" cannot be removed.", mod->name);
1375             return EXIT_FAILURE;
1376         }
1377     }
1378 
1379     mods = ly_set_new();
1380     disabled = ly_set_new();
1381 
1382     /* enable the module, including its dependencies */
1383     lys_set_enabled_(mods, mod);
1384 
1385     /* we will go through the all disabled modules in the context, if the module has no dependency (import)
1386      * that is still disabled AND at least one of its imported module is from the set we are enabling now,
1387      * it is going to be also enabled. This way we try to revert everething that was possibly done by
1388      * lys_set_disabled(). */
1389 checkdependency:
1390     for (i = ctx->internal_module_count; i < ctx->models.used; i++) {
1391         mod = ctx->models.list[i]; /* shortcut */
1392         if (!mod->disabled || ly_set_contains(disabled, mod) != -1) {
1393             /* skip the enabled modules */
1394             continue;
1395         }
1396 
1397         /* check imported modules */
1398         for (u = 0; u < mod->imp_size; u++) {
1399             if (mod->imp[u].module->disabled) {
1400                 /* it has disabled dependency so it must stay disabled */
1401                 break;
1402             }
1403         }
1404         if (u < mod->imp_size) {
1405             /* it has disabled dependency, continue with the next module in the context */
1406             continue;
1407         }
1408 
1409         /* get know if at least one of the imported modules is being enabled this time */
1410         for (u = 0; u < mod->imp_size; u++) {
1411             for (v = 0; v < mods->number; v++) {
1412                 if (mod->imp[u].module == mods->set.g[v]) {
1413                     /* yes, it is, so they are connected and we are going to enable it as well,
1414                      * it is not necessary to call recursive lys_set_enable_() because we already
1415                      * know that there is no disabled import to enable */
1416                     mod->disabled = 0;
1417                     ly_set_add(mods, mod, 0);
1418                     for (w = 0; w < mod->inc_size; w++) {
1419                         mod->inc[w].submodule->disabled = 0;
1420                     }
1421                     /* we have to start again because some of the already checked modules can
1422                      * depend on the one we have just decided to enable */
1423                     goto checkdependency;
1424                 }
1425             }
1426         }
1427 
1428         /* this module is disabled, but it does not depend on any other disabled module and none
1429          * of its imports was not enabled in this call. No future enabling of the disabled module
1430          * will change this so we can remember the module and skip it next time we will have to go
1431          * through the all context because of the checkdependency goto.
1432          */
1433         ly_set_add(disabled, mod, 0);
1434     }
1435 
1436     /* maintain backlinks */
1437     ctx_modules_redo_backlinks(mods);
1438 
1439     /* re-apply the deviations and augments */
1440     for (v = 0; v < mods->number; v++) {
1441         if (((struct lys_module *)mods->set.g[v])->implemented) {
1442             lys_sub_module_apply_devs_augs((struct lys_module *)mods->set.g[v]);
1443         }
1444     }
1445 
1446     /* free the sets */
1447     ly_set_free(mods);
1448     ly_set_free(disabled);
1449 
1450     /* update the module-set-id */
1451     ctx->models.module_set_id++;
1452 
1453     return EXIT_SUCCESS;
1454 }
1455 
1456 API int
ly_ctx_remove_module(const struct lys_module * module,void (* private_destructor)(const struct lys_node * node,void * priv))1457 ly_ctx_remove_module(const struct lys_module *module,
1458                      void (*private_destructor)(const struct lys_node *node, void *priv))
1459 {
1460     FUN_IN;
1461 
1462     struct ly_ctx *ctx; /* shortcut */
1463     struct lys_module *mod = NULL;
1464     struct ly_set *mods;
1465     uint8_t j, imported;
1466     int i, o;
1467     unsigned int u;
1468 
1469     if (!module) {
1470         LOGARG;
1471         return EXIT_FAILURE;
1472     }
1473 
1474     mod = (struct lys_module *)module;
1475     ctx = mod->ctx;
1476 
1477     /* avoid removing internal modules ... */
1478     for (i = 0; i < ctx->internal_module_count; i++) {
1479         if (mod == ctx->models.list[i]) {
1480             LOGERR(ctx, LY_EINVAL, "Internal module \"%s\" cannot be removed.", mod->name);
1481             return EXIT_FAILURE;
1482         }
1483     }
1484     /* ... and hide the module from the further processing of the context modules list */
1485     for (i = ctx->internal_module_count; i < ctx->models.used; i++) {
1486         if (mod == ctx->models.list[i]) {
1487             ctx->models.list[i] = NULL;
1488             break;
1489         }
1490     }
1491 
1492     /* get the complete list of modules to remove because of dependencies,
1493      * we are going also to remove all the imported (not implemented) modules
1494      * that are not used in any other module */
1495     mods = ly_set_new();
1496     ly_set_add(mods, mod, 0);
1497 checkdependency:
1498     for (i = ctx->internal_module_count; i < ctx->models.used; i++) {
1499         mod = ctx->models.list[i]; /* shortcut */
1500         if (!mod) {
1501             /* skip modules already selected for removing */
1502             continue;
1503         }
1504 
1505         /* check depndency of imported modules */
1506         for (j = 0; j < mod->imp_size; j++) {
1507             for (u = 0; u < mods->number; u++) {
1508                 if (mod->imp[j].module == mods->set.g[u]) {
1509                     /* module is importing some module to remove, so it must be also removed */
1510                     ly_set_add(mods, mod, 0);
1511                     ctx->models.list[i] = NULL;
1512                     /* we have to start again because some of the already checked modules can
1513                      * depend on the one we have just decided to remove */
1514                     goto checkdependency;
1515                 }
1516             }
1517         }
1518         /* check if the imported module is used in any module supposed to be kept */
1519         if (!mod->implemented) {
1520             imported = 0;
1521             for (o = ctx->internal_module_count; o < ctx->models.used; o++) {
1522                 if (!ctx->models.list[o]) {
1523                     /* skip modules already selected for removing */
1524                     continue;
1525                 }
1526                 for (j = 0; j < ctx->models.list[o]->imp_size; j++) {
1527                     if (ctx->models.list[o]->imp[j].module == mod) {
1528                         /* the module is used in some other module not yet selected to be deleted */
1529                         imported = 1;
1530                         goto imported;
1531                     }
1532                 }
1533             }
1534 imported:
1535             if (!imported) {
1536                 /* module is not implemented and neither imported by any other module in context
1537                  * which is supposed to be kept after this operation, so we are going to remove also
1538                  * this useless module */
1539                 ly_set_add(mods, mod, 0);
1540                 ctx->models.list[i] = NULL;
1541                 /* we have to start again, this time not because other module can depend on this one
1542                  * (we know that there is no such module), but because the module can import module
1543                  * that could became useless. If there are no imports, we can continue */
1544                 if (mod->imp_size) {
1545                     goto checkdependency;
1546                 }
1547             }
1548         }
1549     }
1550 
1551 
1552     /* consolidate the modules list */
1553     for (i = o = ctx->internal_module_count; i < ctx->models.used; ++i, ++o) {
1554         if (!ctx->models.list[o]) {
1555             /* the current output cell is empty, move here a non-empty input cell */
1556             while (!ctx->models.list[i]) {
1557                 ++i;
1558                 if (i == ctx->models.used) {
1559                     break;
1560                 }
1561             }
1562             if (i == ctx->models.used) {
1563                 break;
1564             }
1565 
1566             ctx->models.list[o] = ctx->models.list[i];
1567             ctx->models.list[i] = NULL;
1568         }
1569     }
1570     /* get the last used cell to get know the number of used */
1571     while (!ctx->models.list[o]) {
1572         o--;
1573     }
1574     ctx->models.used = o + 1;
1575     ctx->models.module_set_id++;
1576 
1577     /* maintain backlinks (start with internal ietf-yang-library which have leafs as possible targets of leafrefs */
1578     ctx_modules_undo_backlinks(ctx, mods);
1579 
1580     /* free the modules */
1581     for (u = 0; u < mods->number; u++) {
1582         /* remove the applied deviations and augments */
1583         lys_sub_module_remove_devs_augs((struct lys_module *)mods->set.g[u]);
1584         /* remove the module */
1585         lys_free((struct lys_module *)mods->set.g[u], private_destructor, 1, 0);
1586     }
1587     ly_set_free(mods);
1588 
1589     return EXIT_SUCCESS;
1590 }
1591 
1592 API void
ly_ctx_clean(struct ly_ctx * ctx,void (* private_destructor)(const struct lys_node * node,void * priv))1593 ly_ctx_clean(struct ly_ctx *ctx, void (*private_destructor)(const struct lys_node *node, void *priv))
1594 {
1595     FUN_IN;
1596 
1597     if (!ctx) {
1598         return;
1599     }
1600 
1601     /* models list */
1602     for (; ctx->models.used > ctx->internal_module_count; ctx->models.used--) {
1603         /* remove the applied deviations and augments */
1604         lys_sub_module_remove_devs_augs(ctx->models.list[ctx->models.used - 1]);
1605         /* remove the module */
1606         lys_free(ctx->models.list[ctx->models.used - 1], private_destructor, 1, 0);
1607         /* clean it for safer future use */
1608         ctx->models.list[ctx->models.used - 1] = NULL;
1609     }
1610     ctx->models.module_set_id++;
1611 
1612     /* maintain backlinks (actually done only with ietf-yang-library since its leafs can be target of leafref) */
1613     ctx_modules_undo_backlinks(ctx, NULL);
1614 }
1615 
1616 API const struct lys_module *
ly_ctx_get_module_iter(const struct ly_ctx * ctx,uint32_t * idx)1617 ly_ctx_get_module_iter(const struct ly_ctx *ctx, uint32_t *idx)
1618 {
1619     FUN_IN;
1620 
1621     if (!ctx || !idx) {
1622         LOGARG;
1623         return NULL;
1624     }
1625 
1626     for ( ; *idx < (unsigned)ctx->models.used; (*idx)++) {
1627         if (!ctx->models.list[(*idx)]->disabled) {
1628             return ctx->models.list[(*idx)++];
1629         }
1630     }
1631 
1632     return NULL;
1633 }
1634 
1635 API const struct lys_module *
ly_ctx_get_disabled_module_iter(const struct ly_ctx * ctx,uint32_t * idx)1636 ly_ctx_get_disabled_module_iter(const struct ly_ctx *ctx, uint32_t *idx)
1637 {
1638     FUN_IN;
1639 
1640     if (!ctx || !idx) {
1641         LOGARG;
1642         return NULL;
1643     }
1644 
1645     for ( ; *idx < (unsigned)ctx->models.used; (*idx)++) {
1646         if (ctx->models.list[(*idx)]->disabled) {
1647             return ctx->models.list[(*idx)++];
1648         }
1649     }
1650 
1651     return NULL;
1652 }
1653 
1654 static int
ylib_feature(struct lyd_node * parent,struct lys_module * cur_mod)1655 ylib_feature(struct lyd_node *parent, struct lys_module *cur_mod)
1656 {
1657     int i, j;
1658 
1659     /* module features */
1660     for (i = 0; i < cur_mod->features_size; ++i) {
1661         if (!(cur_mod->features[i].flags & LYS_FENABLED)) {
1662             continue;
1663         }
1664 
1665         if (!lyd_new_leaf(parent, NULL, "feature", cur_mod->features[i].name)) {
1666             return EXIT_FAILURE;
1667         }
1668     }
1669 
1670     /* submodule features */
1671     for (i = 0; i < cur_mod->inc_size && cur_mod->inc[i].submodule; ++i) {
1672         for (j = 0; j < cur_mod->inc[i].submodule->features_size; ++j) {
1673             if (!(cur_mod->inc[i].submodule->features[j].flags & LYS_FENABLED)) {
1674                 continue;
1675             }
1676 
1677             if (!lyd_new_leaf(parent, NULL, "feature", cur_mod->inc[i].submodule->features[j].name)) {
1678                 return EXIT_FAILURE;
1679             }
1680         }
1681     }
1682 
1683     return EXIT_SUCCESS;
1684 }
1685 
1686 static int
ylib_deviation(struct lyd_node * parent,struct lys_module * cur_mod,int bis)1687 ylib_deviation(struct lyd_node *parent, struct lys_module *cur_mod, int bis)
1688 {
1689     uint32_t i = 0, j;
1690     const struct lys_module *mod;
1691     struct lyd_node *cont;
1692     const char *ptr;
1693 
1694     if (cur_mod->deviated) {
1695         while ((mod = ly_ctx_get_module_iter(cur_mod->ctx, &i))) {
1696             if (mod == cur_mod) {
1697                 continue;
1698             }
1699 
1700             for (j = 0; j < mod->deviation_size; ++j) {
1701                 ptr = strstr(mod->deviation[j].target_name, cur_mod->name);
1702                 if (ptr && ptr[strlen(cur_mod->name)] == ':') {
1703                     if (bis) {
1704                         if (!lyd_new_leaf(parent, NULL, "deviation", mod->name)) {
1705                             return EXIT_FAILURE;
1706                         }
1707                     } else {
1708                         cont = lyd_new(parent, NULL, "deviation");
1709                         if (!cont) {
1710                             return EXIT_FAILURE;
1711                         }
1712                         if (!lyd_new_leaf(cont, NULL, "name", mod->name)) {
1713                             return EXIT_FAILURE;
1714                         }
1715                         if (!lyd_new_leaf(cont, NULL, "revision", (mod->rev_size ? mod->rev[0].date : ""))) {
1716                             return EXIT_FAILURE;
1717                         }
1718                     }
1719 
1720                     break;
1721                 }
1722             }
1723         }
1724     }
1725 
1726     return EXIT_SUCCESS;
1727 }
1728 
1729 static int
ylib_submodules(struct lyd_node * parent,struct lys_module * cur_mod,int bis)1730 ylib_submodules(struct lyd_node *parent, struct lys_module *cur_mod, int bis)
1731 {
1732     int i;
1733     char *str;
1734     struct lyd_node *item;
1735 
1736     for (i = 0; i < cur_mod->inc_size && cur_mod->inc[i].submodule; ++i) {
1737         item = lyd_new(parent, NULL, "submodule");
1738         if (!item) {
1739             return EXIT_FAILURE;
1740         }
1741 
1742         if (!lyd_new_leaf(item, NULL, "name", cur_mod->inc[i].submodule->name)) {
1743             return EXIT_FAILURE;
1744         }
1745         if ((!bis || cur_mod->inc[i].submodule->rev_size)
1746                 && !lyd_new_leaf(item, NULL, "revision",
1747                     (cur_mod->inc[i].submodule->rev_size ? cur_mod->inc[i].submodule->rev[0].date : ""))) {
1748             return EXIT_FAILURE;
1749         }
1750         if (cur_mod->inc[i].submodule->filepath) {
1751             if (asprintf(&str, "file://%s", cur_mod->inc[i].submodule->filepath) == -1) {
1752                 LOGMEM(cur_mod->ctx);
1753                 return EXIT_FAILURE;
1754             } else if (!lyd_new_leaf(item, NULL, bis ? "location" : "schema", str)) {
1755                 free(str);
1756                 return EXIT_FAILURE;
1757             }
1758             free(str);
1759         }
1760     }
1761 
1762     return EXIT_SUCCESS;
1763 }
1764 
1765 API uint16_t
ly_ctx_get_module_set_id(const struct ly_ctx * ctx)1766 ly_ctx_get_module_set_id(const struct ly_ctx *ctx)
1767 {
1768     FUN_IN;
1769 
1770     return ctx->models.module_set_id;
1771 }
1772 
1773 API struct lyd_node *
ly_ctx_info(struct ly_ctx * ctx)1774 ly_ctx_info(struct ly_ctx *ctx)
1775 {
1776     FUN_IN;
1777 
1778     int i, bis = 0;
1779     char id[8];
1780     char *str;
1781     const struct lys_module *mod;
1782     struct lyd_node *root, *root_bis = NULL, *cont = NULL, *set_bis = NULL;
1783 
1784     if (!ctx) {
1785         LOGARG;
1786         return NULL;
1787     }
1788 
1789     mod = ly_ctx_get_module(ctx, "ietf-yang-library", NULL, 1);
1790     if (!mod || !mod->data) {
1791         LOGERR(ctx, LY_EINVAL, "ietf-yang-library is not implemented.");
1792         return NULL;
1793     }
1794     if (mod->rev && !strcmp(mod->rev[0].date, "2016-06-21")) {
1795         bis = 0;
1796     } else if (mod->rev && !strcmp(mod->rev[0].date, IETF_YANG_LIB_REV)) {
1797         bis = 1;
1798     } else {
1799         LOGERR(ctx, LY_EINVAL, "Incompatible ietf-yang-library version in context.");
1800         return NULL;
1801     }
1802 
1803     root = lyd_new(NULL, mod, "modules-state");
1804     if (!root) {
1805         return NULL;
1806     }
1807 
1808     if (bis) {
1809         if (!(root_bis = lyd_new(NULL, mod, "yang-library"))) {
1810             goto error;
1811         }
1812 
1813         if (!(set_bis = lyd_new(root_bis, NULL, "module-set"))) {
1814             goto error;
1815         }
1816 
1817         if (!lyd_new_leaf(set_bis, NULL, "name", "complete")) {
1818             goto error;
1819         }
1820     }
1821 
1822     for (i = 0; i < ctx->models.used; ++i) {
1823         if (ctx->models.list[i]->disabled) {
1824             /* skip the disabled modules */
1825             continue;
1826         }
1827 
1828         /*
1829          * deprecated legacy
1830          */
1831         cont = lyd_new(root, NULL, "module");
1832         if (!cont) {
1833             goto error;
1834         }
1835         /* name */
1836         if (!lyd_new_leaf(cont, NULL, "name", ctx->models.list[i]->name)) {
1837             goto error;
1838         }
1839         /* revision */
1840         if (!lyd_new_leaf(cont, NULL, "revision", (ctx->models.list[i]->rev_size ? ctx->models.list[i]->rev[0].date : ""))) {
1841             goto error;
1842         }
1843         /* schema */
1844         if (ctx->models.list[i]->filepath) {
1845             if (asprintf(&str, "file://%s", ctx->models.list[i]->filepath) == -1) {
1846                 LOGMEM(ctx);
1847                 goto error;
1848             }
1849             if (!lyd_new_leaf(cont, NULL, "schema", str)) {
1850                 free(str);
1851                 goto error;
1852             }
1853             free(str);
1854         }
1855         /* namespace */
1856         if (!lyd_new_leaf(cont, NULL, "namespace", ctx->models.list[i]->ns)) {
1857             goto error;
1858         }
1859         /* feature leaf-list */
1860         if (ylib_feature(cont, ctx->models.list[i])) {
1861             goto error;
1862         }
1863         /* deviation list */
1864         if (ylib_deviation(cont, ctx->models.list[i], 0)) {
1865             goto error;
1866         }
1867         /* conformance-type */
1868         if (!lyd_new_leaf(cont, NULL, "conformance-type", ctx->models.list[i]->implemented ? "implement" : "import")) {
1869             goto error;
1870         }
1871         /* submodule list */
1872         if (ylib_submodules(cont, ctx->models.list[i], 0)) {
1873             goto error;
1874         }
1875 
1876         /*
1877          * current revision
1878          */
1879         if (bis) {
1880             if (ctx->models.list[i]->implemented) {
1881                 if (!(cont = lyd_new(set_bis, NULL, "module"))) {
1882                     goto error;
1883                 }
1884             } else {
1885                 if (!(cont = lyd_new(set_bis, NULL, "import-only-module"))) {
1886                     goto error;
1887                 }
1888             }
1889             /* name */
1890             if (!lyd_new_leaf(cont, NULL, "name", ctx->models.list[i]->name)) {
1891                 goto error;
1892             }
1893             /* revision */
1894             if ((!ctx->models.list[i]->implemented || ctx->models.list[i]->rev_size)
1895                     && !lyd_new_leaf(cont, NULL, "revision", ctx->models.list[i]->rev[0].date)) {
1896                 goto error;
1897             }
1898             /* namespace */
1899             if (!lyd_new_leaf(cont, NULL, "namespace", ctx->models.list[i]->ns)) {
1900                 goto error;
1901             }
1902             /* location */
1903             if (ctx->models.list[i]->filepath) {
1904                 if (asprintf(&str, "file://%s", ctx->models.list[i]->filepath) == -1) {
1905                     LOGMEM(ctx);
1906                     goto error;
1907                 }
1908                 if (!lyd_new_leaf(cont, NULL, "location", str)) {
1909                     free(str);
1910                     goto error;
1911                 }
1912                 free(str);
1913             }
1914             /* submodule list */
1915             if (ylib_submodules(cont, ctx->models.list[i], 1)) {
1916                 goto error;
1917             }
1918             if (ctx->models.list[i]->implemented) {
1919                 /* feature list */
1920                 if (ylib_feature(cont, ctx->models.list[i])) {
1921                     goto error;
1922                 }
1923                 /* deviation */
1924                 if (ylib_deviation(cont, ctx->models.list[i], 1)) {
1925                     goto error;
1926                 }
1927             }
1928         }
1929     }
1930 
1931     /* IDs */
1932     sprintf(id, "%u", ctx->models.module_set_id);
1933     if (!lyd_new_leaf(root, NULL, "module-set-id", id)) {
1934         goto error;
1935     }
1936     if (bis) {
1937         /* create one complete schema */
1938         if (!(cont = lyd_new(root_bis, NULL, "schema"))) {
1939             goto error;
1940         }
1941 
1942         if (!lyd_new_leaf(cont, NULL, "name", "complete")) {
1943             goto error;
1944         }
1945 
1946         if (!lyd_new_leaf(cont, NULL, "module-set", "complete")) {
1947             goto error;
1948         }
1949 
1950         /* content-id */
1951         if (!lyd_new_leaf(root_bis, NULL, "content-id", id)) {
1952             goto error;
1953         }
1954     }
1955 
1956     if (root_bis) {
1957         if (lyd_insert_sibling(&root_bis, root)) {
1958             goto error;
1959         }
1960         root = root_bis;
1961         root_bis = 0;
1962     }
1963 
1964     if (lyd_validate(&root, LYD_OPT_NOSIBLINGS, NULL)) {
1965         goto error;
1966     }
1967 
1968     return root;
1969 
1970 error:
1971     lyd_free_withsiblings(root);
1972     lyd_free_withsiblings(root_bis);
1973     return NULL;
1974 }
1975 
1976 API const struct lys_node *
ly_ctx_get_node(const struct ly_ctx * ctx,const struct lys_node * start,const char * nodeid,int output)1977 ly_ctx_get_node(const struct ly_ctx *ctx, const struct lys_node *start, const char *nodeid, int output)
1978 {
1979     FUN_IN;
1980 
1981     const struct lys_node *node;
1982 
1983     if ((!ctx && !start) || !nodeid || ((nodeid[0] != '/') && !start)) {
1984         LOGARG;
1985         return NULL;
1986     }
1987 
1988     if (!ctx) {
1989         ctx = start->module->ctx;
1990     }
1991 
1992     /* sets error and everything */
1993     node = resolve_json_nodeid(nodeid, ctx, start, output);
1994 
1995     return node;
1996 }
1997 
1998 API struct ly_set *
ly_ctx_find_path(struct ly_ctx * ctx,const char * path)1999 ly_ctx_find_path(struct ly_ctx *ctx, const char *path)
2000 {
2001     FUN_IN;
2002 
2003     struct ly_set *resultset = NULL;
2004 
2005     if (!ctx || !path) {
2006         LOGARG;
2007         return NULL;
2008     }
2009 
2010     /* start in internal module without data to make sure that all the nodes are prefixed */
2011     resolve_schema_nodeid(path, NULL, ctx->models.list[0], &resultset, 1, 1);
2012     return resultset;
2013 }
2014