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