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