/src/libyang/src/context.c
Line | Count | Source (jump to first uncovered line) |
1 | | /** |
2 | | * @file context.c |
3 | | * @author Radek Krejci <rkrejci@cesnet.cz> |
4 | | * @author Michal Vasko <mvasko@cesnet.cz> |
5 | | * @brief Context implementations |
6 | | * |
7 | | * Copyright (c) 2015 - 2024 CESNET, z.s.p.o. |
8 | | * |
9 | | * This source code is licensed under BSD 3-Clause License (the "License"). |
10 | | * You may not use this file except in compliance with the License. |
11 | | * You may obtain a copy of the License at |
12 | | * |
13 | | * https://opensource.org/licenses/BSD-3-Clause |
14 | | */ |
15 | | #define _GNU_SOURCE /* asprintf, strdup */ |
16 | | #if defined (__NetBSD__) || defined (__OpenBSD__) |
17 | | /* realpath */ |
18 | | #define _XOPEN_SOURCE 1 |
19 | | #define _XOPEN_SOURCE_EXTENDED 1 |
20 | | #endif |
21 | | |
22 | | #include "context.h" |
23 | | |
24 | | #include <assert.h> |
25 | | #include <errno.h> |
26 | | #include <pthread.h> |
27 | | #include <stdarg.h> |
28 | | #include <stddef.h> |
29 | | #include <stdio.h> |
30 | | #include <stdlib.h> |
31 | | #include <string.h> |
32 | | #include <sys/stat.h> |
33 | | #include <unistd.h> |
34 | | |
35 | | #include "compat.h" |
36 | | #include "hash_table.h" |
37 | | #include "in.h" |
38 | | #include "ly_common.h" |
39 | | #include "parser_data.h" |
40 | | #include "plugins_internal.h" |
41 | | #include "plugins_types.h" |
42 | | #include "schema_compile.h" |
43 | | #include "set.h" |
44 | | #include "tree.h" |
45 | | #include "tree_data.h" |
46 | | #include "tree_data_internal.h" |
47 | | #include "tree_schema.h" |
48 | | #include "tree_schema_free.h" |
49 | | #include "tree_schema_internal.h" |
50 | | |
51 | | #include "../models/ietf-datastores@2018-02-14.h" |
52 | | #include "../models/ietf-inet-types@2013-07-15.h" |
53 | | #include "../models/ietf-yang-library@2019-01-04.h" |
54 | | #include "../models/ietf-yang-metadata@2016-08-05.h" |
55 | | #include "../models/ietf-yang-schema-mount@2019-01-14.h" |
56 | | #include "../models/ietf-yang-structure-ext@2020-06-17.h" |
57 | | #include "../models/ietf-yang-types@2013-07-15.h" |
58 | | #include "../models/yang@2025-01-29.h" |
59 | 0 | #define IETF_YANG_LIB_REV "2019-01-04" |
60 | | |
61 | | static struct internal_modules_s { |
62 | | const char *name; |
63 | | const char *revision; |
64 | | const char *data; |
65 | | ly_bool implemented; |
66 | | LYS_INFORMAT format; |
67 | | } internal_modules[] = { |
68 | | {"ietf-yang-metadata", "2016-08-05", (const char *)ietf_yang_metadata_2016_08_05_yang, 0, LYS_IN_YANG}, |
69 | | {"yang", "2025-01-29", (const char *)yang_2025_01_29_yang, 1, LYS_IN_YANG}, |
70 | | {"ietf-inet-types", "2013-07-15", (const char *)ietf_inet_types_2013_07_15_yang, 0, LYS_IN_YANG}, |
71 | | {"ietf-yang-types", "2013-07-15", (const char *)ietf_yang_types_2013_07_15_yang, 0, LYS_IN_YANG}, |
72 | | {"ietf-yang-schema-mount", "2019-01-14", (const char *)ietf_yang_schema_mount_2019_01_14_yang, 1, LYS_IN_YANG}, |
73 | | {"ietf-yang-structure-ext", "2020-06-17", (const char *)ietf_yang_structure_ext_2020_06_17_yang, 0, LYS_IN_YANG}, |
74 | | /* ietf-datastores and ietf-yang-library must be right here at the end of the list! */ |
75 | | {"ietf-datastores", "2018-02-14", (const char *)ietf_datastores_2018_02_14_yang, 1, LYS_IN_YANG}, |
76 | | {"ietf-yang-library", IETF_YANG_LIB_REV, (const char *)ietf_yang_library_2019_01_04_yang, 1, LYS_IN_YANG} |
77 | | }; |
78 | | |
79 | 60.6k | #define LY_INTERNAL_MODS_COUNT sizeof(internal_modules) / sizeof(struct internal_modules_s) |
80 | | |
81 | | LIBYANG_API_DEF LY_ERR |
82 | | ly_ctx_set_searchdir(struct ly_ctx *ctx, const char *search_dir) |
83 | 0 | { |
84 | 0 | int rc = LY_SUCCESS; |
85 | 0 | struct stat st; |
86 | 0 | char *new_dir = NULL; |
87 | 0 | uint32_t i; |
88 | 0 | LY_ARRAY_COUNT_TYPE u; |
89 | 0 | struct lys_module *mod; |
90 | |
|
91 | 0 | LY_CHECK_ARG_RET(ctx, ctx, LY_EINVAL); |
92 | |
|
93 | 0 | if (!search_dir) { |
94 | | /* fine, ignore */ |
95 | 0 | goto cleanup; |
96 | 0 | } |
97 | | |
98 | 0 | new_dir = realpath(search_dir, NULL); |
99 | 0 | if (!new_dir) { |
100 | 0 | LOGERR(ctx, LY_ESYS, "Unable to use search directory \"%s\" (%s).", search_dir, strerror(errno)), |
101 | 0 | rc = LY_EINVAL; |
102 | 0 | goto cleanup; |
103 | 0 | } |
104 | 0 | if (strcmp(search_dir, new_dir)) { |
105 | 0 | LOGVRB("Search directory string \"%s\" canonized to \"%s\".", search_dir, new_dir); |
106 | 0 | } |
107 | |
|
108 | 0 | if (access(new_dir, R_OK | X_OK)) { |
109 | 0 | LOGERR(ctx, LY_ESYS, "Unable to fully access search directory \"%s\" (%s).", new_dir, strerror(errno)); |
110 | 0 | rc = LY_EINVAL; |
111 | 0 | goto cleanup; |
112 | 0 | } |
113 | 0 | if (stat(new_dir, &st)) { |
114 | 0 | LOGERR(ctx, LY_ESYS, "stat() failed for \"%s\" (%s).", new_dir, strerror(errno)); |
115 | 0 | rc = LY_ESYS; |
116 | 0 | goto cleanup; |
117 | 0 | } |
118 | 0 | if (!S_ISDIR(st.st_mode)) { |
119 | 0 | LOGERR(ctx, LY_ESYS, "Given search directory \"%s\" is not a directory.", new_dir); |
120 | 0 | rc = LY_EINVAL; |
121 | 0 | goto cleanup; |
122 | 0 | } |
123 | | |
124 | | /* avoid path duplication */ |
125 | 0 | for (i = 0; i < ctx->search_paths.count; ++i) { |
126 | 0 | if (!strcmp(new_dir, ctx->search_paths.objs[i])) { |
127 | 0 | rc = LY_EEXIST; |
128 | 0 | goto cleanup; |
129 | 0 | } |
130 | 0 | } |
131 | 0 | if (ly_set_add(&ctx->search_paths, new_dir, 1, NULL)) { |
132 | 0 | rc = LY_EMEM; |
133 | 0 | goto cleanup; |
134 | 0 | } |
135 | | |
136 | | /* new searchdir - reset latests flags (possibly new revisions available) */ |
137 | 0 | for (i = 0; i < ctx->list.count; ++i) { |
138 | 0 | mod = ctx->list.objs[i]; |
139 | |
|
140 | 0 | mod->latest_revision &= ~LYS_MOD_LATEST_SEARCHDIRS; |
141 | 0 | if (mod->parsed && mod->parsed->includes) { |
142 | 0 | for (u = 0; u < LY_ARRAY_COUNT(mod->parsed->includes); ++u) { |
143 | 0 | mod->parsed->includes[u].submodule->latest_revision &= ~LYS_MOD_LATEST_SEARCHDIRS; |
144 | 0 | } |
145 | 0 | } |
146 | 0 | } |
147 | |
|
148 | 0 | cleanup: |
149 | 0 | if (rc) { |
150 | 0 | free(new_dir); |
151 | 0 | } |
152 | 0 | return rc; |
153 | 0 | } |
154 | | |
155 | | LIBYANG_API_DEF const char * const * |
156 | | ly_ctx_get_searchdirs(const struct ly_ctx *ctx) |
157 | 6.74k | { |
158 | 6.74k | #define LY_CTX_SEARCHDIRS_SIZE_STEP 8 |
159 | 6.74k | void **new; |
160 | | |
161 | 6.74k | LY_CHECK_ARG_RET(ctx, ctx, NULL); |
162 | | |
163 | 6.74k | if (ctx->search_paths.count == ctx->search_paths.size) { |
164 | | /* not enough space for terminating NULL byte */ |
165 | 3.37k | new = realloc(((struct ly_ctx *)ctx)->search_paths.objs, |
166 | 3.37k | (ctx->search_paths.size + LY_CTX_SEARCHDIRS_SIZE_STEP) * sizeof *ctx->search_paths.objs); |
167 | 3.37k | LY_CHECK_ERR_RET(!new, LOGMEM(NULL), NULL); |
168 | 3.37k | ((struct ly_ctx *)ctx)->search_paths.size += LY_CTX_SEARCHDIRS_SIZE_STEP; |
169 | 3.37k | ((struct ly_ctx *)ctx)->search_paths.objs = new; |
170 | 3.37k | } |
171 | | /* set terminating NULL byte to the strings list */ |
172 | 6.74k | ctx->search_paths.objs[ctx->search_paths.count] = NULL; |
173 | | |
174 | 6.74k | return (const char * const *)ctx->search_paths.objs; |
175 | 6.74k | } |
176 | | |
177 | | LIBYANG_API_DEF LY_ERR |
178 | | ly_ctx_unset_searchdir(struct ly_ctx *ctx, const char *value) |
179 | 0 | { |
180 | 0 | LY_CHECK_ARG_RET(ctx, ctx, LY_EINVAL); |
181 | |
|
182 | 0 | if (!ctx->search_paths.count) { |
183 | 0 | return LY_SUCCESS; |
184 | 0 | } |
185 | | |
186 | 0 | if (value) { |
187 | | /* remove specific search directory */ |
188 | 0 | uint32_t index; |
189 | |
|
190 | 0 | for (index = 0; index < ctx->search_paths.count; ++index) { |
191 | 0 | if (!strcmp(value, ctx->search_paths.objs[index])) { |
192 | 0 | break; |
193 | 0 | } |
194 | 0 | } |
195 | 0 | if (index == ctx->search_paths.count) { |
196 | 0 | LOGARG(ctx, value); |
197 | 0 | return LY_EINVAL; |
198 | 0 | } else { |
199 | 0 | return ly_set_rm_index(&ctx->search_paths, index, free); |
200 | 0 | } |
201 | 0 | } else { |
202 | | /* remove them all */ |
203 | 0 | ly_set_erase(&ctx->search_paths, free); |
204 | 0 | memset(&ctx->search_paths, 0, sizeof ctx->search_paths); |
205 | 0 | } |
206 | | |
207 | 0 | return LY_SUCCESS; |
208 | 0 | } |
209 | | |
210 | | LIBYANG_API_DEF LY_ERR |
211 | | ly_ctx_unset_searchdir_last(struct ly_ctx *ctx, uint32_t count) |
212 | 0 | { |
213 | 0 | LY_CHECK_ARG_RET(ctx, ctx, LY_EINVAL); |
214 | |
|
215 | 0 | for ( ; count > 0 && ctx->search_paths.count; --count) { |
216 | 0 | LY_CHECK_RET(ly_set_rm_index(&ctx->search_paths, ctx->search_paths.count - 1, free)) |
217 | 0 | } |
218 | | |
219 | 0 | return LY_SUCCESS; |
220 | 0 | } |
221 | | |
222 | | LIBYANG_API_DEF struct lys_module * |
223 | | ly_ctx_load_module(struct ly_ctx *ctx, const char *name, const char *revision, const char **features) |
224 | 0 | { |
225 | 0 | struct lys_module *mod = NULL; |
226 | 0 | LY_ERR ret = LY_SUCCESS; |
227 | |
|
228 | 0 | LY_CHECK_ARG_RET(ctx, ctx, name, NULL); |
229 | | |
230 | | /* load and parse */ |
231 | 0 | ret = lys_parse_load(ctx, name, revision, &ctx->unres.creating, &mod); |
232 | 0 | LY_CHECK_GOTO(ret, cleanup); |
233 | | |
234 | | /* implement */ |
235 | 0 | ret = _lys_set_implemented(mod, features, &ctx->unres); |
236 | 0 | LY_CHECK_GOTO(ret, cleanup); |
237 | |
|
238 | 0 | if (!(ctx->flags & LY_CTX_EXPLICIT_COMPILE)) { |
239 | | /* create dep set for the module and mark all the modules that will be (re)compiled */ |
240 | 0 | LY_CHECK_GOTO(ret = lys_unres_dep_sets_create(ctx, &ctx->unres.dep_sets, mod), cleanup); |
241 | | |
242 | | /* (re)compile the whole dep set (other dep sets will have no modules marked for compilation) */ |
243 | 0 | LY_CHECK_GOTO(ret = lys_compile_depset_all(ctx, &ctx->unres), cleanup); |
244 | | |
245 | | /* unres resolved */ |
246 | 0 | lys_unres_glob_erase(&ctx->unres); |
247 | 0 | } |
248 | | |
249 | | /* new context state */ |
250 | 0 | ly_ctx_new_change(ctx); |
251 | |
|
252 | 0 | cleanup: |
253 | 0 | if (ret) { |
254 | 0 | lys_unres_glob_revert(ctx, &ctx->unres); |
255 | 0 | lys_unres_glob_erase(&ctx->unres); |
256 | 0 | mod = NULL; |
257 | 0 | } |
258 | 0 | return mod; |
259 | 0 | } |
260 | | |
261 | | /** |
262 | | * @brief Hash table value-equal callback for comparing context error hash table record. |
263 | | */ |
264 | | static ly_bool |
265 | | ly_ctx_ht_err_equal_cb(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *UNUSED(cb_data)) |
266 | 12 | { |
267 | 12 | struct ly_ctx_err_rec *err1 = val1_p, *err2 = val2_p; |
268 | | |
269 | 12 | return !memcmp(&err1->tid, &err2->tid, sizeof err1->tid); |
270 | 12 | } |
271 | | |
272 | | /** |
273 | | * @brief Hash table value-equal callback for comparing leafref links hash table record. |
274 | | */ |
275 | | static ly_bool |
276 | | ly_ctx_ht_leafref_links_equal_cb(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *UNUSED(cb_data)) |
277 | 0 | { |
278 | 0 | struct lyd_leafref_links_rec **rec1 = val1_p, **rec2 = val2_p; |
279 | |
|
280 | 0 | return (*rec1)->node == (*rec2)->node; |
281 | 0 | } |
282 | | |
283 | | /** |
284 | | * @brief Callback for freeing leafref links recorcd internal resources. |
285 | | * |
286 | | * @param[in] val_p Pointer to leafref links record |
287 | | */ |
288 | | static void |
289 | | ly_ctx_ht_leafref_links_rec_free(void *val_p) |
290 | 0 | { |
291 | 0 | struct lyd_leafref_links_rec **rec = val_p; |
292 | |
|
293 | 0 | lyd_free_leafref_links_rec(*rec); |
294 | 0 | free(*rec); |
295 | 0 | } |
296 | | |
297 | | LIBYANG_API_DEF LY_ERR |
298 | | ly_ctx_new(const char *search_dir, uint16_t options, struct ly_ctx **new_ctx) |
299 | 3.37k | { |
300 | 3.37k | struct ly_ctx *ctx = NULL; |
301 | 3.37k | struct lys_module *module; |
302 | 3.37k | char *search_dir_list, *sep, *dir; |
303 | 3.37k | const char **imp_f, *all_f[] = {"*", NULL}; |
304 | 3.37k | uint32_t i; |
305 | 3.37k | struct ly_in *in = NULL; |
306 | 3.37k | LY_ERR rc = LY_SUCCESS; |
307 | 3.37k | struct lys_glob_unres unres = {0}; |
308 | 3.37k | ly_bool builtin_plugins_only; |
309 | | |
310 | 3.37k | LY_CHECK_ARG_RET(NULL, new_ctx, LY_EINVAL); |
311 | | |
312 | 3.37k | ctx = calloc(1, sizeof *ctx); |
313 | 3.37k | LY_CHECK_ERR_GOTO(!ctx, LOGMEM(NULL); rc = LY_EMEM, cleanup); |
314 | | |
315 | | /* dictionary */ |
316 | 3.37k | lydict_init(&ctx->dict); |
317 | | |
318 | | /* plugins */ |
319 | 3.37k | builtin_plugins_only = (options & LY_CTX_BUILTIN_PLUGINS_ONLY) ? 1 : 0; |
320 | 3.37k | LY_CHECK_ERR_GOTO(lyplg_init(builtin_plugins_only), LOGINT(NULL); rc = LY_EINT, cleanup); |
321 | | |
322 | 3.37k | if (options & LY_CTX_LEAFREF_LINKING) { |
323 | | /** |
324 | | * storing the pointer instead of record itself is needed to avoid invalid memory reads. Hash table can reallocate |
325 | | * its memory completely during various manipulation function (e.g. remove, insert). In case of using pointers, the |
326 | | * pointer can be reallocated safely, while record itself remains untouched and can be accessed/modified freely |
327 | | * */ |
328 | 0 | ctx->leafref_links_ht = lyht_new(1, sizeof(struct lyd_leafref_links_rec *), ly_ctx_ht_leafref_links_equal_cb, NULL, 1); |
329 | 0 | LY_CHECK_ERR_GOTO(!ctx->leafref_links_ht, rc = LY_EMEM, cleanup); |
330 | 0 | } |
331 | | |
332 | | /* initialize thread-specific error hash table */ |
333 | 3.37k | ctx->err_ht = lyht_new(1, sizeof(struct ly_ctx_err_rec), ly_ctx_ht_err_equal_cb, NULL, 1); |
334 | 3.37k | LY_CHECK_ERR_GOTO(!ctx->err_ht, rc = LY_EMEM, cleanup); |
335 | | |
336 | | /* init LYB hash lock */ |
337 | 3.37k | pthread_mutex_init(&ctx->lyb_hash_lock, NULL); |
338 | | |
339 | | /* modules list */ |
340 | 3.37k | ctx->flags = options; |
341 | 3.37k | if (search_dir) { |
342 | 0 | search_dir_list = strdup(search_dir); |
343 | 0 | LY_CHECK_ERR_GOTO(!search_dir_list, LOGMEM(NULL); rc = LY_EMEM, cleanup); |
344 | |
|
345 | 0 | for (dir = search_dir_list; (sep = strchr(dir, PATH_SEPARATOR[0])) != NULL && rc == LY_SUCCESS; dir = sep + 1) { |
346 | 0 | *sep = 0; |
347 | 0 | rc = ly_ctx_set_searchdir(ctx, dir); |
348 | 0 | if (rc == LY_EEXIST) { |
349 | | /* ignore duplication */ |
350 | 0 | rc = LY_SUCCESS; |
351 | 0 | } |
352 | 0 | } |
353 | 0 | if (*dir && (rc == LY_SUCCESS)) { |
354 | 0 | rc = ly_ctx_set_searchdir(ctx, dir); |
355 | 0 | if (rc == LY_EEXIST) { |
356 | | /* ignore duplication */ |
357 | 0 | rc = LY_SUCCESS; |
358 | 0 | } |
359 | 0 | } |
360 | 0 | free(search_dir_list); |
361 | | |
362 | | /* If ly_ctx_set_searchdir() failed, the error is already logged. Just exit */ |
363 | 0 | LY_CHECK_GOTO(rc, cleanup); |
364 | 0 | } |
365 | 3.37k | ctx->change_count = 1; |
366 | | |
367 | 3.37k | if (!(options & LY_CTX_EXPLICIT_COMPILE)) { |
368 | | /* use it for creating the initial context */ |
369 | 3.37k | ctx->flags |= LY_CTX_EXPLICIT_COMPILE; |
370 | 3.37k | } |
371 | | |
372 | | /* create dummy in */ |
373 | 3.37k | rc = ly_in_new_memory(internal_modules[0].data, &in); |
374 | 3.37k | LY_CHECK_GOTO(rc, cleanup); |
375 | | |
376 | | /* load internal modules */ |
377 | 30.3k | for (i = 0; i < ((options & LY_CTX_NO_YANGLIBRARY) ? (LY_INTERNAL_MODS_COUNT - 2) : LY_INTERNAL_MODS_COUNT); i++) { |
378 | 26.9k | ly_in_memory(in, internal_modules[i].data); |
379 | 26.9k | LY_CHECK_GOTO(rc = lys_parse_in(ctx, in, internal_modules[i].format, NULL, &unres.creating, &module), cleanup); |
380 | 26.9k | if (internal_modules[i].implemented || (ctx->flags & LY_CTX_ALL_IMPLEMENTED)) { |
381 | 13.4k | imp_f = (ctx->flags & LY_CTX_ENABLE_IMP_FEATURES) ? all_f : NULL; |
382 | 13.4k | LY_CHECK_GOTO(rc = lys_implement(module, imp_f, &unres), cleanup); |
383 | 13.4k | } |
384 | 26.9k | } |
385 | | |
386 | 3.37k | if (!(options & LY_CTX_EXPLICIT_COMPILE)) { |
387 | | /* compile now */ |
388 | 3.37k | LY_CHECK_GOTO(rc = ly_ctx_compile(ctx), cleanup); |
389 | 3.37k | ctx->flags &= ~LY_CTX_EXPLICIT_COMPILE; |
390 | 3.37k | } |
391 | | |
392 | 3.37k | cleanup: |
393 | 3.37k | ly_in_free(in, 0); |
394 | 3.37k | lys_unres_glob_erase(&unres); |
395 | 3.37k | if (rc) { |
396 | 0 | ly_ctx_destroy(ctx); |
397 | 3.37k | } else { |
398 | 3.37k | *new_ctx = ctx; |
399 | 3.37k | } |
400 | 3.37k | return rc; |
401 | 3.37k | } |
402 | | |
403 | | static LY_ERR |
404 | | ly_ctx_new_yl_legacy(struct ly_ctx *ctx, const struct lyd_node *yltree) |
405 | 0 | { |
406 | 0 | struct lyd_node *module, *node; |
407 | 0 | struct ly_set *set; |
408 | 0 | const char **feature_arr = NULL; |
409 | 0 | const char *name = NULL, *revision = NULL; |
410 | 0 | struct ly_set features = {0}; |
411 | 0 | ly_bool imported = 0; |
412 | 0 | const struct lys_module *mod; |
413 | 0 | LY_ERR ret = LY_SUCCESS; |
414 | 0 | uint32_t i, j; |
415 | |
|
416 | 0 | LY_CHECK_RET(ret = lyd_find_xpath(yltree, "/ietf-yang-library:yang-library/modules-state/module", &set)); |
417 | | |
418 | | /* process the data tree */ |
419 | 0 | for (i = 0; i < set->count; ++i) { |
420 | 0 | module = set->dnodes[i]; |
421 | | |
422 | | /* initiate */ |
423 | 0 | revision = NULL; |
424 | 0 | name = NULL; |
425 | 0 | imported = 0; |
426 | |
|
427 | 0 | LY_LIST_FOR(lyd_child(module), node) { |
428 | 0 | if (!strcmp(node->schema->name, "name")) { |
429 | 0 | name = lyd_get_value(node); |
430 | 0 | } else if (!strcmp(node->schema->name, "revision")) { |
431 | 0 | revision = lyd_get_value(node); |
432 | 0 | } else if (!strcmp(node->schema->name, "feature")) { |
433 | 0 | LY_CHECK_GOTO(ret = ly_set_add(&features, node, 0, NULL), cleanup); |
434 | 0 | } else if (!strcmp(node->schema->name, "conformance-type") && |
435 | 0 | !strcmp(lyd_get_value(node), "import")) { |
436 | | /* imported module - skip it, it will be loaded as a side effect |
437 | | * of loading another module */ |
438 | 0 | imported = 1; |
439 | 0 | break; |
440 | 0 | } |
441 | 0 | } |
442 | | |
443 | 0 | if (imported) { |
444 | 0 | continue; |
445 | 0 | } |
446 | | |
447 | 0 | feature_arr = malloc((features.count + 1) * sizeof *feature_arr); |
448 | 0 | LY_CHECK_ERR_GOTO(!feature_arr, ret = LY_EMEM, cleanup); |
449 | | |
450 | | /* Parse features into an array of strings */ |
451 | 0 | for (j = 0; j < features.count; ++j) { |
452 | 0 | feature_arr[j] = lyd_get_value(features.dnodes[j]); |
453 | 0 | } |
454 | 0 | feature_arr[features.count] = NULL; |
455 | 0 | ly_set_clean(&features, free); |
456 | | |
457 | | /* use the gathered data to load the module */ |
458 | 0 | mod = ly_ctx_load_module(ctx, name, revision, feature_arr); |
459 | 0 | free(feature_arr); |
460 | 0 | if (!mod) { |
461 | 0 | LOGERR(ctx, LY_EINVAL, "Unable to load module specified by yang library data."); |
462 | 0 | ly_set_free(set, free); |
463 | 0 | return LY_EINVAL; |
464 | 0 | } |
465 | 0 | } |
466 | | |
467 | 0 | cleanup: |
468 | 0 | ly_set_clean(&features, free); |
469 | 0 | ly_set_free(set, free); |
470 | 0 | return ret; |
471 | 0 | } |
472 | | |
473 | | LIBYANG_API_DEF LY_ERR |
474 | | ly_ctx_new_ylpath(const char *search_dir, const char *path, LYD_FORMAT format, int options, struct ly_ctx **ctx) |
475 | 0 | { |
476 | 0 | LY_ERR ret = LY_SUCCESS; |
477 | 0 | struct ly_ctx *ctx_yl = NULL; |
478 | 0 | struct lyd_node *data_yl = NULL; |
479 | |
|
480 | 0 | LY_CHECK_ARG_RET(NULL, path, ctx, LY_EINVAL); |
481 | | |
482 | | /* create a seperate context for the data */ |
483 | 0 | LY_CHECK_GOTO(ret = ly_ctx_new(search_dir, 0, &ctx_yl), cleanup); |
484 | | |
485 | | /* parse yang library data tree */ |
486 | 0 | LY_CHECK_GOTO(ret = lyd_parse_data_path(ctx_yl, path, format, 0, LYD_VALIDATE_PRESENT, &data_yl), cleanup); |
487 | | |
488 | | /* create the new context */ |
489 | 0 | ret = ly_ctx_new_yldata(search_dir, data_yl, options, ctx); |
490 | |
|
491 | 0 | cleanup: |
492 | 0 | lyd_free_all(data_yl); |
493 | 0 | ly_ctx_destroy(ctx_yl); |
494 | 0 | return ret; |
495 | 0 | } |
496 | | |
497 | | LIBYANG_API_DEF LY_ERR |
498 | | ly_ctx_new_ylmem(const char *search_dir, const char *data, LYD_FORMAT format, int options, struct ly_ctx **ctx) |
499 | 0 | { |
500 | 0 | LY_ERR ret = LY_SUCCESS; |
501 | 0 | struct ly_ctx *ctx_yl = NULL; |
502 | 0 | struct lyd_node *data_yl = NULL; |
503 | |
|
504 | 0 | LY_CHECK_ARG_RET(NULL, data, ctx, LY_EINVAL); |
505 | | |
506 | | /* create a seperate context for the data */ |
507 | 0 | LY_CHECK_GOTO(ret = ly_ctx_new(search_dir, 0, &ctx_yl), cleanup); |
508 | | |
509 | | /* parse yang library data tree */ |
510 | 0 | LY_CHECK_GOTO(ret = lyd_parse_data_mem(ctx_yl, data, format, 0, LYD_VALIDATE_PRESENT, &data_yl), cleanup); |
511 | | |
512 | | /* create the new context */ |
513 | 0 | ret = ly_ctx_new_yldata(search_dir, data_yl, options, ctx); |
514 | |
|
515 | 0 | cleanup: |
516 | 0 | lyd_free_all(data_yl); |
517 | 0 | ly_ctx_destroy(ctx_yl); |
518 | 0 | return ret; |
519 | 0 | } |
520 | | |
521 | | LIBYANG_API_DEF LY_ERR |
522 | | ly_ctx_new_yldata(const char *search_dir, const struct lyd_node *tree, int options, struct ly_ctx **ctx) |
523 | 0 | { |
524 | 0 | const char *name = NULL, *revision = NULL; |
525 | 0 | struct lyd_node *module, *node; |
526 | 0 | struct ly_set *set = NULL; |
527 | 0 | const char **feature_arr = NULL; |
528 | 0 | struct ly_set features = {0}; |
529 | 0 | LY_ERR ret = LY_SUCCESS; |
530 | 0 | const struct lys_module *mod; |
531 | 0 | struct ly_ctx *ctx_new = NULL; |
532 | 0 | ly_bool no_expl_compile = 0; |
533 | 0 | uint32_t i, j; |
534 | |
|
535 | 0 | LY_CHECK_ARG_RET(NULL, tree, ctx, LY_EINVAL); |
536 | | |
537 | | /* create a new context */ |
538 | 0 | if (*ctx == NULL) { |
539 | 0 | LY_CHECK_GOTO(ret = ly_ctx_new(search_dir, options, &ctx_new), cleanup); |
540 | 0 | } else { |
541 | 0 | ctx_new = *ctx; |
542 | 0 | } |
543 | | |
544 | | /* redundant to compile modules one-by-one */ |
545 | 0 | if (!(options & LY_CTX_EXPLICIT_COMPILE)) { |
546 | 0 | ctx_new->flags |= LY_CTX_EXPLICIT_COMPILE; |
547 | 0 | no_expl_compile = 1; |
548 | 0 | } |
549 | |
|
550 | 0 | LY_CHECK_GOTO(ret = lyd_find_xpath(tree, "/ietf-yang-library:yang-library/module-set[1]/module", &set), cleanup); |
551 | 0 | if (set->count == 0) { |
552 | | /* perhaps a legacy data tree? */ |
553 | 0 | LY_CHECK_GOTO(ret = ly_ctx_new_yl_legacy(ctx_new, tree), cleanup); |
554 | 0 | } else { |
555 | | /* process the data tree */ |
556 | 0 | for (i = 0; i < set->count; ++i) { |
557 | 0 | module = set->dnodes[i]; |
558 | | |
559 | | /* initiate */ |
560 | 0 | name = NULL; |
561 | 0 | revision = NULL; |
562 | | |
563 | | /* iterate over data */ |
564 | 0 | LY_LIST_FOR(lyd_child(module), node) { |
565 | 0 | if (!strcmp(node->schema->name, "name")) { |
566 | 0 | name = lyd_get_value(node); |
567 | 0 | } else if (!strcmp(node->schema->name, "revision")) { |
568 | 0 | revision = lyd_get_value(node); |
569 | 0 | } else if (!strcmp(node->schema->name, "feature")) { |
570 | 0 | LY_CHECK_GOTO(ret = ly_set_add(&features, node, 0, NULL), cleanup); |
571 | 0 | } |
572 | 0 | } |
573 | | |
574 | 0 | feature_arr = malloc((features.count + 1) * sizeof *feature_arr); |
575 | 0 | LY_CHECK_ERR_GOTO(!feature_arr, ret = LY_EMEM, cleanup); |
576 | | |
577 | | /* parse features into an array of strings */ |
578 | 0 | for (j = 0; j < features.count; ++j) { |
579 | 0 | feature_arr[j] = lyd_get_value(features.dnodes[j]); |
580 | 0 | } |
581 | 0 | feature_arr[features.count] = NULL; |
582 | 0 | ly_set_clean(&features, NULL); |
583 | | |
584 | | /* use the gathered data to load the module */ |
585 | 0 | mod = ly_ctx_load_module(ctx_new, name, revision, feature_arr); |
586 | 0 | free(feature_arr); |
587 | 0 | if (!mod) { |
588 | 0 | LOGERR(*ctx ? *ctx : LYD_CTX(tree), LY_EINVAL, "Unable to load module %s@%s specified by yang library data.", |
589 | 0 | name, revision ? revision : "<none>"); |
590 | 0 | ret = LY_EINVAL; |
591 | 0 | goto cleanup; |
592 | 0 | } |
593 | 0 | } |
594 | 0 | } |
595 | | |
596 | | /* compile */ |
597 | 0 | LY_CHECK_GOTO(ret = ly_ctx_compile(ctx_new), cleanup); |
598 | |
|
599 | 0 | if (no_expl_compile) { |
600 | | /* unset flag */ |
601 | 0 | ctx_new->flags &= ~LY_CTX_EXPLICIT_COMPILE; |
602 | 0 | } |
603 | |
|
604 | 0 | cleanup: |
605 | 0 | ly_set_free(set, NULL); |
606 | 0 | ly_set_erase(&features, NULL); |
607 | 0 | if (*ctx == NULL) { |
608 | 0 | *ctx = ctx_new; |
609 | 0 | if (ret) { |
610 | 0 | ly_ctx_destroy(*ctx); |
611 | 0 | *ctx = NULL; |
612 | 0 | } |
613 | 0 | } |
614 | 0 | return ret; |
615 | 0 | } |
616 | | |
617 | | LIBYANG_API_DEF LY_ERR |
618 | | ly_ctx_compile(struct ly_ctx *ctx) |
619 | 3.37k | { |
620 | 3.37k | LY_ERR ret = LY_SUCCESS; |
621 | | |
622 | 3.37k | LY_CHECK_ARG_RET(NULL, ctx, LY_EINVAL); |
623 | | |
624 | | /* create dep sets and mark all the modules that will be (re)compiled */ |
625 | 3.37k | LY_CHECK_GOTO(ret = lys_unres_dep_sets_create(ctx, &ctx->unres.dep_sets, NULL), cleanup); |
626 | | |
627 | | /* (re)compile all the dep sets */ |
628 | 3.37k | LY_CHECK_GOTO(ret = lys_compile_depset_all(ctx, &ctx->unres), cleanup); |
629 | | |
630 | 3.37k | cleanup: |
631 | 3.37k | if (ret) { |
632 | | /* revert changes of modules */ |
633 | 0 | lys_unres_glob_revert(ctx, &ctx->unres); |
634 | 0 | } |
635 | 3.37k | lys_unres_glob_erase(&ctx->unres); |
636 | 3.37k | return ret; |
637 | 3.37k | } |
638 | | |
639 | | LIBYANG_API_DEF uint16_t |
640 | | ly_ctx_get_options(const struct ly_ctx *ctx) |
641 | 255k | { |
642 | 255k | LY_CHECK_ARG_RET(ctx, ctx, 0); |
643 | | |
644 | 255k | return ctx->flags; |
645 | 255k | } |
646 | | |
647 | | LIBYANG_API_DEF LY_ERR |
648 | | ly_ctx_set_options(struct ly_ctx *ctx, uint16_t option) |
649 | 0 | { |
650 | 0 | LY_ERR lyrc = LY_SUCCESS; |
651 | 0 | struct lys_module *mod; |
652 | 0 | uint32_t i; |
653 | |
|
654 | 0 | LY_CHECK_ARG_RET(ctx, ctx, LY_EINVAL); |
655 | 0 | LY_CHECK_ERR_RET((option & LY_CTX_NO_YANGLIBRARY) && !(ctx->flags & LY_CTX_NO_YANGLIBRARY), |
656 | 0 | LOGARG(ctx, option), LY_EINVAL); |
657 | |
|
658 | 0 | if (!(ctx->flags & LY_CTX_BUILTIN_PLUGINS_ONLY) && (option & LY_CTX_BUILTIN_PLUGINS_ONLY)) { |
659 | 0 | LOGERR(ctx, LY_EINVAL, |
660 | 0 | "Invalid argument %s (LY_CTX_BUILTIN_PLUGINS_ONLY can be set only when creating a new context) (%s()).", |
661 | 0 | "option", __func__); |
662 | 0 | return LY_EINVAL; |
663 | 0 | } |
664 | | |
665 | 0 | if (!(ctx->flags & LY_CTX_LEAFREF_LINKING) && (option & LY_CTX_LEAFREF_LINKING)) { |
666 | 0 | ctx->leafref_links_ht = lyht_new(1, sizeof(struct lyd_leafref_links_rec *), ly_ctx_ht_leafref_links_equal_cb, NULL, 1); |
667 | 0 | LY_CHECK_ERR_RET(!ctx->leafref_links_ht, LOGARG(ctx, option), LY_EMEM); |
668 | 0 | } |
669 | | |
670 | 0 | if (!(ctx->flags & LY_CTX_SET_PRIV_PARSED) && (option & LY_CTX_SET_PRIV_PARSED)) { |
671 | 0 | ctx->flags |= LY_CTX_SET_PRIV_PARSED; |
672 | | /* recompile the whole context to set the priv pointers */ |
673 | 0 | for (i = 0; i < ctx->list.count; ++i) { |
674 | 0 | mod = ctx->list.objs[i]; |
675 | 0 | if (mod->implemented) { |
676 | 0 | mod->to_compile = 1; |
677 | 0 | } |
678 | 0 | } |
679 | 0 | lyrc = ly_ctx_compile(ctx); |
680 | 0 | if (lyrc) { |
681 | 0 | ly_ctx_unset_options(ctx, LY_CTX_SET_PRIV_PARSED); |
682 | 0 | } |
683 | 0 | } |
684 | | |
685 | | /* set the option(s) */ |
686 | 0 | if (!lyrc) { |
687 | 0 | ctx->flags |= option; |
688 | 0 | } |
689 | |
|
690 | 0 | return lyrc; |
691 | 0 | } |
692 | | |
693 | | static LY_ERR |
694 | | lysc_node_clear_priv_dfs_cb(struct lysc_node *node, void *UNUSED(data), ly_bool *UNUSED(dfs_continue)) |
695 | 0 | { |
696 | 0 | node->priv = NULL; |
697 | 0 | return LY_SUCCESS; |
698 | 0 | } |
699 | | |
700 | | LIBYANG_API_DEF LY_ERR |
701 | | ly_ctx_unset_options(struct ly_ctx *ctx, uint16_t option) |
702 | 0 | { |
703 | 0 | LY_ARRAY_COUNT_TYPE u, v; |
704 | 0 | const struct lysc_ext_instance *ext; |
705 | 0 | struct lysc_node *root; |
706 | |
|
707 | 0 | LY_CHECK_ARG_RET(ctx, ctx, LY_EINVAL); |
708 | 0 | LY_CHECK_ERR_RET(option & LY_CTX_NO_YANGLIBRARY, LOGARG(ctx, option), LY_EINVAL); |
709 | |
|
710 | 0 | if ((ctx->flags & LY_CTX_LEAFREF_LINKING) && (option & LY_CTX_LEAFREF_LINKING)) { |
711 | 0 | lyht_free(ctx->leafref_links_ht, ly_ctx_ht_leafref_links_rec_free); |
712 | 0 | ctx->leafref_links_ht = NULL; |
713 | 0 | } |
714 | |
|
715 | 0 | if ((ctx->flags & LY_CTX_SET_PRIV_PARSED) && (option & LY_CTX_SET_PRIV_PARSED)) { |
716 | 0 | struct lys_module *mod; |
717 | 0 | uint32_t index; |
718 | |
|
719 | 0 | index = 0; |
720 | 0 | while ((mod = ly_ctx_get_module_iter(ctx, &index))) { |
721 | 0 | if (!mod->compiled) { |
722 | 0 | continue; |
723 | 0 | } |
724 | | |
725 | | /* set NULL for all ::lysc_node.priv pointers in module */ |
726 | 0 | lysc_module_dfs_full(mod, lysc_node_clear_priv_dfs_cb, NULL); |
727 | | |
728 | | /* set NULL for all ::lysc_node.priv pointers in compiled extension instances */ |
729 | 0 | LY_ARRAY_FOR(mod->compiled->exts, u) { |
730 | 0 | ext = &mod->compiled->exts[u]; |
731 | 0 | LY_ARRAY_FOR(ext->substmts, v) { |
732 | 0 | if (ext->substmts[v].stmt & LY_STMT_DATA_NODE_MASK) { |
733 | 0 | LY_LIST_FOR(*ext->substmts[v].storage_p, root) { |
734 | 0 | lysc_tree_dfs_full(root, lysc_node_clear_priv_dfs_cb, NULL); |
735 | 0 | } |
736 | 0 | } |
737 | 0 | } |
738 | 0 | } |
739 | 0 | } |
740 | 0 | } |
741 | | |
742 | | /* unset the option(s) */ |
743 | 0 | ctx->flags &= ~option; |
744 | |
|
745 | 0 | return LY_SUCCESS; |
746 | 0 | } |
747 | | |
748 | | LIBYANG_API_DEF uint16_t |
749 | | ly_ctx_get_change_count(const struct ly_ctx *ctx) |
750 | 0 | { |
751 | 0 | LY_CHECK_ARG_RET(ctx, ctx, 0); |
752 | |
|
753 | 0 | return ctx->change_count; |
754 | 0 | } |
755 | | |
756 | | LIBYANG_API_DEF uint32_t |
757 | | ly_ctx_get_modules_hash(const struct ly_ctx *ctx) |
758 | 0 | { |
759 | 0 | LY_CHECK_ARG_RET(ctx, ctx, 0); |
760 | |
|
761 | 0 | return ctx->mod_hash; |
762 | 0 | } |
763 | | |
764 | | void |
765 | | ly_ctx_new_change(struct ly_ctx *ctx) |
766 | 30.3k | { |
767 | 30.3k | const struct lys_module *mod; |
768 | 30.3k | uint32_t i = ly_ctx_internal_modules_count(ctx), hash = 0, fi = 0; |
769 | 30.3k | struct lysp_feature *f = NULL; |
770 | | |
771 | | /* change counter */ |
772 | 30.3k | ctx->change_count++; |
773 | | |
774 | | /* module hash */ |
775 | 57.2k | while ((mod = ly_ctx_get_module_iter(ctx, &i))) { |
776 | | /* name */ |
777 | 26.9k | hash = lyht_hash_multi(hash, mod->name, strlen(mod->name)); |
778 | | |
779 | | /* revision */ |
780 | 26.9k | if (mod->revision) { |
781 | 0 | hash = lyht_hash_multi(hash, mod->revision, strlen(mod->revision)); |
782 | 0 | } |
783 | | |
784 | | /* enabled features */ |
785 | 26.9k | while ((f = lysp_feature_next(f, mod->parsed, &fi))) { |
786 | 0 | if (f->flags & LYS_FENABLED) { |
787 | 0 | hash = lyht_hash_multi(hash, f->name, strlen(f->name)); |
788 | 0 | } |
789 | 0 | } |
790 | | |
791 | | /* imported/implemented */ |
792 | 26.9k | hash = lyht_hash_multi(hash, (char *)&mod->implemented, sizeof mod->implemented); |
793 | 26.9k | } |
794 | | |
795 | 30.3k | ctx->mod_hash = lyht_hash_multi(hash, NULL, 0); |
796 | 30.3k | } |
797 | | |
798 | | LIBYANG_API_DEF ly_module_imp_clb |
799 | | ly_ctx_get_module_imp_clb(const struct ly_ctx *ctx, void **user_data) |
800 | 0 | { |
801 | 0 | LY_CHECK_ARG_RET(ctx, ctx, NULL); |
802 | |
|
803 | 0 | if (user_data) { |
804 | 0 | *user_data = ctx->imp_clb_data; |
805 | 0 | } |
806 | 0 | return ctx->imp_clb; |
807 | 0 | } |
808 | | |
809 | | LIBYANG_API_DEF void |
810 | | ly_ctx_set_module_imp_clb(struct ly_ctx *ctx, ly_module_imp_clb clb, void *user_data) |
811 | 0 | { |
812 | 0 | LY_CHECK_ARG_RET(ctx, ctx, ); |
813 | |
|
814 | 0 | ctx->imp_clb = clb; |
815 | 0 | ctx->imp_clb_data = user_data; |
816 | | |
817 | | /* new import callback - reset latests flags (possibly new revisions available) */ |
818 | 0 | for (uint32_t v = 0; v < ctx->list.count; ++v) { |
819 | 0 | struct lys_module *mod = ctx->list.objs[v]; |
820 | |
|
821 | 0 | mod->latest_revision &= ~LYS_MOD_LATEST_IMPCLB; |
822 | 0 | if (mod->parsed && mod->parsed->includes) { |
823 | 0 | for (LY_ARRAY_COUNT_TYPE u = 0; u < LY_ARRAY_COUNT(mod->parsed->includes); ++u) { |
824 | 0 | mod->parsed->includes[u].submodule->latest_revision &= ~LYS_MOD_LATEST_IMPCLB; |
825 | 0 | } |
826 | 0 | } |
827 | 0 | } |
828 | 0 | } |
829 | | |
830 | | LIBYANG_API_DEF ly_ext_data_clb |
831 | | ly_ctx_set_ext_data_clb(struct ly_ctx *ctx, ly_ext_data_clb clb, void *user_data) |
832 | 0 | { |
833 | 0 | ly_ext_data_clb prev; |
834 | |
|
835 | 0 | LY_CHECK_ARG_RET(ctx, ctx, NULL); |
836 | |
|
837 | 0 | prev = ctx->ext_clb; |
838 | 0 | ctx->ext_clb = clb; |
839 | 0 | ctx->ext_clb_data = user_data; |
840 | |
|
841 | 0 | return prev; |
842 | 0 | } |
843 | | |
844 | | LIBYANG_API_DEF struct lys_module * |
845 | | ly_ctx_get_module_iter(const struct ly_ctx *ctx, uint32_t *index) |
846 | 281k | { |
847 | 281k | LY_CHECK_ARG_RET(ctx, ctx, index, NULL); |
848 | | |
849 | 281k | if (*index < ctx->list.count) { |
850 | 231k | return ctx->list.objs[(*index)++]; |
851 | 231k | } else { |
852 | 50.5k | return NULL; |
853 | 50.5k | } |
854 | 281k | } |
855 | | |
856 | | /** |
857 | | * @brief Iterate over the modules in the given context. Returned modules must match the given key at the offset of |
858 | | * lysp_module and lysc_module structures (they are supposed to be placed at the same offset in both structures). |
859 | | * |
860 | | * @param[in] ctx Context where to iterate. |
861 | | * @param[in] key Key value to search for. |
862 | | * @param[in] key_size Optional length of the @p key. If zero, NULL-terminated key is expected. |
863 | | * @param[in] key_offset Key's offset in struct lys_module to get value from the context's modules to match with the key. |
864 | | * @param[in,out] Iterator to pass between the function calls. On the first call, the variable is supposed to be |
865 | | * initiated to 0. After each call returning a module, the value is greater by 1 than the index of the returned |
866 | | * module in the context. |
867 | | * @return Module matching the given key, NULL if no such module found. |
868 | | */ |
869 | | static struct lys_module * |
870 | | ly_ctx_get_module_by_iter(const struct ly_ctx *ctx, const char *key, size_t key_size, size_t key_offset, uint32_t *index) |
871 | 353k | { |
872 | 353k | struct lys_module *mod; |
873 | 353k | const char *value; |
874 | | |
875 | 2.60M | for ( ; *index < ctx->list.count; ++(*index)) { |
876 | 2.40M | mod = ctx->list.objs[*index]; |
877 | 2.40M | value = *(const char **)(((int8_t *)(mod)) + key_offset); |
878 | 2.40M | if ((!key_size && !strcmp(key, value)) || (key_size && !strncmp(key, value, key_size) && (value[key_size] == '\0'))) { |
879 | | /* increment index for the next run */ |
880 | 155k | ++(*index); |
881 | 155k | return mod; |
882 | 155k | } |
883 | 2.40M | } |
884 | | /* done */ |
885 | 198k | return NULL; |
886 | 353k | } |
887 | | |
888 | | /** |
889 | | * @brief Unifying function for ly_ctx_get_module() and ly_ctx_get_module_ns() |
890 | | * @param[in] ctx Context where to search. |
891 | | * @param[in] key Name or Namespace as a search key. |
892 | | * @param[in] key_offset Key's offset in struct lys_module to get value from the context's modules to match with the key. |
893 | | * @param[in] revision Revision date to match. If NULL, the matching module must have no revision. To search for the latest |
894 | | * revision module, use ly_ctx_get_module_latest_by(). |
895 | | * @return Matching module if any. |
896 | | */ |
897 | | static struct lys_module * |
898 | | ly_ctx_get_module_by(const struct ly_ctx *ctx, const char *key, size_t key_offset, const char *revision) |
899 | 37.0k | { |
900 | 37.0k | struct lys_module *mod; |
901 | 37.0k | uint32_t index = 0; |
902 | | |
903 | 37.0k | while ((mod = ly_ctx_get_module_by_iter(ctx, key, 0, key_offset, &index))) { |
904 | 3.37k | if (!revision) { |
905 | 0 | if (!mod->revision) { |
906 | | /* found requested module without revision */ |
907 | 0 | return mod; |
908 | 0 | } |
909 | 3.37k | } else { |
910 | 3.37k | if (mod->revision && !strcmp(mod->revision, revision)) { |
911 | | /* found requested module of the specific revision */ |
912 | 3.37k | return mod; |
913 | 3.37k | } |
914 | 3.37k | } |
915 | 3.37k | } |
916 | | |
917 | 33.7k | return NULL; |
918 | 37.0k | } |
919 | | |
920 | | LIBYANG_API_DEF struct lys_module * |
921 | | ly_ctx_get_module_ns(const struct ly_ctx *ctx, const char *ns, const char *revision) |
922 | 0 | { |
923 | 0 | LY_CHECK_ARG_RET(ctx, ctx, ns, NULL); |
924 | 0 | return ly_ctx_get_module_by(ctx, ns, offsetof(struct lys_module, ns), revision); |
925 | 0 | } |
926 | | |
927 | | LIBYANG_API_DEF struct lys_module * |
928 | | ly_ctx_get_module(const struct ly_ctx *ctx, const char *name, const char *revision) |
929 | 37.0k | { |
930 | 37.0k | LY_CHECK_ARG_RET(ctx, ctx, name, NULL); |
931 | 37.0k | return ly_ctx_get_module_by(ctx, name, offsetof(struct lys_module, name), revision); |
932 | 37.0k | } |
933 | | |
934 | | /** |
935 | | * @brief Unifying function for ly_ctx_get_module_latest() and ly_ctx_get_module_latest_ns() |
936 | | * @param[in] ctx Context where to search. |
937 | | * @param[in] key Name or Namespace as a search key. |
938 | | * @param[in] key_offset Key's offset in struct lys_module to get value from the context's modules to match with the key. |
939 | | * @return Matching module if any. |
940 | | */ |
941 | | static struct lys_module * |
942 | | ly_ctx_get_module_latest_by(const struct ly_ctx *ctx, const char *key, size_t key_offset) |
943 | 74.1k | { |
944 | 74.1k | struct lys_module *mod; |
945 | 74.1k | uint32_t index = 0; |
946 | | |
947 | 74.1k | while ((mod = ly_ctx_get_module_by_iter(ctx, key, 0, key_offset, &index))) { |
948 | 6.74k | if (mod->latest_revision & LYS_MOD_LATEST_REV) { |
949 | 6.74k | return mod; |
950 | 6.74k | } |
951 | 6.74k | } |
952 | | |
953 | 67.4k | return NULL; |
954 | 74.1k | } |
955 | | |
956 | | LIBYANG_API_DEF struct lys_module * |
957 | | ly_ctx_get_module_latest(const struct ly_ctx *ctx, const char *name) |
958 | 40.4k | { |
959 | 40.4k | LY_CHECK_ARG_RET(ctx, ctx, name, NULL); |
960 | 40.4k | return ly_ctx_get_module_latest_by(ctx, name, offsetof(struct lys_module, name)); |
961 | 40.4k | } |
962 | | |
963 | | LIBYANG_API_DEF struct lys_module * |
964 | | ly_ctx_get_module_latest_ns(const struct ly_ctx *ctx, const char *ns) |
965 | 33.7k | { |
966 | 33.7k | LY_CHECK_ARG_RET(ctx, ctx, ns, NULL); |
967 | 33.7k | return ly_ctx_get_module_latest_by(ctx, ns, offsetof(struct lys_module, ns)); |
968 | 33.7k | } |
969 | | |
970 | | /** |
971 | | * @brief Unifying function for ly_ctx_get_module_implemented() and ly_ctx_get_module_implemented_ns() |
972 | | * @param[in] ctx Context where to search. |
973 | | * @param[in] key Name or Namespace as a search key. |
974 | | * @param[in] key_size Optional length of the @p key. If zero, NULL-terminated key is expected. |
975 | | * @param[in] key_offset Key's offset in struct lys_module to get value from the context's modules to match with the key. |
976 | | * @return Matching module if any. |
977 | | */ |
978 | | static struct lys_module * |
979 | | ly_ctx_get_module_implemented_by(const struct ly_ctx *ctx, const char *key, size_t key_size, size_t key_offset) |
980 | 208k | { |
981 | 208k | struct lys_module *mod; |
982 | 208k | uint32_t index = 0; |
983 | | |
984 | 242k | while ((mod = ly_ctx_get_module_by_iter(ctx, key, key_size, key_offset, &index))) { |
985 | 144k | if (mod->implemented) { |
986 | 111k | return mod; |
987 | 111k | } |
988 | 144k | } |
989 | | |
990 | 97.4k | return NULL; |
991 | 208k | } |
992 | | |
993 | | LIBYANG_API_DEF struct lys_module * |
994 | | ly_ctx_get_module_implemented(const struct ly_ctx *ctx, const char *name) |
995 | 42.7k | { |
996 | 42.7k | LY_CHECK_ARG_RET(ctx, ctx, name, NULL); |
997 | 42.2k | return ly_ctx_get_module_implemented_by(ctx, name, 0, offsetof(struct lys_module, name)); |
998 | 42.7k | } |
999 | | |
1000 | | struct lys_module * |
1001 | | ly_ctx_get_module_implemented2(const struct ly_ctx *ctx, const char *name, size_t name_len) |
1002 | 166k | { |
1003 | 166k | LY_CHECK_ARG_RET(ctx, ctx, name, NULL); |
1004 | 166k | return ly_ctx_get_module_implemented_by(ctx, name, name_len, offsetof(struct lys_module, name)); |
1005 | 166k | } |
1006 | | |
1007 | | LIBYANG_API_DEF struct lys_module * |
1008 | | ly_ctx_get_module_implemented_ns(const struct ly_ctx *ctx, const char *ns) |
1009 | 0 | { |
1010 | 0 | LY_CHECK_ARG_RET(ctx, ctx, ns, NULL); |
1011 | 0 | return ly_ctx_get_module_implemented_by(ctx, ns, 0, offsetof(struct lys_module, ns)); |
1012 | 0 | } |
1013 | | |
1014 | | /** |
1015 | | * @brief Try to find a submodule in a module. |
1016 | | * |
1017 | | * @param[in] module Module where to search in. |
1018 | | * @param[in] submodule Name of the submodule to find. |
1019 | | * @param[in] revision Revision of the submodule to find. NULL for submodule with no revision. |
1020 | | * @param[in] latest Ignore @p revision and look for the latest revision. |
1021 | | * @return Pointer to the specified submodule if it is present in the context. |
1022 | | */ |
1023 | | static const struct lysp_submodule * |
1024 | | _ly_ctx_get_submodule2(const struct lys_module *module, const char *submodule, const char *revision, ly_bool latest) |
1025 | 151k | { |
1026 | 151k | struct lysp_include *inc; |
1027 | 151k | LY_ARRAY_COUNT_TYPE u; |
1028 | | |
1029 | 303k | LY_CHECK_ARG_RET(NULL, module, module->parsed, submodule, NULL); |
1030 | | |
1031 | 151k | LY_ARRAY_FOR(module->parsed->includes, u) { |
1032 | 0 | if (module->parsed->includes[u].submodule && !strcmp(submodule, module->parsed->includes[u].submodule->name)) { |
1033 | 0 | inc = &module->parsed->includes[u]; |
1034 | |
|
1035 | 0 | if (latest && inc->submodule->latest_revision) { |
1036 | | /* latest revision */ |
1037 | 0 | return inc->submodule; |
1038 | 0 | } else if (!revision && !inc->submodule->revs) { |
1039 | | /* no revision */ |
1040 | 0 | return inc->submodule; |
1041 | 0 | } else if (revision && inc->submodule->revs && !strcmp(revision, inc->submodule->revs[0].date)) { |
1042 | | /* specific revision */ |
1043 | 0 | return inc->submodule; |
1044 | 0 | } |
1045 | 0 | } |
1046 | 0 | } |
1047 | | |
1048 | 151k | return NULL; |
1049 | 151k | } |
1050 | | |
1051 | | /** |
1052 | | * @brief Try to find a submodule in the context. |
1053 | | * |
1054 | | * @param[in] ctx Context where to search in. |
1055 | | * @param[in] submodule Name of the submodule to find. |
1056 | | * @param[in] revision Revision of the submodule to find. NULL for submodule with no revision. |
1057 | | * @param[in] latest Ignore @p revision and look for the latest revision. |
1058 | | * @return Pointer to the specified submodule if it is present in the context. |
1059 | | */ |
1060 | | static const struct lysp_submodule * |
1061 | | _ly_ctx_get_submodule(const struct ly_ctx *ctx, const char *submodule, const char *revision, ly_bool latest) |
1062 | 33.7k | { |
1063 | 33.7k | const struct lys_module *mod; |
1064 | 33.7k | const struct lysp_submodule *submod = NULL; |
1065 | 33.7k | uint32_t v; |
1066 | | |
1067 | 33.7k | LY_CHECK_ARG_RET(ctx, ctx, submodule, NULL); |
1068 | | |
1069 | 185k | for (v = 0; v < ctx->list.count; ++v) { |
1070 | 151k | mod = ctx->list.objs[v]; |
1071 | 151k | if (!mod->parsed) { |
1072 | 0 | continue; |
1073 | 0 | } |
1074 | | |
1075 | 151k | submod = _ly_ctx_get_submodule2(mod, submodule, revision, latest); |
1076 | 151k | if (submod) { |
1077 | 0 | break; |
1078 | 0 | } |
1079 | 151k | } |
1080 | | |
1081 | 33.7k | return submod; |
1082 | 33.7k | } |
1083 | | |
1084 | | LIBYANG_API_DEF const struct lysp_submodule * |
1085 | | ly_ctx_get_submodule(const struct ly_ctx *ctx, const char *submodule, const char *revision) |
1086 | 0 | { |
1087 | 0 | return _ly_ctx_get_submodule(ctx, submodule, revision, 0); |
1088 | 0 | } |
1089 | | |
1090 | | LIBYANG_API_DEF const struct lysp_submodule * |
1091 | | ly_ctx_get_submodule_latest(const struct ly_ctx *ctx, const char *submodule) |
1092 | 33.7k | { |
1093 | 33.7k | return _ly_ctx_get_submodule(ctx, submodule, NULL, 1); |
1094 | 33.7k | } |
1095 | | |
1096 | | LIBYANG_API_DEF const struct lysp_submodule * |
1097 | | ly_ctx_get_submodule2(const struct lys_module *module, const char *submodule, const char *revision) |
1098 | 0 | { |
1099 | 0 | return _ly_ctx_get_submodule2(module, submodule, revision, 0); |
1100 | 0 | } |
1101 | | |
1102 | | LIBYANG_API_DEF const struct lysp_submodule * |
1103 | | ly_ctx_get_submodule2_latest(const struct lys_module *module, const char *submodule) |
1104 | 0 | { |
1105 | 0 | return _ly_ctx_get_submodule2(module, submodule, NULL, 1); |
1106 | 0 | } |
1107 | | |
1108 | | LIBYANG_API_DEF uint32_t |
1109 | | ly_ctx_internal_modules_count(const struct ly_ctx *ctx) |
1110 | 30.3k | { |
1111 | 30.3k | if (!ctx) { |
1112 | 0 | return 0; |
1113 | 0 | } |
1114 | | |
1115 | 30.3k | if (ctx->flags & LY_CTX_NO_YANGLIBRARY) { |
1116 | 0 | return LY_INTERNAL_MODS_COUNT - 2; |
1117 | 30.3k | } else { |
1118 | 30.3k | return LY_INTERNAL_MODS_COUNT; |
1119 | 30.3k | } |
1120 | 30.3k | } |
1121 | | |
1122 | | static LY_ERR |
1123 | | ylib_feature(struct lyd_node *parent, const struct lysp_module *pmod) |
1124 | 0 | { |
1125 | 0 | LY_ARRAY_COUNT_TYPE u; |
1126 | 0 | struct lysp_feature *f; |
1127 | |
|
1128 | 0 | if (!pmod->mod->implemented) { |
1129 | | /* no features can be enabled */ |
1130 | 0 | return LY_SUCCESS; |
1131 | 0 | } |
1132 | | |
1133 | 0 | LY_ARRAY_FOR(pmod->features, struct lysp_feature, f) { |
1134 | 0 | if (!(f->flags & LYS_FENABLED)) { |
1135 | 0 | continue; |
1136 | 0 | } |
1137 | | |
1138 | 0 | LY_CHECK_RET(lyd_new_term(parent, NULL, "feature", f->name, 0, NULL)); |
1139 | 0 | } |
1140 | | |
1141 | 0 | LY_ARRAY_FOR(pmod->includes, u) { |
1142 | 0 | LY_ARRAY_FOR(pmod->includes[u].submodule->features, struct lysp_feature, f) { |
1143 | 0 | if (!(f->flags & LYS_FENABLED)) { |
1144 | 0 | continue; |
1145 | 0 | } |
1146 | | |
1147 | 0 | LY_CHECK_RET(lyd_new_term(parent, NULL, "feature", f->name, 0, NULL)); |
1148 | 0 | } |
1149 | 0 | } |
1150 | | |
1151 | 0 | return LY_SUCCESS; |
1152 | 0 | } |
1153 | | |
1154 | | static LY_ERR |
1155 | | ylib_deviation(struct lyd_node *parent, const struct lys_module *cur_mod, ly_bool bis) |
1156 | 0 | { |
1157 | 0 | LY_ARRAY_COUNT_TYPE i; |
1158 | 0 | struct lys_module *mod; |
1159 | |
|
1160 | 0 | if (!cur_mod->implemented) { |
1161 | | /* no deviations of the module for certain */ |
1162 | 0 | return LY_SUCCESS; |
1163 | 0 | } |
1164 | | |
1165 | 0 | LY_ARRAY_FOR(cur_mod->deviated_by, i) { |
1166 | 0 | mod = cur_mod->deviated_by[i]; |
1167 | |
|
1168 | 0 | if (bis) { |
1169 | 0 | LY_CHECK_RET(lyd_new_term(parent, NULL, "deviation", mod->name, 0, NULL)); |
1170 | 0 | } else { |
1171 | 0 | LY_CHECK_RET(lyd_new_list(parent, NULL, "deviation", 0, NULL, mod->name, |
1172 | 0 | (mod->parsed->revs ? mod->parsed->revs[0].date : ""))); |
1173 | 0 | } |
1174 | 0 | } |
1175 | | |
1176 | 0 | return LY_SUCCESS; |
1177 | 0 | } |
1178 | | |
1179 | | static LY_ERR |
1180 | | ylib_submodules(struct lyd_node *parent, const struct lysp_module *pmod, ly_bool bis) |
1181 | 0 | { |
1182 | 0 | LY_ERR ret; |
1183 | 0 | LY_ARRAY_COUNT_TYPE i; |
1184 | 0 | struct lyd_node *cont; |
1185 | 0 | struct lysp_submodule *submod; |
1186 | 0 | int r; |
1187 | 0 | char *str; |
1188 | |
|
1189 | 0 | LY_ARRAY_FOR(pmod->includes, i) { |
1190 | 0 | submod = pmod->includes[i].submodule; |
1191 | |
|
1192 | 0 | if (bis) { |
1193 | 0 | LY_CHECK_RET(lyd_new_list(parent, NULL, "submodule", 0, &cont, submod->name)); |
1194 | |
|
1195 | 0 | if (submod->revs) { |
1196 | 0 | LY_CHECK_RET(lyd_new_term(cont, NULL, "revision", submod->revs[0].date, 0, NULL)); |
1197 | 0 | } |
1198 | 0 | } else { |
1199 | 0 | LY_CHECK_RET(lyd_new_list(parent, NULL, "submodule", 0, &cont, submod->name, |
1200 | 0 | (submod->revs ? submod->revs[0].date : ""))); |
1201 | 0 | } |
1202 | | |
1203 | 0 | if (submod->filepath) { |
1204 | 0 | r = asprintf(&str, "file://%s", submod->filepath); |
1205 | 0 | LY_CHECK_ERR_RET(r == -1, LOGMEM(pmod->mod->ctx), LY_EMEM); |
1206 | |
|
1207 | 0 | ret = lyd_new_term(cont, NULL, bis ? "location" : "schema", str, 0, NULL); |
1208 | 0 | free(str); |
1209 | 0 | LY_CHECK_RET(ret); |
1210 | 0 | } |
1211 | 0 | } |
1212 | | |
1213 | 0 | return LY_SUCCESS; |
1214 | 0 | } |
1215 | | |
1216 | | LIBYANG_API_DEF LY_ERR |
1217 | | ly_ctx_get_yanglib_data(const struct ly_ctx *ctx, struct lyd_node **root_p, const char *content_id_format, ...) |
1218 | 0 | { |
1219 | 0 | LY_ERR ret; |
1220 | 0 | uint32_t i; |
1221 | 0 | ly_bool bis = 0; |
1222 | 0 | int r; |
1223 | 0 | char *str; |
1224 | 0 | const struct lys_module *mod; |
1225 | 0 | struct lyd_node *root = NULL, *root_bis = NULL, *cont, *set_bis = NULL; |
1226 | 0 | va_list ap; |
1227 | |
|
1228 | 0 | LY_CHECK_ARG_RET(ctx, ctx, root_p, LY_EINVAL); |
1229 | |
|
1230 | 0 | mod = ly_ctx_get_module_implemented(ctx, "ietf-yang-library"); |
1231 | 0 | LY_CHECK_ERR_RET(!mod, LOGERR(ctx, LY_EINVAL, "Module \"ietf-yang-library\" is not implemented."), LY_EINVAL); |
1232 | |
|
1233 | 0 | if (mod->parsed->revs && !strcmp(mod->parsed->revs[0].date, "2016-06-21")) { |
1234 | 0 | bis = 0; |
1235 | 0 | } else if (mod->parsed->revs && !strcmp(mod->parsed->revs[0].date, IETF_YANG_LIB_REV)) { |
1236 | 0 | bis = 1; |
1237 | 0 | } else { |
1238 | 0 | LOGERR(ctx, LY_EINVAL, "Incompatible ietf-yang-library version in context."); |
1239 | 0 | return LY_EINVAL; |
1240 | 0 | } |
1241 | | |
1242 | 0 | LY_CHECK_GOTO(ret = lyd_new_inner(NULL, mod, "modules-state", 0, &root), error); |
1243 | |
|
1244 | 0 | if (bis) { |
1245 | 0 | LY_CHECK_GOTO(ret = lyd_new_inner(NULL, mod, "yang-library", 0, &root_bis), error); |
1246 | 0 | LY_CHECK_GOTO(ret = lyd_new_list(root_bis, NULL, "module-set", 0, &set_bis, "complete"), error); |
1247 | 0 | } |
1248 | | |
1249 | 0 | for (i = 0; i < ctx->list.count; ++i) { |
1250 | 0 | mod = ctx->list.objs[i]; |
1251 | 0 | if (!mod->parsed) { |
1252 | 0 | LOGERR(ctx, LY_ENOTFOUND, "Parsed module \"%s\" missing in the context.", mod->name); |
1253 | 0 | goto error; |
1254 | 0 | } |
1255 | | |
1256 | | /* |
1257 | | * deprecated legacy |
1258 | | */ |
1259 | 0 | LY_CHECK_GOTO(ret = lyd_new_list(root, NULL, "module", 0, &cont, mod->name, |
1260 | 0 | (mod->parsed->revs ? mod->parsed->revs[0].date : "")), error); |
1261 | | |
1262 | | /* schema */ |
1263 | 0 | if (mod->filepath) { |
1264 | 0 | r = asprintf(&str, "file://%s", mod->filepath); |
1265 | 0 | LY_CHECK_ERR_GOTO(r == -1, LOGMEM(ctx); ret = LY_EMEM, error); |
1266 | |
|
1267 | 0 | ret = lyd_new_term(cont, NULL, "schema", str, 0, NULL); |
1268 | 0 | free(str); |
1269 | 0 | LY_CHECK_GOTO(ret, error); |
1270 | 0 | } |
1271 | | |
1272 | | /* namespace */ |
1273 | 0 | LY_CHECK_GOTO(ret = lyd_new_term(cont, NULL, "namespace", mod->ns, 0, NULL), error); |
1274 | | |
1275 | | /* feature leaf-list */ |
1276 | 0 | LY_CHECK_GOTO(ret = ylib_feature(cont, mod->parsed), error); |
1277 | | |
1278 | | /* deviation list */ |
1279 | 0 | LY_CHECK_GOTO(ret = ylib_deviation(cont, mod, 0), error); |
1280 | | |
1281 | | /* conformance-type */ |
1282 | 0 | LY_CHECK_GOTO(ret = lyd_new_term(cont, NULL, "conformance-type", mod->implemented ? "implement" : "import", 0, NULL), error); |
1283 | | |
1284 | | /* submodule list */ |
1285 | 0 | LY_CHECK_GOTO(ret = ylib_submodules(cont, mod->parsed, 0), error); |
1286 | | |
1287 | | /* |
1288 | | * current revision |
1289 | | */ |
1290 | 0 | if (bis) { |
1291 | | /* name and revision */ |
1292 | 0 | if (mod->implemented) { |
1293 | 0 | LY_CHECK_GOTO(ret = lyd_new_list(set_bis, NULL, "module", 0, &cont, mod->name), error); |
1294 | |
|
1295 | 0 | if (mod->parsed->revs) { |
1296 | 0 | LY_CHECK_GOTO(ret = lyd_new_term(cont, NULL, "revision", mod->parsed->revs[0].date, 0, NULL), error); |
1297 | 0 | } |
1298 | 0 | } else { |
1299 | 0 | LY_CHECK_GOTO(ret = lyd_new_list(set_bis, NULL, "import-only-module", 0, &cont, mod->name, |
1300 | 0 | (mod->parsed->revs ? mod->parsed->revs[0].date : "")), error); |
1301 | 0 | } |
1302 | | |
1303 | | /* namespace */ |
1304 | 0 | LY_CHECK_GOTO(ret = lyd_new_term(cont, NULL, "namespace", mod->ns, 0, NULL), error); |
1305 | | |
1306 | | /* location */ |
1307 | 0 | if (mod->filepath) { |
1308 | 0 | r = asprintf(&str, "file://%s", mod->filepath); |
1309 | 0 | LY_CHECK_ERR_GOTO(r == -1, LOGMEM(ctx); ret = LY_EMEM, error); |
1310 | |
|
1311 | 0 | ret = lyd_new_term(cont, NULL, "location", str, 0, NULL); |
1312 | 0 | free(str); |
1313 | 0 | LY_CHECK_GOTO(ret, error); |
1314 | 0 | } |
1315 | | |
1316 | | /* submodule list */ |
1317 | 0 | LY_CHECK_GOTO(ret = ylib_submodules(cont, mod->parsed, 1), error); |
1318 | | |
1319 | | /* feature list */ |
1320 | 0 | LY_CHECK_GOTO(ret = ylib_feature(cont, mod->parsed), error); |
1321 | | |
1322 | | /* deviation */ |
1323 | 0 | LY_CHECK_GOTO(ret = ylib_deviation(cont, mod, 1), error); |
1324 | 0 | } |
1325 | 0 | } |
1326 | | |
1327 | | /* IDs */ |
1328 | 0 | va_start(ap, content_id_format); |
1329 | 0 | r = vasprintf(&str, content_id_format, ap); |
1330 | 0 | va_end(ap); |
1331 | 0 | LY_CHECK_ERR_GOTO(r == -1, LOGMEM(ctx); ret = LY_EMEM, error); |
1332 | 0 | ret = lyd_new_term(root, NULL, "module-set-id", str, 0, NULL); |
1333 | 0 | LY_CHECK_ERR_GOTO(ret, free(str), error); |
1334 | |
|
1335 | 0 | if (bis) { |
1336 | | /* create one complete schema */ |
1337 | 0 | LY_CHECK_ERR_GOTO(ret = lyd_new_list(root_bis, NULL, "schema", 0, &cont, "complete"), free(str), error); |
1338 | |
|
1339 | 0 | LY_CHECK_ERR_GOTO(ret = lyd_new_term(cont, NULL, "module-set", "complete", 0, NULL), free(str), error); |
1340 | | |
1341 | | /* content-id */ |
1342 | 0 | LY_CHECK_ERR_GOTO(ret = lyd_new_term(root_bis, NULL, "content-id", str, 0, NULL), free(str), error); |
1343 | 0 | } |
1344 | 0 | free(str); |
1345 | |
|
1346 | 0 | if (root_bis) { |
1347 | 0 | if (lyd_insert_sibling(root, root_bis, &root)) { |
1348 | 0 | goto error; |
1349 | 0 | } |
1350 | 0 | root_bis = NULL; |
1351 | 0 | } |
1352 | | |
1353 | 0 | LY_CHECK_GOTO(ret = lyd_validate_all(&root, NULL, LYD_VALIDATE_PRESENT, NULL), error); |
1354 | |
|
1355 | 0 | *root_p = root; |
1356 | 0 | return LY_SUCCESS; |
1357 | | |
1358 | 0 | error: |
1359 | 0 | lyd_free_all(root); |
1360 | 0 | lyd_free_all(root_bis); |
1361 | 0 | return ret; |
1362 | 0 | } |
1363 | | |
1364 | | /** |
1365 | | * @brief Callback for freeing context error hash table values. |
1366 | | * |
1367 | | * @param[in] val_p Pointer to a pointer to an error item to free with all the siblings. |
1368 | | */ |
1369 | | static void |
1370 | | ly_ctx_ht_err_rec_free(void *val_p) |
1371 | 4 | { |
1372 | 4 | struct ly_ctx_err_rec *err = val_p; |
1373 | | |
1374 | 4 | ly_err_free(err->err); |
1375 | 4 | } |
1376 | | |
1377 | | LIBYANG_API_DEF void |
1378 | | ly_ctx_destroy(struct ly_ctx *ctx) |
1379 | 3.37k | { |
1380 | 3.37k | struct lysf_ctx fctx = {.ctx = ctx}; |
1381 | | |
1382 | 3.37k | if (!ctx) { |
1383 | 0 | return; |
1384 | 0 | } |
1385 | | |
1386 | | /* modules list */ |
1387 | 37.0k | for ( ; ctx->list.count; ctx->list.count--) { |
1388 | 33.7k | fctx.mod = ctx->list.objs[ctx->list.count - 1]; |
1389 | | |
1390 | | /* remove the module */ |
1391 | 33.7k | if (fctx.mod->implemented) { |
1392 | 20.2k | fctx.mod->implemented = 0; |
1393 | 20.2k | lysc_module_free(&fctx, fctx.mod->compiled); |
1394 | 20.2k | fctx.mod->compiled = NULL; |
1395 | 20.2k | } |
1396 | 33.7k | lys_module_free(&fctx, fctx.mod, 0); |
1397 | 33.7k | } |
1398 | 3.37k | free(ctx->list.objs); |
1399 | | |
1400 | | /* free extensions */ |
1401 | 3.37k | lysf_ctx_erase(&fctx); |
1402 | | |
1403 | | /* search paths list */ |
1404 | 3.37k | ly_set_erase(&ctx->search_paths, free); |
1405 | | |
1406 | | /* leftover unres */ |
1407 | 3.37k | lys_unres_glob_erase(&ctx->unres); |
1408 | | |
1409 | | /* clean the leafref links hash table */ |
1410 | 3.37k | if (ctx->leafref_links_ht) { |
1411 | 0 | lyht_free(ctx->leafref_links_ht, ly_ctx_ht_leafref_links_rec_free); |
1412 | 0 | } |
1413 | | |
1414 | | /* clean the error hash table */ |
1415 | 3.37k | lyht_free(ctx->err_ht, ly_ctx_ht_err_rec_free); |
1416 | | |
1417 | | /* dictionary */ |
1418 | 3.37k | lydict_clean(&ctx->dict); |
1419 | | |
1420 | | /* LYB hash lock */ |
1421 | 3.37k | pthread_mutex_destroy(&ctx->lyb_hash_lock); |
1422 | | |
1423 | | /* context specific plugins */ |
1424 | 3.37k | ly_set_erase(&ctx->plugins_types, NULL); |
1425 | 3.37k | ly_set_erase(&ctx->plugins_extensions, NULL); |
1426 | | |
1427 | | /* shared plugins - will be removed only if this is the last context */ |
1428 | 3.37k | lyplg_clean(); |
1429 | | |
1430 | 3.37k | free(ctx); |
1431 | 3.37k | } |