/src/libyang/src/plugins_exts/yangdata.c
Line | Count | Source (jump to first uncovered line) |
1 | | /** |
2 | | * @file yangdata.c |
3 | | * @author Radek Krejci <rkrejci@cesnet.cz> |
4 | | * @author Michal Vasko <mvasko@cesnet.cz> |
5 | | * @brief libyang extension plugin - yang-data (RFC 8040) |
6 | | * |
7 | | * Copyright (c) 2021 - 2022 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 | | |
16 | | #include <assert.h> |
17 | | #include <stdint.h> |
18 | | #include <stdlib.h> |
19 | | #include <string.h> |
20 | | |
21 | | #include "compat.h" |
22 | | #include "libyang.h" |
23 | | #include "plugins_exts.h" |
24 | | |
25 | | static void yangdata_cfree(const struct ly_ctx *ctx, struct lysc_ext_instance *ext); |
26 | | |
27 | | /** |
28 | | * @brief Parse yang-data extension instances. |
29 | | * |
30 | | * Implementation of ::lyplg_ext_parse_clb callback set as lyext_plugin::parse. |
31 | | */ |
32 | | static LY_ERR |
33 | | yangdata_parse(struct lysp_ctx *pctx, struct lysp_ext_instance *ext) |
34 | 0 | { |
35 | 0 | LY_ERR ret; |
36 | 0 | LY_ARRAY_COUNT_TYPE u; |
37 | 0 | struct lysp_module *pmod; |
38 | | |
39 | | /* yang-data can appear only at the top level of a YANG module or submodule */ |
40 | 0 | if ((ext->parent_stmt != LY_STMT_MODULE) && (ext->parent_stmt != LY_STMT_SUBMODULE)) { |
41 | 0 | lyplg_ext_parse_log(pctx, ext, LY_LLWRN, 0, "Extension %s is ignored since it appears as a non top-level statement " |
42 | 0 | "in \"%s\" statement.", ext->name, lyplg_ext_stmt2str(ext->parent_stmt)); |
43 | 0 | return LY_ENOT; |
44 | 0 | } |
45 | | |
46 | 0 | pmod = ext->parent; |
47 | | |
48 | | /* check for duplication */ |
49 | 0 | LY_ARRAY_FOR(pmod->exts, u) { |
50 | 0 | if ((&pmod->exts[u] != ext) && (pmod->exts[u].name == ext->name) && !strcmp(pmod->exts[u].argument, ext->argument)) { |
51 | | /* duplication of the same yang-data extension in a single module */ |
52 | 0 | lyplg_ext_parse_log(pctx, ext, LY_LLERR, LY_EVALID, "Extension %s is instantiated multiple times.", ext->name); |
53 | 0 | return LY_EVALID; |
54 | 0 | } |
55 | 0 | } |
56 | | |
57 | | /* parse yang-data substatements */ |
58 | 0 | LY_ARRAY_CREATE_GOTO(lyplg_ext_parse_get_cur_pmod(pctx)->mod->ctx, ext->substmts, 3, ret, emem); |
59 | 0 | LY_ARRAY_INCREMENT(ext->substmts); |
60 | 0 | ext->substmts[0].stmt = LY_STMT_CONTAINER; |
61 | 0 | ext->substmts[0].storage = &ext->parsed; |
62 | |
|
63 | 0 | LY_ARRAY_INCREMENT(ext->substmts); |
64 | 0 | ext->substmts[1].stmt = LY_STMT_CHOICE; |
65 | 0 | ext->substmts[1].storage = &ext->parsed; |
66 | |
|
67 | 0 | LY_ARRAY_INCREMENT(ext->substmts); |
68 | 0 | ext->substmts[2].stmt = LY_STMT_USES; |
69 | 0 | ext->substmts[2].storage = &ext->parsed; |
70 | |
|
71 | 0 | if ((ret = lyplg_ext_parse_extension_instance(pctx, ext))) { |
72 | 0 | return ret; |
73 | 0 | } |
74 | | |
75 | 0 | return LY_SUCCESS; |
76 | | |
77 | 0 | emem: |
78 | 0 | lyplg_ext_parse_log(pctx, ext, LY_LLERR, LY_EMEM, "Memory allocation failed (%s()).", __func__); |
79 | 0 | return LY_EMEM; |
80 | 0 | } |
81 | | |
82 | | /** |
83 | | * @brief Compile yang-data extension instances. |
84 | | * |
85 | | * Implementation of ::lyplg_ext_compile_clb callback set as lyext_plugin::compile. |
86 | | */ |
87 | | static LY_ERR |
88 | | yangdata_compile(struct lysc_ctx *cctx, const struct lysp_ext_instance *extp, struct lysc_ext_instance *ext) |
89 | 0 | { |
90 | 0 | LY_ERR ret; |
91 | 0 | const struct lysc_node *child; |
92 | 0 | ly_bool valid = 1; |
93 | 0 | uint32_t prev_options = *lyplg_ext_compile_get_options(cctx); |
94 | | |
95 | | /* compile yangg-data substatements */ |
96 | 0 | LY_ARRAY_CREATE_GOTO(cctx->ctx, ext->substmts, 3, ret, emem); |
97 | 0 | LY_ARRAY_INCREMENT(ext->substmts); |
98 | 0 | ext->substmts[0].stmt = LY_STMT_CONTAINER; |
99 | 0 | ext->substmts[0].storage = &ext->compiled; |
100 | |
|
101 | 0 | LY_ARRAY_INCREMENT(ext->substmts); |
102 | 0 | ext->substmts[1].stmt = LY_STMT_CHOICE; |
103 | 0 | ext->substmts[1].storage = &ext->compiled; |
104 | |
|
105 | 0 | LY_ARRAY_INCREMENT(ext->substmts); |
106 | 0 | ext->substmts[2].stmt = LY_STMT_USES; |
107 | 0 | ext->substmts[2].storage = &ext->compiled; |
108 | |
|
109 | 0 | *lyplg_ext_compile_get_options(cctx) |= LYS_COMPILE_NO_CONFIG | LYS_COMPILE_NO_DISABLED; |
110 | 0 | ret = lyplg_ext_compile_extension_instance(cctx, extp, ext); |
111 | 0 | *lyplg_ext_compile_get_options(cctx) = prev_options; |
112 | 0 | if (ret) { |
113 | 0 | return ret; |
114 | 0 | } |
115 | | |
116 | | /* check that we have really just a single container data definition in the top */ |
117 | 0 | child = ext->compiled; |
118 | 0 | if (!child) { |
119 | 0 | valid = 0; |
120 | 0 | lyplg_ext_compile_log(cctx, ext, LY_LLERR, LY_EVALID, |
121 | 0 | "Extension %s is instantiated without any top level data node, but exactly one container data node is expected.", |
122 | 0 | extp->name); |
123 | 0 | } else if (child->next) { |
124 | 0 | valid = 0; |
125 | 0 | lyplg_ext_compile_log(cctx, ext, LY_LLERR, LY_EVALID, |
126 | 0 | "Extension %s is instantiated with multiple top level data nodes, but only a single container data node is allowed.", |
127 | 0 | extp->name); |
128 | 0 | } else if (child->nodetype == LYS_CHOICE) { |
129 | | /* all the choice's case are expected to result to a single container node */ |
130 | 0 | struct lysc_module *mod_c = ext->parent; |
131 | 0 | const struct lysc_node *snode = NULL; |
132 | |
|
133 | 0 | while ((snode = lys_getnext(snode, child, mod_c, 0))) { |
134 | 0 | if (snode->next) { |
135 | 0 | valid = 0; |
136 | 0 | lyplg_ext_compile_log(cctx, ext, LY_LLERR, LY_EVALID, |
137 | 0 | "Extension %s is instantiated with multiple top level data nodes (inside a single choice's case), " |
138 | 0 | "but only a single container data node is allowed.", extp->name); |
139 | 0 | break; |
140 | 0 | } else if (snode->nodetype != LYS_CONTAINER) { |
141 | 0 | valid = 0; |
142 | 0 | lyplg_ext_compile_log(cctx, ext, LY_LLERR, LY_EVALID, |
143 | 0 | "Extension %s is instantiated with %s top level data node (inside a choice), " |
144 | 0 | "but only a single container data node is allowed.", extp->name, lys_nodetype2str(snode->nodetype)); |
145 | 0 | break; |
146 | 0 | } |
147 | 0 | } |
148 | 0 | } else if (child->nodetype != LYS_CONTAINER) { |
149 | | /* via uses */ |
150 | 0 | valid = 0; |
151 | 0 | lyplg_ext_compile_log(cctx, ext, LY_LLERR, LY_EVALID, |
152 | 0 | "Extension %s is instantiated with %s top level data node, but only a single container data node is allowed.", |
153 | 0 | extp->name, lys_nodetype2str(child->nodetype)); |
154 | 0 | } |
155 | |
|
156 | 0 | if (!valid) { |
157 | 0 | yangdata_cfree(lyplg_ext_compile_get_ctx(cctx), ext); |
158 | 0 | ext->compiled = ext->substmts = NULL; |
159 | 0 | return LY_EVALID; |
160 | 0 | } |
161 | | |
162 | 0 | return LY_SUCCESS; |
163 | | |
164 | 0 | emem: |
165 | 0 | lyplg_ext_compile_log(cctx, ext, LY_LLERR, LY_EMEM, "Memory allocation failed (%s()).", __func__); |
166 | 0 | return LY_EMEM; |
167 | 0 | } |
168 | | |
169 | | /** |
170 | | * @brief INFO printer |
171 | | * |
172 | | * Implementation of ::lyplg_ext_sprinter_info_clb set as ::lyext_plugin::printer_info |
173 | | */ |
174 | | static LY_ERR |
175 | | yangdata_printer_info(struct lyspr_ctx *ctx, struct lysc_ext_instance *ext, ly_bool *flag) |
176 | 0 | { |
177 | 0 | lyplg_ext_print_info_extension_instance(ctx, ext, flag); |
178 | 0 | return LY_SUCCESS; |
179 | 0 | } |
180 | | |
181 | | /** |
182 | | * @brief Free parsed yang-data extension instance data. |
183 | | * |
184 | | * Implementation of ::lyplg_clb_parse_free_clb callback set as lyext_plugin::pfree. |
185 | | */ |
186 | | static void |
187 | | yangdata_pfree(const struct ly_ctx *ctx, struct lysp_ext_instance *ext) |
188 | 0 | { |
189 | 0 | lyplg_ext_pfree_instance_substatements(ctx, ext->substmts); |
190 | 0 | } |
191 | | |
192 | | /** |
193 | | * @brief Free compiled yang-data extension instance data. |
194 | | * |
195 | | * Implementation of ::lyplg_clb_compile_free_clb callback set as lyext_plugin::cfree. |
196 | | */ |
197 | | static void |
198 | | yangdata_cfree(const struct ly_ctx *ctx, struct lysc_ext_instance *ext) |
199 | 0 | { |
200 | 0 | lyplg_ext_cfree_instance_substatements(ctx, ext->substmts); |
201 | 0 | } |
202 | | |
203 | | static void |
204 | | yangdata_sprinter_node(uint16_t nodetype, const char **flags) |
205 | 0 | { |
206 | 0 | if (nodetype & LYS_USES) { |
207 | 0 | *flags = "-u"; |
208 | 0 | } else { |
209 | 0 | *flags = "--"; |
210 | 0 | } |
211 | 0 | } |
212 | | |
213 | | static LY_ERR |
214 | | yangdata_sprinter_cnode(const struct lysc_node *node, const void *UNUSED(plugin_priv), ly_bool *UNUSED(skip), |
215 | | const char **flags, const char **UNUSED(add_opts)) |
216 | 0 | { |
217 | 0 | yangdata_sprinter_node(node->nodetype, flags); |
218 | 0 | return LY_SUCCESS; |
219 | 0 | } |
220 | | |
221 | | static LY_ERR |
222 | | yangdata_sprinter_pnode(const struct lysp_node *node, const void *UNUSED(plugin_priv), ly_bool *UNUSED(skip), |
223 | | const char **flags, const char **UNUSED(add_opts)) |
224 | 0 | { |
225 | 0 | yangdata_sprinter_node(node->nodetype, flags); |
226 | 0 | return LY_SUCCESS; |
227 | 0 | } |
228 | | |
229 | | static LY_ERR |
230 | | yangdata_sprinter_ctree(struct lysc_ext_instance *ext, const struct lyspr_tree_ctx *ctx, |
231 | | const char **UNUSED(flags), const char **UNUSED(add_opts)) |
232 | 0 | { |
233 | 0 | LY_ERR rc = LY_SUCCESS; |
234 | |
|
235 | 0 | assert(ctx); |
236 | 0 | rc = lyplg_ext_sprinter_ctree_add_ext_nodes(ctx, ext, yangdata_sprinter_cnode); |
237 | 0 | return rc; |
238 | 0 | } |
239 | | |
240 | | static LY_ERR |
241 | | yangdata_sprinter_ptree(struct lysp_ext_instance *ext, const struct lyspr_tree_ctx *ctx, |
242 | | const char **UNUSED(flags), const char **UNUSED(add_opts)) |
243 | 0 | { |
244 | 0 | LY_ERR rc = LY_SUCCESS; |
245 | |
|
246 | 0 | assert(ctx); |
247 | 0 | rc = lyplg_ext_sprinter_ptree_add_ext_nodes(ctx, ext, yangdata_sprinter_pnode); |
248 | 0 | return rc; |
249 | 0 | } |
250 | | |
251 | | /** |
252 | | * @brief Plugin descriptions for the yang-data extension |
253 | | * |
254 | | * Note that external plugins are supposed to use: |
255 | | * |
256 | | * LYPLG_EXTENSIONS = { |
257 | | */ |
258 | | const struct lyplg_ext_record plugins_yangdata[] = { |
259 | | { |
260 | | .module = "ietf-restconf", |
261 | | .revision = "2017-01-26", |
262 | | .name = "yang-data", |
263 | | |
264 | | .plugin.id = "ly2 yang-data v1", |
265 | | .plugin.parse = yangdata_parse, |
266 | | .plugin.compile = yangdata_compile, |
267 | | .plugin.printer_info = yangdata_printer_info, |
268 | | .plugin.printer_ctree = yangdata_sprinter_ctree, |
269 | | .plugin.printer_ptree = yangdata_sprinter_ptree, |
270 | | .plugin.node = NULL, |
271 | | .plugin.snode = NULL, |
272 | | .plugin.validate = NULL, |
273 | | .plugin.pfree = yangdata_pfree, |
274 | | .plugin.cfree = yangdata_cfree |
275 | | }, |
276 | | {0} /* terminating zeroed record */ |
277 | | }; |