1 /*
2 *
3 ***** BEGIN LICENSE BLOCK *****
4
5 Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
6 Copyright (C) 2017-2019 Olof Hagsand
7 Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC(Netgate)
8
9 This file is part of CLIXON.
10
11 Licensed under the Apache License, Version 2.0 (the "License");
12 you may not use this file except in compliance with the License.
13 You may obtain a copy of the License at
14
15 http://www.apache.org/licenses/LICENSE-2.0
16
17 Unless required by applicable law or agreed to in writing, software
18 distributed under the License is distributed on an "AS IS" BASIS,
19 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 See the License for the specific language governing permissions and
21 limitations under the License.
22
23 Alternatively, the contents of this file may be used under the terms of
24 the GNU General Public License Version 3 or later (the "GPL"),
25 in which case the provisions of the GPL are applicable instead
26 of those above. If you wish to allow use of your version of this file only
27 under the terms of the GPL, and not to allow others to
28 use your version of this file under the terms of Apache License version 2,
29 indicate your decision by deleting the provisions above and replace them with
30 the notice and other provisions required by the GPL. If you do not delete
31 the provisions above, a recipient may use your version of this file under
32 the terms of any one of the Apache License version 2 or the GPL.
33
34 ***** END LICENSE BLOCK *****
35
36 *
37 * Translation / mapping code between formats
38 */
39 #ifdef HAVE_CONFIG_H
40 #include "clixon_config.h" /* generated by config & autoconf */
41 #endif
42
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <unistd.h>
46 #include <errno.h>
47 #include <ctype.h>
48 #include <string.h>
49 #include <syslog.h>
50 #include <fcntl.h>
51 #include <assert.h>
52 #include <arpa/inet.h>
53 #include <sys/param.h>
54 #include <netinet/in.h>
55
56 /* cligen */
57 #include <cligen/cligen.h>
58
59 /* clicon */
60
61 #include "clixon_string.h"
62 #include "clixon_queue.h"
63 #include "clixon_hash.h"
64 #include "clixon_handle.h"
65 #include "clixon_string.h"
66 #include "clixon_yang.h"
67 #include "clixon_xml.h"
68 #include "clixon_options.h"
69 #include "clixon_data.h"
70 #include "clixon_yang_module.h"
71 #include "clixon_plugin.h"
72 #include "clixon_xml_nsctx.h"
73 #include "clixon_xpath_ctx.h"
74 #include "clixon_xpath.h"
75 #include "clixon_log.h"
76 #include "clixon_err.h"
77 #include "clixon_netconf_lib.h"
78 #include "clixon_xml_sort.h"
79 #include "clixon_yang_type.h"
80 #include "clixon_xml_map.h"
81
82 /* Local types
83 */
84 /* Merge code needs a two-phase pass where objects subject to merge are first checked for,
85 * the actually inserted.
86 * This is to mitigate a search problem where objects inserted are among the ones checked for
87 */
88 typedef struct {
89 cxobj *mt_x0c;
90 cxobj *mt_x1c;
91 yang_stmt *mt_yc;
92 } merge_twophase;
93
94 /*! Is attribute and is either of form xmlns="", or xmlns:x="" */
95 int
isxmlns(cxobj * x)96 isxmlns(cxobj *x)
97 {
98 char *prefix = NULL;
99
100 if (xml_type(x) != CX_ATTR)
101 return 0;
102 if (strcmp(xml_name(x), "xmlns")==0)
103 return 1;
104 if ((prefix = xml_prefix(x)) != NULL
105 && strcmp(xml_prefix(x), "xmlns")==0)
106 return 1;
107 return 0;
108 }
109
110 /*! x is element and has eactly one child which in turn has none
111 * @see child_type in clixon_json.c
112 */
113 static int
tleaf(cxobj * x)114 tleaf(cxobj *x)
115 {
116 cxobj *xc;
117
118 if (xml_type(x) != CX_ELMNT)
119 return 0;
120 if (xml_child_nr_notype(x, CX_ATTR) != 1)
121 return 0;
122 /* From here exactly one noattr child, get it */
123 xc = NULL;
124 while ((xc = xml_child_each(x, xc, -1)) != NULL)
125 if (xml_type(xc) != CX_ATTR)
126 break;
127 if (xc == NULL)
128 return -1; /* n/a */
129 return (xml_child_nr_notype(xc, CX_ATTR) == 0);
130 }
131
132 /*! Translate XML to a "pseudo-code" textual format using a callback - internal function
133 * @param[in] f File to print to
134 * @param[in] x XML object to print
135 * @param[in] fn Callback to make print function
136 * @param[in] level print 4 spaces per level in front of each line
137 */
138 static int
xml2txt_recurse(FILE * f,cxobj * x,clicon_output_cb * fn,int level)139 xml2txt_recurse(FILE *f,
140 cxobj *x,
141 clicon_output_cb *fn,
142 int level)
143 {
144 cxobj *xc = NULL;
145 int children=0;
146 int retval = -1;
147
148 if (f == NULL || x == NULL || fn == NULL){
149 clicon_err(OE_XML, EINVAL, "f, x or fn is NULL");
150 goto done;
151 }
152 xc = NULL; /* count children (elements and bodies, not attributes) */
153 while ((xc = xml_child_each(x, xc, -1)) != NULL)
154 if (xml_type(xc) == CX_ELMNT || xml_type(xc) == CX_BODY)
155 children++;
156 if (!children){ /* If no children print line */
157 switch (xml_type(x)){
158 case CX_BODY:
159 (*fn)(f, "%s;\n", xml_value(x));
160 break;
161 case CX_ELMNT:
162 (*fn)(f, "%*s;\n", 4*level, xml_name(x));
163 break;
164 default:
165 break;
166 }
167 goto ok;
168 }
169 (*fn)(f, "%*s", 4*level, "");
170 (*fn)(f, "%s ", xml_name(x));
171 if (!tleaf(x))
172 (*fn)(f, "{\n");
173 xc = NULL;
174 while ((xc = xml_child_each(x, xc, -1)) != NULL){
175 if (xml_type(xc) == CX_ELMNT || xml_type(xc) == CX_BODY)
176 if (xml2txt_recurse(f, xc, fn, level+1) < 0)
177 break;
178 }
179 if (!tleaf(x))
180 (*fn)(f, "%*s}\n", 4*level, "");
181 ok:
182 retval = 0;
183 done:
184 return retval;
185 }
186
187 /*! Translate XML to a "pseudo-code" textual format using a callback
188 * @param[in] f File to print to
189 * @param[in] x XML object to print
190 * @param[in] fn Callback to make print function
191 */
192 int
xml2txt_cb(FILE * f,cxobj * x,clicon_output_cb * fn)193 xml2txt_cb(FILE *f,
194 cxobj *x,
195 clicon_output_cb *fn)
196 {
197 return xml2txt_recurse(f, x, fn, 0);
198 }
199
200 /*! Translate XML to a "pseudo-code" textual format using stdio file
201 * @param[in] f File to print to
202 * @param[in] x XML object to print
203 * @param[in] level print 4 spaces per level in front of each line
204 * @see xml2txt_cb
205 */
206 int
xml2txt(FILE * f,cxobj * x,int level)207 xml2txt(FILE *f,
208 cxobj *x,
209 int level)
210 {
211 return xml2txt_recurse(f, x, fprintf, 0);
212 }
213
214 /*! Translate from XML to CLI commands
215 * Howto: join strings and pass them down.
216 * Identify unique/index keywords for correct set syntax.
217 * @param[in] f Where to print cli commands
218 * @param[in] x XML Parse-tree (to translate)
219 * @param[in] prepend Print this text in front of all commands.
220 * @param[in] gt option to steer cli syntax
221 * @param[in] fn Callback to make print function
222 */
223 int
xml2cli_recurse(FILE * f,cxobj * x,char * prepend,enum genmodel_type gt,clicon_output_cb * fn)224 xml2cli_recurse(FILE *f,
225 cxobj *x,
226 char *prepend,
227 enum genmodel_type gt,
228 clicon_output_cb *fn)
229 {
230 int retval = -1;
231 cxobj *xe = NULL;
232 cbuf *cbpre = NULL;
233 yang_stmt *ys;
234 int match;
235 char *body;
236
237 if (xml_type(x)==CX_ATTR)
238 goto ok;
239 if ((ys = xml_spec(x)) == NULL)
240 goto ok;
241 if (yang_keyword_get(ys) == Y_LEAF || yang_keyword_get(ys) == Y_LEAF_LIST){
242 if (prepend)
243 (*fn)(f, "%s", prepend);
244 if (gt == GT_ALL || gt == GT_VARS || gt == GT_HIDE)
245 (*fn)(f, "%s ", xml_name(x));
246 if ((body = xml_body(x)) != NULL){
247 if (index(body, ' '))
248 (*fn)(f, "\"%s\"", body);
249 else
250 (*fn)(f, "%s", body);
251 }
252 (*fn)(f, "\n");
253 goto ok;
254 }
255 /* Create prepend variable string */
256 if ((cbpre = cbuf_new()) == NULL){
257 clicon_err(OE_PLUGIN, errno, "cbuf_new");
258 goto done;
259 }
260 if (prepend)
261 cprintf(cbpre, "%s", prepend);
262
263 /* If non-presence container && HIDE mode && only child is
264 * a list, then skip container keyword
265 * See also yang2cli_container */
266 if (yang_container_cli_hide(ys, gt) == 0)
267 cprintf(cbpre, "%s ", xml_name(x));
268
269 if (yang_keyword_get(ys) == Y_LIST){
270 /* If list then first loop through keys */
271 xe = NULL;
272 while ((xe = xml_child_each(x, xe, -1)) != NULL){
273 if ((match = yang_key_match(ys, xml_name(xe))) < 0)
274 goto done;
275 if (!match)
276 continue;
277 if (gt == GT_ALL)
278 cprintf(cbpre, "%s ", xml_name(xe));
279 cprintf(cbpre, "%s ", xml_body(xe));
280 }
281 }
282 /* Then loop through all other (non-keys) */
283 xe = NULL;
284 while ((xe = xml_child_each(x, xe, -1)) != NULL){
285 if (yang_keyword_get(ys) == Y_LIST){
286 if ((match = yang_key_match(ys, xml_name(xe))) < 0)
287 goto done;
288 if (match){
289 (*fn)(f, "%s\n", cbuf_get(cbpre));
290 continue; /* Not key itself */
291 }
292 }
293 if (xml2cli_recurse(f, xe, cbuf_get(cbpre), gt, fn) < 0)
294 goto done;
295 }
296 ok:
297 retval = 0;
298 done:
299 if (cbpre)
300 cbuf_free(cbpre);
301 return retval;
302 }
303
304 /*! Translate from XML to CLI commands
305 * Howto: join strings and pass them down.
306 * Identify unique/index keywords for correct set syntax.
307 * @param[in] f Where to print cli commands
308 * @param[in] x XML Parse-tree (to translate)
309 * @param[in] prepend Print this text in front of all commands.
310 * @param[in] gt option to steer cli syntax
311 * @param[in] fn Callback to make print function
312 */
313 int
xml2cli_cb(FILE * f,cxobj * x,char * prepend,enum genmodel_type gt,clicon_output_cb * fn)314 xml2cli_cb(FILE *f,
315 cxobj *x,
316 char *prepend,
317 enum genmodel_type gt,
318 clicon_output_cb *fn)
319 {
320 return xml2cli_recurse(f, x, prepend, gt, fn);
321 }
322
323 /*! Translate from XML to CLI commands
324 * Howto: join strings and pass them down.
325 * Identify unique/index keywords for correct set syntax.
326 * Args:
327 * @param[in] f Where to print cli commands
328 * @param[in] x XML Parse-tree (to translate)
329 * @param[in] prepend Print this text in front of all commands.
330 * @param[in] gt option to steer cli syntax
331 */
332 int
xml2cli(FILE * f,cxobj * x,char * prepend,enum genmodel_type gt)333 xml2cli(FILE *f,
334 cxobj *x,
335 char *prepend,
336 enum genmodel_type gt)
337 {
338 return xml2cli_recurse(f, x, prepend, gt, fprintf);
339 }
340
341 /*! Translate a single xml node to a cligen variable vector. Note not recursive
342 * @param[in] xt XML tree containing one top node
343 * @param[in] ys Yang spec containing type specification of top-node of xt
344 * @param[out] cvv CLIgen variable vector. Should be freed by cvec_free()
345 * @retval 0 Everything OK, cvv allocated and set
346 * @retval -1 Something wrong, clicon_err() called to set error. No cvv returned
347 * @note cvv Should be freed by cvec_free() after use.
348 * 'Not recursive' means that only one level of XML bodies is translated to cvec:s.
349 * If range is wriong (eg 1000 for uint8) a warning is logged, the value is
350 * skipped, and continues.
351 * yang is needed to know which type an xml element has.
352 * Example:
353 <a>
354 <b>23</b>
355 <c>88</c>
356 <d>
357 <e>99</e>
358 </d>
359 </a>
360 --> b:23, c:88
361 * @see cvec2xml
362 */
363 int
xml2cvec(cxobj * xt,yang_stmt * yt,cvec ** cvv0)364 xml2cvec(cxobj *xt,
365 yang_stmt *yt,
366 cvec **cvv0)
367 {
368 int retval = -1;
369 cvec *cvv = NULL;
370 cxobj *xc; /* xml iteration variable */
371 yang_stmt *ys; /* yang spec */
372 cg_var *cv;
373 cg_var *ycv;
374 char *body;
375 char *reason = NULL;
376 int ret;
377 char *name;
378
379 xc = NULL;
380 /* Tried to allocate whole cvv here, but some cg_vars may be invalid */
381 if ((cvv = cvec_new(0)) == NULL){
382 clicon_err(OE_UNIX, errno, "cvec_new");
383 goto err;
384 }
385 xc = NULL;
386 /* Go through all children of the xml tree */
387 while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL){
388 name = xml_name(xc);
389 if ((ys = yang_find_datanode(yt, name)) == NULL){
390 clicon_debug(0, "%s: yang sanity problem: %s in xml but not present in yang under %s",
391 __FUNCTION__, name, yang_argument_get(yt));
392 if ((body = xml_body(xc)) != NULL){
393 if ((cv = cv_new(CGV_STRING)) == NULL){
394 clicon_err(OE_PLUGIN, errno, "cv_new");
395 goto err;
396 }
397 cv_name_set(cv, name);
398 if ((ret = cv_parse1(body, cv, &reason)) < 0){
399 clicon_err(OE_PLUGIN, errno, "cv_parse %s",name);
400 goto err;
401 }
402 /* If value is out-of-range, log and skip value, and continue */
403 if (ret == 0){
404 clicon_log(LOG_WARNING, "cv_parse %s: %s", name, reason);
405 if (reason)
406 free(reason);
407 }
408 else
409 cvec_append_var(cvv, cv); /* Add to variable vector */
410 cv_free(cv);
411 }
412 }
413 else if ((ycv = yang_cv_get(ys)) != NULL){
414 if ((body = xml_body(xc)) != NULL){
415 if ((cv = cv_new(CGV_STRING)) == NULL){
416 clicon_err(OE_PLUGIN, errno, "cv_new");
417 goto err;
418 }
419 if (cv_cp(cv, ycv) < 0){
420 clicon_err(OE_PLUGIN, errno, "cv_cp");
421 goto err;
422 }
423 if ((ret = cv_parse1(body, cv, &reason)) < 0){
424 clicon_err(OE_PLUGIN, errno, "cv_parse: %s", name);
425 goto err;
426 }
427 if (ret == 0){
428 clicon_log(LOG_WARNING, "cv_parse %s: %s", name, reason);
429 if (reason)
430 free(reason);
431 }
432 else
433 cvec_append_var(cvv, cv); /* Add to variable vector */
434 cv_free(cv);
435 }
436 }
437 }
438 if (clicon_debug_get() > 1){
439 clicon_debug(2, "%s cvv:\n", __FUNCTION__);
440 cvec_print(stderr, cvv);
441 }
442 *cvv0 = cvv;
443 return 0;
444 err:
445 if (cvv)
446 cvec_free(cvv);
447 return retval;
448 }
449
450 /*! Translate a cligen variable vector to an XML tree with depth one
451 * @param[in] cvv CLIgen variable vector. Should be freed by cvec_free()
452 * @param[in] toptag The XML tree in xt will have this XML tag
453 * @param[in] xt Parent, or NULL
454 * @param[out] xt Pointer to XML tree containing one top node. Should be freed with xml_free
455 * @retval 0 Everything OK, cvv allocated and set
456 * @retval -1 Something wrong, clicon_err() called to set error. No xt returned
457 * @see xml2cvec
458 * @see cvec2xml This does more but has an internal xml2cvec translation
459 */
460 int
cvec2xml_1(cvec * cvv,char * toptag,cxobj * xp,cxobj ** xt0)461 cvec2xml_1(cvec *cvv,
462 char *toptag,
463 cxobj *xp,
464 cxobj **xt0)
465 {
466 int retval = -1;
467 cxobj *xt = NULL;
468 cxobj *xn;
469 cxobj *xb;
470 cg_var *cv;
471 char *val;
472 int len=0;
473 int i;
474
475 cv = NULL;
476 while ((cv = cvec_each(cvv, cv)) != NULL)
477 len++;
478 if ((xt = xml_new(toptag, xp, CX_ELMNT)) == NULL)
479 goto err;
480 if (xml_childvec_set(xt, len) < 0)
481 goto err;
482 cv = NULL;
483 i = 0;
484 while ((cv = cvec_each(cvv, cv)) != NULL) {
485 if (cv_type_get(cv)==CGV_ERR || cv_name_get(cv) == NULL)
486 continue;
487 if ((xn = xml_new(cv_name_get(cv), NULL, CX_ELMNT)) == NULL) /* this leaks */
488 goto err;
489 xml_parent_set(xn, xt);
490 xml_child_i_set(xt, i++, xn);
491 if ((xb = xml_new("body", xn, CX_BODY)) == NULL) /* this leaks */
492 goto err;
493 val = cv2str_dup(cv);
494 xml_value_set(xb, val); /* this leaks */
495 if (val)
496 free(val);
497 }
498 *xt0 = xt;
499 return 0;
500 err:
501 if (xt)
502 xml_free(xt);
503 return retval;
504 }
505
506 /*! Recursive help function to compute differences between two xml trees
507 * @param[in] x0 First XML tree
508 * @param[in] x1 Second XML tree
509 * @param[out] x0vec Pointervector to XML nodes existing in only first tree
510 * @param[out] x0veclen Length of first vector
511 * @param[out] x1vec Pointervector to XML nodes existing in only second tree
512 * @param[out] x1veclen Length of x1vec vector
513 * @param[out] changed_x0 Pointervector to XML nodes changed orig value
514 * @param[out] changed_x1 Pointervector to XML nodes changed wanted value
515 * @param[out] changedlen Length of changed vector
516 * Algorithm to compare two sorted lists A, B:
517 * A 0 1 2 3 5 6
518 * B 0 2 4 5 6
519 * Let (a, b) be first elements of (A, B) respectively(*)
520 * a = b : EITHER leafs: a!=b : add a in changed_x0, b in changed_x1,
521 * OR: Set (A,B) to children of (a,b) and call algorithm recursively
522 * , get next (a,b)
523 * a < b : add a in x0, get next a
524 * a > b : add b in x1, get next b
525 * (*) "comparing" a&b here is made by xml_cmp() which judges equality from a structural
526 * perspective, ie both have the same yang spec, if they are lists, they have the
527 * the same keys. NOT that the values are equal!
528 * @see xml_diff API function, this one is internal and recursive
529 */
530 static int
xml_diff1(cxobj * x0,cxobj * x1,cxobj *** x0vec,int * x0veclen,cxobj *** x1vec,int * x1veclen,cxobj *** changed_x0,cxobj *** changed_x1,int * changedlen)531 xml_diff1(cxobj *x0,
532 cxobj *x1,
533 cxobj ***x0vec,
534 int *x0veclen,
535 cxobj ***x1vec,
536 int *x1veclen,
537 cxobj ***changed_x0,
538 cxobj ***changed_x1,
539 int *changedlen)
540 {
541 int retval = -1;
542 cxobj *x0c = NULL; /* x0 child */
543 cxobj *x1c = NULL; /* x1 child */
544 yang_stmt *yc;
545 char *b1;
546 char *b2;
547 int eq;
548
549 /* Traverse x0 and x1 in lock-step */
550 x0c = x1c = NULL;
551 x0c = xml_child_each(x0, x0c, CX_ELMNT);
552 x1c = xml_child_each(x1, x1c, CX_ELMNT);
553 for (;;){
554 if (x0c == NULL && x1c == NULL)
555 goto ok;
556 else if (x0c == NULL){
557 if (cxvec_append(x1c, x1vec, x1veclen) < 0)
558 goto done;
559 x1c = xml_child_each(x1, x1c, CX_ELMNT);
560 continue;
561 }
562 else if (x1c == NULL){
563 if (cxvec_append(x0c, x0vec, x0veclen) < 0)
564 goto done;
565 x0c = xml_child_each(x0, x0c, CX_ELMNT);
566 continue;
567 }
568 /* Both x0c and x1c exists, check if they are equal. */
569 eq = xml_cmp(x0c, x1c, 0, 0, NULL);
570 if (eq < 0){
571 if (cxvec_append(x0c, x0vec, x0veclen) < 0)
572 goto done;
573 x0c = xml_child_each(x0, x0c, CX_ELMNT);
574 continue;
575 }
576 else if (eq > 0){
577 if (cxvec_append(x1c, x1vec, x1veclen) < 0)
578 goto done;
579 x1c = xml_child_each(x1, x1c, CX_ELMNT);
580 continue;
581 }
582 else{ /* equal */
583 /* xml-spec NULL could happen with anydata children for example,
584 * if so, continute compare children but without yang
585 */
586 yc = xml_spec(x0c);
587 if (yc && yang_keyword_get(yc) == Y_LEAF){
588 /* if x0c and x1c are leafs w bodies, then they may be changed */
589 if ((b1 = xml_body(x0c)) != NULL && /* skip empty type */
590 (b2 = xml_body(x1c)) != NULL && /* skip empty type */
591 strcmp(b1, b2) != 0){
592 if (cxvec_append(x0c, changed_x0, changedlen) < 0)
593 goto done;
594 (*changedlen)--; /* append two vectors */
595 if (cxvec_append(x1c, changed_x1, changedlen) < 0)
596 goto done;
597 }
598 }
599 else if (xml_diff1(x0c, x1c,
600 x0vec, x0veclen,
601 x1vec, x1veclen,
602 changed_x0, changed_x1, changedlen)< 0)
603 goto done;
604 }
605 x0c = xml_child_each(x0, x0c, CX_ELMNT);
606 x1c = xml_child_each(x1, x1c, CX_ELMNT);
607 }
608 ok:
609 retval = 0;
610 done:
611 return retval;
612 }
613
614 /*! Compute differences between two xml trees
615 * @param[in] yspec Yang specification
616 * @param[in] x0 First XML tree
617 * @param[in] x1 Second XML tree
618 * @param[out] first Pointervector to XML nodes existing in only first tree
619 * @param[out] firstlen Length of first vector
620 * @param[out] second Pointervector to XML nodes existing in only second tree
621 * @param[out] secondlen Length of second vector
622 * @param[out] changed_x0 Pointervector to XML nodes changed orig value
623 * @param[out] changed_x1 Pointervector to XML nodes changed wanted value
624 * @param[out] changedlen Length of changed vector
625 * All xml vectors should be freed after use.
626 */
627 int
xml_diff(yang_stmt * yspec,cxobj * x0,cxobj * x1,cxobj *** first,int * firstlen,cxobj *** second,int * secondlen,cxobj *** changed_x0,cxobj *** changed_x1,int * changedlen)628 xml_diff(yang_stmt *yspec,
629 cxobj *x0,
630 cxobj *x1,
631 cxobj ***first,
632 int *firstlen,
633 cxobj ***second,
634 int *secondlen,
635 cxobj ***changed_x0,
636 cxobj ***changed_x1,
637 int *changedlen)
638 {
639 int retval = -1;
640
641 *firstlen = 0;
642 *secondlen = 0;
643 *changedlen = 0;
644 if (x0 == NULL && x1 == NULL)
645 return 0;
646 if (x1 == NULL){
647 if (cxvec_append(x0, first, firstlen) < 0)
648 goto done;
649 goto ok;
650 }
651 if (x0 == NULL){
652 if (cxvec_append(x0, second, secondlen) < 0)
653 goto done;
654 goto ok;
655 }
656 if (xml_diff1(x0, x1,
657 first, firstlen,
658 second, secondlen,
659 changed_x0, changed_x1, changedlen) < 0)
660 goto done;
661 ok:
662 retval = 0;
663 done:
664 return retval;
665 }
666
667 /*! Prune everything that does not pass test or have at least a child* does not
668 * @param[in] xt XML tree with some node marked
669 * @param[in] flag Which flag to test for
670 * @param[in] test 1: test that flag is set, 0: test that flag is not set
671 * @param[out] upmark Set if a child (recursively) has marked set.
672 * The function removes all branches that does not pass the test
673 * Purge all nodes that dont have MARK flag set recursively.
674 * Save all nodes that is MARK:ed or have at least one (grand*)child that is MARKed
675 * @code
676 * xml_tree_prune_flagged_sub(xt, XML_FLAG_MARK, 1, NULL);
677 * @endcode
678 * @note This function seems a little too complex semantics
679 * @see xml_tree_prune_flagged for a simpler variant
680 */
681 #if 1
682 int
xml_tree_prune_flagged_sub(cxobj * xt,int flag,int test,int * upmark)683 xml_tree_prune_flagged_sub(cxobj *xt,
684 int flag,
685 int test,
686 int *upmark)
687 {
688 int retval = -1;
689 int submark;
690 int mark;
691 cxobj *x;
692 cxobj *xprev;
693 int iskey;
694 int anykey=0;
695 yang_stmt *yt;
696
697 mark = 0;
698 yt = xml_spec(xt); /* xan be null */
699 x = NULL;
700 xprev = x = NULL;
701 while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
702 if (xml_flag(x, flag) == test?flag:0){
703 /* Pass test */
704 mark++;
705 xprev = x;
706 continue; /* mark and stop here */
707 }
708 /* If it is key dont remove it yet (see second round) */
709 if (yt){
710 if ((iskey = yang_key_match(yt, xml_name(x))) < 0)
711 goto done;
712 if (iskey){
713 anykey++;
714 xprev = x; /* skip if this is key */
715 continue;
716 }
717 }
718 if (xml_tree_prune_flagged_sub(x, flag, test, &submark) < 0)
719 goto done;
720 /* if xt is list and submark anywhere, then key subs are also marked
721 */
722 if (submark)
723 mark++;
724 else{ /* Safe with xml_child_each if last */
725 if (xml_purge(x) < 0)
726 goto done;
727 x = xprev;
728 }
729 xprev = x;
730 }
731 /* Second round: if any keys were found, and no marks detected, purge now */
732 if (anykey && !mark){
733 x = NULL;
734 xprev = x = NULL;
735 while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
736 /* If it is key remove it here */
737 if (yt){
738 if ((iskey = yang_key_match(yt, xml_name(x))) < 0)
739 goto done;
740 if (iskey && xml_purge(x) < 0)
741 goto done;
742 x = xprev;
743 }
744 xprev = x;
745 }
746 }
747 retval = 0;
748 done:
749 if (upmark)
750 *upmark = mark;
751 return retval;
752 }
753 #else
754 /* This is optimized in the sense that xml_purge is replaced with xml_child_rm but it leaks memory,
755 * in poarticualr attributes and namespace caches
756 */
757 int
xml_tree_prune_flagged_sub(cxobj * xt,int flag,int test,int * upmark)758 xml_tree_prune_flagged_sub(cxobj *xt,
759 int flag,
760 int test,
761 int *upmark)
762 {
763 int retval = -1;
764 int submark;
765 int mark;
766 cxobj *x;
767 cxobj *xprev;
768 int iskey;
769 int anykey=0;
770 yang_stmt *yt;
771 int i;
772
773 mark = 0;
774 yt = xml_spec(xt); /* xan be null */
775 x = NULL;
776 xprev = x = NULL;
777 i = 0;
778 while ((x = xml_child_each(xt, x, -1)) != NULL) {
779 i++;
780 if (xml_type(x) != CX_ELMNT){
781 xprev = x;
782 continue;
783 }
784 if (xml_flag(x, flag) == test?flag:0){
785 /* Pass test */
786 mark++;
787 xprev = x;
788 continue; /* mark and stop here */
789 }
790 /* If it is key dont remove it yet (see second round) */
791 if (yt){
792 if ((iskey = yang_key_match(yt, xml_name(x))) < 0)
793 goto done;
794 if (iskey){
795 anykey++;
796 xprev = x; /* skip if this is key */
797 continue;
798 }
799 }
800 if (xml_tree_prune_flagged_sub(x, flag, test, &submark) < 0)
801 goto done;
802 /* if xt is list and submark anywhere, then key subs are also marked
803 */
804 if (submark)
805 mark++;
806 else{ /* Safe with xml_child_each if last */
807 if (xml_child_rm(xt, i-1) < 0)
808 goto done;
809 i--;
810 x = xprev;
811 }
812 xprev = x;
813 }
814 /* Second round: if any keys were found, and no marks detected, purge now */
815 if (anykey && !mark){
816 x = NULL;
817 xprev = x = NULL;
818 i = 0;
819 while ((x = xml_child_each(xt, x, -1)) != NULL) {
820 i++;
821 if (xml_type(x) != CX_ELMNT){
822 xprev = x;
823 continue;
824 }
825 /* If it is key remove it here */
826 if (yt){
827 if ((iskey = yang_key_match(yt, xml_name(x))) < 0)
828 goto done;
829 if (xml_child_rm(xt, i-1) < 0)
830 goto done;
831 i--;
832 x = xprev;
833 }
834 xprev = x;
835 }
836 }
837 retval = 0;
838 done:
839 if (upmark)
840 *upmark = mark;
841 return retval;
842 }
843 #endif
844
845 /*! Prune everything that passes test
846 * @param[in] xt XML tree with some node marked
847 * @param[in] flag Which flag to test for
848 * @param[in] test 1: test that flag is set, 0: test that flag is not set
849 * The function removes all branches that does not pass test
850 * @code
851 * xml_tree_prune_flagged(xt, XML_FLAG_MARK, 1);
852 * @endcode
853 */
854 int
xml_tree_prune_flagged(cxobj * xt,int flag,int test)855 xml_tree_prune_flagged(cxobj *xt,
856 int flag,
857 int test)
858 {
859 int retval = -1;
860 cxobj *x;
861 cxobj *xprev;
862
863 x = NULL;
864 xprev = NULL;
865 while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
866 if (xml_flag(x, flag) == (test?flag:0)){ /* Pass test means purge */
867 if (xml_purge(x) < 0)
868 goto done;
869 x = xprev;
870 continue;
871 }
872 if (xml_tree_prune_flagged(x, flag, test) < 0)
873 goto done;
874 xprev = x;
875 }
876 retval = 0;
877 done:
878 return retval;
879 }
880
881 /*! Add prefix:namespace pair to xml node, set cache, etc
882 * @param[in] x XML node whose namespace should change
883 * @param[in] xp XML node where namespace attribute should be declared (can be same)
884 * @param[in] prefix1 Use this prefix
885 * @param[in] namespace Use this namespace
886 * @note x and xp must be different if x is an attribute and may be different otherwise
887 */
888 static int
add_namespace(cxobj * x,cxobj * xp,char * prefix,char * namespace)889 add_namespace(cxobj *x,
890 cxobj *xp,
891 char *prefix,
892 char *namespace)
893 {
894 int retval = -1;
895 cxobj *xa = NULL;
896
897 /* Add binding to x1p. We add to parent due to heurestics, so we dont
898 * end up in adding it to large number of siblings
899 */
900 if (nscache_set(x, prefix, namespace) < 0)
901 goto done;
902 /* Create xmlns attribute to x1p/x1 XXX same code v */
903 if (prefix){
904 if ((xa = xml_new(prefix, xp, CX_ATTR)) == NULL)
905 goto done;
906 if (xml_prefix_set(xa, "xmlns") < 0)
907 goto done;
908 }
909 else{
910 if ((xa = xml_new("xmlns", xp, CX_ATTR)) == NULL)
911 goto done;
912 }
913 if (xml_value_set(xa, namespace) < 0)
914 goto done;
915 xml_sort(xp); /* Ensure attr is first / XXX xml_insert? */
916
917 retval = 0;
918 done:
919 return retval;
920 }
921
922 /*! Change namespace of XML node
923 *
924 * @param[in] x XML node
925 * @param[in] ns Change to this namespace (if ns does not exist in tree)
926 * @param[in] prefix If change, use this prefix
927 * @param 0 OK
928 * @param -1 Error
929 */
930 int
xml_namespace_change(cxobj * x,char * ns,char * prefix)931 xml_namespace_change(cxobj *x,
932 char *ns,
933 char *prefix)
934 {
935 int retval = -1;
936 char *ns0 = NULL; /* existing namespace */
937 char *prefix0 = NULL; /* existing prefix */
938 cxobj *xp;
939
940 ns0 = NULL;
941 if (xml2ns(x, xml_prefix(x), &ns0) < 0)
942 goto done;
943 if (ns0 && strcmp(ns0, ns) == 0)
944 goto ok; /* Already has right namespace */
945 /* Is namespace already declared? */
946 if (xml2prefix(x, ns, &prefix0) == 1){
947 /* Yes it is declared and the prefix is prefix0 */
948 if (xml_prefix_set(x, prefix0) < 0)
949 goto done;
950 }
951 else{ /* Namespace does not exist, add it */
952 /* Clear old prefix if any */
953 if (xml_prefix_set(x, NULL) < 0)
954 goto done;
955 if (xml_type(x) == CX_ELMNT) /* If not element, do the namespace addition to the element */
956 xp = x;
957 else
958 xp = xml_parent(x);
959 if (add_namespace(x, xp, prefix, ns) < 0)
960 goto done;
961 /* Add prefix to x, if any */
962 if (prefix && xml_prefix_set(x, prefix) < 0)
963 goto done;
964
965 }
966 ok:
967 retval = 0;
968 done:
969 return retval;
970 }
971
972 int
xml_default_create1(yang_stmt * y,cxobj * xt,int top,cxobj ** xcp)973 xml_default_create1(yang_stmt *y,
974 cxobj *xt,
975 int top,
976 cxobj **xcp)
977 {
978 int retval = -1;
979 char *namespace;
980 char *prefix;
981 int ret;
982 cxobj *xc = NULL;
983
984 if ((xc = xml_new(yang_argument_get(y), NULL, CX_ELMNT)) == NULL)
985 goto done;
986 xml_spec_set(xc, y);
987
988 /* assign right prefix */
989 if ((namespace = yang_find_mynamespace(y)) != NULL){
990 prefix = NULL;
991 if ((ret = xml2prefix(xt, namespace, &prefix)) < 0)
992 goto done;
993 if (ret){ /* Namespace found, prefix returned in prefix */
994 if (xml_prefix_set(xc, prefix) < 0)
995 goto done;
996 }
997 else{ /* Namespace does not exist in target, must add it w xmlns attr.
998 use source prefix */
999 if (!top){
1000 if ((prefix = yang_find_myprefix(y)) == NULL){
1001 clicon_err(OE_UNIX, errno, "strdup");
1002 goto done;
1003 }
1004 }
1005 if (add_namespace(xc, xc, prefix, namespace) < 0)
1006 goto done;
1007 /* Add prefix to x, if any */
1008 if (prefix && xml_prefix_set(xc, prefix) < 0)
1009 goto done;
1010 }
1011 }
1012 if (xml_addsub(xt, xc) < 0)
1013 goto done;
1014 *xcp = xc;
1015 retval = 0;
1016 done:
1017 return retval;
1018 }
1019
1020 /*! Create leaf from default value
1021 *
1022 * @param[in] yt Yang spec
1023 * @param[in] xt XML tree
1024 * @param[in] top Use default namespace (if you create xmlns statement)
1025 * @retval 0 OK
1026 * @retval -1 Error
1027 */
1028 static int
xml_default_create(yang_stmt * y,cxobj * xt,int top)1029 xml_default_create(yang_stmt *y,
1030 cxobj *xt,
1031 int top)
1032 {
1033 int retval = -1;
1034 cxobj *xc = NULL;
1035 cxobj *xb;
1036 char *str;
1037
1038 if (xml_default_create1(y, xt, top, &xc) < 0)
1039 goto done;
1040 xml_flag_set(xc, XML_FLAG_DEFAULT);
1041 if ((xb = xml_new("body", xc, CX_BODY)) == NULL)
1042 goto done;
1043 if ((str = cv2str_dup(yang_cv_get(y))) == NULL){
1044 clicon_err(OE_UNIX, errno, "cv2str_dup");
1045 goto done;
1046 }
1047 if (xml_value_set(xb, str) < 0)
1048 goto done;
1049 free(str);
1050 retval = 0;
1051 done:
1052 return retval;
1053 }
1054
1055 /*! Try to see if intermediate nodes are necessary for default values, create if so
1056 *
1057 * @param[in] yt Yang container (no-presence)
1058 * @param[out] createp Need to create XML container
1059 * @retval 0 OK
1060 * @retval -1 Error
1061 */
1062 static int
xml_nopresence_try(yang_stmt * yt,int * createp)1063 xml_nopresence_try(yang_stmt *yt,
1064 int *createp)
1065 {
1066 int retval = -1;
1067 yang_stmt *y;
1068
1069 if (yt == NULL || yang_keyword_get(yt) != Y_CONTAINER){
1070 clicon_err(OE_XML, EINVAL, "yt argument is not container");
1071 goto done;
1072 }
1073 *createp = 0;
1074 y = NULL;
1075 while ((y = yn_each(yt, y)) != NULL) {
1076 switch (yang_keyword_get(y)){
1077 case Y_LEAF:
1078 if (!cv_flag(yang_cv_get(y), V_UNSET)){ /* Default value exists */
1079 /* Need to create container */
1080 *createp = 1;
1081 goto ok;
1082 }
1083 break;
1084 case Y_CONTAINER:
1085 if (yang_find(y, Y_PRESENCE, NULL) == NULL){
1086 /* If this is non-presence, (and it does not exist in xt) call recursively
1087 * and create nodes if any default value exist first. Then continue and populate?
1088 */
1089 if (xml_nopresence_try(y, createp) < 0)
1090 goto done;
1091 if (*createp)
1092 goto ok;
1093 }
1094 break;
1095 default:
1096 break;
1097 } /* switch */
1098 }
1099 ok:
1100 retval = 0;
1101 done:
1102 return retval;
1103 }
1104
1105 /*! Ensure default values are set on (children of) one single xml node
1106 *
1107 * Not recursive, except in one case with one or several non-presence containers, in which case
1108 * XML containers may be created to host default values. That code may be a little too recursive.
1109 * @param[in] yt Yang spec
1110 * @param[in] xt XML tree (with yt as spec of xt, informally)
1111 * @param[in] state Set if global state, otherwise config
1112 * @retval 0 OK
1113 * @retval -1 Error
1114 */
1115 static int
xml_default1(yang_stmt * yt,cxobj * xt,int state)1116 xml_default1(yang_stmt *yt,
1117 cxobj *xt,
1118 int state)
1119 {
1120 int retval = -1;
1121 yang_stmt *yc;
1122 cxobj *xc;
1123 int top=0; /* Top symbol (set default namespace) */
1124 int create = 0;
1125
1126 if (xt == NULL){ /* No xml */
1127 clicon_err(OE_XML, EINVAL, "No XML argument");
1128 goto done;
1129 }
1130 switch (yang_keyword_get(yt)){
1131 case Y_MODULE:
1132 case Y_SUBMODULE:
1133 top++;
1134 case Y_CONTAINER: /* XXX maybe check for non-presence here as well */
1135 case Y_LIST:
1136 case Y_INPUT:
1137 case Y_OUTPUT:
1138 yc = NULL;
1139 while ((yc = yn_each(yt, yc)) != NULL) {
1140 if (!state && !yang_config(yc))
1141 continue;
1142 switch (yang_keyword_get(yc)){
1143 case Y_LEAF:
1144 if (!cv_flag(yang_cv_get(yc), V_UNSET)){ /* Default value exists */
1145 if (xml_find_type(xt, NULL, yang_argument_get(yc), CX_ELMNT) == NULL){
1146 /* No such child exist, create this leaf */
1147 if (xml_default_create(yc, xt, top) < 0)
1148 goto done;
1149 xml_sort(xt);
1150 }
1151 }
1152 break;
1153 case Y_CONTAINER:
1154 if (yang_find(yc, Y_PRESENCE, NULL) == NULL){
1155 /* If this is non-presence, (and it does not exist in xt) call
1156 * recursively and create nodes if any default value exist first.
1157 * Then continue and populate?
1158 */
1159 if (xml_find_type(xt, NULL, yang_argument_get(yc), CX_ELMNT) == NULL){
1160 /* No such container exist, recursively try if needed */
1161 if (xml_nopresence_try(yc, &create) < 0)
1162 goto done;
1163 if (create){
1164 /* Retval shows there is a default value need to create the
1165 * container */
1166 if (xml_default_create1(yc, xt, top, &xc) < 0)
1167 goto done;
1168 xml_sort(xt);
1169 /* Then call it recursively */
1170 if (xml_default1(yc, xc, state) < 0)
1171 goto done;
1172 }
1173 }
1174 }
1175 break;
1176 default:
1177 break;
1178 }
1179 }
1180 break;
1181 default:
1182 break;
1183 } /* switch */
1184 retval = 0;
1185 done:
1186 return retval;
1187 }
1188
1189 /*! Ensure default values are set on existing leaf children of this node
1190 *
1191 * Assume yang is bound to the tree
1192 * @param[in] xt XML tree
1193 * @param[in] state If set expand defaults also for state data, otherwise only config
1194 * @retval 0 OK
1195 * @retval -1 Error
1196 */
1197 static int
xml_default(cxobj * xt,int state)1198 xml_default(cxobj *xt,
1199 int state)
1200 {
1201 int retval = -1;
1202 yang_stmt *ys;
1203
1204 if ((ys = (yang_stmt*)xml_spec(xt)) == NULL){
1205 retval = 0;
1206 goto done;
1207 }
1208 if (xml_default1(ys, xt, state) < 0)
1209 goto done;
1210 retval = 0;
1211 done:
1212 return retval;
1213 }
1214
1215 /*! Recursively fill in default values in an XML tree
1216 * @param[in] xt XML tree
1217 * @param[in] state If set expand defaults also for state data, otherwise only config
1218 * @retval 0 OK
1219 * @retval -1 Error
1220 */
1221 int
xml_default_recurse(cxobj * xn,int state)1222 xml_default_recurse(cxobj *xn,
1223 int state)
1224 {
1225 int retval = -1;
1226 cxobj *x;
1227 yang_stmt *y;
1228
1229 if (xml_default(xn, state) < 0)
1230 goto done;
1231 x = NULL;
1232 while ((x = xml_child_each(xn, x, CX_ELMNT)) != NULL) {
1233 if ((y = (yang_stmt*)xml_spec(x)) != NULL){
1234 if (!state && !yang_config(y))
1235 continue;
1236 }
1237 if (xml_default_recurse(x, state) < 0)
1238 goto done;
1239 }
1240 retval = 0;
1241 done:
1242 return retval;
1243 }
1244
1245 /*! Expand and set default values of global top-level on XML tree
1246 *
1247 * Not recursive, except in one case with one or several non-presence containers
1248 * @param[in] xt XML tree
1249 * @param[in] yspec Top-level YANG specification tree, all modules
1250 * @param[in] state Set if global state, otherwise config
1251 * @retval 0 OK
1252 * @retval -1 Error
1253 */
1254 static int
xml_global_defaults_create(cxobj * xt,yang_stmt * yspec,int state)1255 xml_global_defaults_create(cxobj *xt,
1256 yang_stmt *yspec,
1257 int state)
1258
1259 {
1260 int retval = -1;
1261 yang_stmt *ymod = NULL;
1262
1263 if (yspec == NULL || yang_keyword_get(yspec) != Y_SPEC){
1264 clicon_err(OE_XML, EINVAL, "yspec argument is not yang spec");
1265 goto done;
1266 }
1267 while ((ymod = yn_each(yspec, ymod)) != NULL)
1268 if (xml_default1(ymod, xt, state) < 0)
1269 goto done;
1270 retval = 0;
1271 done:
1272 return retval;
1273 }
1274
1275 /*! Expand and set default values of global top-level on XML tree
1276 *
1277 * Not recursive, except in one case with one or several non-presence containers
1278 * @param[in] h Clixon handle
1279 * @param[in] xt XML tree, assume already filtered with xpath
1280 * @param[in] xpath Filter global defaults with this and merge with xt
1281 * @param[in] yspec Top-level YANG specification tree, all modules
1282 * @param[in] state Set if global state, otherwise config
1283 * @retval 0 OK
1284 * @retval -1 Error
1285 * Uses cache?
1286 */
1287 int
xml_global_defaults(clicon_handle h,cxobj * xt,cvec * nsc,const char * xpath,yang_stmt * yspec,int state)1288 xml_global_defaults(clicon_handle h,
1289 cxobj *xt,
1290 cvec *nsc,
1291 const char *xpath,
1292 yang_stmt *yspec,
1293 int state)
1294 {
1295 int retval = -1;
1296 db_elmnt de0 = {0,};
1297 db_elmnt *de = NULL;
1298 cxobj *xcache = NULL;
1299 cxobj *xpart = NULL;
1300 cxobj **xvec = NULL;
1301 size_t xlen;
1302 int i;
1303 cxobj *x0;
1304 int ret;
1305 char *key;
1306
1307 /* Use different keys for config and state */
1308 key = state ? "global-defaults-state" : "global-defaults-config";
1309 /* First get or compute global xml tree cache */
1310 if ((de = clicon_db_elmnt_get(h, key)) == NULL){
1311 /* Create it */
1312 if ((xcache = xml_new("config", NULL, CX_ELMNT)) == NULL)
1313 goto done;
1314 if (xml_global_defaults_create(xcache, yspec, state) < 0)
1315 goto done;
1316 de0.de_xml = xcache;
1317 clicon_db_elmnt_set(h, key, &de0);
1318 }
1319 else
1320 xcache = de->de_xml;
1321
1322 /* Here xcache has all global defaults. Now find the matching nodes
1323 * XXX: nsc as 2nd argument
1324 */
1325 if (xpath_vec(xcache, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
1326 goto done;
1327 /* Iterate through match vector
1328 * For every node found in x0, mark the tree up to t1
1329 */
1330 for (i=0; i<xlen; i++){
1331 x0 = xvec[i];
1332 xml_flag_set(x0, XML_FLAG_MARK);
1333 xml_apply_ancestor(x0, (xml_applyfn_t*)xml_flag_set, (void*)XML_FLAG_CHANGE);
1334 }
1335 /* Create a new tree and copy over the parts from the cache that matches xpath */
1336 if ((xpart = xml_new("config", NULL, CX_ELMNT)) == NULL)
1337 goto done;
1338 if (xml_copy_marked(xcache, xpart) < 0) /* config */
1339 goto done;
1340 if (xml_apply(xcache, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)(XML_FLAG_MARK|XML_FLAG_CHANGE)) < 0)
1341 goto done;
1342 if (xml_apply(xpart, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)(XML_FLAG_MARK|XML_FLAG_CHANGE)) < 0)
1343 goto done;
1344 /* Merge global pruned tree with xt */
1345 if ((ret = xml_merge(xt, xpart, yspec, NULL)) < 1) /* XXX reason */
1346 goto done;
1347 retval = 0;
1348 done:
1349 if (xpart)
1350 xml_free(xpart);
1351 if (xvec)
1352 free(xvec);
1353 return retval;
1354 }
1355
1356 /*! This node is a default set value or (recursively) a non-presence container
1357 * @retval 1 xt is a nopresence/default node (ie "virtual")
1358 * @retval 0 xt is not such a node
1359 */
1360 int
xml_nopresence_default(cxobj * xt)1361 xml_nopresence_default(cxobj *xt)
1362 {
1363 cxobj *xc;
1364 yang_stmt *yt;
1365
1366 if ((yt = xml_spec(xt)) == NULL)
1367 return 0;
1368 switch (yang_keyword_get(yt)){
1369 case Y_CONTAINER:
1370 if (yang_find(yt, Y_PRESENCE, NULL))
1371 return 0;
1372 break;
1373 case Y_LEAF:
1374 return xml_flag(xt, XML_FLAG_DEFAULT)?1:0;
1375 break;
1376 default:
1377 return 0;
1378 }
1379 xc = NULL;
1380 while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL) {
1381 if (xml_nopresence_default(xc) == 0)
1382 return 0;
1383 }
1384 return 1;
1385 }
1386
1387 /*! Remove xml container if it is non-presence and only contains default leafs
1388 * Called from xml_apply. Reason for marking is to delete it afterwords.
1389 * @param[in] x
1390 * @param[in] arg (flag value)
1391 * @code
1392 * if (xml_apply(xt, CX_ELMNT, xml_nopresence_default_mark, (void*)XML_FLAG_DEFAULT) < 0)
1393 * err;
1394 * if (xml_tree_prune_flagged(xt, XML_FLAG_DEFAULT, 1) < 0)
1395 * goto done;
1396 * @endcode
1397 */
1398 int
xml_nopresence_default_mark(cxobj * x,void * arg)1399 xml_nopresence_default_mark(cxobj *x,
1400 void *arg)
1401 {
1402 if (xml_nopresence_default(x))
1403 xml_flag_set(x, (intptr_t)arg);
1404 return 0;
1405 }
1406
1407 /*! Sanitize an xml tree: xml node has matching yang_stmt pointer
1408 * @param[in] xt XML top of tree
1409 */
1410 int
xml_sanity(cxobj * xt,void * arg)1411 xml_sanity(cxobj *xt,
1412 void *arg)
1413 {
1414 int retval = -1;
1415 yang_stmt *ys;
1416 char *name;
1417
1418 if ((ys = (yang_stmt*)xml_spec(xt)) == NULL){
1419 retval = 0;
1420 goto done;
1421 }
1422 name = xml_name(xt);
1423 if (strstr(yang_argument_get(ys), name)==NULL){
1424 clicon_err(OE_XML, 0, "xml node name '%s' does not match yang spec arg '%s'",
1425 name, yang_argument_get(ys));
1426 goto done;
1427 }
1428 retval = 0;
1429 done:
1430 return retval;
1431 }
1432
1433 /*! Detect state data: Either mark or break on first occurence and return xerror
1434 * @param[in] xt XML tree
1435 * @param[out] xerr If set return netconf error, abort and return if a state variable found
1436 * @retval -1 Error
1437 * @retval 0 Status node found and return xerror
1438 * @retval 1 OK
1439 * Note that the behaviour is quite different if xerr is set or not,...
1440 */
1441 int
xml_non_config_data(cxobj * xt,cxobj ** xerr)1442 xml_non_config_data(cxobj *xt,
1443 cxobj **xerr)
1444 {
1445 int retval = -1;
1446 cxobj *x;
1447 yang_stmt *y;
1448 int ret;
1449 cbuf *cb = NULL;
1450
1451 x = NULL;
1452 while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
1453 if ((y = (yang_stmt*)xml_spec(x)) == NULL)
1454 goto ok;
1455 if (!yang_config(y)){ /* config == false means state data */
1456 if (xerr){ /* behaviour 1: return on error */
1457 if ((cb = cbuf_new()) == NULL){
1458 clicon_err(OE_UNIX, errno, "cbuf_new");
1459 goto done;
1460 }
1461 cprintf(cb, "%s in module %s: state data node unexpected",
1462 yang_argument_get(y), yang_argument_get(ys_module(y)));
1463 if (netconf_invalid_value_xml(xerr, "application", cbuf_get(cb)) < 0)
1464 goto done;
1465 retval = 0;
1466 goto done;
1467 }
1468 xml_flag_set(x, XML_FLAG_MARK); /* behaviour 2: mark and continue */
1469 }
1470 if ((ret = xml_non_config_data(x, xerr)) < 0)
1471 goto done;
1472 if (ret == 0){
1473 retval = 0;
1474 goto done;
1475 }
1476 }
1477 ok:
1478 retval = 1;
1479 done:
1480 if (cb)
1481 cbuf_free(cb);
1482 return retval;
1483 }
1484
1485 /*! Given an XML node, build an xpath to root, internal function
1486 * @retval 0 OK
1487 * @retval -1 Error. eg XML malformed
1488 */
1489 static int
xml2xpath1(cxobj * x,cbuf * cb)1490 xml2xpath1(cxobj *x,
1491 cbuf *cb)
1492 {
1493 int retval = -1;
1494 cxobj *xp;
1495 yang_stmt *y = NULL;
1496 cvec *cvk = NULL; /* vector of index keys */
1497 cg_var *cvi;
1498 char *keyname;
1499 cxobj *xkey;
1500 cxobj *xb;
1501 char *b;
1502 enum rfc_6020 keyword;
1503
1504 if ((xp = xml_parent(x)) != NULL &&
1505 xml_spec(xp) != NULL)
1506 xml2xpath1(xp, cb);
1507 /* XXX: sometimes there should be a /, sometimes not */
1508 cprintf(cb, "/%s", xml_name(x));
1509 if ((y = xml_spec(x)) != NULL){
1510 keyword = yang_keyword_get(y);
1511 switch (keyword){
1512 case Y_LEAF_LIST:
1513 if ((b = xml_body(x)) != NULL)
1514 cprintf(cb, "[.=\"%s\"]", b);
1515 else
1516 cprintf(cb, "[.=\"\"]");
1517 break;
1518 case Y_LIST:
1519 cvk = yang_cvec_get(y);
1520 cvi = NULL;
1521 while ((cvi = cvec_each(cvk, cvi)) != NULL) {
1522 keyname = cv_string_get(cvi);
1523 if ((xkey = xml_find(x, keyname)) == NULL)
1524 goto done; /* No key in xml */
1525 if ((xb = xml_find(x, keyname)) == NULL)
1526 goto done;
1527 b = xml_body(xb);
1528 cprintf(cb, "[%s=\"%s\"]", keyname, b?b:"");
1529 }
1530 break;
1531 default:
1532 break;
1533 }
1534 }
1535 retval = 0;
1536 done:
1537 return retval;
1538 }
1539
1540 /*! Given an XML node, build an xpath to root
1541 * Builds only unqualified xpaths, ie no predicates []
1542 * @param[in] x XML object
1543 * @param[out] xpath Malloced xpath string. Need to free() after use
1544 * @retval 0 OK
1545 * @retval -1 Error. (eg XML malformed)
1546 * @note x needs to be bound to YANG, see eg xml_bind_yang()
1547 */
1548 int
xml2xpath(cxobj * x,char ** xpathp)1549 xml2xpath(cxobj *x,
1550 char **xpathp)
1551 {
1552 int retval = -1;
1553 cbuf *cb;
1554 char *xpath = NULL;
1555
1556 if ((cb = cbuf_new()) == NULL){
1557 clicon_err(OE_XML, errno, "cbuf_new");
1558 goto done;
1559 }
1560 if (xml2xpath1(x, cb) < 0)
1561 goto done;
1562 /* XXX: see xpath in test statement,.. */
1563 xpath = cbuf_get(cb);
1564 if (xpathp){
1565 if ((*xpathp = strdup(xpath)) == NULL){
1566 clicon_err(OE_UNIX, errno, "strdup");
1567 goto done;
1568 }
1569 xpath = NULL;
1570 }
1571 retval = 0;
1572 done:
1573 if (cb)
1574 cbuf_free(cb);
1575 return retval;
1576 }
1577
1578 /*! Check if the module tree x is in is assigned right XML namespace, assign if not
1579 * @param[in] x XML node
1580 *(0. You should probably find the XML root and apply this function to that.)
1581 * 1. Check which namespace x should have (via yang). This is correct namespace.
1582 * 2. Check which namespace x has via its XML tree
1583 * 3. If equal, OK
1584 * 4. Assign the correct namespace to the XML node
1585 * Assign default namespace to x. Create an "xmlns"
1586 * @code
1587 * xml_yang_root(x, &xroot);
1588 * xmlns_assign(xroot);
1589 * @endcode
1590 */
1591 int
xmlns_assign(cxobj * x)1592 xmlns_assign(cxobj *x)
1593 {
1594 int retval = -1;
1595 yang_stmt *y;
1596 char *ns_correct; /* correct uri */
1597 char *ns_xml; /* may be null or incorrect */
1598
1599 if ((y = xml_spec(x)) == NULL){
1600 clicon_err(OE_YANG, ENOENT, "XML %s does not have yang spec", xml_name(x));
1601 goto done;
1602 }
1603 /* 1. Check which namespace x should have (via yang). This is correct namespace. */
1604 if ((ns_correct = yang_find_mynamespace(y)) == NULL){
1605 clicon_err(OE_YANG, ENOENT, "yang node %s does not have namespace", yang_argument_get(y));
1606 goto done;
1607 }
1608 /* 2. Check which namespace x has via its XML tree */
1609 if (xml2ns(x, NULL, &ns_xml) < 0)
1610 goto done;
1611 /* 3. If equal, OK, 4. Else, find root of XML tree */
1612 if (ns_xml && strcmp(ns_xml, ns_correct)==0)
1613 goto ok;
1614 /* 4. Assign the correct namespace */
1615 if (xmlns_set(x, NULL, ns_correct) < 0)
1616 goto done;
1617 ok:
1618 retval = 0;
1619 done:
1620 return retval;
1621 }
1622
1623 /*! Given a src element node x0 and a target node x1, assign (optional) prefix and namespace
1624 * @see assign_namespace_element this is a subroutine
1625 */
1626 static int
assign_namespace(cxobj * x0,cxobj * x1,cxobj * x1p,int isroot,char * ns,char * prefix0)1627 assign_namespace(cxobj *x0, /* source */
1628 cxobj *x1, /* target */
1629 cxobj *x1p,
1630 int isroot,
1631 char *ns,
1632 char *prefix0)
1633 {
1634 int retval = -1;
1635 char *prefix1 = NULL;
1636 char *pexist = NULL;
1637 cvec *nsc0 = NULL;
1638 cvec *nsc = NULL;
1639 yang_stmt *y;
1640
1641 /* 2a. Detect if namespace is declared in x1 target parent */
1642 if (xml2prefix(x1p, ns, &pexist) == 1){
1643 /* Yes, and it has prefix pexist */
1644 if (pexist){
1645 if ((prefix1 = strdup(pexist)) == NULL){
1646 clicon_err(OE_UNIX, errno, "strdup");
1647 goto done;
1648 }
1649 }
1650 else
1651 prefix1 = NULL;
1652 /* 3. If yes, assign prefix to x1 */
1653 if (prefix1 && xml_prefix_set(x1, prefix1) < 0)
1654 goto done;
1655 /* And copy namespace context from parent to child */
1656 if ((nsc0 = nscache_get_all(x1p)) != NULL){
1657 if ((nsc = cvec_dup(nsc0)) == NULL){
1658 clicon_err(OE_UNIX, errno, "cvec_dup");
1659 goto done;
1660 }
1661 nscache_replace(x1, nsc);
1662 }
1663 /* Just in case */
1664 if (nscache_set(x1, prefix1, ns) < 0)
1665 goto done;
1666 }
1667 else{ /* No, namespace does not exist in x1 _parent_
1668 * Check if it is exists in x1 itself */
1669 if (xml2prefix(x1, ns, &pexist) == 1){
1670 /* Yes it exists, but is it equal? */
1671 if (clicon_strcmp(pexist, prefix0) == 0)
1672 ; /* Equal, reuse */
1673 else{ /* namespace exist, but not equal, use existing */
1674 /* Add prefix to x1, if any */
1675 if (pexist && xml_prefix_set(x1, pexist) < 0)
1676 goto done;
1677 }
1678 goto ok; /* skip */
1679 }
1680 else { /* namespace does not exist in target x1, use source prefix
1681 * use the prefix defined in the module
1682 */
1683 if (isroot){
1684 if (prefix0 && (prefix1 = strdup(prefix0)) == NULL){
1685 clicon_err(OE_UNIX, errno, "strdup");
1686 goto done;
1687 }
1688 }
1689 else{
1690 char *ptmp;
1691 if ((y = xml_spec(x0)) == NULL){
1692 clicon_err(OE_YANG, ENOENT, "XML %s does not have yang spec",
1693 xml_name(x0));
1694 goto done;
1695 }
1696 /* Find local (imported) prefix for that module namespace */
1697 if (yang_find_prefix_by_namespace(y, ns, &ptmp) < 0)
1698 goto done;
1699 if ((prefix1 = strdup(ptmp)) == NULL){
1700 clicon_err(OE_UNIX, errno, "strdup");
1701 goto done;
1702 }
1703
1704 }
1705 }
1706 if (add_namespace(x1, x1, prefix1, ns) < 0)
1707 goto done;
1708 if (prefix1 && xml_prefix_set(x1, prefix1) < 0)
1709 goto done;
1710 }
1711 ok:
1712 /* 6. Ensure x1 cache is updated (I think it is done w xmlns_set above) */
1713 retval = 0;
1714 done:
1715 if (prefix1)
1716 free(prefix1);
1717 return retval;
1718 }
1719
1720 /*! Given a src element node x0 and a target node x1, assign (optional) prefix and namespace
1721 * @param[in] x0 Source XML tree
1722 * @param[in] x1 Target XML tree
1723 * @param[in] x1p Target XML tree parent
1724 * @retval 0 OK
1725 * @retval -1 OK
1726 * 1. Find N=namespace(x0)
1727 * 2. Detect if N is declared in x1 parent
1728 * 3. If yes, assign prefix to x1
1729 * 4. If no, create new prefix/namespace binding and assign that to x1p (x1 if x1p is root)
1730 * 5. Add prefix to x1, if any
1731 * 6. Ensure x1 cache is updated
1732 * @note switch use of x0 and x1 compared to datastore text_modify
1733 * @see xml2ns
1734 */
1735 int
assign_namespace_element(cxobj * x0,cxobj * x1,cxobj * x1p)1736 assign_namespace_element(cxobj *x0, /* source */
1737 cxobj *x1, /* target */
1738 cxobj *x1p)
1739 {
1740 int retval = -1;
1741 char *namespace = NULL;
1742 char *prefix0 = NULL;;
1743 int isroot;
1744
1745 /* XXX: need to identify root better than hiereustics and strcmp,... */
1746 isroot = xml_parent(x1p)==NULL &&
1747 (strcmp(xml_name(x1p), "config") == 0 || strcmp(xml_name(x1p), "top") == 0)&&
1748 xml_prefix(x1p)==NULL;
1749
1750 /* 1. Find N=namespace(x0) in x0 element */
1751 prefix0 = xml_prefix(x0);
1752 if (xml2ns(x0, prefix0, &namespace) < 0)
1753 goto done;
1754 if (namespace == NULL){
1755 clicon_err(OE_XML, ENOENT, "No namespace found for prefix:%s",
1756 prefix0?prefix0:"NULL");
1757 goto done;
1758 }
1759 if (assign_namespace(x0, x1, x1p, isroot, namespace, prefix0) < 0)
1760 goto done;
1761 /* 6. Ensure x1 cache is updated (I think it is done w xmlns_set above) */
1762 retval = 0;
1763 done:
1764 return retval;
1765 }
1766
1767 /* If origin body has namespace definitions, copy them. The reason is that
1768 * some bodies rely on namespace prefixes, such as NACM path, but there is
1769 * no way we can now this here.
1770 * However, this may lead to namespace collisions if these prefixes are not
1771 * canonical, and may collide with the assign_namespace_element() above (but that
1772 * is for element sysmbols)
1773 */
1774 int
assign_namespace_body(cxobj * x0,char * x0bstr,cxobj * x1)1775 assign_namespace_body(cxobj *x0, /* source */
1776 char *x0bstr,
1777 cxobj *x1) /* target */
1778 {
1779 int retval = -1;
1780 char *namespace = NULL;
1781 char *name;
1782 char *prefix;
1783 char *prefix0 = NULL;;
1784 char *pexisting = NULL;;
1785 cxobj *xa;
1786
1787 xa = NULL;
1788 while ((xa = xml_child_each(x0, xa, CX_ATTR)) != NULL) {
1789 prefix = xml_prefix(xa);
1790 name = xml_name(xa);
1791 namespace = xml_value(xa);
1792 if ((strcmp(name, "xmlns")==0 && prefix==NULL) ||
1793 (prefix != NULL && strcmp(prefix, "xmlns")==0)){
1794 if (prefix == NULL)
1795 prefix0 = NULL;
1796 else
1797 prefix0 = name;
1798 if (strcmp(namespace, NETCONF_BASE_NAMESPACE) ==0 ||
1799 strcmp(namespace, YANG_XML_NAMESPACE) ==0)
1800 continue;
1801 /* Detect if prefix:namespace is declared already? */
1802 if (xml2prefix(x1, namespace, &pexisting) == 1){
1803 /* Yes, and it has prefix pexist */
1804 if (clicon_strcmp(pexisting, prefix0) ==0)
1805 continue;
1806 }
1807 if (add_namespace(x1, x1, prefix0, namespace) < 0)
1808 goto done;
1809 }
1810 }
1811 /* 6. Ensure x1 cache is updated (I think it is done w xmlns_set above) */
1812 retval = 0;
1813 done:
1814 return retval;
1815 }
1816
1817 /*! Merge a base tree x0 with x1 with yang spec y
1818 * @param[in] x0 Base xml tree (can be NULL in add scenarios)
1819 * @param[in] y0 Yang spec corresponding to xml-node x0. NULL if x0 is NULL
1820 * @param[in] x0p Parent of x0
1821 * @param[in] x1 xml tree which modifies base
1822 * @param[out] reason If retval=0 a malloced string
1823 * @retval 1 OK
1824 * @retval 0 Yang error, reason is set
1825 * @retval -1 Error
1826 * Assume x0 and x1 are same on entry and that y is the spec
1827 */
1828 static int
xml_merge1(cxobj * x0,yang_stmt * y0,cxobj * x0p,cxobj * x1,char ** reason)1829 xml_merge1(cxobj *x0, /* the target */
1830 yang_stmt *y0,
1831 cxobj *x0p,
1832 cxobj *x1, /* the source */
1833 char **reason)
1834 {
1835 int retval = -1;
1836 char *x1cname; /* child name */
1837 cxobj *x0c; /* base child */
1838 cxobj *x0b; /* base body */
1839 cxobj *x1c; /* mod child */
1840 char *x1bstr; /* mod body string */
1841 yang_stmt *yc; /* yang child */
1842 cbuf *cbr = NULL; /* Reason buffer */
1843 int ret;
1844 int i;
1845 merge_twophase *twophase = NULL;
1846 int twophase_len;
1847
1848 assert(x1 && xml_type(x1) == CX_ELMNT);
1849 assert(y0);
1850
1851 if (x0 == NULL){
1852 cvec *nsc = NULL;
1853 cg_var *cv;
1854 char *ns;
1855 char *px;
1856 nsc = cvec_dup(nscache_get_all(x1));
1857 if (xml_rm(x1) < 0)
1858 goto done;
1859 /* This is to make the anydata case a little more robust, more could be done */
1860 if (xml_spec(x1) == NULL){
1861 if (xml_addsub(x0p, x1) < 0)
1862 goto done;
1863 }
1864 else
1865 if (xml_insert(x0p, x1, INS_LAST, NULL, NULL) < 0)
1866 goto done;
1867 cv = NULL;
1868 while ((cv = cvec_each(nsc, cv)) != NULL){
1869 px = cv_name_get(cv);
1870 ns = cv_string_get(cv);
1871 /* Check if it exists */
1872 if (xml2prefix(x1, ns, NULL) == 0)
1873 if (xmlns_set(x1, px, ns) < 0)
1874 goto done;
1875 }
1876 if (nsc)
1877 cvec_free(nsc);
1878 goto ok;
1879 }
1880 if (yang_keyword_get(y0) == Y_LEAF_LIST || yang_keyword_get(y0) == Y_LEAF){
1881 x1bstr = xml_body(x1);
1882 if (x1bstr){
1883 if ((x0b = xml_body_get(x0)) == NULL){
1884 if ((x0b = xml_new("body", x0, CX_BODY)) == NULL)
1885 goto done;
1886 }
1887 if (xml_value_set(x0b, x1bstr) < 0)
1888 goto done;
1889 }
1890 if (xml_parent(x0) == NULL &&
1891 xml_insert(x0p, x0, INS_LAST, NULL, NULL) < 0)
1892 goto done;
1893 if (assign_namespace_element(x1, x0, x0p) < 0)
1894 goto done;
1895 } /* if LEAF|LEAF_LIST */
1896 else { /* eg Y_CONTAINER, Y_LIST */
1897 if (assign_namespace_element(x1, x0, x0p) < 0)
1898 goto done;
1899 twophase_len = xml_child_nr(x1);
1900 if ((twophase = calloc(twophase_len, sizeof(*twophase))) == NULL){
1901 clicon_err(OE_UNIX, errno, "calloc");
1902 goto done;
1903 }
1904 i = 0;
1905 /* Loop through children of the modification tree */
1906 x1c = NULL;
1907 while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) {
1908 x1cname = xml_name(x1c);
1909 /* Get yang spec of the child */
1910 if ((yc = yang_find_datanode(y0, x1cname)) == NULL){
1911 if (reason){
1912 if ((cbr = cbuf_new()) == NULL){
1913 clicon_err(OE_XML, errno, "cbuf_new");
1914 goto done;
1915 }
1916 cprintf(cbr, "XML node %s/%s has no corresponding yang specification (Invalid XML or wrong Yang spec?", xml_name(x1), x1cname);
1917 if ((*reason = strdup(cbuf_get(cbr))) == NULL){
1918 clicon_err(OE_UNIX, errno, "strdup");
1919 goto done;
1920 }
1921 }
1922 goto fail;
1923 }
1924 /* See if there is a corresponding node in the base tree */
1925 x0c = NULL;
1926 if (yc && match_base_child(x0, x1c, yc, &x0c) < 0)
1927 goto done;
1928 /* If x0 already has a value, do not replace it with a default value in x1 */
1929 if (x0c && xml_flag(x1c, XML_FLAG_DEFAULT))
1930 continue;
1931 /* Save x0c, x1c, yc and merge in second wave, so that x1c entries dont "interfer"
1932 * with itself, ie that later searches are among earlier objects already added
1933 * to x0 */
1934 twophase[i].mt_x0c = x0c;
1935 twophase[i].mt_x1c = x1c;
1936 twophase[i].mt_yc = yc;
1937 i++;
1938 } /* while */
1939 twophase_len = i; /* Inital length included non-elements */
1940 /* Second run where actual merging is done
1941 * Loop through children of the modification tree */
1942 for (i=0; i<twophase_len; i++){
1943 assert(twophase[i].mt_x1c);
1944 if ((ret = xml_merge1(twophase[i].mt_x0c,
1945 twophase[i].mt_yc,
1946 x0,
1947 twophase[i].mt_x1c,
1948 reason)) < 0)
1949 goto done;
1950 if (ret == 0)
1951 goto fail;
1952 }
1953 if (xml_parent(x0) == NULL &&
1954 xml_insert(x0p, x0, INS_LAST, NULL, NULL) < 0)
1955 goto done;
1956 } /* else Y_CONTAINER */
1957 ok:
1958 retval = 1;
1959 done:
1960 if (twophase)
1961 free(twophase);
1962 if (cbr)
1963 cbuf_free(cbr);
1964 return retval;
1965 fail:
1966 retval = 0;
1967 goto done;
1968 }
1969
1970 /*! Merge XML trees x1 into x0 according to yang spec yspec
1971 * @param[in] x0 Base xml tree (can be NULL in add scenarios)
1972 * @param[in] x1 xml tree which modifies base
1973 * @param[in] yspec Yang spec
1974 * @param[out] reason If retval=0, reason is set. Malloced. Needs to be freed by caller
1975 * @retval 1 OK
1976 * @retval 0 Yang error, reason is set
1977 * @retval -1 Error
1978 * @note both x0 and x1 need to be top-level trees
1979 */
1980 int
xml_merge(cxobj * x0,cxobj * x1,yang_stmt * yspec,char ** reason)1981 xml_merge(cxobj *x0,
1982 cxobj *x1,
1983 yang_stmt *yspec,
1984 char **reason)
1985 {
1986 int retval = -1;
1987 char *x1cname; /* child name */
1988 cxobj *x0c; /* base child */
1989 cxobj *x1c; /* mod child */
1990 yang_stmt *yc;
1991 yang_stmt *ymod;
1992 cbuf *cbr = NULL; /* Reason buffer */
1993 int i;
1994 merge_twophase *twophase = NULL;
1995 int twophase_len;
1996 int ret;
1997
1998 if (x0 == NULL || x1 == NULL){
1999 clicon_err(OE_UNIX, EINVAL, "parameters x0 or x1 is NULL");
2000 goto done;
2001 }
2002 twophase_len = xml_child_nr(x1);
2003 if ((twophase = calloc(twophase_len, sizeof(*twophase))) == NULL){
2004 clicon_err(OE_UNIX, errno, "calloc");
2005 goto done;
2006 }
2007 /* Loop through children of the modification tree */
2008 i = 0;
2009 x1c = NULL;
2010 while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) {
2011 x1cname = xml_name(x1c);
2012 if ((ys_module_by_xml(yspec, x1c, &ymod)) < 0)
2013 goto done;
2014 if (ymod == NULL){
2015 if (reason &&
2016 (*reason = strdup("Namespace not found or yang spec not loaded")) == NULL){
2017 clicon_err(OE_UNIX, errno, "strdup");
2018 goto done;
2019 }
2020 goto fail;
2021 }
2022 /* Get yang spec of the child */
2023 if ((yc = yang_find_datanode(ymod, x1cname)) == NULL){
2024 if (reason){
2025 if ((cbr = cbuf_new()) == NULL){
2026 clicon_err(OE_XML, errno, "cbuf_new");
2027 goto done;
2028 }
2029 cprintf(cbr, "XML node %s/%s has no corresponding yang specification (Invalid XML or wrong Yang spec?)", xml_name(x1), x1cname);
2030 if ((*reason = strdup(cbuf_get(cbr))) == NULL){
2031 clicon_err(OE_UNIX, errno, "strdup");
2032 goto done;
2033 }
2034 }
2035 goto fail;
2036 }
2037 x0c = NULL;
2038 /* See if there is a corresponding node (x1c) in the base tree (x0) */
2039 if (yc && match_base_child(x0, x1c, yc, &x0c) < 0)
2040 goto done;
2041 /* If x0 already has a value, do not replace it with a default value in x1 */
2042 if (x0c && xml_flag(x1c, XML_FLAG_DEFAULT))
2043 continue;
2044 /* Save x0c, x1c, yc and merge in second wave, so that x1c entries don't "interfere"
2045 * with itself, ie that later searches are among earlier objects already added
2046 * to x0 */
2047 twophase[i].mt_x0c = x0c;
2048 twophase[i].mt_x1c = x1c;
2049 twophase[i].mt_yc = yc;
2050 i++;
2051 }
2052 twophase_len = i; /* Inital length included non-elements */
2053 /* Second run where actual merging is done
2054 * Loop through children of the modification tree */
2055 for (i=0; i<twophase_len; i++){
2056 assert(twophase[i].mt_x1c);
2057 if ((ret = xml_merge1(twophase[i].mt_x0c,
2058 twophase[i].mt_yc,
2059 x0,
2060 twophase[i].mt_x1c,
2061 reason)) < 0)
2062 goto done;
2063 if (ret == 0)
2064 goto fail;
2065 }
2066 retval = 1; /* OK */
2067 done:
2068 if (twophase)
2069 free(twophase);
2070 if (cbr)
2071 cbuf_free(cbr);
2072 return retval;
2073 fail:
2074 retval = 0;
2075 goto done;
2076 }
2077
2078 /*! Get integer value from xml node from yang enumeration
2079 * @param[in] node XML node in a tree
2080 * @param[out] val Integer value returned
2081 * @retval 0 OK, value parsed
2082 * @retval -1 Error, value not obtained or parsed, no reason given
2083 * @note assume yang spec set
2084 * @note The function only returns assigned values, but according to RFC:
2085 If a value is not specified, then one will be automatically assigned.
2086 If the "enum" substatement is the first one defined, the assigned
2087 value is zero (0); otherwise, the assigned value is one greater than
2088 the current highest enum value (i.e., the highest enum value,
2089 * Thanks: Matthew Smith
2090 */
2091 int
yang_enum_int_value(cxobj * node,int32_t * val)2092 yang_enum_int_value(cxobj *node,
2093 int32_t *val)
2094 {
2095 int retval = -1;
2096 yang_stmt *yspec;
2097 yang_stmt *ys;
2098 yang_stmt *ytype;
2099 yang_stmt *yrestype; /* resolved type */
2100 yang_stmt *yenum;
2101 yang_stmt *yval;
2102 char *reason = NULL;
2103
2104 if (node == NULL)
2105 goto done;
2106 if ((ys = (yang_stmt *) xml_spec(node)) == NULL)
2107 goto done;
2108 if ((yspec = ys_spec(ys)) == NULL)
2109 goto done;
2110 if ((ytype = yang_find(ys, Y_TYPE, NULL)) == NULL)
2111 goto done;
2112 if (yang_type_resolve(ys, ys, ytype, &yrestype,
2113 NULL, NULL, NULL, NULL, NULL) < 0)
2114 goto done;
2115 if (yrestype==NULL || strcmp(yang_argument_get(yrestype), "enumeration"))
2116 goto done;
2117 if ((yenum = yang_find(yrestype, Y_ENUM, xml_body(node))) == NULL)
2118 goto done;
2119 /* Should assign value if yval not found */
2120 if ((yval = yang_find(yenum, Y_VALUE, NULL)) == NULL)
2121 goto done;
2122 /* reason is string containing why int could not be parsed */
2123 if (parse_int32(yang_argument_get(yval), val, &reason) < 0)
2124 goto done;
2125 retval = 0;
2126 done:
2127 return retval;
2128 }
2129
2130
2131 /*! Given XML tree x0 with marked nodes, copy marked nodes to new tree x1
2132 * Two marks are used: XML_FLAG_MARK and XML_FLAG_CHANGE
2133 *
2134 * The algorithm works as following:
2135 * (1) Copy individual nodes marked with XML_FLAG_CHANGE
2136 * until nodes marked with XML_FLAG_MARK are reached, where
2137 * (2) the complete subtree of that node is copied.
2138 * (3) Special case: key nodes in lists are copied if any node in list is marked
2139 * @note you may want to check:!yang_config(ys)
2140 */
2141 int
xml_copy_marked(cxobj * x0,cxobj * x1)2142 xml_copy_marked(cxobj *x0,
2143 cxobj *x1)
2144 {
2145 int retval = -1;
2146 int mark;
2147 cxobj *x;
2148 cxobj *xcopy;
2149 int iskey;
2150 yang_stmt *yt;
2151 char *name;
2152 char *prefix;
2153
2154 assert(x0 && x1);
2155 yt = xml_spec(x0); /* can be null */
2156 xml_spec_set(x1, yt);
2157 /* Copy prefix*/
2158 if ((prefix = xml_prefix(x0)) != NULL)
2159 if (xml_prefix_set(x1, prefix) < 0)
2160 goto done;
2161
2162 /* Copy all attributes */
2163 x = NULL;
2164 while ((x = xml_child_each(x0, x, CX_ATTR)) != NULL) {
2165 name = xml_name(x);
2166 if ((xcopy = xml_new(name, x1, CX_ATTR)) == NULL)
2167 goto done;
2168 if (xml_copy(x, xcopy) < 0)
2169 goto done;
2170 }
2171
2172 /* Go through children to detect any marked nodes:
2173 * (3) Special case: key nodes in lists are copied if any
2174 * node in list is marked
2175 */
2176 mark = 0;
2177 x = NULL;
2178 while ((x = xml_child_each(x0, x, CX_ELMNT)) != NULL) {
2179 if (xml_flag(x, XML_FLAG_MARK|XML_FLAG_CHANGE)){
2180 mark++;
2181 break;
2182 }
2183 }
2184 x = NULL;
2185 while ((x = xml_child_each(x0, x, CX_ELMNT)) != NULL) {
2186 name = xml_name(x);
2187 if (xml_flag(x, XML_FLAG_MARK)){
2188 /* (2) the complete subtree of that node is copied. */
2189 if ((xcopy = xml_new(name, x1, CX_ELMNT)) == NULL)
2190 goto done;
2191 if (xml_copy(x, xcopy) < 0)
2192 goto done;
2193 continue;
2194 }
2195 if (xml_flag(x, XML_FLAG_CHANGE)){
2196 /* Copy individual nodes marked with XML_FLAG_CHANGE */
2197 if ((xcopy = xml_new(name, x1, CX_ELMNT)) == NULL)
2198 goto done;
2199 if (xml_copy_marked(x, xcopy) < 0) /* */
2200 goto done;
2201 }
2202 /* (3) Special case: key nodes in lists are copied if any
2203 * node in list is marked */
2204 if (mark && yt && yang_keyword_get(yt) == Y_LIST){
2205 /* XXX: I think yang_key_match is suboptimal here */
2206 if ((iskey = yang_key_match(yt, name)) < 0)
2207 goto done;
2208 if (iskey){
2209 if ((xcopy = xml_new(name, x1, CX_ELMNT)) == NULL)
2210 goto done;
2211 if (xml_copy(x, xcopy) < 0)
2212 goto done;
2213 }
2214 }
2215 }
2216 retval = 0;
2217 done:
2218 return retval;
2219 }
2220
2221