1 /*
2 *
3 ***** BEGIN LICENSE BLOCK *****
4
5 Copyright (C) 2009-2019 Olof Hagsand
6 Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC(Netgate)
7
8 This file is part of CLIXON.
9
10 Licensed under the Apache License, Version 2.0 (the "License");
11 you may not use this file except in compliance with the License.
12 You may obtain a copy of the License at
13
14 http://www.apache.org/licenses/LICENSE-2.0
15
16 Unless required by applicable law or agreed to in writing, software
17 distributed under the License is distributed on an "AS IS" BASIS,
18 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 See the License for the specific language governing permissions and
20 limitations under the License.
21
22 Alternatively, the contents of this file may be used under the terms of
23 the GNU General Public License Version 3 or later (the "GPL"),
24 in which case the provisions of the GPL are applicable instead
25 of those above. If you wish to allow use of your version of this file only
26 under the terms of the GPL, and not to allow others to
27 use your version of this file under the terms of Apache License version 2,
28 indicate your decision by deleting the provisions above and replace them with
29 the notice and other provisions required by the GPL. If you do not delete
30 the provisions above, a recipient may use your version of this file under
31 the terms of any one of the Apache License version 2 or the GPL.
32
33 ***** END LICENSE BLOCK *****
34
35 * Yang functions
36 * @see https://tools.ietf.org/html/rfc6020 YANG 1.0
37 * @see https://tools.ietf.org/html/rfc7950 YANG 1.1
38 */
39
40 #ifdef HAVE_CONFIG_H
41 #include "clixon_config.h" /* generated by config & autoconf */
42 #endif
43
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <errno.h>
47 #include <limits.h>
48 #include <ctype.h>
49 #include <unistd.h>
50 #include <string.h>
51 #include <arpa/inet.h>
52 #include <regex.h>
53 #include <dirent.h>
54 #include <sys/types.h>
55 #include <fcntl.h>
56 #include <syslog.h>
57 #include <assert.h>
58 #include <libgen.h>
59 #include <sys/stat.h>
60 #include <sys/param.h>
61 #include <netinet/in.h>
62 #include <libgen.h>
63
64 /* cligen */
65 #include <cligen/cligen.h>
66
67 /* clicon */
68 #include "clixon_log.h"
69 #include "clixon_err.h"
70 #include "clixon_string.h"
71 #include "clixon_queue.h"
72 #include "clixon_hash.h"
73 #include "clixon_handle.h"
74 #include "clixon_file.h"
75 #include "clixon_yang.h"
76 #include "clixon_hash.h"
77 #include "clixon_xml.h"
78 #include "clixon_xml_nsctx.h"
79 #include "clixon_yang_module.h"
80 #include "clixon_plugin.h"
81 #include "clixon_data.h"
82 #include "clixon_options.h"
83 #include "clixon_yang_parse.h"
84 #include "clixon_yang_parse_lib.h"
85 #include "clixon_yang_cardinality.h"
86 #include "clixon_yang_type.h"
87 #include "clixon_yang_internal.h" /* internal included by this file only, not API*/
88
89 #ifdef XML_EXPLICIT_INDEX
90 static int yang_search_index_extension(clicon_handle h, yang_stmt *yext, yang_stmt *ys);
91 #endif
92
93 /*
94 * Local variables
95 */
96 /* Mapping between yang keyword string <--> clicon constants
97 * Here is also the place where doc on some types store variables (cv)
98 */
99 static const map_str2int ykmap[] = {
100 {"anydata", Y_ANYDATA},
101 {"anyxml", Y_ANYXML},
102 {"argument", Y_ARGUMENT},
103 {"augment", Y_AUGMENT},
104 {"base", Y_BASE},
105 {"belongs-to", Y_BELONGS_TO},
106 {"bit", Y_BIT},
107 {"case", Y_CASE},
108 {"choice", Y_CHOICE},
109 {"config", Y_CONFIG}, /* cv: boolean config flag */
110 {"contact", Y_CONTACT},
111 {"container", Y_CONTAINER},
112 {"default", Y_DEFAULT},
113 {"description", Y_DESCRIPTION},
114 {"deviate", Y_DEVIATE},
115 {"deviation", Y_DEVIATION},
116 {"enum", Y_ENUM},
117 {"error-app-tag", Y_ERROR_APP_TAG},
118 {"error_message", Y_ERROR_MESSAGE},
119 {"extension", Y_EXTENSION},
120 {"feature", Y_FEATURE}, /* cv: feature as boolean */
121 {"fraction-digits", Y_FRACTION_DIGITS}, /* cv: fraction-digits as uint8 */
122 {"grouping", Y_GROUPING},
123 {"identity", Y_IDENTITY},
124 {"if-feature", Y_IF_FEATURE},
125 {"import", Y_IMPORT},
126 {"include", Y_INCLUDE},
127 {"input", Y_INPUT},
128 {"key", Y_KEY},
129 {"leaf", Y_LEAF}, /* cv: store default value (if any)*/
130 {"leaf-list", Y_LEAF_LIST}, /* cv: store default value (if any)*/
131 {"length", Y_LENGTH},
132 {"list", Y_LIST},
133 {"mandatory", Y_MANDATORY}, /* cv: store mandatory boolean */
134 {"max-elements", Y_MAX_ELEMENTS},
135 {"min-elements", Y_MIN_ELEMENTS},
136 {"modifier", Y_MODIFIER},
137 {"module", Y_MODULE},
138 {"must", Y_MUST},
139 {"namespace", Y_NAMESPACE},
140 {"notification", Y_NOTIFICATION},
141 {"ordered-by", Y_ORDERED_BY},
142 {"organization", Y_ORGANIZATION},
143 {"output", Y_OUTPUT},
144 {"path", Y_PATH},
145 {"pattern", Y_PATTERN},
146 {"position", Y_POSITION},
147 {"prefix", Y_PREFIX},
148 {"presence", Y_PRESENCE},
149 {"range", Y_RANGE},
150 {"reference", Y_REFERENCE},
151 {"refine", Y_REFINE},
152 {"require-instance", Y_REQUIRE_INSTANCE},
153 {"revision", Y_REVISION}, /* cv: YYYY-MM-DD as uint32 */
154 {"revision-date", Y_REVISION_DATE}, /* cv: YYYY-MM-DD as uint32 */
155 {"rpc", Y_RPC},
156 {"status", Y_STATUS},
157 {"submodule", Y_SUBMODULE},
158 {"type", Y_TYPE},
159 {"typedef", Y_TYPEDEF},
160 {"unique", Y_UNIQUE},
161 {"units", Y_UNITS},
162 {"unknown", Y_UNKNOWN}, /* cv: store extra string */
163 {"uses", Y_USES},
164 {"value", Y_VALUE},
165 {"when", Y_WHEN},
166 {"yang-version", Y_YANG_VERSION},
167 {"yin-element", Y_YIN_ELEMENT},
168 {"yang-specification", Y_SPEC}, /* XXX: NOTE NOT YANG STATEMENT, reserved
169 for top level spec */
170 {NULL, -1}
171 };
172
173 /* Forward static */
174 static int yang_type_cache_free(yang_type_cache *ycache);
175 static int yang_type_cache_cp(yang_stmt *ynew, yang_stmt *yold);
176
177 /* Access functions
178 */
179
180 /*! Get Number of children yang statements
181 * @param[in] ys Yang statement node
182 */
183 int
yang_len_get(yang_stmt * ys)184 yang_len_get(yang_stmt *ys)
185 {
186 return ys->ys_len;
187 }
188
189 /*! Get Specific Yang statement child
190 * @param[in] ys Yang statement node
191 * @param[in] i Return this child
192 */
193 yang_stmt *
yang_child_i(yang_stmt * ys,int i)194 yang_child_i(yang_stmt *ys,
195 int i)
196 {
197 return ys->ys_stmt[i];
198 }
199
200 /*! Get yang statement parent
201 * @param[in] ys Yang statement node
202 */
203 yang_stmt *
yang_parent_get(yang_stmt * ys)204 yang_parent_get(yang_stmt *ys)
205 {
206 return ys->ys_parent;
207 }
208
209 /*! Get yang statement keyword
210 * @param[in] ys Yang statement node
211 */
212 enum rfc_6020
yang_keyword_get(yang_stmt * ys)213 yang_keyword_get(yang_stmt *ys)
214 {
215 return ys->ys_keyword;
216 }
217
218 /*! Get yang statement context-dependent argument
219 * @param[in] ys Yang statement node
220 */
221 char*
yang_argument_get(yang_stmt * ys)222 yang_argument_get(yang_stmt *ys)
223 {
224 return ys->ys_argument;
225 }
226
227 /*
228 * Note on cvec on XML nodes:
229 * 1. It is always created in xml_new. It could be lazily created on use to save a little memory
230 * 2. Only some yang statements use the cvec, as follows:
231 * 2a. ranges and lengths: [min, max]
232 * 2b. list: keys
233 * 2c. identity types: derived instances: identityrefs, save <module>:<idref>
234 * 2d. type: leafref types: derived instances.
235 */
236 /*! Set yang argument, not not copied
237 * @param[in] ys Yang statement node
238 * @param[in] arg Argument
239 * Typically only done at parsing / initiation
240 */
241 int
yang_argument_set(yang_stmt * ys,char * arg)242 yang_argument_set(yang_stmt *ys,
243 char *arg)
244 {
245 ys->ys_argument = arg; /* not strdup/copied */
246 return 0;
247 }
248
249 /*! Get yang statement CLIgen variable
250 * @param[in] ys Yang statement node
251 */
252 cg_var*
yang_cv_get(yang_stmt * ys)253 yang_cv_get(yang_stmt *ys)
254 {
255 return ys->ys_cv;
256 }
257
258 /*! Get yang statement CLIgen variable vector
259 * @param[in] ys Yang statement node
260 */
261 cvec*
yang_cvec_get(yang_stmt * ys)262 yang_cvec_get(yang_stmt *ys)
263 {
264 return ys->ys_cvec;
265 }
266
267 /*! Set yang statement CLIgen variable vector
268 * @param[in] ys Yang statement node
269 * @param[in] cvec CLIgen vector
270 * @retval 0 OK
271 * @retval -1 Error
272 */
273 int
yang_cvec_set(yang_stmt * ys,cvec * cvv)274 yang_cvec_set(yang_stmt *ys,
275 cvec *cvv)
276 {
277 if (ys->ys_cvec)
278 cvec_free(ys->ys_cvec);
279 ys->ys_cvec = cvv;
280 return 0;
281 }
282
283 /*! Get yang stmt flags, used for internal algorithms
284 * @param[in] ys Yang statement
285 * @param[in] flag Flags value(s) to get, see YANG_FLAG_*
286 * @retval value Flags value masked by "flag" parameter, see YANG_FLAG_*
287 */
288 uint16_t
yang_flag_get(yang_stmt * ys,uint16_t flag)289 yang_flag_get(yang_stmt *ys,
290 uint16_t flag)
291 {
292 return ys->ys_flags&flag;
293 }
294
295 /*! Set yang stmt flags, used for internal algorithms
296 * @param[in] ys Yang statement
297 * @param[in] flag Flag value(s) to set, see YANG_FLAG_*
298 */
299 int
yang_flag_set(yang_stmt * ys,uint16_t flag)300 yang_flag_set(yang_stmt *ys,
301 uint16_t flag)
302 {
303 ys->ys_flags |= flag;
304 return 0;
305 }
306
307 /*! Reset yang stmt flags, used for internal algorithms
308 * @param[in] ys Yang statement
309 * @param[in] flag Flag value(s) to reset, see YANG_FLAG_*
310 */
311 int
yang_flag_reset(yang_stmt * ys,uint16_t flag)312 yang_flag_reset(yang_stmt *ys,
313 uint16_t flag)
314 {
315 ys->ys_flags &= ~flag;
316 return 0;
317 }
318
319 /*! Get yang xpath for "when"-associated augment
320 *
321 * Ie, for yang structures like: augment <path> { when <xpath>; ... }
322 * Will insert new yang nodes at <path> with this special "when" struct (not yang node)
323 * @param[in] ys Yang statement
324 * @retval xpath xpath should evaluate to true at validation
325 * @retval NULL Not set
326 */
327 char*
yang_when_xpath_get(yang_stmt * ys)328 yang_when_xpath_get(yang_stmt *ys)
329 {
330 return ys->ys_when_xpath;
331 }
332
333 /*! Set yang xpath and namespace context for "when"-associated augment
334 *
335 * Ie, for yang structures like: augment <path> { when <xpath>; ... }
336 * Will insert new yang nodes at <path> with this special "when" struct (not yang node)
337 * @param[in] ys Yang statement
338 * @param[in] xpath If set, this xpath should evaluate to true at validation, copied
339 * @retval 0 OK
340 * @retval -1 Error
341 */
342 int
yang_when_xpath_set(yang_stmt * ys,char * xpath)343 yang_when_xpath_set(yang_stmt *ys,
344 char *xpath)
345 {
346 int retval = -1;
347
348 if (xpath == NULL){
349 clicon_err(OE_YANG, EINVAL, "xpath is NULL");
350 goto done;
351 }
352 if ((ys->ys_when_xpath = strdup(xpath)) == NULL){
353 clicon_err(OE_YANG, errno, "strdup");
354 goto done;
355
356 }
357 retval = 0;
358 done:
359 return retval;
360 }
361
362 /*! Get yang namespace context for "when"-associated augment
363 *
364 * Ie, for yang structures like: augment <path> { when <xpath>; ... }
365 * Will insert new yang nodes at <path> with this special "when" struct (not yang node)
366 * @param[in] ys Yang statement
367 * @retval nsc Namespace context
368 * @note retval is direct pointer, may need to be copied
369 */
370 cvec *
yang_when_nsc_get(yang_stmt * ys)371 yang_when_nsc_get(yang_stmt *ys)
372 {
373 return ys->ys_when_nsc;
374 }
375
376 /*! Set yang namespace context for "when"-associated augment
377 *
378 * Ie, for yang structures like: augment <path> { when <xpath>; ... }
379 * Will insert new yang nodes at <path> with this special "when" struct (not yang node)
380 * @param[in] ys Yang statement
381 * @param[in] nsc Namespace context for when xpath
382 * @retval 0 OK
383 * @retval -1 Error
384 */
385 int
yang_when_nsc_set(yang_stmt * ys,cvec * nsc)386 yang_when_nsc_set(yang_stmt *ys,
387 cvec *nsc)
388 {
389 int retval = -1;
390
391 if (nsc && (ys->ys_when_nsc = cvec_dup(nsc)) == NULL){
392 clicon_err(OE_YANG, errno, "cvec_dup");
393 goto done;
394 }
395 retval = 0;
396 done:
397 return retval;
398 }
399
400 /* End access functions */
401
402 /*! Create new yang specification
403 * @retval yspec Free with yspec_free()
404 * @retval NULL Error
405 */
406 yang_stmt *
yspec_new(void)407 yspec_new(void)
408 {
409 yang_stmt *yspec;
410
411 if ((yspec = malloc(sizeof(*yspec))) == NULL){
412 clicon_err(OE_YANG, errno, "malloc");
413 return NULL;
414 }
415 memset(yspec, 0, sizeof(*yspec));
416 yspec->ys_keyword = Y_SPEC;
417 return yspec;
418 }
419
420 /*! Create new yang node/statement
421 * @retval ys Free with ys_free()
422 * @retval NULL Error
423 */
424 yang_stmt *
ys_new(enum rfc_6020 keyw)425 ys_new(enum rfc_6020 keyw)
426 {
427 yang_stmt *ys;
428 cvec *cvv;
429
430 if ((ys = malloc(sizeof(*ys))) == NULL){
431 clicon_err(OE_YANG, errno, "malloc");
432 return NULL;
433 }
434 memset(ys, 0, sizeof(*ys));
435 ys->ys_keyword = keyw;
436 /* The cvec contains stmt-specific variables. Only few stmts need variables so the
437 cvec could be lazily created to save some heap and cycles. */
438 if ((cvv = cvec_new(0)) == NULL){
439 clicon_err(OE_YANG, errno, "cvec_new");
440 return NULL;
441 }
442 yang_cvec_set(ys, cvv);
443 return ys;
444 }
445
446 /*! Free a single yang statement, dont remove children
447 *
448 * @param[in] ys Yang node to remove
449 * @param[in] self Free own node
450 * @retval 0 OK
451 * @retval -1 Error
452 * @see ys_free
453 */
454 static int
ys_free1(yang_stmt * ys,int self)455 ys_free1(yang_stmt *ys,
456 int self)
457 {
458 if (ys->ys_argument){
459 free(ys->ys_argument);
460 ys->ys_argument = NULL;
461 }
462 if (ys->ys_cv){
463 cv_free(ys->ys_cv);
464 ys->ys_cv = NULL;
465 }
466 if (ys->ys_cvec){
467 cvec_free(ys->ys_cvec);
468 ys->ys_cvec = NULL;
469 }
470 if (ys->ys_typecache){
471 yang_type_cache_free(ys->ys_typecache);
472 ys->ys_typecache = NULL;
473 }
474 if (ys->ys_when_xpath)
475 free(ys->ys_when_xpath);
476 if (ys->ys_when_nsc)
477 cvec_free(ys->ys_when_nsc);
478 if (self)
479 free(ys);
480 return 0;
481 }
482
483 /*! Remove child i from parent yp (dont free)
484 * @param[in] yp Parent node
485 * @param[in] i Order of child to remove
486 * @retval NULL No such node, nothing done
487 * @retval yc returned orphaned yang node
488 * @see ys_free Deallocate yang node
489 * @note Do not call this in a loop of yang children (unless you know what you are doing)
490 */
491 yang_stmt *
ys_prune(yang_stmt * yp,int i)492 ys_prune(yang_stmt *yp,
493 int i)
494 {
495 size_t size;
496 yang_stmt *yc = NULL;
497
498 if (i >= yp->ys_len)
499 goto done;
500 size = (yp->ys_len - i - 1)*sizeof(struct yang_stmt *);
501 yc = yp->ys_stmt[i];
502 memmove(&yp->ys_stmt[i],
503 &yp->ys_stmt[i+1],
504 size);
505 yp->ys_stmt[yp->ys_len--] = NULL;
506 done:
507 return yc;
508 }
509
510 /*! Free a yang statement tree recursively
511 * @param[in] ys Yang node to remove and all its children recursively
512 * @note does not remove yang node from tree
513 * @see ys_prune Remove from parent
514 */
515 int
ys_free(yang_stmt * ys)516 ys_free(yang_stmt *ys)
517 {
518 int i;
519 yang_stmt *yc;
520
521 for (i=0; i<ys->ys_len; i++){
522 if ((yc = ys->ys_stmt[i]) != NULL)
523 ys_free(yc);
524 }
525 if (ys->ys_stmt)
526 free(ys->ys_stmt);
527 ys_free1(ys, 1);
528 return 0;
529 }
530
531 /*! Free a yang specification recursively
532 */
533 int
yspec_free(yang_stmt * yspec)534 yspec_free(yang_stmt *yspec)
535 {
536 int i;
537 yang_stmt *ys;
538
539 for (i=0; i<yspec->ys_len; i++){
540 if ((ys = yspec->ys_stmt[i]) != NULL)
541 ys_free(ys);
542 }
543 if (yspec->ys_stmt)
544 free(yspec->ys_stmt);
545 free(yspec);
546 return 0;
547 }
548
549 /*! Allocate larger yang statement vector adding empty field last */
550 static int
yn_realloc(yang_stmt * yn)551 yn_realloc(yang_stmt *yn)
552 {
553 yn->ys_len++;
554
555 if ((yn->ys_stmt = realloc(yn->ys_stmt, (yn->ys_len)*sizeof(yang_stmt *))) == 0){
556 clicon_err(OE_YANG, errno, "realloc");
557 return -1;
558 }
559 yn->ys_stmt[yn->ys_len - 1] = NULL; /* init field */
560 return 0;
561 }
562
563 /*! Copy yang statement recursively from old to new
564 * @param[in] ynew New empty (but created) yang statement (to)
565 * @param[in] yold Old existing yang statement (from)
566 * @retval 0 OK
567 * @retval -1 Error
568 * @code
569 * yang_stmt *new = ys_new(Y_LEAF);
570 * if (ys_cp(new, old) < 0)
571 * err;
572 * @endcode
573 * @see ys_replace
574 */
575 int
ys_cp(yang_stmt * ynew,yang_stmt * yold)576 ys_cp(yang_stmt *ynew,
577 yang_stmt *yold)
578 {
579 int retval = -1;
580 int i;
581 yang_stmt *ycn; /* new child */
582 yang_stmt *yco; /* old child */
583
584 memcpy(ynew, yold, sizeof(*yold));
585 ynew->ys_parent = NULL;
586 if (yold->ys_stmt)
587 if ((ynew->ys_stmt = calloc(yold->ys_len, sizeof(yang_stmt *))) == NULL){
588 clicon_err(OE_YANG, errno, "calloc");
589 goto done;
590 }
591 if (yold->ys_argument)
592 if ((ynew->ys_argument = strdup(yold->ys_argument)) == NULL){
593 clicon_err(OE_YANG, errno, "strdup");
594 goto done;
595 }
596 if (yold->ys_cv)
597 if ((ynew->ys_cv = cv_dup(yold->ys_cv)) == NULL){
598 clicon_err(OE_YANG, errno, "cv_dup");
599 goto done;
600 }
601 if (yold->ys_cvec)
602 if ((ynew->ys_cvec = cvec_dup(yold->ys_cvec)) == NULL){
603 clicon_err(OE_YANG, errno, "cvec_dup");
604 goto done;
605 }
606 if (yold->ys_typecache){
607 ynew->ys_typecache = NULL;
608 if (yang_type_cache_cp(ynew, yold) < 0)
609 goto done;
610 }
611 if (yold->ys_when_xpath)
612 if ((ynew->ys_when_xpath = strdup(yold->ys_when_xpath)) == NULL){
613 clicon_err(OE_YANG, errno, "strdup");
614 goto done;
615 }
616 if (yold->ys_when_nsc){
617 if ((ynew->ys_when_nsc = cvec_dup(yold->ys_when_nsc)) == NULL){
618 clicon_err(OE_YANG, errno, "cvec_dup");
619 goto done;
620 }
621 }
622 for (i=0; i<ynew->ys_len; i++){
623 yco = yold->ys_stmt[i];
624 if ((ycn = ys_dup(yco)) == NULL)
625 goto done;
626 ynew->ys_stmt[i] = ycn;
627 ycn->ys_parent = ynew;
628 }
629 retval = 0;
630 done:
631 return retval;
632 }
633
634 /*! Create a new yang node and copy the contents recursively from the original. *
635 * @param[in] old Old existing yang statement (from)
636 * @retval NULL Error
637 * @retval nw New created yang statement
638 * @retval 0 OK
639 * @retval -1 Error
640 * This may involve duplicating strings, etc.
641 * The new yang tree needs to be freed by ys_free().
642 * The parent of new is NULL, it needs to be explicityl inserted somewhere
643 */
644 yang_stmt *
ys_dup(yang_stmt * old)645 ys_dup(yang_stmt *old)
646 {
647 yang_stmt *nw;
648
649 if ((nw = ys_new(old->ys_keyword)) == NULL)
650 return NULL;
651 if (nw->ys_cvec){
652 cvec_free(nw->ys_cvec);
653 nw->ys_cvec = NULL;
654 }
655 if (ys_cp(nw, old) < 0){
656 ys_free(nw);
657 return NULL;
658 }
659 return nw;
660 }
661
662 /*! Replace yold with ynew (insert ynew at the exact place of yold). Keep yold pointer as-is.
663 *
664 * @param[in] yorig Existing yang statement
665 * @param[in] yfrom New empty (but created) yang statement
666
667 * @retval 0 OK
668 * @retval -1 Error
669 * @code
670 * if (ys_replace(new, old) < 0)
671 * err;
672 * @endcode
673 * @see ys_cp
674 * @note yfrom is left in its original state
675 */
676 int
ys_replace(yang_stmt * yorig,yang_stmt * yfrom)677 ys_replace(yang_stmt *yorig,
678 yang_stmt *yfrom)
679 {
680 int retval = -1;
681 yang_stmt *yp; /* parent */
682 yang_stmt *yc; /* child */
683
684 yp = yang_parent_get(yorig);
685 /* Remove old yangs all children */
686 yc = NULL;
687 while ((yc = yn_each(yorig, yc)) != NULL)
688 ys_free(yc);
689 if (yorig->ys_stmt){
690 free(yorig->ys_stmt);
691 yorig->ys_stmt = NULL;
692 yorig->ys_len = 0;
693 }
694 ys_free1(yorig, 0); /* Remove all in yold except the actual object */
695 if (ys_cp(yorig, yfrom) < 0)
696 goto done;
697 yorig->ys_parent = yp;
698 retval = 0;
699 done:
700 return retval;
701 }
702
703 /*! Append yang statement as child of a parent yang_statement, last in list
704 *
705 * @param[in] ys_parent Add child to this parent
706 * @param[in] ys_child Add this child
707 * @retval 0 OK
708 * @retval -1 Error
709 * Also add parent to child as up-pointer
710 * @see ys_prune
711 */
712 int
yn_insert(yang_stmt * ys_parent,yang_stmt * ys_child)713 yn_insert(yang_stmt *ys_parent,
714 yang_stmt *ys_child)
715 {
716 int pos = ys_parent->ys_len;
717
718 if (yn_realloc(ys_parent) < 0)
719 return -1;
720 ys_parent->ys_stmt[pos] = ys_child;
721 ys_child->ys_parent = ys_parent;
722 return 0;
723 }
724
725 /*! Iterate through all yang statements from a yang node
726 *
727 * @param[in] yparent yang statement whose children should be iterated
728 * @param[in] yprev previous child, or NULL on init
729 * @code
730 * yang_stmt *yprev = NULL;
731 * while ((yprev = yn_each(yparent, yprev)) != NULL) {
732 * ...yprev...
733 * }
734 * @endcode
735 * @note makes uses _ys_vector_i:can be changed if list changed between calls
736 * @note also does not work in recursive calls (to same node)
737 */
738 yang_stmt *
yn_each(yang_stmt * yparent,yang_stmt * yprev)739 yn_each(yang_stmt *yparent,
740 yang_stmt *yprev)
741 {
742 int i;
743 yang_stmt *yc = NULL;
744
745 if (yparent == NULL)
746 return NULL;
747 for (i=yprev?yprev->_ys_vector_i+1:0; i<yparent->ys_len; i++){
748 if ((yc = yparent->ys_stmt[i]) == NULL){
749 assert(yc); /* XXX Check if happens */
750 continue;
751 }
752 /* make room for other conditionals */
753 break; /* this is next object after previous */
754 }
755 if (i < yparent->ys_len) /* found */
756 yc->_ys_vector_i = i;
757 else
758 yc = NULL;
759 return yc;
760 }
761
762 /*! Find first child yang_stmt with matching keyword and argument
763 *
764 * @param[in] yn Yang node, current context node.
765 * @param[in] keyword if 0 match any keyword
766 * @param[in] argument String compare w argument. if NULL, match any.
767 * @retval ys Yang statement, if any
768 * This however means that if you actually want to match only a yang-stmt with
769 * argument==NULL you cannot, but I have not seen any such examples.
770 * @see yang_find_datanode
771 * @see yang_match returns number of matches
772 */
773 yang_stmt *
yang_find(yang_stmt * yn,int keyword,const char * argument)774 yang_find(yang_stmt *yn,
775 int keyword,
776 const char *argument)
777 {
778 yang_stmt *ys = NULL;
779 int i;
780 yang_stmt *yret = NULL;
781 char *name;
782 yang_stmt *yspec;
783 yang_stmt *ym;
784
785 for (i=0; i<yn->ys_len; i++){
786 ys = yn->ys_stmt[i];
787 if (keyword == 0 || ys->ys_keyword == keyword){
788 if (argument == NULL ||
789 (ys->ys_argument && strcmp(argument, ys->ys_argument) == 0)){
790 yret = ys;
791 break;
792 }
793 }
794 }
795 /* Special case: if not match and yang node is module or submodule, extend
796 * search to include submodules */
797 if (yret == NULL &&
798 (yang_keyword_get(yn) == Y_MODULE ||
799 yang_keyword_get(yn) == Y_SUBMODULE)){
800 yspec = ys_spec(yn);
801 for (i=0; i<yn->ys_len; i++){
802 ys = yn->ys_stmt[i];
803 if (yang_keyword_get(ys) == Y_INCLUDE){
804 name = yang_argument_get(ys);
805 if ((ym = yang_find_module_by_name(yspec, name)) != NULL &&
806 (yret = yang_find(ym, keyword, argument)) != NULL)
807 break;
808 }
809 }
810 }
811 return yret;
812 }
813
814 /*! Count number of children that matches keyword and argument
815 *
816 * @param[in] yn Yang node, current context node.
817 * @param[in] keyword if 0 match any keyword
818 * @param[in] argument String compare w argument. if NULL, match any.
819 * @retval n Number of matches
820 * This however means that if you actually want to match only a yang-stmt with
821 * argument==NULL you cannot, but I have not seen any such examples.
822 * @see yang_find
823 */
824 int
yang_match(yang_stmt * yn,int keyword,char * argument)825 yang_match(yang_stmt *yn,
826 int keyword,
827 char *argument)
828 {
829 yang_stmt *ys = NULL;
830 int i;
831 int match = 0;
832
833 for (i=0; i<yn->ys_len; i++){
834 ys = yn->ys_stmt[i];
835 if (keyword == 0 || ys->ys_keyword == keyword){
836 if (argument == NULL)
837 match++;
838 else
839 if (ys->ys_argument && strcmp(argument, ys->ys_argument) == 0)
840 match++;
841 }
842 }
843 return match;
844 }
845
846 /*! Find child data node with matching argument (container, leaf, list, leaf-list)
847 *
848 * @param[in] yn Yang node, current context node.
849 * @param[in] argument if NULL, match any(first) argument. XXX is that really a case?
850 *
851 * @see yang_find Looks for any node
852 * @note May deviate from RFC since it explores choice/case not just return it.
853 */
854 yang_stmt *
yang_find_datanode(yang_stmt * yn,char * argument)855 yang_find_datanode(yang_stmt *yn,
856 char *argument)
857 {
858 yang_stmt *ys = NULL;
859 yang_stmt *yc = NULL;
860 yang_stmt *yspec;
861 yang_stmt *ysmatch = NULL;
862 char *name;
863
864 ys = NULL;
865 while ((ys = yn_each(yn, ys)) != NULL){
866 if (yang_keyword_get(ys) == Y_CHOICE){ /* Look for its children */
867 yc = NULL;
868 while ((yc = yn_each(ys, yc)) != NULL){
869 if (yang_keyword_get(yc) == Y_CASE) /* Look for its children */
870 ysmatch = yang_find_datanode(yc, argument);
871 else
872 if (yang_datanode(yc)){
873 if (argument == NULL)
874 ysmatch = yc;
875 else
876 if (yc->ys_argument && strcmp(argument, yc->ys_argument) == 0)
877 ysmatch = yc;
878 }
879 if (ysmatch)
880 goto match;
881 }
882 } /* Y_CHOICE */
883 else{
884 if (yang_datanode(ys)){
885 if (argument == NULL)
886 ysmatch = ys;
887 else
888 if (ys->ys_argument && strcmp(argument, ys->ys_argument) == 0)
889 ysmatch = ys;
890 if (ysmatch)
891 goto match;
892 }
893 }
894 }
895 /* Special case: if not match and yang node is module or submodule, extend
896 * search to include submodules */
897 if (ysmatch == NULL &&
898 (yang_keyword_get(yn) == Y_MODULE ||
899 yang_keyword_get(yn) == Y_SUBMODULE)){
900 yspec = ys_spec(yn);
901 ys = NULL;
902 while ((ys = yn_each(yn, ys)) != NULL){
903 if (yang_keyword_get(ys) == Y_INCLUDE){
904 name = yang_argument_get(ys);
905 yc = yang_find_module_by_name(yspec, name);
906 if ((ysmatch = yang_find_datanode(yc, argument)) != NULL)
907 break;
908 }
909 }
910 }
911 match:
912 return ysmatch;
913 }
914
915 /*! Find child schema node with matching argument (container, leaf, etc)
916 * @param[in] yn Yang node, current context node.
917 * @param[in] argument if NULL, match any(first) argument.
918 * @note XXX unify code with yang_find_datanode?
919 * @see yang_find_datanode
920 */
921 yang_stmt *
yang_find_schemanode(yang_stmt * yn,char * argument)922 yang_find_schemanode(yang_stmt *yn,
923 char *argument)
924 {
925 yang_stmt *ys = NULL;
926 yang_stmt *yc = NULL;
927 yang_stmt *yspec;
928 yang_stmt *ysmatch = NULL;
929 char *name;
930 int i, j;
931
932 for (i=0; i<yn->ys_len; i++){
933 ys = yn->ys_stmt[i];
934 if (ys->ys_keyword == Y_CHOICE){ /* Look for its children */
935 for (j=0; j<ys->ys_len; j++){
936 yc = ys->ys_stmt[j];
937 if (yc->ys_keyword == Y_CASE) /* Look for its children */
938 ysmatch = yang_find_schemanode(yc, argument);
939 else
940 if (yang_schemanode(yc)){
941 if (argument == NULL)
942 ysmatch = yc;
943 else
944 if (yc->ys_argument && strcmp(argument, yc->ys_argument) == 0)
945 ysmatch = yc;
946 }
947 if (ysmatch)
948 goto match;
949 }
950 } /* Y_CHOICE */
951 else
952 if (yang_schemanode(ys)){
953 if (argument == NULL)
954 ysmatch = ys;
955 else
956 if (ys->ys_argument && strcmp(argument, ys->ys_argument) == 0)
957 ysmatch = ys;
958 if (ysmatch)
959 goto match;
960 }
961 }
962 /* Special case: if not match and yang node is module or submodule, extend
963 * search to include submodules */
964 if (ysmatch == NULL &&
965 (yang_keyword_get(yn) == Y_MODULE ||
966 yang_keyword_get(yn) == Y_SUBMODULE)){
967 yspec = ys_spec(yn);
968 for (i=0; i<yn->ys_len; i++){
969 ys = yn->ys_stmt[i];
970 if (yang_keyword_get(ys) == Y_INCLUDE){
971 name = yang_argument_get(ys);
972 yc = yang_find_module_by_name(yspec, name);
973 if ((ysmatch = yang_find_schemanode(yc, argument)) != NULL)
974 break;
975 }
976 }
977 }
978 match:
979 return ysmatch;
980 }
981
982 /*! Given a yang statement, find the prefix associated to this module
983 *
984 * @param[in] ys Yang statement in module tree (or module itself)
985 * @retval NULL No prefix found. This is an error
986 * @retval prefix OK: Prefix as char* pointer into yang tree
987 * @code
988 * char *myprefix;
989 * myprefix = yang_find_myprefix(ys);
990 * @endcode
991 */
992 char *
yang_find_myprefix(yang_stmt * ys)993 yang_find_myprefix(yang_stmt *ys)
994 {
995 yang_stmt *ymod = NULL; /* My module */
996 yang_stmt *yprefix;
997 char *prefix = NULL;
998
999 /* Not good enough with submodule, must be actual module */
1000 if (ys_real_module(ys, &ymod) < 0)
1001 goto done;
1002 if (ymod == NULL){
1003 clicon_err(OE_YANG, ENOENT, "Internal error: no module");
1004 goto done;
1005 }
1006 if ((yprefix = yang_find(ymod, Y_PREFIX, NULL)) == NULL){
1007 clicon_err(OE_YANG, ENOENT, "No prefix found for module %s", yang_argument_get(ymod));
1008 goto done;
1009 }
1010 prefix = yang_argument_get(yprefix);
1011 done:
1012 return prefix;
1013 }
1014
1015 /*! Given a yang statement, find the namespace URI associated to this module
1016 *
1017 * @param[in] ys Yang statement in module tree (or module itself)
1018 * @retval NULL Error: No namespace found. This is an error
1019 * @retval ns Namspace URI as char* pointer into yang tree
1020 * @code
1021 * char *myns = yang_find_mynamespace(ys);
1022 * @endcode
1023 * @see yang_find_module_by_namespace
1024 */
1025 char *
yang_find_mynamespace(yang_stmt * ys)1026 yang_find_mynamespace(yang_stmt *ys)
1027 {
1028 yang_stmt *ymod = NULL; /* My module */
1029 yang_stmt *ynamespace;
1030 char *ns = NULL;
1031
1032 if (ys_real_module(ys, &ymod) < 0)
1033 goto done;
1034 if ((ynamespace = yang_find(ymod, Y_NAMESPACE, NULL)) == NULL){
1035 clicon_err(OE_YANG, ENOENT, "No namespace found for module %s", yang_argument_get(ymod));
1036 goto done;
1037 }
1038 ns = yang_argument_get(ynamespace);
1039 done:
1040 return ns;
1041 }
1042
1043 /*! Given a yang statement and namespace, find local prefix valid in module
1044 * This is useful if you want to make a "reverse" lookup, you know the
1045 * (global) namespace of a module, but you do not know the local prefix
1046 * used to access it in XML.
1047 * @param[in] ys Yang statement in module tree (or module itself)
1048 * @param[in] ns Namspace URI as char* pointer into yang tree
1049 * @param[out] prefix Local prefix to access module with (direct pointer)
1050 * @retval 0 not found
1051 * @retval -1 found
1052 * @note prefix NULL is not returned, if own module, then return its prefix
1053 * @code
1054 * char *prefix = NULL;
1055 * if (yang_find_prefix_by_namespace(ys, "urn:example:clixon", &prefix) < 0)
1056 * err;
1057 * @endcode
1058 */
1059 int
yang_find_prefix_by_namespace(yang_stmt * ys,char * ns,char ** prefix)1060 yang_find_prefix_by_namespace(yang_stmt *ys,
1061 char *ns,
1062 char **prefix)
1063 {
1064 int retval = 0; /* not found */
1065 yang_stmt *my_ymod; /* My module */
1066 char *myns; /* My ns */
1067 yang_stmt *yspec;
1068 yang_stmt *ymod;
1069 char *modname = NULL;
1070 yang_stmt *yimport;
1071 yang_stmt *yprefix;
1072
1073 clicon_debug(1, "%s", __FUNCTION__);
1074 /* First check if namespace is my own module */
1075 myns = yang_find_mynamespace(ys);
1076 if (strcmp(myns, ns) == 0){
1077 *prefix = yang_find_myprefix(ys); /* or NULL? */
1078 goto found;
1079 }
1080 /* Next, find namespaces in imported modules */
1081 yspec = ys_spec(ys);
1082 if ((ymod = yang_find_module_by_namespace(yspec, ns)) == NULL)
1083 goto notfound;
1084 modname = yang_argument_get(ymod);
1085 my_ymod = ys_module(ys);
1086 /* Loop through import statements to find a match with ymod */
1087 yimport = NULL;
1088 while ((yimport = yn_each(my_ymod, yimport)) != NULL) {
1089 if (yang_keyword_get(yimport) == Y_IMPORT &&
1090 strcmp(modname, yang_argument_get(yimport)) == 0){ /* match */
1091 yprefix = yang_find(yimport, Y_PREFIX, NULL);
1092 *prefix = yang_argument_get(yprefix);
1093 goto found;
1094 }
1095 }
1096 notfound:
1097 return retval;
1098 found:
1099 assert(*prefix);
1100 return 1;
1101 }
1102
1103 /*! Return topmost yang root node directly under module/submodule
1104 *
1105 * @param[in] ys Yang statement
1106 * @retval ytop Topmost yang node (can be ys itself)
1107 * @retval NULL ys is spec, module, NULL etc with no reasonable rootnode
1108 */
1109 yang_stmt *
yang_myroot(yang_stmt * ys)1110 yang_myroot(yang_stmt *ys)
1111 {
1112 yang_stmt *yp;
1113 enum rfc_6020 kw;
1114
1115 kw = yang_keyword_get(ys);
1116 if (ys==NULL || kw==Y_SPEC || kw == Y_MODULE || kw == Y_SUBMODULE)
1117 return NULL;
1118 yp = yang_parent_get(ys);
1119 while((yp = yang_parent_get(ys)) != NULL) {
1120 kw = yang_keyword_get(yp);
1121 if (kw == Y_MODULE || kw == Y_SUBMODULE)
1122 return ys;
1123 ys = yp;
1124 }
1125 return NULL;
1126 }
1127
1128 /*! If a given yang stmt has a choice/case as parent, return the choice statement
1129 */
1130 yang_stmt *
yang_choice(yang_stmt * y)1131 yang_choice(yang_stmt *y)
1132 {
1133 yang_stmt *yp;
1134
1135 if ((yp = y->ys_parent) != NULL){
1136 switch (yang_keyword_get(yp)){
1137 case Y_CHOICE:
1138 return yp;
1139 break;
1140 case Y_CASE:
1141 return yang_parent_get(yp);
1142 break;
1143 default:
1144 break;
1145 }
1146 }
1147 return NULL;
1148 }
1149
1150 /*! Find matching y in yp:s children, "yang order" of y when y is choice
1151 * @param[in] yp Choice node
1152 * @param[in] y Yang datanode to find
1153 * @param[out] index Index of y in yp:s list of children
1154 * @retval 0 not found (must be datanode)
1155 * @retval 1 found
1156 * @see order1 the main function
1157 * There are two distinct cases, either (1) the choice has case statements, or
1158 * (2) it uses shortcut mode without case statements.
1159 * In (1) we need to count how many sub-statements and keep a max
1160 * In (2) we increment with only 1.
1161 */
1162 static int
order1_choice(yang_stmt * yp,yang_stmt * y,int * index)1163 order1_choice(yang_stmt *yp,
1164 yang_stmt *y,
1165 int *index)
1166 {
1167 yang_stmt *ys;
1168 yang_stmt *yc;
1169 int i;
1170 int j;
1171 int shortcut=0;
1172 int max=0;
1173
1174 for (i=0; i<yp->ys_len; i++){ /* Loop through choice */
1175 ys = yp->ys_stmt[i];
1176 if (ys->ys_keyword == Y_CASE){ /* Loop through case */
1177 for (j=0; j<ys->ys_len; j++){
1178 yc = ys->ys_stmt[j];
1179 if (yang_datanode(yc) && yc == y){
1180 *index += j;
1181 return 1;
1182 }
1183 }
1184 if (j>max)
1185 max = j;
1186 }
1187 else {
1188 shortcut = 1; /* Shortcut, no case */
1189 if (yang_datanode(ys) && ys == y)
1190 return 1;
1191 }
1192 }
1193 if (shortcut)
1194 (*index)++;
1195 else
1196 *index += max;
1197 return 0;
1198 }
1199
1200 /*! Find matching y in yp:s children, return "yang order" of y or -1 if not found
1201 * @param[in] yp Parent
1202 * @param[in] y Yang datanode to find
1203 * @param[out] index Index of y in yp:s list of children
1204 * @retval 0 not found (must be datanode)
1205 * @retval 1 found
1206 */
1207 static int
order1(yang_stmt * yp,yang_stmt * y,int * index)1208 order1(yang_stmt *yp,
1209 yang_stmt *y,
1210 int *index)
1211 {
1212 yang_stmt *ys;
1213 int i;
1214
1215 for (i=0; i<yp->ys_len; i++){
1216 ys = yp->ys_stmt[i];
1217 if (ys->ys_keyword == Y_CHOICE){
1218 if (order1_choice(ys, y, index) == 1) /* If one of the choices is "y" */
1219 return 1;
1220 }
1221 else {
1222 if (!yang_datanode(ys))
1223 continue;
1224 if (ys==y)
1225 return 1;
1226 (*index)++;
1227 }
1228 }
1229 return 0;
1230 }
1231
1232 /*! Return order of yang statement y in parents child vector
1233 * @param[in] y Find position of this data-node
1234 * @param[out] index Index of y in yp:s list of children
1235 * @retval >=0 Order of child with specified argument
1236 * @retval -1 Not found
1237 * @note special handling if y is child of (sub)module
1238 */
1239 int
yang_order(yang_stmt * y)1240 yang_order(yang_stmt *y)
1241 {
1242 yang_stmt *yp;
1243 yang_stmt *ypp;
1244 yang_stmt *ym;
1245 int i;
1246 int j=0;
1247 int tot = 0;
1248
1249 if (y == NULL)
1250 return -1;
1251 /* Some special handling if yp is choice (or case)
1252 * if so, the real parent (from an xml point of view) is the parents
1253 * parent.
1254 */
1255 yp = yang_parent_get(y);
1256 while (yang_keyword_get(yp) == Y_CASE || yang_keyword_get(yp) == Y_CHOICE)
1257 yp = yp->ys_parent;
1258
1259 /* XML nodes with yang specs that are children of modules are special -
1260 * In clixon, they are seen as an "implicit" container where the XML can come from different
1261 * modules. The order must therefore be global among yang top-symbols to be unique.
1262 * Example: <x xmlns="foo"/><y xmlns="bar"/>
1263 * The order of x and y cannot be compared within a single yang module since they belong to different
1264 */
1265 if (yang_keyword_get(yp) == Y_MODULE || yang_keyword_get(yp) == Y_SUBMODULE){
1266 ypp = yang_parent_get(yp); /* yang spec */
1267 for (i=0; i<ypp->ys_len; i++){ /* iterate through other modules */
1268 ym = ypp->ys_stmt[i];
1269 if (yp == ym)
1270 break;
1271 tot += ym->ys_len;
1272 }
1273 }
1274 if (order1(yp, y, &j) == 1)
1275 return tot + j;
1276 return -1;
1277 }
1278
1279 char *
yang_key2str(int keyword)1280 yang_key2str(int keyword)
1281 {
1282 return (char*)clicon_int2str(ykmap, keyword);
1283 }
1284
1285 /*! Find top data node among all modules by namespace in xml tree
1286 * @param[in] yspec Yang specification
1287 * @param[in] xt XML node
1288 * @param[out] ymod Yang module (NULL if not found)
1289 * @retval 0 OK
1290 * @retval -1 Error
1291 * @note works for xml namespaces (xmlns / xmlns:ns)
1292 * Note that xt xml symbol may belong to submodule of ymod
1293 */
1294 int
ys_module_by_xml(yang_stmt * yspec,cxobj * xt,yang_stmt ** ymodp)1295 ys_module_by_xml(yang_stmt *yspec,
1296 cxobj *xt,
1297 yang_stmt **ymodp)
1298 {
1299 int retval = -1;
1300 yang_stmt *ym = NULL; /* module */
1301 char *prefix = NULL;
1302 char *ns = NULL; /* namespace URI */
1303
1304 if (ymodp)
1305 *ymodp = NULL;
1306 prefix = xml_prefix(xt);
1307 if (xml2ns(xt, prefix, &ns) < 0) /* prefix may be NULL */
1308 goto done;
1309 /* No namespace found, give up */
1310 if (ns == NULL)
1311 goto ok;
1312 /* We got the namespace, now get the module */
1313 ym = yang_find_module_by_namespace(yspec, ns);
1314 /* Set result param */
1315 if (ymodp && ym)
1316 *ymodp = ym;
1317 ok:
1318 retval = 0;
1319 done:
1320 return retval;
1321 }
1322
1323 /*! Find the top module or sub-module given a statement from within a yang tree
1324 * Ultimate top is yang spec, dont return that
1325 * The routine recursively finds ancestors.
1326 * @param[in] ys Any yang statement in a yang tree
1327 * @retval ymod The top module or sub-module
1328 * @see ys_spec
1329 * @see ys_real_module find the submodule's belongs-to module
1330 * @note For an augmented node, the original module is returned
1331 */
1332 yang_stmt *
ys_module(yang_stmt * ys)1333 ys_module(yang_stmt *ys)
1334 {
1335 yang_stmt *yn;
1336
1337 if (ys==NULL || ys->ys_keyword==Y_SPEC)
1338 return NULL;
1339 if (ys->ys_keyword == Y_MODULE || ys->ys_keyword == Y_SUBMODULE)
1340 return ys;
1341 while (ys != NULL &&
1342 ys->ys_keyword != Y_MODULE &&
1343 ys->ys_keyword != Y_SUBMODULE){
1344 if (ys->ys_mymodule){ /* shortcut due to augment */
1345 ys = ys->ys_mymodule;
1346 break;
1347 }
1348 yn = ys->ys_parent;
1349 /* Some extra stuff to ensure ys is a stmt */
1350 if (yn && yn->ys_keyword == Y_SPEC)
1351 yn = NULL;
1352 ys = (yang_stmt*)yn;
1353 }
1354 /* Here it is either NULL or is a typedef-kind yang-stmt */
1355 return ys;
1356 }
1357
1358 /*! Find real top module given a statement in a yang tree
1359 * With "real" top module means that if sub-module is the top-node,
1360 * the module that the sub-module belongs-to is found recursively
1361 * @param[in] ys Any yang statement in a yang tree
1362 * @param[out] ymod The top module or sub-module
1363 * @retval 0 OK, returned module in ymod
1364 * @retval -1 YANG validation error: all yang statements should have a "real" module
1365 * @see ys_module
1366 * @note For an augmented node, the original module is returned
1367 */
1368 int
ys_real_module(yang_stmt * ys,yang_stmt ** ymod)1369 ys_real_module(yang_stmt *ys,
1370 yang_stmt **ymod)
1371 {
1372 int retval = -1;
1373 yang_stmt *ym = NULL;
1374 yang_stmt *yb;
1375 char *name;
1376 yang_stmt *yspec;
1377
1378 if (ymod == NULL){
1379 clicon_err(OE_YANG, EINVAL, "ymod is NULL");
1380 goto done;
1381 }
1382 if ((ym = ys_module(ys)) != NULL){
1383 yspec = ys_spec(ym);
1384 while (ym && yang_keyword_get(ym) == Y_SUBMODULE){
1385 if ((yb = yang_find(ym, Y_BELONGS_TO, NULL)) == NULL){
1386 clicon_err(OE_YANG, ENOENT, "No belongs-to statement of submodule %s", yang_argument_get(ym)); /* shouldnt happen */
1387 goto done;
1388 }
1389 if ((name = yang_argument_get(yb)) == NULL){
1390 clicon_err(OE_YANG, ENOENT, "Belongs-to statement of submodule %s has no argument", yang_argument_get(ym)); /* shouldnt happen */
1391 goto done;
1392 }
1393 if ((ym = yang_find_module_by_name(yspec, name)) == NULL)
1394 goto done;
1395 }
1396 }
1397 *ymod = ym;
1398 retval = 0;
1399 done:
1400 return retval;
1401 }
1402
1403 /*! Find top of tree, the yang specification from within the tree
1404 * @param[in] ys Any yang statement in a yang tree
1405 * @retval yspec The top yang specification
1406 * @see ys_module
1407 * @see yang_augment_node where shortcut is set
1408 */
1409 yang_stmt *
ys_spec(yang_stmt * ys)1410 ys_spec(yang_stmt *ys)
1411 {
1412 yang_stmt *yn;
1413
1414 while (ys != NULL && ys->ys_keyword != Y_SPEC){
1415 yn = ys->ys_parent;
1416 ys = (yang_stmt*)yn;
1417 }
1418 /* Here it is either NULL or is a typedef-kind yang-stmt */
1419 return (yang_stmt*)ys;
1420 }
1421
1422 /*! string is quoted if it contains space or tab, needs double '' */
1423 static int inline
quotedstring(char * s)1424 quotedstring(char *s)
1425 {
1426 int len = strlen(s);
1427 int i;
1428
1429 for (i=0; i<len; i++)
1430 if (isblank(s[i]))
1431 break;
1432 return i < len;
1433 }
1434
1435 /*! Print yang specification to file
1436 * @param[in] f File to print to.
1437 * @param[in] yn Yang node to print
1438 * @param[in] fn Callback to make print function
1439 * @see yang_print_cbuf
1440 */
1441 int
yang_print_cb(FILE * f,yang_stmt * yn,clicon_output_cb * fn)1442 yang_print_cb(FILE *f,
1443 yang_stmt *yn,
1444 clicon_output_cb *fn)
1445 {
1446 int retval = -1;
1447 cbuf *cb = NULL;
1448
1449 if ((cb = cbuf_new()) == NULL){
1450 clicon_err(OE_YANG, errno, "cbuf_new");
1451 goto done;
1452 }
1453 if (yang_print_cbuf(cb, yn, 0) < 0)
1454 goto done;
1455 (*fn)(f, "%s", cbuf_get(cb));
1456 if (cb)
1457 cbuf_free(cb);
1458 retval = 0;
1459 done:
1460 return retval;
1461 }
1462
1463 /*! Print yang specification to file
1464 * @param[in] f File to print to.
1465 * @param[in] yn Yang node to print
1466 * @see yang_print_cbuf
1467 */
1468 int
yang_print(FILE * f,yang_stmt * yn)1469 yang_print(FILE *f,
1470 yang_stmt *yn)
1471 {
1472 return yang_print_cb(f, yn, fprintf);
1473 }
1474
1475 /*! Print yang specification to cligen buf
1476 * @param[in] cb Cligen buffer. This is where the pretty print is.
1477 * @param[in] yn Yang node to print
1478 * @param[in] marginal Tab indentation, mainly for recursion.
1479 * @code
1480 * cbuf *cb = cbuf_new();
1481 * yang_print_cbuf(cb, yn, 0);
1482 * // output is in cbuf_buf(cb);
1483 * cbuf_free(cb);
1484 * @endcode
1485 */
1486 int
yang_print_cbuf(cbuf * cb,yang_stmt * yn,int marginal)1487 yang_print_cbuf(cbuf *cb,
1488 yang_stmt *yn,
1489 int marginal)
1490 {
1491 yang_stmt *ys = NULL;
1492
1493 while ((ys = yn_each(yn, ys)) != NULL) {
1494 if (ys->ys_keyword == Y_UNKNOWN){ /* dont print unknown - proxy for extension*/
1495 cprintf(cb, "%*s", marginal-1, "");
1496 }
1497 else
1498 cprintf(cb, "%*s%s", marginal, "", yang_key2str(ys->ys_keyword));
1499 if (ys->ys_argument){
1500 if (quotedstring(ys->ys_argument))
1501 cprintf(cb, " \"%s\"", ys->ys_argument);
1502 else
1503 cprintf(cb, " %s", ys->ys_argument);
1504 }
1505 if (ys->ys_len){
1506 cprintf(cb, " {\n");
1507 yang_print_cbuf(cb, ys, marginal+3);
1508 cprintf(cb, "%*s%s\n", marginal, "", "}");
1509 }
1510 else
1511 cprintf(cb, ";\n");
1512 }
1513 return 0;
1514 }
1515
1516 /*! Populate yang leafs after parsing. Create cv and fill it in.
1517 *
1518 * Populate leaf in 2nd round of yang parsing, now that context is complete:
1519 * 1. Find type specification and set cv type accordingly
1520 * 2. Create the CV using cvtype and name it
1521 * 3. Check if default value. Here we parse the string in the default-stmt and add it to leafs cv.
1522 * 4. Check if leaf is part of list, if key exists mark leaf as key/unique
1523 * XXX: extend type search
1524 *
1525 * @param[in] h Clicon handle
1526 * @param[in] ys The yang statement to populate.
1527 * @retval 0 OK
1528 * @retval -1 Error with clicon_err called
1529 */
1530 static int
ys_populate_leaf(clicon_handle h,yang_stmt * ys)1531 ys_populate_leaf(clicon_handle h,
1532 yang_stmt *ys)
1533 {
1534 int retval = -1;
1535 cg_var *cv = NULL;
1536 yang_stmt *yparent;
1537 yang_stmt *ydef;
1538 enum cv_type cvtype = CGV_ERR;
1539 int cvret;
1540 int ret;
1541 char *reason = NULL;
1542 yang_stmt *yrestype; /* resolved type */
1543 char *restype; /* resolved type */
1544 char *origtype=NULL; /* original type */
1545 uint8_t fraction_digits;
1546 int options = 0x0;
1547 yang_stmt *ytypedef; /* where type is define */
1548
1549 yparent = ys->ys_parent; /* Find parent: list/container */
1550 /* 1. Find type specification and set cv type accordingly */
1551 if (yang_type_get(ys, &origtype, &yrestype, &options, NULL, NULL, NULL, &fraction_digits)
1552 < 0)
1553 goto done;
1554 restype = yrestype?yrestype->ys_argument:NULL;
1555 if (clicon_type2cv(origtype, restype, ys, &cvtype) < 0) /* This handles non-resolved also */
1556 goto done;
1557 /* 2. Create the CV using cvtype and name it */
1558 if ((cv = cv_new(cvtype)) == NULL){
1559 clicon_err(OE_YANG, errno, "cv_new");
1560 goto done;
1561 }
1562 if (options & YANG_OPTIONS_FRACTION_DIGITS && cvtype == CGV_DEC64) /* XXX: Seems misplaced? / too specific */
1563 cv_dec64_n_set(cv, fraction_digits);
1564
1565 if (cv_name_set(cv, ys->ys_argument) == NULL){
1566 clicon_err(OE_YANG, errno, "cv_new_set");
1567 goto done;
1568 }
1569 /* get parent of where type is defined, can be original object */
1570 ytypedef = yrestype?yang_parent_get(yrestype):ys;
1571
1572 /* 3. Check if default value. Here we parse the string in the default-stmt
1573 * and add it to the leafs cv.
1574 * 3a) First check local default
1575 */
1576 if ((ydef = yang_find(ys, Y_DEFAULT, NULL)) != NULL){
1577 if ((cvret = cv_parse1(ydef->ys_argument, cv, &reason)) < 0){ /* error */
1578 clicon_err(OE_YANG, errno, "parsing cv");
1579 goto done;
1580 }
1581 if (cvret == 0){ /* parsing failed */
1582 clicon_err(OE_YANG, errno, "Parsing CV: %s", reason);
1583 free(reason);
1584 goto done;
1585 }
1586 }
1587 /* 2. then check typedef default */
1588 else if (ytypedef != ys &&
1589 (ydef = yang_find(ytypedef, Y_DEFAULT, NULL)) != NULL) {
1590 if ((cvret = cv_parse1(ydef->ys_argument, cv, &reason)) < 0){ /* error */
1591 clicon_err(OE_YANG, errno, "parsing cv");
1592 goto done;
1593 }
1594 if (cvret == 0){ /* parsing failed */
1595 clicon_err(OE_YANG, errno, "Parsing CV: %s", reason);
1596 free(reason);
1597 goto done;
1598 }
1599 }
1600 else{
1601 /* 3b. If not default value, indicate empty cv. */
1602 cv_flag_set(cv, V_UNSET); /* no value (no default) */
1603 }
1604 /* 4. Check if leaf is part of list, if key exists mark leaf as key/unique */
1605 if (yparent && yparent->ys_keyword == Y_LIST){
1606 if ((ret = yang_key_match(yparent, ys->ys_argument)) < 0)
1607 goto done;
1608 }
1609 ys->ys_cv = cv;
1610 retval = 0;
1611 done:
1612 if (origtype)
1613 free(origtype);
1614 if (cv && retval < 0)
1615 cv_free(cv);
1616 return retval;
1617 }
1618
1619 /*! Populate list yang statement
1620 * @param[in] h Clicon handle
1621 * @param[in] ys The yang statement (type) to populate.
1622 */
1623 static int
ys_populate_list(clicon_handle h,yang_stmt * ys)1624 ys_populate_list(clicon_handle h,
1625 yang_stmt *ys)
1626 {
1627 yang_stmt *ykey;
1628
1629 if ((ykey = yang_find(ys, Y_KEY, NULL)) == NULL)
1630 return 0;
1631 if (ys->ys_cvec)
1632 cvec_free(ys->ys_cvec);
1633 if ((ys->ys_cvec = yang_arg2cvec(ykey, " ")) == NULL)
1634 return -1;
1635 return 0;
1636 }
1637
1638 /*! Set range or length boundary for built-in yang types
1639 * Help functions to range and length statements
1640 */
1641 static int
bound_add(yang_stmt * ys,enum cv_type cvtype,char * name,char * val,uint8_t fraction_digits)1642 bound_add(yang_stmt *ys,
1643 enum cv_type cvtype,
1644 char *name,
1645 char *val,
1646 uint8_t fraction_digits)
1647 {
1648 int retval = -1;
1649 cg_var *cv;
1650 char *reason = NULL;
1651 int ret = 1;
1652
1653 if ((cv = cvec_add(ys->ys_cvec, cvtype)) == NULL){
1654 clicon_err(OE_YANG, errno, "cvec_add");
1655 goto done;
1656 }
1657 if (cv_name_set(cv, name) == NULL){
1658 clicon_err(OE_YANG, errno, "cv_name_set(%s)", name);
1659 goto done;
1660 }
1661 if (cvtype == CGV_DEC64)
1662 cv_dec64_n_set(cv, fraction_digits);
1663 if (strcmp(val, "min") == 0)
1664 cv_min_set(cv);
1665 else if (strcmp(val, "max") == 0)
1666 cv_max_set(cv);
1667 else if ((ret = cv_parse1(val, cv, &reason)) < 0){
1668 clicon_err(OE_YANG, errno, "cv_parse1");
1669 goto done;
1670 }
1671 if (ret == 0){ /* parsing failed */
1672 clicon_err(OE_YANG, errno, "range statement %s: %s", val, reason);
1673 free(reason);
1674 goto done;
1675 }
1676 retval = 0;
1677 done:
1678 return retval;
1679 }
1680
1681 /*! Common range length parsing of "x .. y | z..w " statements
1682 */
1683 static int
range_parse(yang_stmt * ys,enum cv_type cvtype,uint8_t fraction_digits)1684 range_parse(yang_stmt *ys,
1685 enum cv_type cvtype,
1686 uint8_t fraction_digits)
1687 {
1688 int retval = -1;
1689 char **vec = NULL;
1690 int nvec;
1691 int i;
1692 char *v;
1693 char *v2;
1694
1695 if ((vec = clicon_strsep(ys->ys_argument, "|", &nvec)) == NULL)
1696 goto done;
1697 for (i=0; i<nvec; i++){
1698 v = vec[i];
1699 if ((v2 = strstr(v, "..")) != NULL){
1700 *v2 = '\0';
1701 v2 += 2;
1702 v2 = clixon_trim(v2); /* trim blanks */
1703 }
1704 v = clixon_trim(v); /* trim blanks */
1705 if (bound_add(ys, cvtype, "range_min", v, fraction_digits) < 0)
1706 goto done;
1707 if (v2)
1708 if (bound_add(ys, cvtype, "range_max", v2, fraction_digits) < 0)
1709 goto done;
1710 }
1711 retval = 0;
1712 done:
1713 if (vec)
1714 free(vec);
1715 return retval;
1716 }
1717
1718 /*! Populate string built-in range statement
1719 *
1720 * Create cvec variables "range_min" and "range_max". Assume parent is type.
1721 * @param[in] h Clicon handle
1722 * @param[in] ys The yang statement (range) to populate.
1723 * Actually: bound[..bound] (| bound[..bound])*
1724 * where bound is integer, decimal or keywords 'min' or 'max.
1725 * RFC 7950 9.2.4:
1726 * A range consists of an explicit value, or a lower-inclusive bound,
1727 * two consecutive dots "..", and an upper-inclusive bound. Multiple
1728 * values or ranges can be given, separated by "|". If multiple values
1729 * or ranges are given, they all MUST be disjoint and MUST be in
1730 * ascending order
1731 */
1732 static int
ys_populate_range(clicon_handle h,yang_stmt * ys)1733 ys_populate_range(clicon_handle h,
1734 yang_stmt *ys)
1735 {
1736 int retval = -1;
1737 yang_stmt *yparent; /* type */
1738 char *origtype = NULL; /* orig type */
1739 yang_stmt *yrestype; /* resolved type */
1740 char *restype; /* resolved type */
1741 int options = 0x0;
1742 uint8_t fraction_digits;
1743 enum cv_type cvtype = CGV_ERR;
1744
1745 yparent = ys->ys_parent; /* Find parent: type */
1746 if (yparent->ys_keyword != Y_TYPE){
1747 clicon_err(OE_YANG, 0, "parent should be type");
1748 goto done;
1749 }
1750 if (yang_type_resolve(ys, ys, (yang_stmt*)yparent, &yrestype,
1751 &options, NULL, NULL, NULL, &fraction_digits) < 0)
1752 goto done;
1753 restype = yrestype?yrestype->ys_argument:NULL;
1754 if (nodeid_split(yang_argument_get(yparent), NULL, &origtype) < 0)
1755 goto done;
1756 /* This handles non-resolved also */
1757 if (clicon_type2cv(origtype, restype, ys, &cvtype) < 0)
1758 goto done;
1759 if (!cv_isint(cvtype) && cvtype != CGV_DEC64){
1760 clicon_err(OE_YANG, 0, "The range substatement only applies to int types, not to type: %s", origtype);
1761 goto done;
1762 }
1763 if (range_parse(ys, cvtype, fraction_digits) < 0)
1764 goto done;
1765 retval = 0;
1766 done:
1767 if (origtype)
1768 free(origtype);
1769 return retval;
1770 }
1771
1772 /*! Populate integer built-in length statement
1773 *
1774 * Create cvec variables "range_min" and "range_max". Assume parent is type.
1775 * @param[in] h Clicon handle
1776 * @param[in] ys The yang statement (length) to populate.
1777 *
1778 * Actually: len[..len] (| len[..len])*
1779 * len is unsigned integer or keywords 'min' or 'max'
1780 *
1781 * From RFC 7950 Sec 9.4.4:
1782 * A length range consists of an explicit value, or a lower bound, two
1783 * consecutive dots "..", and an upper bound. Multiple values or ranges
1784 * can be given, separated by "|". Length-restricting values MUST NOT
1785 * be negative. If multiple values or ranges are given, they all MUST
1786 * be disjoint and MUST be in ascending order.
1787 */
1788 static int
ys_populate_length(clicon_handle h,yang_stmt * ys)1789 ys_populate_length(clicon_handle h,
1790 yang_stmt *ys)
1791 {
1792 int retval = -1;
1793 yang_stmt *yparent; /* type */
1794 enum cv_type cvtype = CGV_ERR;
1795
1796 yparent = ys->ys_parent; /* Find parent: type */
1797 if (yparent->ys_keyword != Y_TYPE){
1798 clicon_err(OE_YANG, 0, "parent should be type");
1799 goto done;
1800 }
1801 cvtype = CGV_UINT64;
1802 if (range_parse(ys, cvtype, 0) < 0)
1803 goto done;
1804 retval = 0;
1805 done:
1806 return retval;
1807 }
1808
1809 /*! Sanity check yang type statement
1810 * XXX: Replace with generic parent/child type-check
1811 * @param[in] h Clicon handle
1812 * @param[in] ys The yang statement (type) to populate.
1813 */
1814 static int
ys_populate_type(clicon_handle h,yang_stmt * ys)1815 ys_populate_type(clicon_handle h,
1816 yang_stmt *ys)
1817
1818 {
1819 int retval = -1;
1820 yang_stmt *ybase;
1821
1822 if (strcmp(ys->ys_argument, "decimal64") == 0){
1823 if (yang_find(ys, Y_FRACTION_DIGITS, NULL) == NULL){
1824 clicon_err(OE_YANG, 0, "decimal64 type requires fraction-digits sub-statement");
1825 goto done;
1826 }
1827 }
1828 else
1829 if (strcmp(ys->ys_argument, "identityref") == 0){
1830 if ((ybase = yang_find(ys, Y_BASE, NULL)) == NULL){
1831 clicon_err(OE_YANG, 0, "identityref type requires base sub-statement");
1832 goto done;
1833 }
1834 if ((yang_find_identity(ys, ybase->ys_argument)) == NULL){
1835 clicon_err(OE_YANG, 0, "Identity %s not found (base type of %s)",
1836 ybase->ys_argument, ys->ys_argument);
1837 goto done;
1838 }
1839 }
1840 retval = 0;
1841 done:
1842 return retval;
1843 }
1844
1845 /*! Sanity check yang identity statement recursively and create derived id list
1846 *
1847 * Find base identities if any and add this identity to derived identity list.
1848 * Do this recursively
1849 * The derived identity list is a list of <module>:<id> pairs. Prefixes cannot
1850 * be used since they are local in scope.
1851 * @param[in] h Clicon handle
1852 * @param[in] ys The yang identity to populate.
1853 * @param[in] idref If set contains the derived identifier(NULL on top call)
1854 * @see validate_identityref which in runtime validates actual values
1855 */
1856 static int
ys_populate_identity(clicon_handle h,yang_stmt * ys,char * idref)1857 ys_populate_identity(clicon_handle h,
1858 yang_stmt *ys,
1859 char *idref)
1860 {
1861 int retval = -1;
1862 yang_stmt *yc = NULL;
1863 yang_stmt *ybaseid;
1864 cg_var *cv;
1865 char *baseid;
1866 char *prefix = NULL;
1867 char *id = NULL;
1868 cbuf *cb = NULL;
1869 yang_stmt *ymod;
1870 cvec *idrefvec; /* Derived identityref list: (module:id)**/
1871
1872 /* Top-call (no recursion) create idref
1873 * The idref is (here) in "canonical form": <module>:<id>
1874 */
1875 if (idref == NULL){
1876 /* Create derived identity through prefix:id if not recursively called*/
1877 if ((cb = cbuf_new()) == NULL){
1878 clicon_err(OE_UNIX, errno, "cbuf_new");
1879 goto done;
1880 }
1881 if (nodeid_split(yang_argument_get(ys), &prefix, &id) < 0)
1882 goto done;
1883 if ((ymod = ys_module(ys)) == NULL){
1884 clicon_err(OE_YANG, ENOENT, "No module found");
1885 goto done;
1886 }
1887 cprintf(cb, "%s:%s", yang_argument_get(ymod), id);
1888 idref = cbuf_get(cb);
1889 }
1890 /* Iterate through all base statements and check the base identity exists
1891 * AND populate the base identity recursively
1892 */
1893 yc = NULL;
1894 while ((yc = yn_each(ys, yc)) != NULL) {
1895 if (yc->ys_keyword != Y_BASE)
1896 continue;
1897 baseid = yang_argument_get(yc); /* on the form: prefix:id */
1898 if (((ybaseid = yang_find_identity(ys, baseid))) == NULL){
1899 clicon_err(OE_YANG, ENOENT, "No such identity: %s", baseid);
1900 goto done;
1901 }
1902 // continue; /* root identity */
1903 /* Check if derived id is already in base identifier
1904 * note that cvec is always created in ys_new()
1905 */
1906 idrefvec = yang_cvec_get(ybaseid);
1907 if (cvec_find(idrefvec, idref) != NULL)
1908 continue;
1909 /* Add derived id to ybaseid */
1910 if ((cv = cv_new(CGV_STRING)) == NULL){
1911 clicon_err(OE_UNIX, errno, "cv_new");
1912 goto done;
1913 }
1914 /* add prefix */
1915 cv_name_set(cv, idref);
1916 cvec_append_var(idrefvec, cv); /* cv copied */
1917 if (cv){
1918 cv_free(cv);
1919 cv = NULL;
1920 }
1921 /* Transitive to the root */
1922 if (ys_populate_identity(h, ybaseid, idref) < 0)
1923 goto done;
1924 }
1925 retval = 0;
1926 done:
1927 if (prefix)
1928 free(prefix);
1929 if (id)
1930 free(id);
1931 if (cb)
1932 cbuf_free(cb);
1933 return retval;
1934 }
1935
1936 /*! Return 1 if feature is enabled, 0 if not using the populated yang tree
1937 *
1938 * @param[in] yspec yang specification
1939 * @param[in] module Name of module
1940 * @param[in] feature Name of feature
1941 * @retval 0 Not found or not set
1942 * @retval 1 Found and set
1943 * XXX: should the in-param be h, ymod, or yspec?
1944 */
1945 int
if_feature(yang_stmt * yspec,char * module,char * feature)1946 if_feature(yang_stmt *yspec,
1947 char *module,
1948 char *feature)
1949 {
1950 yang_stmt *ym; /* module */
1951 yang_stmt *yf; /* feature */
1952 cg_var *cv;
1953
1954 if ((ym = yang_find_module_by_name(yspec, module)) == NULL)
1955 return 0;
1956 if ((yf = yang_find(ym, Y_FEATURE, feature)) == NULL)
1957 return 0;
1958 if ((cv = yang_cv_get(yf)) == NULL)
1959 return 0;
1960 return cv_bool_get(cv);
1961 }
1962
1963 /*! Populate yang feature statement - set cv to 1 if enabled
1964 *
1965 * @param[in] h Clicon handle
1966 * @param[in] ys Feature yang statement to populate.
1967 */
1968 static int
ys_populate_feature(clicon_handle h,yang_stmt * ys)1969 ys_populate_feature(clicon_handle h,
1970 yang_stmt *ys)
1971 {
1972 int retval = -1;
1973 cxobj *x;
1974 yang_stmt *ymod;
1975 int found = 0;
1976 cg_var *cv;
1977 char *module;
1978 char *feature;
1979 cxobj *xc;
1980 char *m;
1981 char *f;
1982
1983 /* get clicon config file in xml form */
1984 if ((x = clicon_conf_xml(h)) == NULL)
1985 goto ok;
1986 if ((ymod = ys_module(ys)) == NULL){
1987 clicon_err(OE_YANG, 0, "module not found");
1988 goto done;
1989 }
1990 module = ymod->ys_argument;
1991 feature = ys->ys_argument;
1992 xc = NULL;
1993 while ((xc = xml_child_each(x, xc, CX_ELMNT)) != NULL && found == 0) {
1994 m = NULL;
1995 f = NULL;
1996 if (strcmp(xml_name(xc), "CLICON_FEATURE") != 0)
1997 continue;
1998 /* CLICON_FEATURE is on the form <module>:<feature>.
1999 * Split on colon to get module(m) and feature(f) respectively */
2000 if (nodeid_split(xml_body(xc), &m, &f) < 0)
2001 goto done;
2002 if (m && f &&
2003 (strcmp(m,"*")==0 ||
2004 strcmp(m, module)==0) &&
2005 (strcmp(f,"*")==0 ||
2006 strcmp(f, feature)==0))
2007 found = 1;
2008 if (m) free(m);
2009 if (f) free(f);
2010 }
2011 if ((cv = cv_new(CGV_BOOL)) == NULL){
2012 clicon_err(OE_YANG, errno, "cv_new");
2013 goto done;
2014 }
2015 cv_name_set(cv, feature);
2016 cv_bool_set(cv, found);
2017 if (found)
2018 clicon_debug(2, "%s %s:%s", __FUNCTION__, module, feature);
2019 ys->ys_cv = cv;
2020 ok:
2021 retval = 0;
2022 done:
2023 return retval;
2024 }
2025
2026 /*! Populate the unique statement with a cvec
2027 * @param[in] h Clicon handle
2028 * @param[in] ys The yang statement (unique) to populate.
2029 */
2030 static int
ys_populate_unique(clicon_handle h,yang_stmt * ys)2031 ys_populate_unique(clicon_handle h,
2032 yang_stmt *ys)
2033 {
2034 if (ys->ys_cvec)
2035 cvec_free(ys->ys_cvec);
2036 if ((ys->ys_cvec = yang_arg2cvec(ys, " ")) == NULL)
2037 return -1;
2038 return 0;
2039 }
2040
2041 /*! Populate unknown node with extension
2042 * @param[in] h Clicon handle
2043 * @param[in] ys The yang statement (unknown) to populate.
2044 * RFC 7950 Sec 7.19:
2045 * If no "argument" statement is present, the keyword expects no argument when
2046 * it is used.
2047 */
2048 static int
ys_populate_unknown(clicon_handle h,yang_stmt * ys)2049 ys_populate_unknown(clicon_handle h,
2050 yang_stmt *ys)
2051 {
2052 int retval = -1;
2053 yang_stmt *ymod;
2054 yang_stmt *yext; /* extension */
2055 char *prefix = NULL;
2056 char *id = NULL;
2057 char *argument; /* This is the unknown optional argument */
2058 cg_var *cv;
2059
2060 /* Find extension, if found, store it as unknown, if not,
2061 break for error */
2062 if (nodeid_split(yang_argument_get(ys), &prefix, &id) < 0)
2063 goto done;
2064 if ((ymod = yang_find_module_by_prefix(ys, prefix)) == NULL){
2065 clicon_err(OE_YANG, ENOENT, "Extension %s:%s, module not found", prefix, id);
2066 goto done;
2067 }
2068 if ((yext = yang_find(ymod, Y_EXTENSION, id)) == NULL){
2069 clicon_err(OE_YANG, ENOENT, "Extension %s:%s not found", prefix, id);
2070 goto done;
2071 }
2072 /* Optional argument (only if "argument") - save it in ys_cv */
2073 if ((cv = yang_cv_get(ys)) != NULL &&
2074 (argument = cv_string_get(cv)) != NULL){
2075 if (yang_find(yext, Y_ARGUMENT, NULL) == NULL &&
2076 argument != NULL){
2077 clicon_err(OE_YANG, 0, "No argument specified in extension %s, but argument %s present when used", yang_argument_get(ys), argument);
2078 goto done;
2079 }
2080 }
2081 #ifdef XML_EXPLICIT_INDEX
2082 /* Add explicit index extension */
2083 if ((retval = yang_search_index_extension(h, yext, ys)) < 0) {
2084 clicon_debug(1, "plugin_extension() failed");
2085 return -1;
2086 }
2087 #endif
2088 /* Make extension callbacks that may alter yang structure */
2089 if (clixon_plugin_extension_all(h, yext, ys) < 0)
2090 goto done;
2091
2092 retval = 0;
2093 done:
2094 if (prefix)
2095 free(prefix);
2096 if (id)
2097 free(id);
2098 return retval;
2099 }
2100
2101 /*! Populate modules / submodules
2102 * @param[in] h Clicon handle
2103 * @param[in] ys The yang statement (module/submodule) to populate.
2104 *
2105 * Check RFC 7950: 7.1.4: All prefixes, including the prefix for the module itself,
2106 * MUST be unique within the module or submodule.
2107 */
2108 static int
ys_populate_module_submodule(clicon_handle h,yang_stmt * ym)2109 ys_populate_module_submodule(clicon_handle h,
2110 yang_stmt *ym)
2111 {
2112 int retval = -1;
2113 yang_stmt *yp;
2114 yang_stmt *yi;
2115 yang_stmt *yi2; /* remaining */
2116 char *p0 = NULL;
2117 char *pi;
2118 char *pi2; /* remaining */
2119
2120 /* Modules but not submodules have prefixes */
2121 if ((yp = yang_find(ym, Y_PREFIX, NULL)) != NULL)
2122 p0 = yang_argument_get(yp);
2123 yi = NULL;
2124 while ((yi = yn_each(ym, yi)) != NULL) {
2125 if (yang_keyword_get(yi) != Y_IMPORT)
2126 continue;
2127 yp = yang_find(yi, Y_PREFIX, NULL);
2128 pi = yang_argument_get(yp);
2129 if (p0 && strcmp(p0, pi) == 0){ /* Check top-level */
2130 clicon_err(OE_YANG, EFAULT, "Prefix %s in module %s is not unique but should be (see RFC 7950 7.1.4)",
2131 pi, yang_argument_get(ym));
2132 goto done;
2133 }
2134 /* Check rest of imports */
2135 yi2 = yi;
2136 while ((yi2 = yn_each(ym, yi2)) != NULL) {
2137 if (yang_keyword_get(yi2) != Y_IMPORT)
2138 continue;
2139 yp = yang_find(yi2, Y_PREFIX, NULL);
2140 pi2 = yang_argument_get(yp);
2141 if (strcmp(pi2, pi) == 0){
2142 clicon_err(OE_YANG, EFAULT, "Prefix %s in module %s is not unique but should be (see RFC 7950 7.1.4)",
2143 pi, yang_argument_get(ym));
2144 goto done;
2145 }
2146 }
2147 }
2148 retval = 0;
2149 done:
2150 return retval;
2151 }
2152
2153 /*! Populate with cligen-variables, default values, etc. Sanity checks on complete tree.
2154 *
2155 * @param[in] ys Yang statement
2156 * @param[in] arg Argument - in effect Clicon handle
2157 * Preferably run this command using yang_apply
2158 * Done in 2nd pass after complete parsing to be sure to have a complete
2159 * parse-tree
2160
2161 * After this pass, cv:s are set for LEAFs and LEAF-LISTs
2162 * @see ys_parse_sub for first pass and what can be assumed
2163 * @see ys_populate2 for after grouping expand and augment
2164 * (there may be more functions (all?) that may be moved to ys_populate2)
2165 */
2166 int
ys_populate(yang_stmt * ys,void * arg)2167 ys_populate(yang_stmt *ys,
2168 void *arg)
2169 {
2170 int retval = -1;
2171 clicon_handle h = (clicon_handle)arg;
2172
2173 switch(ys->ys_keyword){
2174 case Y_IDENTITY:
2175 if (ys_populate_identity(h, ys, NULL) < 0)
2176 goto done;
2177 break;
2178 case Y_LENGTH:
2179 if (ys_populate_length(h, ys) < 0)
2180 goto done;
2181 break;
2182 case Y_LIST:
2183 if (ys_populate_list(h, ys) < 0)
2184 goto done;
2185 break;
2186 case Y_MODULE:
2187 case Y_SUBMODULE:
2188 if (ys_populate_module_submodule(h, ys) < 0)
2189 goto done;
2190 break;
2191 case Y_RANGE:
2192 if (ys_populate_range(h, ys) < 0)
2193 goto done;
2194 break;
2195 case Y_TYPE:
2196 if (ys_populate_type(h, ys) < 0)
2197 goto done;
2198 break;
2199 case Y_UNIQUE:
2200 if (ys_populate_unique(h, ys) < 0)
2201 goto done;
2202 break;
2203 case Y_UNKNOWN:
2204 if (ys_populate_unknown(h, ys) < 0)
2205 goto done;
2206 break;
2207 default:
2208 break;
2209 }
2210 retval = 0;
2211 done:
2212 return retval;
2213 }
2214
2215 /*! Run after grouping expand and augment
2216 * @see ys_populate run before grouping expand and augment
2217 */
2218 int
ys_populate2(yang_stmt * ys,void * arg)2219 ys_populate2(yang_stmt *ys,
2220 void *arg)
2221 {
2222 int retval = -1;
2223 clicon_handle h = (clicon_handle)arg;
2224
2225 switch(ys->ys_keyword){
2226 case Y_LEAF:
2227 case Y_LEAF_LIST:
2228 if (ys_populate_leaf(h, ys) < 0)
2229 goto done;
2230 break;
2231 case Y_MANDATORY: /* call yang_mandatory() to check if set */
2232 case Y_CONFIG:
2233 if (ys_parse(ys, CGV_BOOL) == NULL)
2234 goto done;
2235 break;
2236 default:
2237 break;
2238 }
2239 retval = 0;
2240 done:
2241 return retval;
2242 }
2243
2244 /*! Handle complexity of if-feature node
2245 * @param[in] h Clixon handle
2246 * @param[in] ys Yang if-feature statement
2247 * @retval -1 Error
2248 * @retval 0 Feature not enabled: remove yt
2249 * @retval 1 OK
2250 * @note if-feature syntax is restricted to single, and, or, syntax, such as "a or b"
2251 * but RFC7950 allows for nested expr/term/factor syntax.
2252 * XXX This should really be parsed in yang/lex.
2253 */
2254 static int
yang_if_feature(clicon_handle h,yang_stmt * ys)2255 yang_if_feature(clicon_handle h,
2256 yang_stmt *ys)
2257 {
2258 int retval = -1;
2259 char **vec = NULL;
2260 int nvec;
2261 char *f;
2262 int i;
2263 int j;
2264 char *prefix = NULL;
2265 char *feature = NULL;
2266 yang_stmt *ymod; /* module yang node */
2267 yang_stmt *yfeat; /* feature yang node */
2268 int opand = -1; /* -1:not set, 0:or, 1:and */
2269 int enabled = 0;
2270
2271 if ((vec = clicon_strsep(ys->ys_argument, " \t\r\n", &nvec)) == NULL)
2272 goto done;
2273 /* Two steps: first detect operators
2274 * Step 1: support "a" or "a or b or c" or "a and b and c "
2275 */
2276 j = 0;
2277 for (i=0; i<nvec; i++){
2278 f = vec[i];
2279 if (strcmp(f, "") == 0) /* skip empty */
2280 continue;
2281 if ((j++)%2==0) /* only keep odd i:s 1,3,... */
2282 continue;
2283 /* odd i: operator "and" or "or" */
2284 if (strcmp(f, "or") == 0){
2285 switch (opand){
2286 case -1:
2287 if (i != 1){
2288 clicon_err(OE_YANG, EINVAL, "Syntax error IF_FEATURE \"%s\" (only single if-feature-expr and/or lists allowed)", ys->ys_argument);
2289 goto done;
2290 }
2291 opand = 0;
2292 break;
2293 case 0:
2294 break;
2295 case 1:
2296 clicon_err(OE_YANG, EINVAL, "Syntax error IF_FEATURE \"%s\" (only single if-feature-expr and/or lists allowed)", ys->ys_argument);
2297 goto done;
2298 break;
2299 }
2300 }
2301 else if (strcmp(f, "and") == 0){
2302 switch (opand){
2303 case -1:
2304 if (i != 1){
2305 clicon_err(OE_YANG, EINVAL, "Syntax error IF_FEATURE \"%s\" (only single if-feature-expr and/or lists allowed)", ys->ys_argument);
2306 goto done;
2307 }
2308 opand = 1;
2309 break;
2310 case 0:
2311 clicon_err(OE_YANG, EINVAL, "Syntax error IF_FEATURE \"%s\" (only single if-feature-expr and/or lists allowed)", ys->ys_argument);
2312 goto done;
2313 break;
2314 case 1:
2315 break;
2316 }
2317 }
2318 else{
2319 clicon_err(OE_YANG, EINVAL, "Syntax error IF_FEATURE \"%s\" (only single if-feature-expr and/or lists allowed)", ys->ys_argument);
2320 goto done;
2321 }
2322 } /* for step 1 */
2323 if (j%2 == 0){ /* Must be odd: eg a / "a or b" etc */
2324 clicon_err(OE_YANG, EINVAL, "Syntax error IF_FEATURE \"%s\" (only single if-feature-expr and/or lists allowed)", ys->ys_argument);
2325 goto done;
2326 }
2327
2328 if (opand == -1) /* Uninitialized means single operand */
2329 opand = 1;
2330 if (opand) /* if AND, start as enabled, if OR start as disabled */
2331 enabled = 1;
2332 else
2333 enabled = 0;
2334 /* Step 2: Boolean operations on operands */
2335 j = 0;
2336 for (i=0; i<nvec; i++){
2337 f = vec[i];
2338 if (strcmp(f, "") == 0) /* skip empty */
2339 continue;
2340 if ((j++)%2==1) /* only keep even i:s 0,2,... */
2341 continue;
2342 if (nodeid_split(f, &prefix, &feature) < 0)
2343 goto done;
2344 /* Specifically need to handle? strcmp(prefix, myprefix)) */
2345 if (prefix == NULL)
2346 ymod = ys_module(ys);
2347 else
2348 ymod = yang_find_module_by_prefix(ys, prefix);
2349 /* Check if feature exists, and is set, otherwise remove */
2350 if ((yfeat = yang_find(ymod, Y_FEATURE, feature)) == NULL){
2351 clicon_err(OE_YANG, EINVAL, "Yang module %s has IF_FEATURE %s, but no such FEATURE statement exists",
2352 ymod?yang_argument_get(ymod):"none",
2353 feature);
2354 goto done;
2355 }
2356 /* Check if this feature is enabled or not
2357 * Continue loop to catch unbound features and make verdict at end
2358 */
2359 if (yfeat->ys_cv == NULL || !cv_bool_get(yfeat->ys_cv)){ /* disabled */
2360 /* if AND then this is permanently disabled */
2361 if (opand && enabled)
2362 enabled = 0;
2363 }
2364 else{ /* enabled */
2365 /* if OR then this is permanently enabled */
2366 if (!opand && !enabled)
2367 enabled = 1;
2368 }
2369 if (prefix){
2370 free(prefix);
2371 prefix = NULL;
2372 }
2373 if (feature){
2374 free(feature);
2375 feature = NULL;
2376 }
2377 }
2378 if (!enabled)
2379 goto disabled;
2380 retval = 1;
2381 done:
2382 if (vec)
2383 free(vec);
2384 if (prefix)
2385 free(prefix);
2386 if (feature)
2387 free(feature);
2388 return retval;
2389 disabled:
2390 retval = 0; /* feature not enabled */
2391 goto done;
2392 }
2393
2394 /*! Find feature and if-feature nodes, check features and remove disabled nodes
2395 * @param[in] h Clixon handle
2396 * @param[in] yt Yang statement
2397 * @retval -1 Error
2398 * @retval 0 Feature not enabled: remove yt
2399 * @retval 1 OK
2400 * @note On return 1 the over-lying function need to remove yt from its parent
2401 * @note cannot use yang_apply here since child-list is modified (destructive)
2402 * @note if-feature syntax is restricted to single, and, or, syntax, such as "a or b"
2403 */
2404 int
yang_features(clicon_handle h,yang_stmt * yt)2405 yang_features(clicon_handle h,
2406 yang_stmt *yt)
2407 {
2408 int retval = -1;
2409 int i;
2410 int j;
2411 yang_stmt *ys = NULL;
2412 int ret;
2413
2414 i = 0;
2415 while (i<yt->ys_len){ /* Note, children may be removed */
2416 ys = yt->ys_stmt[i];
2417 if (ys->ys_keyword == Y_IF_FEATURE){
2418 if ((ret = yang_if_feature(h, ys)) < 0)
2419 goto done;
2420 if (ret == 0)
2421 goto disabled;
2422 }
2423 else
2424 if (ys->ys_keyword == Y_FEATURE){
2425 if (ys_populate_feature(h, ys) < 0)
2426 goto done;
2427 } else switch (yang_features(h, ys)){
2428 case -1: /* error */
2429 goto done;
2430 break;
2431 case 0: /* disabled: remove ys */
2432 for (j=i+1; j<yt->ys_len; j++)
2433 yt->ys_stmt[j-1] = yt->ys_stmt[j];
2434 yt->ys_len--;
2435 yt->ys_stmt[yt->ys_len] = NULL;
2436 ys_free(ys);
2437 continue; /* Don't increment i */
2438 break;
2439 default: /* ok */
2440 break;
2441 }
2442 i++;
2443 }
2444 retval = 1;
2445 done:
2446 return retval;
2447 disabled:
2448 retval = 0; /* feature not enabled */
2449 goto done;
2450 }
2451
2452 /*! Apply a function call recursively on all yang-stmt s recursively
2453 *
2454 * Recursively traverse all yang-nodes in a parse-tree and apply fn(arg) for
2455 * each object found. The function is called with the yang-stmt and an
2456 * argument as args.
2457 * The tree is traversed depth-first, which at least guarantees that a parent is
2458 * traversed before a child.
2459 * @param[in] yn yang node
2460 * @param[in] key yang keyword to use as filer or -1 for all
2461 * @param[in] fn Callback
2462 * @param[in] arg Argument
2463 * @retval -1 Error, aborted at first error encounter
2464 * @retval 0 OK, all nodes traversed
2465 * @retval n OK, aborted at first encounter of first match
2466 * @code
2467 * int ys_fn(yang_stmt *ys, void *arg)
2468 * {
2469 * return 0;
2470 * }
2471 * yang_apply(ys, Y_TYPE, ys_fn, NULL);
2472 * @endcode
2473 * @note do not delete or move around any children during this function
2474 */
2475 int
yang_apply(yang_stmt * yn,enum rfc_6020 keyword,yang_applyfn_t fn,void * arg)2476 yang_apply(yang_stmt *yn,
2477 enum rfc_6020 keyword,
2478 yang_applyfn_t fn,
2479 void *arg)
2480 {
2481 int retval = -1;
2482 yang_stmt *ys = NULL;
2483 int i;
2484 int ret;
2485
2486 for (i=0; i<yn->ys_len; i++){
2487 ys = yn->ys_stmt[i];
2488 if ((int)keyword == -1 || keyword == ys->ys_keyword){
2489 if ((ret = fn(ys, arg)) < 0)
2490 goto done;
2491 if (ret > 0){
2492 retval = ret;
2493 goto done;
2494 }
2495 }
2496 if ((ret = yang_apply(ys, keyword, fn, arg)) < 0)
2497 goto done;
2498 if (ret > 0){
2499 retval = ret;
2500 goto done;
2501 }
2502 }
2503 retval = 0;
2504 done:
2505 return retval;
2506 }
2507
2508 /*! Check if a node is a yang "data node"
2509 * @param[in] ys Yang statement node
2510 * @retval 0 Yang node is NOT a data node
2511 * @retval !=0 Yang node IS a data noed
2512 * @see RFC7950 Sec 3:
2513 * o data node: A node in the schema tree that can be instantiated in a
2514 * data tree. One of container, leaf, leaf-list, list, anydata, and
2515 * anyxml.
2516 */
2517 int
yang_datanode(yang_stmt * ys)2518 yang_datanode(yang_stmt *ys)
2519 {
2520 enum rfc_6020 keyw;
2521
2522 keyw = yang_keyword_get(ys);
2523 return (keyw == Y_CONTAINER ||
2524 keyw == Y_LEAF ||
2525 keyw == Y_LIST ||
2526 keyw == Y_LEAF_LIST ||
2527 keyw == Y_ANYXML ||
2528 keyw == Y_ANYDATA);
2529 }
2530
2531 /*! All the work for schema_nodeid functions both absolute and descendant
2532 *
2533 * @param[in] yn Yang node. For absolute schemanodeids this should be a module, otherwise any yang
2534 * @param[in] cvv Schema-node path encoded as a name/value pair list.
2535 * @param[in] nsc Namespace context from yang for the prefixes (names) of cvv
2536 * @param[out] yres Result yang statement node, or NULL if not found
2537 * @retval -1 Error, with clicon_err called
2538 * @retval 0 OK
2539 * A schema node identifier consists of a path of identifiers, separated by slashes ("/").
2540 * References to identifiers defined in external modules MUST be
2541 * qualified with appropriate prefixes, and references to identifiers
2542 * defined in the current module and its submodules MAY use a prefix.
2543 * prefixes are implemented by cvv names, and id:s by cvv strings.
2544 * A namespace context of the original module is nsc as prefix context.
2545 *
2546 * @see RFC7950 Sec 6.5
2547 */
2548 static int
schema_nodeid_iterate(yang_stmt * yn,cvec * nodeid_cvv,cvec * nsc,yang_stmt ** yres)2549 schema_nodeid_iterate(yang_stmt *yn,
2550 cvec *nodeid_cvv,
2551 cvec *nsc,
2552 yang_stmt **yres)
2553 {
2554 int retval = -1;
2555 yang_stmt *ymod;
2556 char *prefix; /* node-identifier = [prefix ":"] identifier */
2557 char *id;
2558 yang_stmt *ys;
2559 yang_stmt *ym2;
2560 yang_stmt *yp;
2561 cg_var *cv;
2562 char *ns;
2563 yang_stmt *yspec;
2564
2565 yspec = ys_spec(yn);
2566 yp = yn;
2567 /* Iterate over node identifiers /prefix:id/... */
2568 cv = NULL;
2569 while ((cv = cvec_each(nodeid_cvv, cv)) != NULL){
2570 prefix = cv_name_get(cv);
2571 id = cv_string_get(cv);
2572 /* Top level is repeated from abs case, but here this is done to match with
2573 * matching module below
2574 * Get namespace */
2575 if ((ns = xml_nsctx_get(nsc, prefix)) == NULL){
2576 clicon_err(OE_YANG, EFAULT, "No namespace for prefix: %s in schema node identifier in module %s",
2577 prefix,
2578 yang_argument_get(ys_module(yn)));
2579 goto done;
2580 }
2581 /* Get yang module */
2582 if ((ymod = yang_find_module_by_namespace(yspec, ns)) == NULL){
2583 clicon_err(OE_YANG, EFAULT, "No module for namespace: %s", ns);
2584 goto done;
2585 }
2586 /* Iterate over children of current node to get a match
2587 * XXX namespace?????
2588 */
2589 ys = NULL;
2590 while ((ys = yn_each(yp, ys)) != NULL) {
2591 if (!yang_schemanode(ys))
2592 continue;
2593
2594 /* some keys dont have arguments, match on key */
2595 if (ys->ys_keyword == Y_INPUT || ys->ys_keyword == Y_OUTPUT){
2596 if (strcmp(id, yang_key2str(ys->ys_keyword)) == 0){
2597 break;
2598 }
2599 }
2600 else {
2601 if (ys->ys_argument && strcmp(id, ys->ys_argument) == 0){
2602 /* Also check for right prefix/module */
2603 ym2 = ys->ys_mymodule?ys->ys_mymodule:ys_module(ys);
2604 if (ym2 == ymod)
2605 break;
2606 }
2607 }
2608 } /* while ys */
2609 if (ys == NULL){
2610 clicon_debug(1, "%s: %s not found", __FUNCTION__, id);
2611 goto ok;
2612 }
2613 yp = ys;
2614 } /* while cv */
2615 assert(yp && yang_schemanode((yang_stmt*)yp));
2616 *yres = (yang_stmt*)yp;
2617 ok:
2618 retval = 0;
2619 done:
2620 return retval;
2621 }
2622
2623 /*! Given an absolute schema-nodeid (eg /a/b/c) find matching yang spec
2624 * @param[in] yspec Yang specification.
2625 * @param[in] yn Original yang stmt (where call is made) if any
2626 * @param[in] schema_nodeid Absolute schema-node-id, ie /a/b
2627 * @param[in] keyword A schemode of this type, or -1 if any
2628 * @param[out] yres Result yang statement node, or NULL if not found
2629 * @retval -1 Error, with clicon_err called
2630 * @retval 0 OK , with result in yres
2631 * Assume schema nodeid:s have prefixes, (actually the first).
2632 * @see RFC7950 6.5
2633 * o schema node: A node in the schema tree. One of action, container,
2634 * leaf, leaf-list, list, choice, case, rpc, input, output,
2635 * notification, anydata, and anyxml.
2636 * Used in yang: deviation, top-level augment
2637 * @see yang_desc_schema_nodeid
2638 */
2639 int
yang_abs_schema_nodeid(yang_stmt * yn,char * schema_nodeid,yang_stmt ** yres)2640 yang_abs_schema_nodeid(yang_stmt *yn,
2641 char *schema_nodeid,
2642 yang_stmt **yres)
2643 {
2644 int retval = -1;
2645 cvec *nodeid_cvv = NULL;
2646 cvec *nsc = NULL;
2647 cg_var *cv;
2648 char *prefix;
2649 char *ns;
2650 yang_stmt *yspec;
2651 yang_stmt *ymod;
2652 char *str;
2653
2654 *yres = NULL;
2655 yspec = ys_spec(yn);
2656 /* check absolute schema_nodeid */
2657 if (schema_nodeid[0] != '/'){
2658 clicon_err(OE_YANG, EINVAL, "absolute schema nodeid should start with /");
2659 goto done;
2660 }
2661 /* Split nodeid on the form /p0:i0/p1:i1 to a cvec with [name:p0 value:i0][...]
2662 */
2663 if (str2cvec(schema_nodeid, '/', ':', &nodeid_cvv) < 0)
2664 goto done;
2665 if (cvec_len(nodeid_cvv) == 0)
2666 goto ok;
2667 /* If p0 is NULL an entry will be: [i0] which needs to be transformed to [NULL:i0] */
2668 cv = NULL;
2669 while ((cv = cvec_each(nodeid_cvv, cv)) != NULL){
2670 if ((str = cv_string_get(cv)) == NULL || !strlen(str)){
2671 if (cv_string_set(cv, cv_name_get(cv)) < 0){
2672 clicon_err(OE_UNIX, errno, "cv_string_set");
2673 goto done;
2674 }
2675 cv_name_set(cv, NULL);
2676 }
2677 }
2678 /* Make a namespace context from yang for the prefixes (names) of nodeid_cvv */
2679 if (xml_nsctx_yang(yn, &nsc) < 0)
2680 goto done;
2681 /* Since this is an _absolute_ schema nodeid start from top
2682 * Get namespace */
2683 cv = cvec_i(nodeid_cvv, 0);
2684 prefix = cv_name_get(cv);
2685 if ((ns = xml_nsctx_get(nsc, prefix)) == NULL){
2686 clicon_err(OE_YANG, EFAULT, "No namespace for prefix: %s in schema node identifier: %s in module %s",
2687 prefix, schema_nodeid, yang_argument_get(ys_module(yn)));
2688 goto done;
2689 }
2690 /* Get yang module */
2691 if ((ymod = yang_find_module_by_namespace(yspec, ns)) == NULL){
2692 clicon_err(OE_YANG, EFAULT, "No module for namespace: %s in schema node identifier: %s",
2693 ns, schema_nodeid);
2694 goto done;
2695 }
2696 /* Iterate through cvv to find schemanode using ymod as starting point (since it is absolute) */
2697 if (schema_nodeid_iterate(ymod, nodeid_cvv, nsc, yres) < 0)
2698 goto done;
2699 ok:
2700 retval = 0;
2701 done:
2702 if (nodeid_cvv)
2703 cvec_free(nodeid_cvv);
2704 if (nsc)
2705 cvec_free(nsc);
2706 return retval;
2707 }
2708
2709 /*! Given a descendant schema-nodeid (eg a/b/c) find matching yang spec
2710 * @param[in] yn Yang node
2711 * @param[in] schema_nodeid Descendant schema-node-id, ie a/b
2712 * @param[in] keyword A schemode of this type, or -1 if any
2713 * @param[out] yres First yang node matching schema nodeid
2714 * @retval 0 OK
2715 * @retval -1 Error, with clicon_err called
2716 * Used in yang: unique, refine, uses augment
2717 * @see yang_abs_schema_nodeid
2718 */
2719 int
yang_desc_schema_nodeid(yang_stmt * yn,char * schema_nodeid,yang_stmt ** yres)2720 yang_desc_schema_nodeid(yang_stmt *yn,
2721 char *schema_nodeid,
2722 yang_stmt **yres)
2723 {
2724 int retval = -1;
2725 cvec *nodeid_cvv = NULL;
2726 cg_var *cv;
2727 char *str;
2728 cvec *nsc = NULL;
2729
2730 if (schema_nodeid == NULL || strlen(schema_nodeid) == 0){
2731 clicon_err(OE_YANG, EINVAL, "nodeid is empty");
2732 goto done;
2733 }
2734 *yres = NULL;
2735 /* check absolute schema_nodeid */
2736 if (schema_nodeid[0] == '/'){
2737 clicon_err(OE_YANG, EINVAL, "descendant schema nodeid should not start with /");
2738 goto done;
2739 }
2740 /* Split nodeid on the form /p0:i0/p1:i1 to a cvec with [name:p0 value:i0][...]
2741 */
2742 if (str2cvec(schema_nodeid, '/', ':', &nodeid_cvv) < 0)
2743 goto done;
2744 if (cvec_len(nodeid_cvv) == 0)
2745 goto ok;
2746 /* If p0 is NULL an entry will be: [i0] which needs to be transformed to [NULL:i0] */
2747 cv = NULL;
2748 while ((cv = cvec_each(nodeid_cvv, cv)) != NULL){
2749 if ((str = cv_string_get(cv)) == NULL || !strlen(str)){
2750 if (cv_string_set(cv, cv_name_get(cv)) < 0){
2751 clicon_err(OE_UNIX, errno, "cv_string_set");
2752 goto done;
2753 }
2754 cv_name_set(cv, NULL);
2755 }
2756 }
2757 /* Make a namespace context from yang for the prefixes (names) of nodeid_cvv */
2758 if (xml_nsctx_yang(yn, &nsc) < 0)
2759 goto done;
2760 /* Iterate through cvv to find schemanode using yn as relative starting point */
2761 if (schema_nodeid_iterate(yn, nodeid_cvv, nsc, yres) < 0)
2762 goto done;
2763 ok:
2764 retval = 0;
2765 done:
2766 if (nsc)
2767 cvec_free(nsc);
2768 if (nodeid_cvv)
2769 cvec_free(nodeid_cvv);
2770 return retval;
2771 }
2772
2773 /*! Check if this leaf is mandatory or not
2774 * Note: one can cache this value in ys_cvec instead of functionally evaluating it.
2775 * @retval 1 yang statement is leaf and it has a mandatory sub-stmt with value true
2776 * @retval 0 The negation of conditions for return value 1.
2777 * @see RFC7950 Sec 3:
2778 * o mandatory node: A mandatory node is one of:
2779 * 1) A leaf, choice, anydata, or anyxml node with a "mandatory"
2780 * statement with the value "true".
2781 * 2) # see below
2782 * 3) A container node without a "presence" statement and that has at
2783 * least one mandatory node as a child.
2784 *
2785 * @note There is also this statement
2786 * 2) A list or leaf-list node with a "min-elements" statement with a
2787 * value greater than zero.
2788 * which we ignore here since:
2789 * (a) it does not consider the XML siblings and therefore returns false positives
2790 * (b) where the actual check is catched by check_list_unique_minmax()
2791 */
2792 int
yang_mandatory(yang_stmt * ys)2793 yang_mandatory(yang_stmt *ys)
2794 {
2795 yang_stmt *ym;
2796
2797 /* 1) A leaf, choice, anydata, or anyxml node with a "mandatory"
2798 * statement with the value "true". */
2799 if (ys->ys_keyword == Y_LEAF || ys->ys_keyword == Y_CHOICE ||
2800 ys->ys_keyword == Y_ANYDATA || ys->ys_keyword == Y_ANYXML){
2801 if ((ym = yang_find(ys, Y_MANDATORY, NULL)) != NULL){
2802 if (ym->ys_cv != NULL) /* shouldnt happen */
2803 return cv_bool_get(ym->ys_cv);
2804 }
2805 }
2806 #if 0 /* See note above */
2807 /* 2) A list or leaf-list node with a "min-elements" statement with a
2808 * value greater than zero. */
2809 else if (ys->ys_keyword == Y_LIST || ys->ys_keyword == Y_LEAF_LIST){
2810 if ((ym = yang_find(ys, Y_MIN_ELEMENTS, NULL)) != NULL){
2811 cv = yang_cv_get(ym);
2812 return cv_uint32_get(cv) > 0; /* Not true if XML considered */
2813 }
2814 }
2815 #endif
2816 /* 3) A container node without a "presence" statement and that has at
2817 * least one mandatory node as a child. */
2818 else if (ys->ys_keyword == Y_CONTAINER &&
2819 yang_find(ys, Y_PRESENCE, NULL) == NULL){
2820 yang_stmt *yc;
2821 int i;
2822 for (i=0; i<ys->ys_len; i++){
2823 yc = ys->ys_stmt[i];
2824 if (yang_mandatory(yc))
2825 return 1;
2826 }
2827 }
2828 return 0;
2829 }
2830
2831 /*! Return config state of this node
2832 * @param[in] ys Yang statement
2833 * @retval 0 If node has a config sub-statement and it is false
2834 * @retval 1 If node has not config sub-statement or it is true
2835 * @see yang_config_ancestor which also takes ancestors into account, which you should normally do.
2836 */
2837 int
yang_config(yang_stmt * ys)2838 yang_config(yang_stmt *ys)
2839 {
2840 yang_stmt *ym;
2841
2842 if ((ym = yang_find(ys, Y_CONFIG, NULL)) != NULL){
2843 if (ym->ys_cv == NULL) /* shouldnt happen */
2844 return 1;
2845 return cv_bool_get(ym->ys_cv);
2846 }
2847 return 1;
2848 }
2849
2850 /*! Return config state of this node taking parents/ancestors into account
2851 *
2852 * config statement is default true.
2853 * @param[in] ys Yang statement
2854 * @retval 0 Node or one of its ancestor has config false
2855 * @retval 1 Neither node nor any of its ancestors has config false
2856 */
2857 int
yang_config_ancestor(yang_stmt * ys)2858 yang_config_ancestor(yang_stmt *ys)
2859 {
2860 yang_stmt *yp;
2861
2862 yp = ys;
2863 do {
2864 if (yang_config(yp) == 0)
2865 return 0;
2866 } while((yp = yang_parent_get(yp)) != NULL);
2867 return 1;
2868 }
2869
2870 /*! Given a yang node, translate the argument string to a cv vector
2871 *
2872 * @param[in] ys Yang statement
2873 * @param[in] delimiter Delimiter character (eg ' ' or ',')
2874 * @retval NULL Error
2875 * @retval cvec Vector of strings. Free with cvec_free()
2876 * @code
2877 * cvec *cvv;
2878 * cg_var *cv = NULL;
2879 * if ((cvv = yang_arg2cvec(ys, " ")) == NULL)
2880 * goto err;
2881 * while ((cv = cvec_each(cvv, cv)) != NULL)
2882 * ...cv_string_get(cv);
2883 * cvec_free(cvv);
2884 * @endcode
2885 * @note must free return value after use w cvec_free
2886 */
2887 cvec *
yang_arg2cvec(yang_stmt * ys,char * delim)2888 yang_arg2cvec(yang_stmt *ys,
2889 char *delim)
2890 {
2891 char **vec = NULL;
2892 int i;
2893 int nvec;
2894 cvec *cvv = NULL;
2895 cg_var *cv;
2896
2897 if ((vec = clicon_strsep(ys->ys_argument, " ", &nvec)) == NULL)
2898 goto done;
2899 if ((cvv = cvec_new(nvec)) == NULL){
2900 clicon_err(OE_YANG, errno, "cvec_new");
2901 goto done;
2902 }
2903 for (i = 0; i < nvec; i++) {
2904 cv = cvec_i(cvv, i);
2905 cv_type_set(cv, CGV_STRING);
2906 if ((cv_string_set(cv, vec[i])) == NULL){
2907 clicon_err(OE_YANG, errno, "cv_string_set");
2908 cvv = NULL;
2909 goto done;
2910 }
2911 }
2912 done:
2913 if (vec)
2914 free(vec);
2915 return cvv;
2916 }
2917
2918 /*! Check if yang is subject to generated cli GT_HIDE boolean
2919 * The yang should be:
2920 * 1) a non-presence container
2921 * 2) parent of a (single) list XXX: or could multiple lists work?
2922 * 3) no other data node children
2923 * @retval 0 No, does not satisfy the GT_HIDE condition
2924 * @retval 1 Yes, satisfies the GT_HIDE condition
2925 * @see clixon-config.yang HIDE enumeration type
2926 */
2927 int
yang_container_cli_hide(yang_stmt * ys,enum genmodel_type gt)2928 yang_container_cli_hide(yang_stmt *ys,
2929 enum genmodel_type gt)
2930 {
2931 yang_stmt *yc = NULL;
2932 int i;
2933 enum rfc_6020 keyw;
2934
2935 keyw = yang_keyword_get(ys);
2936 /* HIDE mode */
2937 if (gt != GT_HIDE)
2938 return 0;
2939 /* A container */
2940 if (yang_keyword_get(ys) != Y_CONTAINER)
2941 return 0;
2942 /* Non-presence */
2943 if (yang_find(ys, Y_PRESENCE, NULL) != NULL)
2944 return 0;
2945 /* Ensure a single list child and no other data nodes */
2946 i = 0; /* Number of list nodes */
2947 while ((yc = yn_each(ys, yc)) != NULL) {
2948 keyw = yang_keyword_get(yc);
2949 /* case/choice could hide anything so disqualify those */
2950 if (keyw == Y_CASE || keyw == Y_CHOICE)
2951 break;
2952 if (!yang_datanode(yc)) /* Allowed, check next */
2953 continue;
2954 if (keyw != Y_LIST) /* Another datanode than list */
2955 break;
2956 if (i++>0) /* More than one list (or could this work?) */
2957 break;
2958 }
2959 if (yc != NULL) /* break from loop */
2960 return 0;
2961 if (i != 1) /* List found */
2962 return 0;
2963 return 1; /* Passed all tests: yes you can hide this keyword */
2964 }
2965
2966 /*! Check if yang node yn has key-stmt as child which matches name
2967 *
2968 * The function looks at the LIST argument string (not actual children)
2969 * @param[in] yn Yang node with sub-statements (look for a key child)
2970 * @param[in] name Check if this name (eg "b") is a key in the yang key statement
2971 *
2972 * @retval -1 Error
2973 * @retval 0 No match
2974 * @retval 1 Yes match
2975 */
2976 int
yang_key_match(yang_stmt * yn,char * name)2977 yang_key_match(yang_stmt *yn,
2978 char *name)
2979 {
2980 int retval = -1;
2981 yang_stmt *ys = NULL;
2982 int i;
2983 cvec *cvv = NULL;
2984 cg_var *cv;
2985
2986 for (i=0; i<yn->ys_len; i++){
2987 ys = yn->ys_stmt[i];
2988 if (ys->ys_keyword == Y_KEY){
2989 if ((cvv = yang_arg2cvec(ys, " ")) == NULL)
2990 goto done;
2991 cv = NULL;
2992 while ((cv = cvec_each(cvv, cv)) != NULL) {
2993 if (strcmp(name, cv_string_get(cv)) == 0){
2994 retval = 1; /* match */
2995 goto done;
2996 }
2997 }
2998 cvec_free(cvv);
2999 cvv = NULL;
3000 }
3001 }
3002 retval = 0;
3003 done:
3004 if (cvv)
3005 cvec_free(cvv);
3006 return retval;
3007 }
3008
3009 /*! Set type cache for yang type
3010 * @param[in] rxmode Kludge to know which regexp engine is used
3011 * @see yang_type_cache_regexp_set where cache is extended w compiled regexps
3012 */
3013 int
yang_type_cache_set(yang_stmt * ys,yang_stmt * resolved,int options,cvec * cvv,cvec * patterns,uint8_t fraction)3014 yang_type_cache_set(yang_stmt *ys,
3015 yang_stmt *resolved,
3016 int options,
3017 cvec *cvv,
3018 cvec *patterns,
3019 uint8_t fraction)
3020 {
3021 int retval = -1;
3022 yang_type_cache *ycache;
3023
3024 if (ys->ys_typecache != NULL){
3025 clicon_err(OE_YANG, EEXIST, "yang type cache");
3026 goto done;
3027 }
3028 if ((ys->ys_typecache = (yang_type_cache *)malloc(sizeof(*ycache))) == NULL){
3029 clicon_err(OE_UNIX, errno, "malloc");
3030 goto done;
3031 }
3032 ycache = ys->ys_typecache;
3033 memset(ycache, 0, sizeof(*ycache));
3034 ycache->yc_resolved = resolved;
3035 ycache->yc_options = options;
3036 if (cvv){
3037 if ((ycache->yc_cvv = cvec_dup(cvv)) == NULL){
3038 clicon_err(OE_UNIX, errno, "cvec_dup");
3039 goto done;
3040 }
3041 }
3042 if (patterns && (ycache->yc_patterns = cvec_dup(patterns)) == NULL){
3043 clicon_err(OE_UNIX, errno, "cvec_dup");
3044 goto done;
3045 }
3046 ycache->yc_fraction = fraction;
3047 retval = 0;
3048 done:
3049 return retval;
3050 }
3051
3052 /*! Extend yang type cache with compiled regexps
3053 * Compiled Regexps are computed in validate code - after initial cache set
3054 * @param[in] regexps
3055 */
3056 int
yang_type_cache_regexp_set(yang_stmt * ytype,int rxmode,cvec * regexps)3057 yang_type_cache_regexp_set(yang_stmt *ytype,
3058 int rxmode,
3059 cvec *regexps)
3060 {
3061 int retval = -1;
3062 yang_type_cache *ycache;
3063
3064 assert(regexps);
3065 assert(yang_keyword_get(ytype) == Y_TYPE);
3066 assert((ycache = ytype->ys_typecache) != NULL);
3067 assert(ycache->yc_regexps == NULL);
3068 ycache->yc_rxmode = rxmode;
3069 if ((ycache->yc_regexps = cvec_dup(regexps)) == NULL){
3070 clicon_err(OE_UNIX, errno, "cvec_dup");
3071 goto done;
3072 }
3073 retval = 0;
3074 done:
3075 return retval;
3076 }
3077
3078 /*! Get individual fields (direct/destructively) from yang type cache.
3079 * @param[out] patterns Initialized cvec of regexp patterns strings
3080 * @retval -1 Error
3081 * @retval 0 No cache
3082 * @retval 1 OK
3083 */
3084 int
yang_type_cache_get(yang_stmt * ytype,yang_stmt ** resolved,int * options,cvec ** cvv,cvec * patterns,int * rxmode,cvec * regexps,uint8_t * fraction)3085 yang_type_cache_get(yang_stmt *ytype,
3086 yang_stmt **resolved,
3087 int *options,
3088 cvec **cvv,
3089 cvec *patterns,
3090 int *rxmode,
3091 cvec *regexps,
3092 uint8_t *fraction)
3093 {
3094 int retval = -1;
3095 cg_var *cv = NULL;
3096 yang_type_cache *ycache;
3097
3098 ycache = ytype->ys_typecache;
3099 if (ycache == NULL){ /* No cache return 0 */
3100 retval = 0;
3101 goto done;
3102 }
3103 if (resolved)
3104 *resolved = ycache->yc_resolved;
3105 if (options)
3106 *options = ycache->yc_options;
3107 if (cvv)
3108 *cvv = ycache->yc_cvv;
3109 if (patterns){
3110 cv = NULL;
3111 while ((cv = cvec_each(ycache->yc_patterns, cv)) != NULL)
3112 cvec_append_var(patterns, cv);
3113 }
3114 if (regexps){
3115 cv = NULL;
3116 while ((cv = cvec_each(ycache->yc_regexps, cv)) != NULL)
3117 cvec_append_var(regexps, cv);
3118 }
3119 if (rxmode)
3120 *rxmode = ycache->yc_rxmode;
3121 if (fraction)
3122 *fraction = ycache->yc_fraction;
3123 retval = 1; /* cache exists and is returned OK */
3124 done:
3125 return retval;
3126 }
3127
3128 /*! Copy yang type cache
3129 */
3130 static int
yang_type_cache_cp(yang_stmt * ynew,yang_stmt * yold)3131 yang_type_cache_cp(yang_stmt *ynew,
3132 yang_stmt *yold)
3133 {
3134 int retval = -1;
3135 int options;
3136 cvec *cvv;
3137 cvec *patterns = NULL;
3138 uint8_t fraction;
3139 yang_stmt *resolved;
3140 int ret;
3141
3142 if ((patterns = cvec_new(0)) == NULL){
3143 clicon_err(OE_UNIX, errno, "cvec_new");
3144 goto done;
3145 }
3146 /* Note, regexps are not copied since they are voids, if they were, they
3147 * could not be freed in a simple way since copies are made at augment/group
3148 */
3149 if ((ret = yang_type_cache_get(yold,
3150 &resolved, &options, &cvv, patterns, NULL, NULL, &fraction)) < 0)
3151 goto done;
3152 if (ret == 1 &&
3153 yang_type_cache_set(ynew, resolved, options, cvv, patterns, fraction) < 0)
3154 goto done;
3155 retval = 0;
3156 done:
3157 if (patterns)
3158 cvec_free(patterns);
3159 return retval;
3160 }
3161
3162 /*! Free yang type cache
3163 */
3164 static int
yang_type_cache_free(yang_type_cache * ycache)3165 yang_type_cache_free(yang_type_cache *ycache)
3166 {
3167 cg_var *cv;
3168 void *p;
3169
3170 if (ycache->yc_cvv)
3171 cvec_free(ycache->yc_cvv);
3172 if (ycache->yc_patterns)
3173 cvec_free(ycache->yc_patterns);
3174 if (ycache->yc_regexps){
3175 cv = NULL;
3176 while ((cv = cvec_each(ycache->yc_regexps, cv)) != NULL){
3177 /* need to store mode since clicon_handle is not available */
3178 switch (ycache->yc_rxmode){
3179 case REGEXP_POSIX:
3180 cligen_regex_posix_free(cv_void_get(cv));
3181 if ((p = cv_void_get(cv)) != NULL){
3182 free(p);
3183 cv_void_set(cv, NULL);
3184 }
3185 break;
3186 case REGEXP_LIBXML2:
3187 cligen_regex_libxml2_free(cv_void_get(cv));
3188 /* Note, already freed in libxml2 case */
3189 if ((p = cv_void_get(cv)) != NULL){
3190 cv_void_set(cv, NULL);
3191 }
3192 break;
3193 default:
3194 break;
3195 }
3196
3197 }
3198 cvec_free(ycache->yc_regexps);
3199 }
3200 free(ycache);
3201 return 0;
3202 }
3203
3204 /*! Add a simple anydata-node
3205 *
3206 * One usecase is CLICON_YANG_UNKNOWN_ANYDATA when unknown data is treated as anydata
3207 * @param[in] yp Yang parent statement
3208 * @param[in] name Node name, will be copied
3209 * @retval ys OK
3210 * @retval NULL Error
3211 * @see ysp_add
3212 */
3213 yang_stmt *
yang_anydata_add(yang_stmt * yp,char * name0)3214 yang_anydata_add(yang_stmt *yp,
3215 char *name0)
3216 {
3217 yang_stmt *ys = NULL;
3218 char *name = NULL;
3219
3220 if ((ys = ys_new(Y_ANYDATA)) == NULL)
3221 goto done;
3222 if ((name = strdup(name0)) == NULL){
3223 clicon_err(OE_UNIX, errno, "strdup");
3224 goto done;
3225 }
3226 yang_argument_set(ys, name);
3227 if (yn_insert(yp, ys) < 0){ /* Insert into hierarchy */
3228 ys = NULL;
3229 goto done;
3230 }
3231 done:
3232 return ys;
3233 }
3234
3235 #ifdef XML_EXPLICIT_INDEX
3236 /*! Mark element as search_index in list
3237 * @retval 0 OK
3238 * @retval -1 Error
3239 */
3240 int
yang_list_index_add(yang_stmt * ys)3241 yang_list_index_add(yang_stmt *ys)
3242 {
3243 int retval = -1;
3244 yang_stmt *yp;
3245
3246 if ((yp = yang_parent_get(ys)) == NULL ||
3247 yang_keyword_get(yp) != Y_LIST){
3248 clicon_log(LOG_WARNING, "search_index should in a list");
3249 goto ok;
3250 }
3251 yang_flag_set(ys, YANG_FLAG_INDEX);
3252 ok:
3253 retval = 0;
3254 // done:
3255 return retval;
3256 }
3257
3258 /*! Callback for yang clixon search_index extension
3259 *
3260 * @param[in] h Clixon handle
3261 * @param[in] yext Yang node of extension
3262 * @param[in] ys Yang node of (unknown) statement belonging to extension
3263 * @retval 0 OK (warnings may appear)
3264 * @retval -1 Error
3265 */
3266 int
yang_search_index_extension(clicon_handle h,yang_stmt * yext,yang_stmt * ys)3267 yang_search_index_extension(clicon_handle h,
3268 yang_stmt *yext,
3269 yang_stmt *ys)
3270 {
3271 int retval = -1;
3272 char *extname;
3273 char *modname;
3274 yang_stmt *ymod;
3275 yang_stmt *yp;
3276
3277 ymod = ys_module(yext);
3278 modname = yang_argument_get(ymod);
3279 extname = yang_argument_get(yext);
3280 if (strcmp(modname, "clixon-config") != 0 || strcmp(extname, "search_index") != 0)
3281 goto ok;
3282 clicon_debug(1, "%s Enabled extension:%s:%s", __FUNCTION__, modname, extname);
3283 yp = yang_parent_get(ys);
3284 if (yang_list_index_add(yp) < 0)
3285 goto done;
3286 ok:
3287 retval = 0;
3288 done:
3289 return retval;
3290 }
3291
3292 #endif /* XML_EXPLICIT_INDEX */
3293