1 /**
2  * @file Libyang.cpp
3  * @author Mislav Novakovic <mislav.novakovic@sartura.hr>
4  * @brief Implementation of header Libyang.hpp
5  *
6  * Copyright (c) 2017 Deutsche Telekom AG.
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 
15 #include <iostream>
16 #include <memory>
17 #include <stdexcept>
18 #include <vector>
19 
20 #include "Xml.hpp"
21 #include "Internal.hpp"
22 #include "Libyang.hpp"
23 #include "Tree_Data.hpp"
24 #include "Tree_Schema.hpp"
25 
26 extern "C" {
27 #include "libyang.h"
28 #include "tree_data.h"
29 #include "tree_schema.h"
30 #include "context.h"
31 }
32 
33 namespace libyang {
34 
Context(ly_ctx * ctx,S_Deleter deleter)35 Context::Context(ly_ctx *ctx, S_Deleter deleter):
36     ctx(ctx),
37     deleter(deleter)
38 {};
Context(const char * search_dir,int options)39 Context::Context(const char *search_dir, int options) {
40     ctx = ly_ctx_new(search_dir, options);
41     if (!ctx) {
42         check_libyang_error(nullptr);
43     }
44     deleter = std::make_shared<Deleter>(ctx);
45 }
Context(const char * search_dir,const char * path,LYD_FORMAT format,int options)46 Context::Context(const char *search_dir, const char *path, LYD_FORMAT format, int options) {
47     ctx = ly_ctx_new_ylpath(search_dir, path, format, options);
48     if (!ctx) {
49         check_libyang_error(nullptr);
50     }
51     deleter = std::make_shared<Deleter>(ctx);
52 }
Context(const char * search_dir,LYD_FORMAT format,const char * data,int options)53 Context::Context(const char *search_dir, LYD_FORMAT format, const char *data, int options) {
54     ctx = ly_ctx_new_ylmem(search_dir, data, format, options);
55     if (!ctx) {
56         check_libyang_error(nullptr);
57     }
58     deleter = std::make_shared<Deleter>(ctx);
59 }
~Context()60 Context::~Context() {}
set_searchdir(const char * search_dir)61 int Context::set_searchdir(const char *search_dir) {
62     int ret = ly_ctx_set_searchdir(ctx, search_dir);
63     if (ret) {
64         check_libyang_error(ctx);
65     }
66     return ret;
67 }
info()68 S_Data_Node Context::info() {
69     struct lyd_node *new_node = ly_ctx_info(ctx);
70     if (!new_node) {
71         check_libyang_error(ctx);
72         return nullptr;
73     }
74 
75     S_Deleter new_deleter = std::make_shared<Deleter>(new_node, deleter);
76     return std::make_shared<Data_Node>(new_node, new_deleter);
77 }
get_module(const char * name,const char * revision,int implemented)78 S_Module Context::get_module(const char *name, const char *revision, int implemented) {
79     const struct lys_module *module = ly_ctx_get_module(ctx, name, revision, implemented);
80     return module ? std::make_shared<Module>((lys_module *) module, deleter) : nullptr;
81 }
get_module_older(S_Module module)82 S_Module Context::get_module_older(S_Module module) {
83     const struct lys_module *new_module = ly_ctx_get_module_older(ctx, module->module);
84     return new_module ? std::make_shared<Module>((lys_module *) new_module, deleter) : nullptr;
85 }
load_module(const char * name,const char * revision)86 S_Module Context::load_module(const char *name, const char *revision) {
87     const struct lys_module *module = ly_ctx_load_module(ctx, name, revision);
88     if (!module) {
89         check_libyang_error(ctx);
90     }
91     return module ? std::make_shared<Module>((lys_module *) module, deleter) : nullptr;
92 }
get_module_by_ns(const char * ns,const char * revision,int implemented)93 S_Module Context::get_module_by_ns(const char *ns, const char *revision, int implemented) {
94     const struct lys_module *module = ly_ctx_get_module_by_ns(ctx, ns, revision, implemented);
95     return module ? std::make_shared<Module>((lys_module *) module, deleter) : nullptr;
96 }
get_module_iter()97 std::vector<S_Module> Context::get_module_iter() {
98     const struct lys_module *mod = nullptr;
99     uint32_t i = 0;
100 
101     std::vector<S_Module> s_vector;
102 
103     while ((mod = ly_ctx_get_module_iter(ctx, &i))) {
104         if (mod == nullptr) {
105             break;
106         }
107         s_vector.push_back(std::make_shared<Module>((lys_module *) mod, deleter));
108     }
109 
110     return s_vector;
111 }
get_disabled_module_iter()112 std::vector<S_Module> Context::get_disabled_module_iter() {
113     const struct lys_module *mod = nullptr;
114     uint32_t i = 0;
115 
116     std::vector<S_Module> s_vector;
117 
118     while ((mod = ly_ctx_get_disabled_module_iter(ctx, &i))) {
119         if (mod == nullptr) {
120             break;
121         }
122         s_vector.push_back(std::make_shared<Module>((lys_module *) mod, deleter));
123     }
124 
125     return s_vector;
126 }
clean()127 void Context::clean() {
128     return ly_ctx_clean(ctx, nullptr);
129 }
get_searchdirs()130 std::vector<std::string> Context::get_searchdirs() {
131     std::vector<std::string> s_vector;
132     const char * const *data = ly_ctx_get_searchdirs(ctx);
133     if (!data) {
134         return s_vector;
135     }
136 
137     int size = 0;
138     while (true) {
139         if (data[size] == nullptr) {
140             break;
141         }
142         s_vector.push_back(std::string(data[size]));
143         size++;
144     }
145 
146     return s_vector;
147 };
get_submodule(const char * module,const char * revision,const char * submodule,const char * sub_revision)148 S_Submodule Context::get_submodule(const char *module, const char *revision, const char *submodule, const char *sub_revision) {
149     const struct lys_submodule *tmp_submodule = nullptr;
150 
151     tmp_submodule = ly_ctx_get_submodule(ctx, module, revision, submodule, sub_revision);
152 
153     return tmp_submodule ? std::make_shared<Submodule>((struct lys_submodule *) tmp_submodule, deleter) : nullptr;
154 }
get_submodule2(S_Module main_module,const char * submodule)155 S_Submodule Context::get_submodule2(S_Module main_module, const char *submodule) {
156     const struct lys_submodule *tmp_submodule = nullptr;
157 
158     tmp_submodule = ly_ctx_get_submodule2(main_module->module, submodule);
159 
160     return tmp_submodule ? std::make_shared<Submodule>((struct lys_submodule *) tmp_submodule, deleter) : nullptr;
161 }
get_node(S_Schema_Node start,const char * data_path,int output)162 S_Schema_Node Context::get_node(S_Schema_Node start, const char *data_path, int output) {
163     const struct lys_node *node = nullptr;
164 
165     node = ly_ctx_get_node(ctx, start ? start->node : NULL, data_path, output);
166 
167     return node ? std::make_shared<Schema_Node>((struct lys_node *) node, deleter) : nullptr;
168 }
find_path(const char * schema_path)169 S_Set Context::find_path(const char *schema_path) {
170     struct ly_set *set = ly_ctx_find_path(ctx, schema_path);
171     if (!set) {
172         return nullptr;
173     }
174 
175     S_Deleter new_deleter = std::make_shared<Deleter>(set, deleter);
176     return std::make_shared<Set>(set, new_deleter);
177 }
data_instantiables(int options)178 std::vector<S_Schema_Node> Context::data_instantiables(int options) {
179     std::vector<S_Schema_Node> s_vector;
180     struct lys_node *iter = NULL;
181     int i;
182 
183     for (i = 0; i < ctx->models.used; i++) {
184         while ((iter = (struct lys_node *)lys_getnext(iter, NULL, ctx->models.list[i], options))) {
185             s_vector.push_back(std::make_shared<Schema_Node>(iter, deleter));
186         }
187     }
188 
189     return s_vector;
190 }
parse_data_mem(const char * data,LYD_FORMAT format,int options)191 S_Data_Node Context::parse_data_mem(const char *data, LYD_FORMAT format, int options) {
192     struct lyd_node *new_node = nullptr;
193 
194     new_node = lyd_parse_mem(ctx, data, format, options, NULL);
195     if (!new_node) {
196         check_libyang_error(ctx);
197         return nullptr;
198     }
199 
200     S_Deleter new_deleter = std::make_shared<Deleter>(new_node, deleter);
201     return std::make_shared<Data_Node>(new_node, new_deleter);
202 }
parse_data_fd(int fd,LYD_FORMAT format,int options)203 S_Data_Node Context::parse_data_fd(int fd, LYD_FORMAT format, int options) {
204     struct lyd_node *new_node = nullptr;
205 
206     new_node = lyd_parse_fd(ctx, fd, format, options, NULL);
207     if (!new_node) {
208         check_libyang_error(ctx);
209         return nullptr;
210     }
211 
212     S_Deleter new_deleter = std::make_shared<Deleter>(new_node, deleter);
213     return std::make_shared<Data_Node>(new_node, new_deleter);
214 }
215 
parse_module_mem(const char * data,LYS_INFORMAT format)216 S_Module Context::parse_module_mem(const char *data, LYS_INFORMAT format) {
217     struct lys_module *module = nullptr;
218 
219     module = (struct lys_module *) lys_parse_mem(ctx, data, format);
220     if (!module) {
221         check_libyang_error(ctx);
222         return nullptr;
223     }
224 
225     S_Deleter new_deleter = std::make_shared<Deleter>(module, deleter);
226     return std::make_shared<Module>(module, new_deleter);
227 }
parse_module_fd(int fd,LYS_INFORMAT format)228 S_Module Context::parse_module_fd(int fd, LYS_INFORMAT format) {
229     struct lys_module *module = nullptr;
230 
231     module = (struct lys_module *) lys_parse_fd(ctx, fd, format);
232     if (!module) {
233         check_libyang_error(ctx);
234         return nullptr;
235     }
236 
237     S_Deleter new_deleter = std::make_shared<Deleter>(module, deleter);
238     return std::make_shared<Module>(module, new_deleter);
239 }
parse_module_path(const char * path,LYS_INFORMAT format)240 S_Module Context::parse_module_path(const char *path, LYS_INFORMAT format) {
241     struct lys_module *module = nullptr;
242 
243     module = (struct lys_module *) lys_parse_path(ctx, path, format);
244     if (!module) {
245         check_libyang_error(ctx);
246         return nullptr;
247     }
248 
249     S_Deleter new_deleter = std::make_shared<Deleter>(module, deleter);
250     return std::make_shared<Module>(module, new_deleter);
251 }
parse_data_path(const char * path,LYD_FORMAT format,int options)252 S_Data_Node Context::parse_data_path(const char *path, LYD_FORMAT format, int options) {
253     struct lyd_node *new_node = nullptr;
254 
255     new_node = lyd_parse_path(ctx, path, format, options, NULL);
256     if (!new_node) {
257         check_libyang_error(ctx);
258         return nullptr;
259     }
260 
261     S_Deleter new_deleter = std::make_shared<Deleter>(new_node, deleter);
262     return std::make_shared<Data_Node>(new_node, new_deleter);
263 }
parse_data_xml(S_Xml_Elem elem,int options)264 S_Data_Node Context::parse_data_xml(S_Xml_Elem elem, int options) {
265     struct lyd_node *new_node = nullptr;
266 
267     if (!elem) {
268         throw std::invalid_argument("Elem can not be empty");
269     }
270 
271     new_node = lyd_parse_xml(ctx, &elem->elem, options, NULL);
272     if (!new_node) {
273         check_libyang_error(ctx);
274         return nullptr;
275     }
276 
277     S_Deleter new_deleter = std::make_shared<Deleter>(new_node, deleter);
278     return std::make_shared<Data_Node>(new_node, new_deleter);
279 }
280 
add_missing_module_callback(const mod_missing_cb_t & callback,const mod_missing_deleter_t & deleter)281 void Context::add_missing_module_callback(const mod_missing_cb_t &callback, const mod_missing_deleter_t &deleter)
282 {
283     if (mod_missing_cb.empty()) {
284         ly_ctx_set_module_imp_clb(ctx, Context::cpp_mod_missing_cb, this);
285     }
286     mod_missing_cb.emplace_back(std::move(callback), std::move(deleter));
287 }
288 
cpp_mod_missing_cb(const char * mod_name,const char * mod_rev,const char * submod_name,const char * sub_rev,void * user_data,LYS_INFORMAT * format,void (** free_module_data)(void *,void *))289 const char* Context::cpp_mod_missing_cb(const char *mod_name, const char *mod_rev, const char *submod_name, const char *sub_rev, void *user_data, LYS_INFORMAT *format, void (**free_module_data)(void*, void*))
290 {
291     Context *ctx = static_cast<Context*>(user_data);
292     for (const auto &x : ctx->mod_missing_cb) {
293         const auto &cb = x.first;
294         auto ret = cb(mod_name, mod_rev, submod_name, sub_rev);
295         if (ret.data) {
296             *format = ret.format;
297             if (x.second) {
298                 ctx->mod_missing_deleter.push_back(&x.second);
299                 *free_module_data = Context::cpp_mod_missing_deleter;
300             }
301             return ret.data;
302         }
303         if (ly_errno != LY_SUCCESS) {
304             // The C API docs say that we should not try any more callbacks
305             return nullptr;
306         }
307     }
308     return nullptr;
309 }
310 
cpp_mod_missing_deleter(void * data,void * user_data)311 void Context::cpp_mod_missing_deleter(void *data, void *user_data)
312 {
313     Context *ctx = static_cast<Context*>(user_data);
314     (*ctx->mod_missing_deleter.back())(data);
315     ctx->mod_missing_deleter.pop_back();
316 }
317 
318 
Error(struct ly_err_item * eitem)319 Error::Error(struct ly_err_item *eitem):
320 	eitem(eitem)
321 {};
322 
get_ly_errors(S_Context context)323 std::vector<S_Error> get_ly_errors(S_Context context)
324 {
325     std::vector<S_Error> s_vector;
326     if (!context) {
327         return s_vector;
328     }
329 
330     struct ly_err_item *first_eitem = ly_err_first(context->ctx);
331     if (!first_eitem) {
332         return s_vector;
333     }
334 
335     struct ly_err_item *eitem = first_eitem;
336     while (eitem) {
337         s_vector.push_back(std::make_shared<Error>(eitem));
338         eitem = eitem->next;
339     }
340 
341     return s_vector;
342 }
343 
set_log_options(int options)344 int set_log_options(int options)
345 {
346     return ly_log_options(options);
347 }
348 
set_log_verbosity(LY_LOG_LEVEL level)349 LY_LOG_LEVEL set_log_verbosity(LY_LOG_LEVEL level)
350 {
351     return ly_verb(level);
352 }
353 
Set()354 Set::Set() {
355     struct ly_set *set_new = ly_set_new();
356     if (!set_new) {
357         check_libyang_error(nullptr);
358     }
359 
360     set = set_new;
361     deleter = std::make_shared<Deleter>(set_new);
362 }
Set(struct ly_set * set,S_Deleter deleter)363 Set::Set(struct ly_set *set, S_Deleter deleter):
364     set(set),
365     deleter(deleter)
366 {};
~Set()367 Set::~Set() {}
data()368 std::vector<S_Data_Node> Set::data() {
369     std::vector<S_Data_Node> s_vector;
370 
371     unsigned int i;
372     for (i = 0; i < set->number; i++){
373         s_vector.push_back(std::make_shared<Data_Node>(set->set.d[i], deleter));
374     }
375 
376     return s_vector;
377 };
schema()378 std::vector<S_Schema_Node> Set::schema() {
379     std::vector<S_Schema_Node> s_vector;
380 
381     unsigned int i;
382     for (i = 0; i < set->number; i++){
383         s_vector.push_back(std::make_shared<Schema_Node>(set->set.s[i], deleter));
384     }
385 
386     return s_vector;
387 };
dup()388 S_Set Set::dup() {
389     ly_set *new_set = ly_set_dup(set);
390     if (!new_set) {
391         return nullptr;
392     }
393 
394     auto deleter = std::make_shared<Deleter>(new_set);
395     return std::make_shared<Set>(new_set, deleter);
396 }
add(S_Data_Node node,int options)397 int Set::add(S_Data_Node node, int options) {
398     if (!node) {
399         throw std::invalid_argument("Node can not be empty");
400     }
401     return ly_set_add(set, (void *) node->node, options);
402 }
add(S_Schema_Node node,int options)403 int Set::add(S_Schema_Node node, int options) {
404     if (!node) {
405         throw std::invalid_argument("Node can not be empty");
406     }
407     return ly_set_add(set, (void *) node->node, options);
408 }
contains(S_Data_Node node)409 int Set::contains(S_Data_Node node) {
410     if (!node) {
411         return -1;
412     }
413     return ly_set_contains(set, (void *) node->node);
414 }
contains(S_Schema_Node node)415 int Set::contains(S_Schema_Node node) {
416     if (!node) {
417         return -1;
418     }
419     return ly_set_contains(set, (void *) node->node);
420 }
clean()421 int Set::clean() {
422     return ly_set_clean(set);
423 }
rm(S_Data_Node node)424 int Set::rm(S_Data_Node node) {
425     if (!node) {
426         throw std::invalid_argument("Node can not be empty");
427     }
428     return ly_set_rm(set, (void *) node->node);
429 }
rm(S_Schema_Node node)430 int Set::rm(S_Schema_Node node) {
431     if (!node) {
432         throw std::invalid_argument("Node can not be empty");
433     }
434     return ly_set_rm(set, (void *) node->node);
435 }
rm_index(unsigned int index)436 int Set::rm_index(unsigned int index) {
437     return ly_set_rm_index(set, index);
438 }
439 
440 /* API for wrapping struct ly_ctx from libnetconf2 python bindings */
create_new_Context(struct ly_ctx * new_ctx)441 S_Context create_new_Context(struct ly_ctx *new_ctx) {
442     return new_ctx ? std::make_shared<Context>(new_ctx, nullptr) : nullptr;
443 }
444 
445 }
446