]> git.proxmox.com Git - mirror_frr.git/blame - lib/yang.c
Merge pull request #6751 from donaldsharp/pim_mem_leaks
[mirror_frr.git] / lib / yang.c
CommitLineData
1c2facd1
RW
1/*
2 * Copyright (C) 2018 NetDEF, Inc.
3 * Renato Westphal
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
8 * any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#include <zebra.h>
21
22#include "log.h"
1c2facd1
RW
23#include "lib_errors.h"
24#include "yang.h"
25#include "yang_translator.h"
26#include "northbound.h"
27
02a0df1f
DL
28#include <libyang/user_types.h>
29
eaf58ba9
DL
30DEFINE_MTYPE_STATIC(LIB, YANG_MODULE, "YANG module")
31DEFINE_MTYPE_STATIC(LIB, YANG_DATA, "YANG data structure")
1c2facd1
RW
32
33/* libyang container. */
34struct ly_ctx *ly_native_ctx;
35
3a11599c
DL
36static struct yang_module_embed *embeds, **embedupd = &embeds;
37
38void yang_module_embed(struct yang_module_embed *embed)
39{
40 embed->next = NULL;
41 *embedupd = embed;
42 embedupd = &embed->next;
43}
44
45static const char *yang_module_imp_clb(const char *mod_name,
46 const char *mod_rev,
47 const char *submod_name,
48 const char *submod_rev,
49 void *user_data,
50 LYS_INFORMAT *format,
51 void (**free_module_data)
52 (void *, void*))
53{
54 struct yang_module_embed *e;
55
56 if (submod_name || submod_rev)
57 return NULL;
58
59 for (e = embeds; e; e = e->next) {
60 if (strcmp(e->mod_name, mod_name))
61 continue;
62 if (mod_rev && strcmp(e->mod_rev, mod_rev))
63 continue;
64
65 *format = e->format;
66 return e->data;
67 }
68
69 flog_warn(EC_LIB_YANG_MODULE_LOAD,
70 "YANG model \"%s@%s\" not embedded, trying external file",
71 mod_name, mod_rev ? mod_rev : "*");
72 return NULL;
73}
74
40664f16 75/* clang-format off */
96f2c009 76static const char *const frr_native_modules[] = {
a1b5f469 77 "frr-interface",
bc867a5d 78 "frr-vrf",
a1b5f469 79 "frr-ripd",
e9ce224b 80 "frr-ripngd",
96f2c009 81 "frr-isisd",
f495425b 82 "frr-vrrpd",
0d2e2bd1 83 "frr-zebra",
a1b5f469 84};
40664f16 85/* clang-format on */
a1b5f469 86
1c2facd1
RW
87/* Generate the yang_modules tree. */
88static inline int yang_module_compare(const struct yang_module *a,
89 const struct yang_module *b)
90{
91 return strcmp(a->name, b->name);
92}
93RB_GENERATE(yang_modules, yang_module, entry, yang_module_compare)
94
95struct yang_modules yang_modules = RB_INITIALIZER(&yang_modules);
96
97struct yang_module *yang_module_load(const char *module_name)
98{
99 struct yang_module *module;
100 const struct lys_module *module_info;
101
102 module_info = ly_ctx_load_module(ly_native_ctx, module_name, NULL);
103 if (!module_info) {
104 flog_err(EC_LIB_YANG_MODULE_LOAD,
105 "%s: failed to load data model: %s", __func__,
106 module_name);
107 exit(1);
108 }
109
110 module = XCALLOC(MTYPE_YANG_MODULE, sizeof(*module));
111 module->name = module_name;
112 module->info = module_info;
113
114 if (RB_INSERT(yang_modules, &yang_modules, module) != NULL) {
115 flog_err(EC_LIB_YANG_MODULE_LOADED_ALREADY,
116 "%s: YANG module is loaded already: %s", __func__,
117 module_name);
118 exit(1);
119 }
120
121 return module;
122}
123
a1b5f469
RW
124void yang_module_load_all(void)
125{
126 for (size_t i = 0; i < array_size(frr_native_modules); i++)
127 yang_module_load(frr_native_modules[i]);
128}
129
1c2facd1
RW
130struct yang_module *yang_module_find(const char *module_name)
131{
132 struct yang_module s;
133
134 s.name = module_name;
135 return RB_FIND(yang_modules, &yang_modules, &s);
136}
137
e0ccfad2
RW
138int yang_snodes_iterate_subtree(const struct lys_node *snode,
139 yang_iterate_cb cb, uint16_t flags, void *arg)
1c2facd1
RW
140{
141 struct lys_node *child;
e0ccfad2 142 int ret = YANG_ITER_CONTINUE;
1c2facd1
RW
143
144 if (CHECK_FLAG(flags, YANG_ITER_FILTER_IMPLICIT)) {
145 switch (snode->nodetype) {
146 case LYS_CASE:
147 case LYS_INPUT:
148 case LYS_OUTPUT:
db452508 149 if (CHECK_FLAG(snode->flags, LYS_IMPLICIT))
1c2facd1
RW
150 goto next;
151 break;
152 default:
153 break;
154 }
155 }
156
157 switch (snode->nodetype) {
158 case LYS_CONTAINER:
159 if (CHECK_FLAG(flags, YANG_ITER_FILTER_NPCONTAINERS)) {
160 struct lys_node_container *scontainer;
161
162 scontainer = (struct lys_node_container *)snode;
163 if (!scontainer->presence)
164 goto next;
165 }
166 break;
167 case LYS_LEAF:
168 if (CHECK_FLAG(flags, YANG_ITER_FILTER_LIST_KEYS)) {
169 struct lys_node_leaf *sleaf;
170
171 /* Ignore list keys. */
172 sleaf = (struct lys_node_leaf *)snode;
173 if (lys_is_key(sleaf, NULL))
174 goto next;
175 }
176 break;
177 case LYS_GROUPING:
178 /* Return since we're not interested in the grouping subtree. */
e0ccfad2 179 return YANG_ITER_CONTINUE;
1c2facd1
RW
180 case LYS_USES:
181 case LYS_AUGMENT:
182 /* Always ignore nodes of these types. */
183 goto next;
184 case LYS_INPUT:
185 case LYS_OUTPUT:
186 if (CHECK_FLAG(flags, YANG_ITER_FILTER_INPUT_OUTPUT))
187 goto next;
188 break;
189 default:
190 break;
191 }
192
e0ccfad2
RW
193 ret = (*cb)(snode, arg);
194 if (ret == YANG_ITER_STOP)
195 return ret;
1c2facd1
RW
196
197next:
198 /*
199 * YANG leafs and leaf-lists can't have child nodes, and trying to
200 * access snode->child is undefined behavior.
201 */
db452508 202 if (CHECK_FLAG(snode->nodetype, LYS_LEAF | LYS_LEAFLIST))
e0ccfad2 203 return YANG_ITER_CONTINUE;
1c2facd1
RW
204
205 LY_TREE_FOR (snode->child, child) {
544ca69a
RW
206 if (!CHECK_FLAG(flags, YANG_ITER_ALLOW_AUGMENTATIONS)
207 && child->parent != snode)
1c2facd1 208 continue;
e0ccfad2
RW
209
210 ret = yang_snodes_iterate_subtree(child, cb, flags, arg);
211 if (ret == YANG_ITER_STOP)
212 return ret;
1c2facd1 213 }
e0ccfad2
RW
214
215 return ret;
1c2facd1
RW
216}
217
e0ccfad2
RW
218int yang_snodes_iterate_module(const struct lys_module *module,
219 yang_iterate_cb cb, uint16_t flags, void *arg)
1c2facd1
RW
220{
221 struct lys_node *snode;
e0ccfad2 222 int ret = YANG_ITER_CONTINUE;
1c2facd1
RW
223
224 LY_TREE_FOR (module->data, snode) {
e0ccfad2
RW
225 ret = yang_snodes_iterate_subtree(snode, cb, flags, arg);
226 if (ret == YANG_ITER_STOP)
227 return ret;
1c2facd1
RW
228 }
229
230 for (uint8_t i = 0; i < module->augment_size; i++) {
e0ccfad2
RW
231 ret = yang_snodes_iterate_subtree(
232 (const struct lys_node *)&module->augment[i], cb, flags,
233 arg);
234 if (ret == YANG_ITER_STOP)
235 return ret;
1c2facd1 236 }
e0ccfad2
RW
237
238 return ret;
1c2facd1
RW
239}
240
e0ccfad2 241int yang_snodes_iterate_all(yang_iterate_cb cb, uint16_t flags, void *arg)
1c2facd1
RW
242{
243 struct yang_module *module;
e0ccfad2
RW
244 int ret = YANG_ITER_CONTINUE;
245
246 RB_FOREACH (module, yang_modules, &yang_modules) {
247 ret = yang_snodes_iterate_module(module->info, cb, flags, arg);
248 if (ret == YANG_ITER_STOP)
249 return ret;
250 }
1c2facd1 251
e0ccfad2 252 return ret;
1c2facd1
RW
253}
254
255void yang_snode_get_path(const struct lys_node *snode, enum yang_path_type type,
256 char *xpath, size_t xpath_len)
257{
258 char *xpath_ptr;
259
260 switch (type) {
261 case YANG_PATH_SCHEMA:
262 xpath_ptr = lys_path(snode, 0);
263 break;
264 case YANG_PATH_DATA:
265 xpath_ptr = lys_data_path(snode);
266 break;
267 default:
268 flog_err(EC_LIB_DEVELOPMENT, "%s: unknown yang path type: %u",
269 __func__, type);
270 exit(1);
271 }
272 strlcpy(xpath, xpath_ptr, xpath_len);
273 free(xpath_ptr);
274}
275
276struct lys_node *yang_snode_real_parent(const struct lys_node *snode)
277{
278 struct lys_node *parent = snode->parent;
279
280 while (parent) {
281 struct lys_node_container *scontainer;
282
283 switch (parent->nodetype) {
284 case LYS_CONTAINER:
285 scontainer = (struct lys_node_container *)parent;
286 if (scontainer->presence)
287 return parent;
288 break;
289 case LYS_LIST:
290 return parent;
291 default:
292 break;
293 }
294 parent = parent->parent;
295 }
296
297 return NULL;
298}
299
300struct lys_node *yang_snode_parent_list(const struct lys_node *snode)
301{
302 struct lys_node *parent = snode->parent;
303
304 while (parent) {
305 switch (parent->nodetype) {
306 case LYS_LIST:
307 return parent;
308 default:
309 break;
310 }
311 parent = parent->parent;
312 }
313
314 return NULL;
315}
316
317bool yang_snode_is_typeless_data(const struct lys_node *snode)
318{
319 struct lys_node_leaf *sleaf;
320
321 switch (snode->nodetype) {
322 case LYS_LEAF:
323 sleaf = (struct lys_node_leaf *)snode;
324 if (sleaf->type.base == LY_TYPE_EMPTY)
325 return true;
326 return false;
327 case LYS_LEAFLIST:
328 return false;
329 default:
330 return true;
331 }
332}
333
334const char *yang_snode_get_default(const struct lys_node *snode)
335{
336 struct lys_node_leaf *sleaf;
337
338 switch (snode->nodetype) {
339 case LYS_LEAF:
340 sleaf = (struct lys_node_leaf *)snode;
341
342 /* NOTE: this might be null. */
343 return sleaf->dflt;
344 case LYS_LEAFLIST:
345 /* TODO: check leaf-list default values */
346 return NULL;
347 default:
348 return NULL;
349 }
350}
351
352const struct lys_type *yang_snode_get_type(const struct lys_node *snode)
353{
354 struct lys_node_leaf *sleaf = (struct lys_node_leaf *)snode;
355 struct lys_type *type;
356
db452508 357 if (!CHECK_FLAG(sleaf->nodetype, LYS_LEAF | LYS_LEAFLIST))
1c2facd1
RW
358 return NULL;
359
360 type = &sleaf->type;
361 while (type->base == LY_TYPE_LEAFREF)
362 type = &type->info.lref.target->type;
363
364 return type;
365}
366
367void yang_dnode_get_path(const struct lyd_node *dnode, char *xpath,
368 size_t xpath_len)
369{
370 char *xpath_ptr;
371
372 xpath_ptr = lyd_path(dnode);
373 strlcpy(xpath, xpath_ptr, xpath_len);
374 free(xpath_ptr);
375}
376
3f662078
RW
377const char *yang_dnode_get_schema_name(const struct lyd_node *dnode,
378 const char *xpath_fmt, ...)
379{
380 if (xpath_fmt) {
381 va_list ap;
382 char xpath[XPATH_MAXLEN];
383
384 va_start(ap, xpath_fmt);
385 vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
386 va_end(ap);
387
388 dnode = yang_dnode_get(dnode, xpath);
389 if (!dnode) {
390 flog_err(EC_LIB_YANG_DNODE_NOT_FOUND,
391 "%s: couldn't find %s", __func__, xpath);
392 zlog_backtrace(LOG_ERR);
393 abort();
394 }
395 }
396
397 return dnode->schema->name;
398}
399
1c2facd1
RW
400struct lyd_node *yang_dnode_get(const struct lyd_node *dnode,
401 const char *xpath_fmt, ...)
402{
403 va_list ap;
404 char xpath[XPATH_MAXLEN];
405 struct ly_set *set;
406 struct lyd_node *dnode_ret = NULL;
407
408 va_start(ap, xpath_fmt);
409 vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
410 va_end(ap);
411
412 set = lyd_find_path(dnode, xpath);
413 assert(set);
414 if (set->number == 0)
415 goto exit;
416
417 if (set->number > 1) {
418 flog_warn(EC_LIB_YANG_DNODE_NOT_FOUND,
419 "%s: found %u elements (expected 0 or 1) [xpath %s]",
420 __func__, set->number, xpath);
421 goto exit;
422 }
423
424 dnode_ret = set->set.d[0];
425
426exit:
427 ly_set_free(set);
428
429 return dnode_ret;
430}
431
432bool yang_dnode_exists(const struct lyd_node *dnode, const char *xpath_fmt, ...)
433{
434 va_list ap;
435 char xpath[XPATH_MAXLEN];
436 struct ly_set *set;
437 bool found;
438
439 va_start(ap, xpath_fmt);
440 vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
441 va_end(ap);
442
443 set = lyd_find_path(dnode, xpath);
444 assert(set);
445 found = (set->number > 0);
446 ly_set_free(set);
447
448 return found;
449}
450
7b611145
RW
451void yang_dnode_iterate(yang_dnode_iter_cb cb, void *arg,
452 const struct lyd_node *dnode, const char *xpath_fmt,
453 ...)
454{
455 va_list ap;
456 char xpath[XPATH_MAXLEN];
457 struct ly_set *set;
458
459 va_start(ap, xpath_fmt);
460 vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
461 va_end(ap);
462
463 set = lyd_find_path(dnode, xpath);
464 assert(set);
465 for (unsigned int i = 0; i < set->number; i++) {
466 int ret;
467
468 dnode = set->set.d[i];
469 ret = (*cb)(dnode, arg);
470 if (ret == YANG_ITER_STOP)
471 return;
472 }
473
474 ly_set_free(set);
475}
476
1c2facd1
RW
477bool yang_dnode_is_default(const struct lyd_node *dnode, const char *xpath_fmt,
478 ...)
479{
480 struct lys_node *snode;
481 struct lys_node_leaf *sleaf;
482 struct lys_node_container *scontainer;
483
484 if (xpath_fmt) {
485 va_list ap;
486 char xpath[XPATH_MAXLEN];
487
488 va_start(ap, xpath_fmt);
489 vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
490 va_end(ap);
491
492 dnode = yang_dnode_get(dnode, xpath);
493 }
494
495 assert(dnode);
496 snode = dnode->schema;
497 switch (snode->nodetype) {
498 case LYS_LEAF:
499 sleaf = (struct lys_node_leaf *)snode;
500 if (sleaf->type.base == LY_TYPE_EMPTY)
501 return false;
502 return lyd_wd_default((struct lyd_node_leaf_list *)dnode);
503 case LYS_LEAFLIST:
504 /* TODO: check leaf-list default values */
505 return false;
506 case LYS_CONTAINER:
507 scontainer = (struct lys_node_container *)snode;
508 if (scontainer->presence)
509 return false;
510 return true;
511 default:
512 return false;
513 }
514}
515
516bool yang_dnode_is_default_recursive(const struct lyd_node *dnode)
517{
518 struct lys_node *snode;
519 struct lyd_node *root, *next, *dnode_iter;
520
521 snode = dnode->schema;
db452508 522 if (CHECK_FLAG(snode->nodetype, LYS_LEAF | LYS_LEAFLIST))
1c2facd1
RW
523 return yang_dnode_is_default(dnode, NULL);
524
525 if (!yang_dnode_is_default(dnode, NULL))
526 return false;
527
528 LY_TREE_FOR (dnode->child, root) {
529 LY_TREE_DFS_BEGIN (root, next, dnode_iter) {
530 if (!yang_dnode_is_default(dnode_iter, NULL))
531 return false;
532
533 LY_TREE_DFS_END(root, next, dnode_iter);
534 }
535 }
536
537 return true;
538}
539
540void yang_dnode_change_leaf(struct lyd_node *dnode, const char *value)
541{
542 assert(dnode->schema->nodetype == LYS_LEAF);
543 lyd_change_leaf((struct lyd_node_leaf_list *)dnode, value);
544}
545
5e02643a 546struct lyd_node *yang_dnode_new(struct ly_ctx *ly_ctx, bool config_only)
1c2facd1
RW
547{
548 struct lyd_node *dnode;
5e02643a
RW
549 int options;
550
551 if (config_only)
552 options = LYD_OPT_CONFIG;
553 else
554 options = LYD_OPT_DATA | LYD_OPT_DATA_NO_YANGLIB;
1c2facd1
RW
555
556 dnode = NULL;
5e02643a 557 if (lyd_validate(&dnode, options, ly_ctx) != 0) {
1c2facd1
RW
558 /* Should never happen. */
559 flog_err(EC_LIB_LIBYANG, "%s: lyd_validate() failed", __func__);
560 exit(1);
561 }
562
563 return dnode;
564}
565
566struct lyd_node *yang_dnode_dup(const struct lyd_node *dnode)
567{
568 return lyd_dup_withsiblings(dnode, 1);
569}
570
571void yang_dnode_free(struct lyd_node *dnode)
572{
e5dc8a44
RW
573 while (dnode->parent)
574 dnode = dnode->parent;
1c2facd1
RW
575 lyd_free_withsiblings(dnode);
576}
577
578struct yang_data *yang_data_new(const char *xpath, const char *value)
579{
1c2facd1
RW
580 struct yang_data *data;
581
1c2facd1
RW
582 data = XCALLOC(MTYPE_YANG_DATA, sizeof(*data));
583 strlcpy(data->xpath, xpath, sizeof(data->xpath));
1c2facd1
RW
584 if (value)
585 data->value = strdup(value);
586
587 return data;
588}
589
590void yang_data_free(struct yang_data *data)
591{
592 if (data->value)
593 free(data->value);
594 XFREE(MTYPE_YANG_DATA, data);
595}
596
597struct list *yang_data_list_new(void)
598{
599 struct list *list;
600
601 list = list_new();
602 list->del = (void (*)(void *))yang_data_free;
603
604 return list;
605}
606
fcb7bffd
RW
607struct yang_data *yang_data_list_find(const struct list *list,
608 const char *xpath_fmt, ...)
609{
610 char xpath[XPATH_MAXLEN];
611 struct yang_data *data;
612 struct listnode *node;
613 va_list ap;
614
615 va_start(ap, xpath_fmt);
616 vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
617 va_end(ap);
618
619 for (ALL_LIST_ELEMENTS_RO(list, node, data))
620 if (strmatch(data->xpath, xpath))
621 return data;
622
623 return NULL;
624}
625
1c2facd1
RW
626/* Make libyang log its errors using FRR logging infrastructure. */
627static void ly_log_cb(LY_LOG_LEVEL level, const char *msg, const char *path)
628{
96679938 629 int priority = LOG_ERR;
1c2facd1
RW
630
631 switch (level) {
632 case LY_LLERR:
633 priority = LOG_ERR;
634 break;
635 case LY_LLWRN:
636 priority = LOG_WARNING;
637 break;
638 case LY_LLVRB:
96679938 639 case LY_LLDBG:
1c2facd1
RW
640 priority = LOG_DEBUG;
641 break;
1c2facd1
RW
642 }
643
644 if (path)
645 zlog(priority, "libyang: %s (%s)", msg, path);
646 else
647 zlog(priority, "libyang: %s", msg);
648}
649
df5eda3d
RW
650const char *yang_print_errors(struct ly_ctx *ly_ctx, char *buf, size_t buf_len)
651{
652 struct ly_err_item *ei;
653 const char *path;
654
655 ei = ly_err_first(ly_ctx);
656 if (!ei)
657 return "";
658
659 strlcpy(buf, "YANG error(s):\n", buf_len);
660 for (; ei; ei = ei->next) {
661 strlcat(buf, " ", buf_len);
662 strlcat(buf, ei->msg, buf_len);
663 strlcat(buf, "\n", buf_len);
664 }
665
666 path = ly_errpath(ly_ctx);
667 if (path) {
668 strlcat(buf, " YANG path: ", buf_len);
669 strlcat(buf, path, buf_len);
670 strlcat(buf, "\n", buf_len);
671 }
672
673 ly_err_clean(ly_ctx, NULL);
674
675 return buf;
676}
677
62ae9ade
RW
678void yang_debugging_set(bool enable)
679{
680 if (enable) {
681 ly_verb(LY_LLDBG);
682 ly_verb_dbg(0xFF);
683 } else {
684 ly_verb(LY_LLERR);
685 ly_verb_dbg(0);
686 }
687}
688
b90204a8 689struct ly_ctx *yang_ctx_new_setup(bool embedded_modules)
591f57cf
DL
690{
691 struct ly_ctx *ctx;
692 const char *yang_models_path = YANG_MODELS_PATH;
693
694 if (access(yang_models_path, R_OK | X_OK)) {
695 yang_models_path = NULL;
696 if (errno == ENOENT)
697 zlog_info("yang model directory \"%s\" does not exist",
698 YANG_MODELS_PATH);
699 else
700 flog_err_sys(EC_LIB_LIBYANG,
701 "cannot access yang model directory \"%s\"",
702 YANG_MODELS_PATH);
703 }
704
705 ctx = ly_ctx_new(yang_models_path, LY_CTX_DISABLE_SEARCHDIR_CWD);
706 if (!ctx)
707 return NULL;
b90204a8
RW
708
709 if (embedded_modules)
710 ly_ctx_set_module_imp_clb(ctx, yang_module_imp_clb, NULL);
711
591f57cf
DL
712 return ctx;
713}
714
b90204a8 715void yang_init(bool embedded_modules)
1c2facd1 716{
1c2facd1
RW
717 /* Initialize libyang global parameters that affect all containers. */
718 ly_set_log_clb(ly_log_cb, 1);
719 ly_log_options(LY_LOLOG | LY_LOSTORE);
720
721 /* Initialize libyang container for native models. */
b90204a8 722 ly_native_ctx = yang_ctx_new_setup(embedded_modules);
1c2facd1
RW
723 if (!ly_native_ctx) {
724 flog_err(EC_LIB_LIBYANG, "%s: ly_ctx_new() failed", __func__);
725 exit(1);
726 }
1c2facd1 727
1c2facd1
RW
728 yang_translator_init();
729}
730
731void yang_terminate(void)
732{
733 struct yang_module *module;
734
735 yang_translator_terminate();
736
737 while (!RB_EMPTY(yang_modules, &yang_modules)) {
738 module = RB_ROOT(yang_modules, &yang_modules);
739
740 /*
741 * We shouldn't call ly_ctx_remove_module() here because this
742 * function also removes other modules that depend on it.
743 *
744 * ly_ctx_destroy() will release all memory for us.
745 */
746 RB_REMOVE(yang_modules, &yang_modules, module);
747 XFREE(MTYPE_YANG_MODULE, module);
748 }
749
750 ly_ctx_destroy(ly_native_ctx, NULL);
751}