/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 | 118k | #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 | 18.2k | { |
161 | 18.2k | #define LY_CTX_SEARCHDIRS_SIZE_STEP 8 |
162 | 18.2k | void **new; |
163 | | |
164 | 18.2k | LY_CHECK_ARG_RET(ctx, ctx, NULL); |
165 | | |
166 | 18.2k | if (ctx->search_paths.count == ctx->search_paths.size) { |
167 | | /* not enough space for terminating NULL byte */ |
168 | 9.13k | new = realloc(((struct ly_ctx *)ctx)->search_paths.objs, |
169 | 9.13k | (ctx->search_paths.size + LY_CTX_SEARCHDIRS_SIZE_STEP) * sizeof *ctx->search_paths.objs); |
170 | 9.13k | LY_CHECK_ERR_RET(!new, LOGMEM(NULL), NULL); |
171 | 9.13k | ((struct ly_ctx *)ctx)->search_paths.size += LY_CTX_SEARCHDIRS_SIZE_STEP; |
172 | 9.13k | ((struct ly_ctx *)ctx)->search_paths.objs = new; |
173 | 9.13k | } |
174 | | /* set terminating NULL byte to the strings list */ |
175 | 18.2k | ctx->search_paths.objs[ctx->search_paths.count] = NULL; |
176 | | |
177 | 18.2k | return (const char * const *)ctx->search_paths.objs; |
178 | 18.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 | 9.13k | { |
267 | 9.13k | struct ly_ctx *ctx = NULL; |
268 | 9.13k | struct lys_module *module; |
269 | 9.13k | char *search_dir_list, *sep, *dir; |
270 | 9.13k | const char **imp_f, *all_f[] = {"*", NULL}; |
271 | 9.13k | uint32_t i; |
272 | 9.13k | struct ly_in *in = NULL; |
273 | 9.13k | LY_ERR rc = LY_SUCCESS; |
274 | 9.13k | struct lys_glob_unres unres = {0}; |
275 | 9.13k | ly_bool builtin_plugins_only, static_plugins_only; |
276 | | |
277 | 9.13k | LY_CHECK_ARG_RET(NULL, new_ctx, LY_EINVAL); |
278 | | |
279 | 9.13k | ctx = calloc(1, sizeof *ctx); |
280 | 9.13k | LY_CHECK_ERR_GOTO(!ctx, LOGMEM(NULL); rc = LY_EMEM, cleanup); |
281 | | |
282 | | /* dictionary */ |
283 | 9.13k | lydict_init(&ctx->dict); |
284 | | |
285 | | /* plugins */ |
286 | 9.13k | builtin_plugins_only = (options & LY_CTX_BUILTIN_PLUGINS_ONLY) ? 1 : 0; |
287 | 9.13k | static_plugins_only = (options & LY_CTX_STATIC_PLUGINS_ONLY) ? 1 : 0; |
288 | 9.13k | LY_CHECK_ERR_GOTO(lyplg_init(builtin_plugins_only, static_plugins_only), LOGINT(NULL); rc = LY_EINT, cleanup); |
289 | | |
290 | | /* options */ |
291 | 9.13k | ctx->opts = options; |
292 | | |
293 | | /* ctx data */ |
294 | 9.13k | rc = ly_ctx_data_add(ctx); |
295 | 9.13k | LY_CHECK_GOTO(rc, cleanup); |
296 | | |
297 | | /* modules list */ |
298 | 9.13k | 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 | 9.13k | ctx->change_count = 1; |
323 | | |
324 | 9.13k | if (!(options & LY_CTX_EXPLICIT_COMPILE)) { |
325 | | /* use it for creating the initial context */ |
326 | 9.13k | ctx->opts |= LY_CTX_EXPLICIT_COMPILE; |
327 | 9.13k | } |
328 | | |
329 | | /* create dummy in */ |
330 | 9.13k | rc = ly_in_new_memory(internal_modules[0].data, &in); |
331 | 9.13k | LY_CHECK_GOTO(rc, cleanup); |
332 | | |
333 | | /* load internal modules */ |
334 | 91.3k | for (i = 0; i < ((options & LY_CTX_NO_YANGLIBRARY) ? (LY_INTERNAL_MODS_COUNT - 2) : LY_INTERNAL_MODS_COUNT); i++) { |
335 | 82.2k | ly_in_memory(in, internal_modules[i].data); |
336 | 82.2k | LY_CHECK_GOTO(rc = lys_parse_in(ctx, in, internal_modules[i].format, NULL, &unres.creating, &module), cleanup); |
337 | 82.2k | if (internal_modules[i].implemented || (ctx->opts & LY_CTX_ALL_IMPLEMENTED)) { |
338 | 54.8k | imp_f = (ctx->opts & LY_CTX_ENABLE_IMP_FEATURES) ? all_f : NULL; |
339 | 54.8k | LY_CHECK_GOTO(rc = lys_implement(module, imp_f, &unres), cleanup); |
340 | 54.8k | } |
341 | 82.2k | } |
342 | | |
343 | 9.13k | if (!(options & LY_CTX_EXPLICIT_COMPILE)) { |
344 | | /* compile now */ |
345 | 9.13k | LY_CHECK_GOTO(rc = ly_ctx_compile(ctx), cleanup); |
346 | 9.13k | ctx->opts &= ~LY_CTX_EXPLICIT_COMPILE; |
347 | 9.13k | } |
348 | | |
349 | 9.13k | cleanup: |
350 | 9.13k | ly_in_free(in, 0); |
351 | 9.13k | lys_unres_glob_erase(&unres); |
352 | 9.13k | if (rc) { |
353 | 0 | ly_ctx_destroy(ctx); |
354 | 9.13k | } else { |
355 | 9.13k | *new_ctx = ctx; |
356 | 9.13k | } |
357 | 9.13k | return rc; |
358 | 9.13k | } |
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 | 9.13k | { |
582 | 9.13k | LY_ERR ret = LY_SUCCESS; |
583 | | |
584 | 9.13k | 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 | 9.13k | 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 | 9.13k | LY_CHECK_GOTO(ret = lys_compile_depset_all(ctx, &ctx->unres), cleanup); |
591 | | |
592 | | /* new context state */ |
593 | 9.13k | ly_ctx_new_change(ctx); |
594 | | |
595 | 9.13k | cleanup: |
596 | 9.13k | if (ret) { |
597 | | /* revert changes of modules */ |
598 | 0 | lys_unres_glob_revert(ctx, &ctx->unres); |
599 | 0 | } |
600 | 9.13k | lys_unres_glob_erase(&ctx->unres); |
601 | 9.13k | return ret; |
602 | 9.13k | } |
603 | | |
604 | | LIBYANG_API_DEF uint32_t |
605 | | ly_ctx_get_options(const struct ly_ctx *ctx) |
606 | 733k | { |
607 | 733k | LY_CHECK_ARG_RET(ctx, ctx, 0); |
608 | | |
609 | 733k | return ctx->opts; |
610 | 733k | } |
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 | | void |
755 | | ly_ctx_new_change(struct ly_ctx *ctx) |
756 | 27.4k | { |
757 | 27.4k | const struct lys_module *mod; |
758 | 27.4k | uint32_t i = ly_ctx_internal_modules_count(ctx), hash = 0; |
759 | 27.4k | LY_ARRAY_COUNT_TYPE u; |
760 | | |
761 | | /* change counter */ |
762 | 27.4k | ctx->change_count++; |
763 | | |
764 | | /* module hash */ |
765 | 54.8k | while ((mod = ly_ctx_get_module_iter(ctx, &i))) { |
766 | | /* name */ |
767 | 27.4k | hash = lyht_hash_multi(hash, mod->name, strlen(mod->name)); |
768 | | |
769 | | /* revision */ |
770 | 27.4k | if (mod->revision) { |
771 | 0 | hash = lyht_hash_multi(hash, mod->revision, strlen(mod->revision)); |
772 | 0 | } |
773 | | |
774 | | /* enabled features */ |
775 | 27.4k | if (mod->implemented) { |
776 | 27.4k | LY_ARRAY_FOR(mod->compiled->features, u) { |
777 | 0 | hash = lyht_hash_multi(hash, mod->compiled->features[u], strlen(mod->compiled->features[u])); |
778 | 0 | } |
779 | 27.4k | } |
780 | | |
781 | | /* imported/implemented */ |
782 | 27.4k | hash = lyht_hash_multi(hash, (char *)&mod->implemented, sizeof mod->implemented); |
783 | 27.4k | } |
784 | | |
785 | 27.4k | ctx->mod_hash = lyht_hash_multi(hash, NULL, 0); |
786 | 27.4k | } |
787 | | |
788 | | LIBYANG_API_DEF ly_module_imp_clb |
789 | | ly_ctx_get_module_imp_clb(const struct ly_ctx *ctx, void **user_data) |
790 | 0 | { |
791 | 0 | LY_CHECK_ARG_RET(ctx, ctx, NULL); |
792 | |
|
793 | 0 | if (user_data) { |
794 | 0 | *user_data = ctx->imp_clb_data; |
795 | 0 | } |
796 | 0 | return ctx->imp_clb; |
797 | 0 | } |
798 | | |
799 | | LIBYANG_API_DEF void |
800 | | ly_ctx_set_module_imp_clb(struct ly_ctx *ctx, ly_module_imp_clb clb, void *user_data) |
801 | 0 | { |
802 | 0 | LY_CHECK_ARG_RET(ctx, ctx, !(ctx->opts & LY_CTX_INT_IMMUTABLE), ); |
803 | |
|
804 | 0 | ctx->imp_clb = clb; |
805 | 0 | ctx->imp_clb_data = user_data; |
806 | | |
807 | | /* new import callback - reset latests flags (possibly new revisions available) */ |
808 | 0 | for (uint32_t v = 0; v < ctx->modules.count; ++v) { |
809 | 0 | struct lys_module *mod = ctx->modules.objs[v]; |
810 | |
|
811 | 0 | mod->latest_revision &= ~LYS_MOD_LATEST_IMPCLB; |
812 | 0 | if (mod->parsed && mod->parsed->includes) { |
813 | 0 | for (LY_ARRAY_COUNT_TYPE u = 0; u < LY_ARRAY_COUNT(mod->parsed->includes); ++u) { |
814 | 0 | mod->parsed->includes[u].submodule->latest_revision &= ~LYS_MOD_LATEST_IMPCLB; |
815 | 0 | } |
816 | 0 | } |
817 | 0 | } |
818 | 0 | } |
819 | | |
820 | | LIBYANG_API_DEF ly_ext_data_clb |
821 | | ly_ctx_set_ext_data_clb(const struct ly_ctx *ctx, ly_ext_data_clb clb, void *user_data) |
822 | 0 | { |
823 | 0 | struct ly_ctx_shared_data *ctx_data; |
824 | 0 | ly_ext_data_clb prev; |
825 | |
|
826 | 0 | LY_CHECK_ARG_RET(ctx, ctx, NULL); |
827 | |
|
828 | 0 | ctx_data = ly_ctx_shared_data_get(ctx); |
829 | | |
830 | | /* EXT CLB LOCK */ |
831 | 0 | pthread_mutex_lock(&ctx_data->ext_clb_lock); |
832 | |
|
833 | 0 | prev = ctx_data->ext_clb; |
834 | 0 | ctx_data->ext_clb = clb; |
835 | 0 | ctx_data->ext_clb_data = user_data; |
836 | | |
837 | | /* EXT CLB UNLOCK */ |
838 | 0 | pthread_mutex_unlock(&ctx_data->ext_clb_lock); |
839 | |
|
840 | 0 | return prev; |
841 | 0 | } |
842 | | |
843 | | LIBYANG_API_DEF struct lys_module * |
844 | | ly_ctx_get_module_iter(const struct ly_ctx *ctx, uint32_t *index) |
845 | 703k | { |
846 | 703k | LY_CHECK_ARG_RET(ctx, ctx, index, NULL); |
847 | | |
848 | 703k | if (*index < ctx->modules.count) { |
849 | 621k | return ctx->modules.objs[(*index)++]; |
850 | 621k | } else { |
851 | 82.3k | return NULL; |
852 | 82.3k | } |
853 | 703k | } |
854 | | |
855 | | /** |
856 | | * @brief Iterate over the modules in the given context. Returned modules must match the given key at the offset of |
857 | | * lysp_module and lysc_module structures (they are supposed to be placed at the same offset in both structures). |
858 | | * |
859 | | * @param[in] ctx Context where to iterate. |
860 | | * @param[in] key Key value to search for. |
861 | | * @param[in] key_size Optional length of the @p key. If zero, NULL-terminated key is expected. |
862 | | * @param[in] key_offset Key's offset in struct lys_module to get value from the context's modules to match with the key. |
863 | | * @param[in,out] Iterator to pass between the function calls. On the first call, the variable is supposed to be |
864 | | * initiated to 0. After each call returning a module, the value is greater by 1 than the index of the returned |
865 | | * module in the context. |
866 | | * @return Module matching the given key, NULL if no such module found. |
867 | | */ |
868 | | static struct lys_module * |
869 | | 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) |
870 | 1.17M | { |
871 | 1.17M | struct lys_module *mod; |
872 | 1.17M | const char *value; |
873 | | |
874 | 9.52M | for ( ; *index < ctx->modules.count; ++(*index)) { |
875 | 9.10M | mod = ctx->modules.objs[*index]; |
876 | 9.10M | value = *(const char **)(((int8_t *)(mod)) + key_offset); |
877 | 9.10M | if ((!key_size && !strcmp(key, value)) || (key_size && !strncmp(key, value, key_size) && (value[key_size] == '\0'))) { |
878 | | /* increment index for the next run */ |
879 | 761k | ++(*index); |
880 | 761k | return mod; |
881 | 761k | } |
882 | 9.10M | } |
883 | | /* done */ |
884 | 415k | return NULL; |
885 | 1.17M | } |
886 | | |
887 | | /** |
888 | | * @brief Unifying function for ly_ctx_get_module() and ly_ctx_get_module_ns() |
889 | | * @param[in] ctx Context where to search. |
890 | | * @param[in] key Name or Namespace as a search key. |
891 | | * @param[in] key_offset Key's offset in struct lys_module to get value from the context's modules to match with the key. |
892 | | * @param[in] revision Revision date to match. If NULL, the matching module must have no revision. To search for the latest |
893 | | * revision module, use ly_ctx_get_module_latest_by(). |
894 | | * @return Matching module if any. |
895 | | */ |
896 | | static struct lys_module * |
897 | | ly_ctx_get_module_by(const struct ly_ctx *ctx, const char *key, size_t key_offset, const char *revision) |
898 | 118k | { |
899 | 118k | struct lys_module *mod; |
900 | 118k | uint32_t index = 0; |
901 | | |
902 | 118k | while ((mod = ly_ctx_get_module_by_iter(ctx, key, 0, key_offset, &index))) { |
903 | 18.2k | if (!revision) { |
904 | 0 | if (!mod->revision) { |
905 | | /* found requested module without revision */ |
906 | 0 | return mod; |
907 | 0 | } |
908 | 18.2k | } else { |
909 | 18.2k | if (mod->revision && !strcmp(mod->revision, revision)) { |
910 | | /* found requested module of the specific revision */ |
911 | 18.2k | return mod; |
912 | 18.2k | } |
913 | 18.2k | } |
914 | 18.2k | } |
915 | | |
916 | 100k | return NULL; |
917 | 118k | } |
918 | | |
919 | | LIBYANG_API_DEF struct lys_module * |
920 | | ly_ctx_get_module_ns(const struct ly_ctx *ctx, const char *ns, const char *revision) |
921 | 0 | { |
922 | 0 | LY_CHECK_ARG_RET(ctx, ctx, ns, NULL); |
923 | 0 | return ly_ctx_get_module_by(ctx, ns, offsetof(struct lys_module, ns), revision); |
924 | 0 | } |
925 | | |
926 | | LIBYANG_API_DEF struct lys_module * |
927 | | ly_ctx_get_module(const struct ly_ctx *ctx, const char *name, const char *revision) |
928 | 118k | { |
929 | 118k | LY_CHECK_ARG_RET(ctx, ctx, name, NULL); |
930 | 118k | return ly_ctx_get_module_by(ctx, name, offsetof(struct lys_module, name), revision); |
931 | 118k | } |
932 | | |
933 | | /** |
934 | | * @brief Unifying function for ly_ctx_get_module_latest() and ly_ctx_get_module_latest_ns() |
935 | | * @param[in] ctx Context where to search. |
936 | | * @param[in] key Name or Namespace as a search key. |
937 | | * @param[in] key_offset Key's offset in struct lys_module to get value from the context's modules to match with the key. |
938 | | * @return Matching module if any. |
939 | | */ |
940 | | static struct lys_module * |
941 | | ly_ctx_get_module_latest_by(const struct ly_ctx *ctx, const char *key, size_t key_offset) |
942 | 219k | { |
943 | 219k | struct lys_module *mod; |
944 | 219k | uint32_t index = 0; |
945 | | |
946 | 219k | while ((mod = ly_ctx_get_module_by_iter(ctx, key, 0, key_offset, &index))) { |
947 | 18.2k | if (mod->latest_revision & LYS_MOD_LATEST_REV) { |
948 | 18.2k | return mod; |
949 | 18.2k | } |
950 | 18.2k | } |
951 | | |
952 | 201k | return NULL; |
953 | 219k | } |
954 | | |
955 | | LIBYANG_API_DEF struct lys_module * |
956 | | ly_ctx_get_module_latest(const struct ly_ctx *ctx, const char *name) |
957 | 118k | { |
958 | 118k | LY_CHECK_ARG_RET(ctx, ctx, name, NULL); |
959 | 118k | return ly_ctx_get_module_latest_by(ctx, name, offsetof(struct lys_module, name)); |
960 | 118k | } |
961 | | |
962 | | LIBYANG_API_DEF struct lys_module * |
963 | | ly_ctx_get_module_latest_ns(const struct ly_ctx *ctx, const char *ns) |
964 | 100k | { |
965 | 100k | LY_CHECK_ARG_RET(ctx, ctx, ns, NULL); |
966 | 100k | return ly_ctx_get_module_latest_by(ctx, ns, offsetof(struct lys_module, ns)); |
967 | 100k | } |
968 | | |
969 | | /** |
970 | | * @brief Unifying function for ly_ctx_get_module_implemented() and ly_ctx_get_module_implemented_ns() |
971 | | * @param[in] ctx Context where to search. |
972 | | * @param[in] key Name or Namespace as a search key. |
973 | | * @param[in] key_size Optional length of the @p key. If zero, NULL-terminated key is expected. |
974 | | * @param[in] key_offset Key's offset in struct lys_module to get value from the context's modules to match with the key. |
975 | | * @return Matching module if any. |
976 | | */ |
977 | | static struct lys_module * |
978 | | ly_ctx_get_module_implemented_by(const struct ly_ctx *ctx, const char *key, size_t key_size, size_t key_offset) |
979 | 728k | { |
980 | 728k | struct lys_module *mod; |
981 | 728k | uint32_t index = 0; |
982 | | |
983 | 838k | while ((mod = ly_ctx_get_module_by_iter(ctx, key, key_size, key_offset, &index))) { |
984 | 724k | if (mod->implemented) { |
985 | 614k | return mod; |
986 | 614k | } |
987 | 724k | } |
988 | | |
989 | 113k | return NULL; |
990 | 728k | } |
991 | | |
992 | | LIBYANG_API_DEF struct lys_module * |
993 | | ly_ctx_get_module_implemented(const struct ly_ctx *ctx, const char *name) |
994 | 127k | { |
995 | 127k | LY_CHECK_ARG_RET(ctx, ctx, name, NULL); |
996 | 127k | return ly_ctx_get_module_implemented_by(ctx, name, 0, offsetof(struct lys_module, name)); |
997 | 127k | } |
998 | | |
999 | | struct lys_module * |
1000 | | ly_ctx_get_module_implemented2(const struct ly_ctx *ctx, const char *name, size_t name_len) |
1001 | 0 | { |
1002 | 0 | LY_CHECK_ARG_RET(ctx, ctx, name, NULL); |
1003 | 0 | return ly_ctx_get_module_implemented_by(ctx, name, name_len, offsetof(struct lys_module, name)); |
1004 | 0 | } |
1005 | | |
1006 | | LIBYANG_API_DEF struct lys_module * |
1007 | | ly_ctx_get_module_implemented_ns(const struct ly_ctx *ctx, const char *ns) |
1008 | 600k | { |
1009 | 600k | LY_CHECK_ARG_RET(ctx, ctx, ns, NULL); |
1010 | 600k | return ly_ctx_get_module_implemented_by(ctx, ns, 0, offsetof(struct lys_module, ns)); |
1011 | 600k | } |
1012 | | |
1013 | | /** |
1014 | | * @brief Try to find a submodule in a module. |
1015 | | * |
1016 | | * @param[in] module Module where to search in. |
1017 | | * @param[in] submodule Name of the submodule to find. |
1018 | | * @param[in] revision Revision of the submodule to find. NULL for submodule with no revision. |
1019 | | * @param[in] latest Ignore @p revision and look for the latest revision. |
1020 | | * @return Pointer to the specified submodule if it is present in the context. |
1021 | | */ |
1022 | | static const struct lysp_submodule * |
1023 | | _ly_ctx_get_submodule2(const struct lys_module *module, const char *submodule, const char *revision, ly_bool latest) |
1024 | 502k | { |
1025 | 502k | struct lysp_include *inc; |
1026 | 502k | LY_ARRAY_COUNT_TYPE u; |
1027 | | |
1028 | 1.00M | LY_CHECK_ARG_RET(NULL, module, module->parsed, submodule, NULL); |
1029 | | |
1030 | 502k | LY_ARRAY_FOR(module->parsed->includes, u) { |
1031 | 0 | if (module->parsed->includes[u].submodule && !strcmp(submodule, module->parsed->includes[u].submodule->name)) { |
1032 | 0 | inc = &module->parsed->includes[u]; |
1033 | |
|
1034 | 0 | if (latest && inc->submodule->latest_revision) { |
1035 | | /* latest revision */ |
1036 | 0 | return inc->submodule; |
1037 | 0 | } else if (!revision && !inc->submodule->revs) { |
1038 | | /* no revision */ |
1039 | 0 | return inc->submodule; |
1040 | 0 | } else if (revision && inc->submodule->revs && !strcmp(revision, inc->submodule->revs[0].date)) { |
1041 | | /* specific revision */ |
1042 | 0 | return inc->submodule; |
1043 | 0 | } |
1044 | 0 | } |
1045 | 0 | } |
1046 | | |
1047 | 502k | return NULL; |
1048 | 502k | } |
1049 | | |
1050 | | /** |
1051 | | * @brief Try to find a submodule in the context. |
1052 | | * |
1053 | | * @param[in] ctx Context where to search in. |
1054 | | * @param[in] submodule Name of the submodule to find. |
1055 | | * @param[in] revision Revision of the submodule to find. NULL for submodule with no revision. |
1056 | | * @param[in] latest Ignore @p revision and look for the latest revision. |
1057 | | * @return Pointer to the specified submodule if it is present in the context. |
1058 | | */ |
1059 | | static const struct lysp_submodule * |
1060 | | _ly_ctx_get_submodule(const struct ly_ctx *ctx, const char *submodule, const char *revision, ly_bool latest) |
1061 | 100k | { |
1062 | 100k | const struct lys_module *mod; |
1063 | 100k | const struct lysp_submodule *submod = NULL; |
1064 | 100k | uint32_t v; |
1065 | | |
1066 | 100k | LY_CHECK_ARG_RET(ctx, ctx, submodule, NULL); |
1067 | | |
1068 | 602k | for (v = 0; v < ctx->modules.count; ++v) { |
1069 | 502k | mod = ctx->modules.objs[v]; |
1070 | 502k | if (!mod->parsed) { |
1071 | 0 | continue; |
1072 | 0 | } |
1073 | | |
1074 | 502k | submod = _ly_ctx_get_submodule2(mod, submodule, revision, latest); |
1075 | 502k | if (submod) { |
1076 | 0 | break; |
1077 | 0 | } |
1078 | 502k | } |
1079 | | |
1080 | 100k | return submod; |
1081 | 100k | } |
1082 | | |
1083 | | LIBYANG_API_DEF const struct lysp_submodule * |
1084 | | ly_ctx_get_submodule(const struct ly_ctx *ctx, const char *submodule, const char *revision) |
1085 | 0 | { |
1086 | 0 | return _ly_ctx_get_submodule(ctx, submodule, revision, 0); |
1087 | 0 | } |
1088 | | |
1089 | | LIBYANG_API_DEF const struct lysp_submodule * |
1090 | | ly_ctx_get_submodule_latest(const struct ly_ctx *ctx, const char *submodule) |
1091 | 100k | { |
1092 | 100k | return _ly_ctx_get_submodule(ctx, submodule, NULL, 1); |
1093 | 100k | } |
1094 | | |
1095 | | LIBYANG_API_DEF const struct lysp_submodule * |
1096 | | ly_ctx_get_submodule2(const struct lys_module *module, const char *submodule, const char *revision) |
1097 | 0 | { |
1098 | 0 | return _ly_ctx_get_submodule2(module, submodule, revision, 0); |
1099 | 0 | } |
1100 | | |
1101 | | LIBYANG_API_DEF const struct lysp_submodule * |
1102 | | ly_ctx_get_submodule2_latest(const struct lys_module *module, const char *submodule) |
1103 | 0 | { |
1104 | 0 | return _ly_ctx_get_submodule2(module, submodule, NULL, 1); |
1105 | 0 | } |
1106 | | |
1107 | | LIBYANG_API_DEF uint32_t |
1108 | | ly_ctx_internal_modules_count(const struct ly_ctx *ctx) |
1109 | 27.4k | { |
1110 | 27.4k | if (!ctx) { |
1111 | 0 | return 0; |
1112 | 0 | } |
1113 | | |
1114 | 27.4k | if (ctx->opts & LY_CTX_NO_YANGLIBRARY) { |
1115 | 0 | return LY_INTERNAL_MODS_COUNT - 2; |
1116 | 27.4k | } else { |
1117 | 27.4k | return LY_INTERNAL_MODS_COUNT; |
1118 | 27.4k | } |
1119 | 27.4k | } |
1120 | | |
1121 | | static LY_ERR |
1122 | | ylib_feature(struct lyd_node *parent, const struct lys_module *mod) |
1123 | 0 | { |
1124 | 0 | LY_ARRAY_COUNT_TYPE u; |
1125 | |
|
1126 | 0 | if (!mod->implemented) { |
1127 | | /* no features can be enabled */ |
1128 | 0 | return LY_SUCCESS; |
1129 | 0 | } |
1130 | | |
1131 | 0 | LY_ARRAY_FOR(mod->compiled->features, u) { |
1132 | 0 | LY_CHECK_RET(lyd_new_term(parent, NULL, "feature", mod->compiled->features[u], 0, NULL)); |
1133 | 0 | } |
1134 | | |
1135 | 0 | return LY_SUCCESS; |
1136 | 0 | } |
1137 | | |
1138 | | static LY_ERR |
1139 | | ylib_deviation(struct lyd_node *parent, const struct lys_module *cur_mod, ly_bool bis) |
1140 | 0 | { |
1141 | 0 | LY_ARRAY_COUNT_TYPE i; |
1142 | 0 | struct lys_module *mod; |
1143 | |
|
1144 | 0 | if (!cur_mod->implemented) { |
1145 | | /* no deviations of the module for certain */ |
1146 | 0 | return LY_SUCCESS; |
1147 | 0 | } |
1148 | | |
1149 | 0 | LY_ARRAY_FOR(cur_mod->deviated_by, i) { |
1150 | 0 | mod = cur_mod->deviated_by[i]; |
1151 | |
|
1152 | 0 | if (bis) { |
1153 | 0 | LY_CHECK_RET(lyd_new_term(parent, NULL, "deviation", mod->name, 0, NULL)); |
1154 | 0 | } else { |
1155 | 0 | LY_CHECK_RET(lyd_new_list(parent, NULL, "deviation", 0, NULL, mod->name, mod->revision ? mod->revision : "")); |
1156 | 0 | } |
1157 | 0 | } |
1158 | | |
1159 | 0 | return LY_SUCCESS; |
1160 | 0 | } |
1161 | | |
1162 | | static LY_ERR |
1163 | | ylib_submodules(struct lyd_node *parent, const struct lys_module *mod, ly_bool bis) |
1164 | 0 | { |
1165 | 0 | LY_ERR ret; |
1166 | 0 | LY_ARRAY_COUNT_TYPE u; |
1167 | 0 | struct lyd_node *cont; |
1168 | 0 | struct lysc_submodule *submod; |
1169 | 0 | int r; |
1170 | 0 | char *str; |
1171 | |
|
1172 | 0 | LY_ARRAY_FOR(mod->submodules, u) { |
1173 | 0 | submod = &mod->submodules[u]; |
1174 | |
|
1175 | 0 | if (bis) { |
1176 | 0 | LY_CHECK_RET(lyd_new_list(parent, NULL, "submodule", 0, &cont, submod->name)); |
1177 | |
|
1178 | 0 | if (submod->revision) { |
1179 | 0 | LY_CHECK_RET(lyd_new_term(cont, NULL, "revision", submod->revision, 0, NULL)); |
1180 | 0 | } |
1181 | 0 | } else { |
1182 | 0 | LY_CHECK_RET(lyd_new_list(parent, NULL, "submodule", 0, &cont, submod->name, |
1183 | 0 | submod->revision ? submod->revision : "")); |
1184 | 0 | } |
1185 | | |
1186 | 0 | if (submod->filepath) { |
1187 | 0 | r = asprintf(&str, "file://%s", submod->filepath); |
1188 | 0 | LY_CHECK_ERR_RET(r == -1, LOGMEM(mod->ctx), LY_EMEM); |
1189 | |
|
1190 | 0 | ret = lyd_new_term(cont, NULL, bis ? "location" : "schema", str, 0, NULL); |
1191 | 0 | free(str); |
1192 | 0 | LY_CHECK_RET(ret); |
1193 | 0 | } |
1194 | 0 | } |
1195 | | |
1196 | 0 | return LY_SUCCESS; |
1197 | 0 | } |
1198 | | |
1199 | | LIBYANG_API_DEF LY_ERR |
1200 | | ly_ctx_get_yanglib_data(const struct ly_ctx *ctx, struct lyd_node **root_p, const char *content_id_format, ...) |
1201 | 0 | { |
1202 | 0 | LY_ERR ret; |
1203 | 0 | uint32_t i; |
1204 | 0 | ly_bool bis = 0; |
1205 | 0 | int r; |
1206 | 0 | char *str; |
1207 | 0 | const struct lys_module *mod; |
1208 | 0 | struct lyd_node *root = NULL, *root_bis = NULL, *cont, *set_bis = NULL; |
1209 | 0 | va_list ap; |
1210 | |
|
1211 | 0 | LY_CHECK_ARG_RET(ctx, ctx, root_p, LY_EINVAL); |
1212 | |
|
1213 | 0 | mod = ly_ctx_get_module_implemented(ctx, "ietf-yang-library"); |
1214 | 0 | LY_CHECK_ERR_RET(!mod, LOGERR(ctx, LY_EINVAL, "Module \"ietf-yang-library\" is not implemented."), LY_EINVAL); |
1215 | |
|
1216 | 0 | if (mod->revision && !strcmp(mod->revision, "2016-06-21")) { |
1217 | 0 | bis = 0; |
1218 | 0 | } else if (mod->revision && !strcmp(mod->revision, IETF_YANG_LIB_REV)) { |
1219 | 0 | bis = 1; |
1220 | 0 | } else { |
1221 | 0 | LOGERR(ctx, LY_EINVAL, "Incompatible ietf-yang-library version in context."); |
1222 | 0 | return LY_EINVAL; |
1223 | 0 | } |
1224 | | |
1225 | 0 | LY_CHECK_GOTO(ret = lyd_new_inner(NULL, mod, "modules-state", 0, &root), error); |
1226 | |
|
1227 | 0 | if (bis) { |
1228 | 0 | LY_CHECK_GOTO(ret = lyd_new_inner(NULL, mod, "yang-library", 0, &root_bis), error); |
1229 | 0 | LY_CHECK_GOTO(ret = lyd_new_list(root_bis, NULL, "module-set", 0, &set_bis, "complete"), error); |
1230 | 0 | } |
1231 | | |
1232 | 0 | for (i = 0; i < ctx->modules.count; ++i) { |
1233 | 0 | mod = ctx->modules.objs[i]; |
1234 | | |
1235 | | /* |
1236 | | * deprecated legacy |
1237 | | */ |
1238 | 0 | LY_CHECK_GOTO(ret = lyd_new_list(root, NULL, "module", 0, &cont, mod->name, mod->revision ? mod->revision : ""), |
1239 | 0 | error); |
1240 | | |
1241 | | /* schema */ |
1242 | 0 | if (mod->filepath) { |
1243 | 0 | r = asprintf(&str, "file://%s", mod->filepath); |
1244 | 0 | LY_CHECK_ERR_GOTO(r == -1, LOGMEM(ctx); ret = LY_EMEM, error); |
1245 | |
|
1246 | 0 | ret = lyd_new_term(cont, NULL, "schema", str, 0, NULL); |
1247 | 0 | free(str); |
1248 | 0 | LY_CHECK_GOTO(ret, error); |
1249 | 0 | } |
1250 | | |
1251 | | /* namespace */ |
1252 | 0 | LY_CHECK_GOTO(ret = lyd_new_term(cont, NULL, "namespace", mod->ns, 0, NULL), error); |
1253 | | |
1254 | | /* feature leaf-list */ |
1255 | 0 | LY_CHECK_GOTO(ret = ylib_feature(cont, mod), error); |
1256 | | |
1257 | | /* deviation list */ |
1258 | 0 | LY_CHECK_GOTO(ret = ylib_deviation(cont, mod, 0), error); |
1259 | | |
1260 | | /* conformance-type */ |
1261 | 0 | LY_CHECK_GOTO(ret = lyd_new_term(cont, NULL, "conformance-type", mod->implemented ? "implement" : "import", 0, NULL), error); |
1262 | | |
1263 | | /* submodule list */ |
1264 | 0 | LY_CHECK_GOTO(ret = ylib_submodules(cont, mod, 0), error); |
1265 | | |
1266 | | /* |
1267 | | * current revision |
1268 | | */ |
1269 | 0 | if (bis) { |
1270 | | /* name and revision */ |
1271 | 0 | if (mod->implemented) { |
1272 | 0 | LY_CHECK_GOTO(ret = lyd_new_list(set_bis, NULL, "module", 0, &cont, mod->name), error); |
1273 | |
|
1274 | 0 | if (mod->revision) { |
1275 | 0 | LY_CHECK_GOTO(ret = lyd_new_term(cont, NULL, "revision", mod->revision, 0, NULL), error); |
1276 | 0 | } |
1277 | 0 | } else { |
1278 | 0 | LY_CHECK_GOTO(ret = lyd_new_list(set_bis, NULL, "import-only-module", 0, &cont, mod->name, |
1279 | 0 | mod->revision ? mod->revision : ""), error); |
1280 | 0 | } |
1281 | | |
1282 | | /* namespace */ |
1283 | 0 | LY_CHECK_GOTO(ret = lyd_new_term(cont, NULL, "namespace", mod->ns, 0, NULL), error); |
1284 | | |
1285 | | /* location */ |
1286 | 0 | if (mod->filepath) { |
1287 | 0 | r = asprintf(&str, "file://%s", mod->filepath); |
1288 | 0 | LY_CHECK_ERR_GOTO(r == -1, LOGMEM(ctx); ret = LY_EMEM, error); |
1289 | |
|
1290 | 0 | ret = lyd_new_term(cont, NULL, "location", str, 0, NULL); |
1291 | 0 | free(str); |
1292 | 0 | LY_CHECK_GOTO(ret, error); |
1293 | 0 | } |
1294 | | |
1295 | | /* submodule list */ |
1296 | 0 | LY_CHECK_GOTO(ret = ylib_submodules(cont, mod, 1), error); |
1297 | | |
1298 | | /* feature list */ |
1299 | 0 | LY_CHECK_GOTO(ret = ylib_feature(cont, mod), error); |
1300 | | |
1301 | | /* deviation */ |
1302 | 0 | LY_CHECK_GOTO(ret = ylib_deviation(cont, mod, 1), error); |
1303 | 0 | } |
1304 | 0 | } |
1305 | | |
1306 | | /* IDs */ |
1307 | 0 | va_start(ap, content_id_format); |
1308 | 0 | r = vasprintf(&str, content_id_format, ap); |
1309 | 0 | va_end(ap); |
1310 | 0 | LY_CHECK_ERR_GOTO(r == -1, LOGMEM(ctx); ret = LY_EMEM, error); |
1311 | 0 | ret = lyd_new_term(root, NULL, "module-set-id", str, 0, NULL); |
1312 | 0 | LY_CHECK_ERR_GOTO(ret, free(str), error); |
1313 | |
|
1314 | 0 | if (bis) { |
1315 | | /* create one complete schema */ |
1316 | 0 | LY_CHECK_ERR_GOTO(ret = lyd_new_list(root_bis, NULL, "schema", 0, &cont, "complete"), free(str), error); |
1317 | |
|
1318 | 0 | LY_CHECK_ERR_GOTO(ret = lyd_new_term(cont, NULL, "module-set", "complete", 0, NULL), free(str), error); |
1319 | | |
1320 | | /* content-id */ |
1321 | 0 | LY_CHECK_ERR_GOTO(ret = lyd_new_term(root_bis, NULL, "content-id", str, 0, NULL), free(str), error); |
1322 | 0 | } |
1323 | 0 | free(str); |
1324 | |
|
1325 | 0 | if (root_bis) { |
1326 | 0 | if (lyd_insert_sibling(root, root_bis, &root)) { |
1327 | 0 | goto error; |
1328 | 0 | } |
1329 | 0 | root_bis = NULL; |
1330 | 0 | } |
1331 | | |
1332 | 0 | LY_CHECK_GOTO(ret = lyd_validate_all(&root, NULL, LYD_VALIDATE_PRESENT, NULL), error); |
1333 | |
|
1334 | 0 | *root_p = root; |
1335 | 0 | return LY_SUCCESS; |
1336 | | |
1337 | 0 | error: |
1338 | 0 | lyd_free_all(root); |
1339 | 0 | lyd_free_all(root_bis); |
1340 | 0 | return ret; |
1341 | 0 | } |
1342 | | |
1343 | | LIBYANG_API_DEF void |
1344 | | ly_ctx_free_parsed(struct ly_ctx *ctx) |
1345 | 0 | { |
1346 | 0 | uint32_t i; |
1347 | 0 | struct lys_module *mod; |
1348 | |
|
1349 | 0 | LY_CHECK_ARG_RET(ctx, ctx, !(ctx->opts & LY_CTX_INT_IMMUTABLE), ); |
1350 | |
|
1351 | 0 | for (i = 0; i < ctx->modules.count; ++i) { |
1352 | 0 | mod = ctx->modules.objs[i]; |
1353 | | |
1354 | | /* free the parsed modules */ |
1355 | 0 | lysp_module_free(ctx, mod->parsed); |
1356 | 0 | mod->parsed = NULL; |
1357 | 0 | } |
1358 | 0 | } |
1359 | | |
1360 | | static ly_bool |
1361 | | ctxs_ptr_val_equal(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *UNUSED(cb_data)) |
1362 | 0 | { |
1363 | 0 | void *val1, *val2; |
1364 | |
|
1365 | 0 | val1 = *(void **)val1_p; |
1366 | 0 | val2 = *(void **)val2_p; |
1367 | |
|
1368 | 0 | return val1 == val2; |
1369 | 0 | } |
1370 | | |
1371 | | LIBYANG_API_DEF int |
1372 | | ly_ctx_compiled_size(const struct ly_ctx *ctx) |
1373 | 0 | { |
1374 | 0 | int size = 0; |
1375 | 0 | struct ly_ht *ht; |
1376 | |
|
1377 | 0 | LY_CHECK_ARG_RET(NULL, ctx, -1); |
1378 | | |
1379 | | /* create hash table for shared structures */ |
1380 | 0 | ht = lyht_new(0, sizeof(void *), ctxs_ptr_val_equal, NULL, 1); |
1381 | 0 | LY_CHECK_RET(!ht, -1); |
1382 | |
|
1383 | 0 | ly_ctx_compiled_size_context(ctx, ht, &size); |
1384 | | |
1385 | | /* cleanup */ |
1386 | 0 | lyht_free(ht, NULL); |
1387 | 0 | return size; |
1388 | 0 | } |
1389 | | |
1390 | | static ly_bool |
1391 | | ctxp_ptr_val_equal(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *UNUSED(cb_data)) |
1392 | 0 | { |
1393 | 0 | struct ctxp_ht_addr *val1, *val2; |
1394 | |
|
1395 | 0 | val1 = val1_p; |
1396 | 0 | val2 = val2_p; |
1397 | |
|
1398 | 0 | return val1->orig_addr == val2->orig_addr; |
1399 | 0 | } |
1400 | | |
1401 | | LIBYANG_API_DEF LY_ERR |
1402 | | ly_ctx_compiled_print(const struct ly_ctx *ctx, void *mem, void **mem_end) |
1403 | 0 | { |
1404 | 0 | struct ly_ctx *pctx; |
1405 | 0 | struct ly_ht *addr_ht = NULL; |
1406 | 0 | struct ly_set ptr_set = {0}; |
1407 | 0 | void **ptr_p; |
1408 | 0 | uint32_t i; |
1409 | |
|
1410 | 0 | LY_CHECK_ARG_RET(ctx, ctx, mem, LY_EINVAL); |
1411 | |
|
1412 | 0 | if (ctx->plugins_types.count || ctx->plugins_extensions.count || !(ctx->opts & LY_CTX_STATIC_PLUGINS_ONLY)) { |
1413 | 0 | LOGERR(ctx, LY_EINVAL, "Printing context with dynamic type or extension plugins is not supported."); |
1414 | 0 | return LY_EINVAL; |
1415 | 0 | } |
1416 | | |
1417 | | /* create hash table for shared structures */ |
1418 | 0 | addr_ht = lyht_new(0, sizeof(struct ctxp_ht_addr), ctxp_ptr_val_equal, NULL, 1); |
1419 | 0 | LY_CHECK_RET(!addr_ht, -1); |
1420 | | |
1421 | | /* context, referenced */ |
1422 | 0 | pctx = mem; |
1423 | 0 | mem = (char *)mem + sizeof *pctx; |
1424 | 0 | ly_ctx_compiled_addr_ht_add(addr_ht, ctx, pctx); |
1425 | | |
1426 | | /* members */ |
1427 | 0 | ly_ctx_compiled_print_context(ctx, pctx, addr_ht, &ptr_set, &mem); |
1428 | | |
1429 | | /* set all the pointers to the printed structures */ |
1430 | 0 | for (i = 0; i < ptr_set.count; ++i) { |
1431 | 0 | ptr_p = ptr_set.objs[i]; |
1432 | 0 | *ptr_p = ly_ctx_compiled_addr_ht_get(addr_ht, *ptr_p, 0); |
1433 | 0 | } |
1434 | | |
1435 | | /* cleanup */ |
1436 | 0 | lyht_free(addr_ht, NULL); |
1437 | 0 | ly_set_erase(&ptr_set, NULL); |
1438 | 0 | if (mem_end) { |
1439 | 0 | *mem_end = mem; |
1440 | 0 | } |
1441 | 0 | return LY_SUCCESS; |
1442 | 0 | } |
1443 | | |
1444 | | LIBYANG_API_DEF LY_ERR |
1445 | | ly_ctx_new_printed(const void *mem, struct ly_ctx **ctx) |
1446 | 0 | { |
1447 | 0 | LY_ERR rc = LY_SUCCESS; |
1448 | 0 | ly_bool builtin_plugins_only, static_plugins_only; |
1449 | |
|
1450 | 0 | LY_CHECK_ARG_RET(NULL, mem, ctx, LY_EINVAL); |
1451 | |
|
1452 | 0 | *ctx = (void *)mem; |
1453 | | |
1454 | | /* ctx data */ |
1455 | 0 | rc = ly_ctx_data_add(*ctx); |
1456 | 0 | if (rc) { |
1457 | 0 | goto cleanup; |
1458 | 0 | } |
1459 | | |
1460 | | /* plugins */ |
1461 | 0 | builtin_plugins_only = ((*ctx)->opts & LY_CTX_BUILTIN_PLUGINS_ONLY) ? 1 : 0; |
1462 | 0 | static_plugins_only = 1; |
1463 | 0 | LY_CHECK_ERR_GOTO(lyplg_init(builtin_plugins_only, static_plugins_only), LOGINT(NULL); rc = LY_EINT, cleanup); |
1464 | |
|
1465 | 0 | cleanup: |
1466 | 0 | if (rc) { |
1467 | 0 | if (rc != LY_EEXIST) { |
1468 | | /* do not free another context's data */ |
1469 | 0 | ly_ctx_data_del(*ctx); |
1470 | 0 | } |
1471 | 0 | *ctx = NULL; |
1472 | 0 | } |
1473 | 0 | return rc; |
1474 | 0 | } |
1475 | | |
1476 | | LIBYANG_API_DEF ly_bool |
1477 | | ly_ctx_is_printed(const struct ly_ctx *ctx) |
1478 | 0 | { |
1479 | 0 | if (!ctx) { |
1480 | 0 | return 0; |
1481 | 0 | } |
1482 | | |
1483 | 0 | return (ctx->opts & LY_CTX_INT_IMMUTABLE) ? 1 : 0; |
1484 | 0 | } |
1485 | | |
1486 | | LIBYANG_API_DEF void |
1487 | | ly_ctx_destroy(struct ly_ctx *ctx) |
1488 | 9.13k | { |
1489 | 9.13k | uint32_t i; |
1490 | 9.13k | struct lys_module *mod; |
1491 | | |
1492 | 9.13k | if (!ctx) { |
1493 | 0 | return; |
1494 | 0 | } |
1495 | | |
1496 | 9.13k | if (ctx->opts & LY_CTX_INT_IMMUTABLE) { |
1497 | 0 | if (!ctx->parent_ctx) { |
1498 | | /* ctx data */ |
1499 | 0 | ly_ctx_data_del(ctx); |
1500 | 0 | } |
1501 | 0 | lyplg_clean(); |
1502 | 0 | return; |
1503 | 0 | } |
1504 | | |
1505 | | /* free the parsed and compiled modules (both can reference ext instances, which need to be freed, so their |
1506 | | * definitions can be freed) */ |
1507 | 109k | for (i = 0; i < ctx->modules.count; ++i) { |
1508 | 100k | mod = ctx->modules.objs[i]; |
1509 | | |
1510 | 100k | lysp_module_free(ctx, mod->parsed); |
1511 | 100k | mod->parsed = NULL; |
1512 | 100k | if (mod->implemented) { |
1513 | 73.0k | mod->implemented = 0; |
1514 | 73.0k | lysc_module_free(ctx, mod->compiled); |
1515 | 73.0k | mod->compiled = NULL; |
1516 | 73.0k | } |
1517 | 100k | } |
1518 | | |
1519 | | /* free the modules (with compiled extension definitions) */ |
1520 | 109k | for (i = 0; i < ctx->modules.count; ++i) { |
1521 | 100k | mod = ctx->modules.objs[i]; |
1522 | 100k | lys_module_free(ctx, mod, 0); |
1523 | 100k | } |
1524 | 9.13k | free(ctx->modules.objs); |
1525 | | |
1526 | | /* search paths list */ |
1527 | 9.13k | ly_set_erase(&ctx->search_paths, free); |
1528 | | |
1529 | | /* leftover unres */ |
1530 | 9.13k | lys_unres_glob_erase(&ctx->unres); |
1531 | | |
1532 | 9.13k | if (!ctx->parent_ctx) { |
1533 | | /* ctx data */ |
1534 | 9.13k | ly_ctx_data_del(ctx); |
1535 | 9.13k | } |
1536 | | |
1537 | | /* dictionary */ |
1538 | 9.13k | lydict_clean(&ctx->dict); |
1539 | | |
1540 | | /* context specific plugins */ |
1541 | 9.13k | ly_set_erase(&ctx->plugins_types, NULL); |
1542 | 9.13k | ly_set_erase(&ctx->plugins_extensions, NULL); |
1543 | | |
1544 | | /* shared plugins - will be removed only if this is the last context */ |
1545 | 9.13k | lyplg_clean(); |
1546 | | |
1547 | 9.13k | free(ctx); |
1548 | 9.13k | } |