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