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