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_bind.h"
81 
82 /*
83  * Local variables
84  */
85 static int _yang_unknown_anydata = 0;
86 
87 /*! Kludge to equate unknown XML with anydata
88  * The problem with this is that its global and should be bound to a handle
89  */
90 int
xml_bind_yang_unknown_anydata(int val)91 xml_bind_yang_unknown_anydata(int val)
92 {
93     _yang_unknown_anydata = val;
94     return 0;
95 }
96 
97 /*! After yang binding, bodies of containers and lists are stripped from XML bodies
98  * May apply to other nodes?
99  */
100 static int
strip_whitespace(cxobj * xt)101 strip_whitespace(cxobj *xt)
102 {
103     yang_stmt    *yt;
104     enum rfc_6020 keyword;
105     cxobj        *xc;
106 
107     if ((yt = xml_spec(xt)) != NULL){
108 	keyword = yang_keyword_get(yt);
109 	if (keyword == Y_LIST || keyword == Y_CONTAINER){
110 	    xc = NULL;
111 	    while ((xc = xml_find_type(xt, NULL, "body", CX_BODY)) != NULL)
112 		xml_purge(xc);
113 	}
114     }
115     return 0;
116 }
117 
118 /*! Associate XML node x with x:s parents yang:s matching child
119  *
120  * @param[in]   xt     XML tree node
121  * @param[out]  xerr   Reason for failure, or NULL
122  * @retval      1      OK Yang assignment made
123  * @retval      2      OK Yang assignment not made because yang parent is anyxml or anydata
124  * @retval      0      Yang assigment not made and xerr set
125  * @retval     -1      Error
126  * @note retval = 2 is special
127  * @see populate_self_top
128  */
129 static int
populate_self_parent(cxobj * xt,cxobj * xsibling,cxobj ** xerr)130 populate_self_parent(cxobj  *xt,
131 		     cxobj  *xsibling,
132 		     cxobj **xerr)
133 {
134     int        retval = -1;
135     yang_stmt *y = NULL;     /* yang node */
136     yang_stmt *yparent;      /* yang parent */
137     cxobj     *xp = NULL;    /* xml parent */
138     char      *name;
139     char      *ns = NULL;    /* XML namespace of xt */
140     char      *nsy = NULL;   /* Yang namespace of xt */
141     cbuf      *cb = NULL;
142 
143     name = xml_name(xt);
144     /* optimization for massive lists - use the first element as role model */
145     if (xsibling &&
146 	xml_child_nr_type(xt, CX_ATTR) == 0){
147 	y = xml_spec(xsibling);
148 	goto set;
149     }
150     xp = xml_parent(xt);
151     if (xp == NULL){
152 	if (xerr &&
153 	    netconf_bad_element_xml(xerr, "application", name, "Missing parent") < 0)
154 	    goto done;
155 	goto fail;
156     }
157     if ((yparent = xml_spec(xp)) == NULL){
158 	if (xerr &&
159 	    netconf_bad_element_xml(xerr, "application", name, "Missing parent yang node") < 0)
160 	    goto done;
161 	goto fail;
162     }
163     if (yang_keyword_get(yparent) == Y_ANYXML || yang_keyword_get(yparent) == Y_ANYDATA){
164 	retval = 2;
165 	goto done;
166     }
167     if (xml2ns(xt, xml_prefix(xt), &ns) < 0)
168 	goto done;
169     if ((y = yang_find_datanode(yparent, name)) == NULL){
170 	if (_yang_unknown_anydata){
171 	    /* Add dummy Y_ANYDATA yang stmt, see ysp_add */
172 	    if ((y = yang_anydata_add(yparent, name)) < 0)
173 		goto done;
174 	    xml_spec_set(xt, y);
175 	    retval = 2; /* treat as anydata */
176 	    clicon_log(LOG_WARNING,
177 		       "%s: %d: No YANG spec for %s, anydata used",
178 		       __FUNCTION__, __LINE__, name);
179 	    goto done;
180 	}
181 	if ((cb = cbuf_new()) == NULL){
182 	    clicon_err(OE_UNIX, errno, "cbuf_new");
183 	    goto done;
184 	}
185 	cprintf(cb, "Failed to find YANG spec of XML node: %s", name);
186 	cprintf(cb, " with parent: %s", xml_name(xp));
187 	if (ns)
188 	    cprintf(cb, " in namespace: %s", ns);
189 	if (xerr &&
190 	    netconf_unknown_element_xml(xerr, "application", name, cbuf_get(cb)) < 0)
191 	    goto done;
192 	goto fail;
193     }
194     nsy = yang_find_mynamespace(y);
195     if (ns == NULL || nsy == NULL){
196 	if (xerr &&
197 	    netconf_bad_element_xml(xerr, "application", name, "Missing namespace") < 0)
198 	    goto done;
199 	goto fail;
200     }
201     /* Assign spec only if namespaces match */
202     if (strcmp(ns, nsy) != 0){
203 	if (xerr &&
204 	    netconf_bad_element_xml(xerr, "application", name, "Namespace mismatch") < 0)
205 	    goto done;
206 	goto fail;
207     }
208  set:
209     xml_spec_set(xt, y);
210 #ifdef XML_EXPLICIT_INDEX
211     if (xml_search_index_p(xt))
212 	xml_search_child_insert(xp, xt);
213 #endif
214     retval = 1;
215  done:
216     if (cb)
217 	cbuf_free(cb);
218     return retval;
219  fail:
220     retval = 0;
221     goto done;
222 }
223 
224 /*! Associate XML node x with yang spec y by going through all top-level modules and finding match
225  *
226  * @param[in]   xt     XML tree node
227  * @param[in]   yspec  Yang spec
228  * @param[out]  xerr   Reason for failure, or NULL
229  * @retval      1      OK yang assignment made
230  * @retval      0      yang assigment not made and xerr set
231  * @retval     -1      Error
232  * @see populate_self_parent
233  */
234 static int
populate_self_top(cxobj * xt,yang_stmt * yspec,cxobj ** xerr)235 populate_self_top(cxobj     *xt,
236 		  yang_stmt *yspec,
237 		  cxobj    **xerr)
238 {
239     int        retval = -1;
240     yang_stmt *y = NULL;     /* yang node */
241     yang_stmt *ymod;         /* yang module */
242     char      *name;
243     char      *ns = NULL;    /* XML namespace of xt */
244     char      *nsy = NULL;   /* Yang namespace of xt */
245     cbuf      *cb = NULL;
246     cxobj     *xp;
247 
248     name = xml_name(xt);
249     if (yspec == NULL){
250 	if (xerr &&
251 	    netconf_bad_element_xml(xerr, "application", name, "Missing yang spec") < 0)
252 	    goto done;
253 	goto fail;
254     }
255     if (ys_module_by_xml(yspec, xt, &ymod) < 0)
256 	goto done;
257     if (xml2ns(xt, xml_prefix(xt), &ns) < 0)
258 	goto done;
259     /* ymod is "real" module, name may belong to included submodule */
260     if (ymod == NULL){
261 	if (xerr){
262 	    if ((cb = cbuf_new()) == NULL){
263 		clicon_err(OE_UNIX, errno, "cbuf_new");
264 		goto done;
265 	    }
266 	    cprintf(cb, "Failed to find YANG spec of XML node: %s", name);
267 	    if ((xp = xml_parent(xt)) != NULL)
268 		cprintf(cb, " with parent: %s", xml_name(xp));
269 	    if (ns)
270 		cprintf(cb, " in namespace: %s", ns);
271 	    if (netconf_unknown_element_xml(xerr, "application", name, cbuf_get(cb)) < 0)
272 		goto done;
273 	}
274 	goto fail;
275     }
276 
277     if ((y = yang_find_schemanode(ymod, name)) == NULL){ /* also rpc */
278 	if (_yang_unknown_anydata){
279 	    /* Add dummy Y_ANYDATA yang stmt, see ysp_add */
280 	    if ((y = yang_anydata_add(ymod, name)) < 0)
281 		goto done;
282 	    xml_spec_set(xt, y);
283 	    retval = 2; /* treat as anydata */
284 	    clicon_log(LOG_WARNING,
285 		       "%s: %d: No YANG spec for %s, anydata used",
286 		       __FUNCTION__, __LINE__, name);
287 	    goto done;
288 	}
289 	if ((cb = cbuf_new()) == NULL){
290 	    clicon_err(OE_UNIX, errno, "cbuf_new");
291 	    goto done;
292 	}
293 	cprintf(cb, "Failed to find YANG spec of XML node: %s", name);
294 	if ((xp = xml_parent(xt)) != NULL)
295 	    cprintf(cb, " with parent: %s", xml_name(xp));
296 	if (ns)
297 	    cprintf(cb, " in namespace: %s", ns);
298 	if (xerr &&
299 	    netconf_unknown_element_xml(xerr, "application", name, cbuf_get(cb)) < 0)
300 	    goto done;
301 	goto fail;
302     }
303     nsy = yang_find_mynamespace(y);
304     if (ns == NULL || nsy == NULL){
305 	if (xerr &&
306 	    netconf_bad_element_xml(xerr, "application", name, "Missing namespace") < 0)
307 	    goto done;
308 	goto fail;
309     }
310     /* Assign spec only if namespaces match */
311     if (strcmp(ns, nsy) != 0){
312 	if (xerr &&
313 	    netconf_bad_element_xml(xerr, "application", name, "Namespace mismatch") < 0)
314 	    goto done;
315 	goto fail;
316     }
317     xml_spec_set(xt, y);
318     retval = 1;
319  done:
320     if (cb)
321 	cbuf_free(cb);
322     return retval;
323  fail:
324     retval = 0;
325     goto done;
326 }
327 
328 /*! Find yang spec association of tree of XML nodes
329  *
330  * Populate xt:s children as top-level symbols
331  * This may be unnecessary if yspec is set on manual creation: x=xml_new(); xml_spec_set(x,y)
332  * @param[in]   xt     XML tree node
333  * @param[in]   yb     How to bind yang to XML top-level when parsing
334  * @param[in]   yspec  Yang spec
335  * @param[out]  xerr   Reason for failure, or NULL
336  * @retval      1      OK yang assignment made
337  * @retval      0      Partial or no yang assigment made (at least one failed) and xerr set
338  * @retval     -1      Error
339  * @code
340  *   cxobj *xerr = NULL;
341  *   if (xml_bind_yang(x, YB_MODULE, yspec, &xerr) < 0)
342  *     err;
343  * @endcode
344  * @note For subs to anyxml nodes will not have spec set
345  * There are several functions in the API family
346  * @see xml_bind_yang_rpc     for incoming rpc
347  * @see xml_bind_yang0        If the calling xml object should also be populated
348  */
349 int
xml_bind_yang(cxobj * xt,yang_bind yb,yang_stmt * yspec,cxobj ** xerr)350 xml_bind_yang(cxobj     *xt,
351 	      yang_bind  yb,
352 	      yang_stmt *yspec,
353 	      cxobj    **xerr)
354 {
355     int    retval = -1;
356     cxobj *xc;         /* xml child */
357     int    ret;
358     int    failed = 0; /* we continue loop after failure, should we stop at fail?`*/
359 
360     strip_whitespace(xt);
361     xc = NULL;     /* Apply on children */
362     while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL) {
363 	if ((ret = xml_bind_yang0(xc, yb, yspec, xerr)) < 0)
364 	    goto done;
365 	if (ret == 0)
366 	    failed++;
367     }
368     if (failed)
369 	goto fail;
370     retval = 1;
371  done:
372     return retval;
373  fail:
374     retval = 0;
375     goto done;
376 }
377 
378 static int
xml_bind_yang0_opt(cxobj * xt,yang_bind yb,cxobj * xsibling,cxobj ** xerr)379 xml_bind_yang0_opt(cxobj     *xt,
380 		   yang_bind  yb,
381 		   cxobj     *xsibling,
382 		   cxobj    **xerr)
383 {
384     int        retval = -1;
385     cxobj     *xc;           /* xml child */
386     int        ret;
387     int        failed = 0; /* we continue loop after failure, should we stop at fail?`*/
388     yang_stmt *yc0 = NULL;
389     cxobj     *xc0 = NULL;
390     cxobj     *xs;
391     char      *name0 = NULL;
392     char      *prefix0 = NULL;
393     char      *name;
394     char      *prefix;
395 
396     switch (yb){
397     case YB_PARENT:
398 	if ((ret = populate_self_parent(xt, xsibling, xerr)) < 0)
399 	    goto done;
400 	break;
401     default:
402 	clicon_err(OE_XML, EINVAL, "Invalid yang binding: %d", yb);
403 	goto done;
404 	break;
405     }
406     if (ret == 0)
407 	goto fail;
408     else if (ret == 2)     /* ret=2 for anyxml from parent^ */
409     	goto ok;
410     strip_whitespace(xt);
411     xc = NULL;     /* Apply on children */
412     while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL) {
413 	/* It is xml2ns in populate_self_parent that needs improvement */
414 	/* cache previous + prefix */
415 	name = xml_name(xc);
416 	prefix = xml_prefix(xc);
417 	if (yc0 != NULL &&
418 	    clicon_strcmp(name0, name) == 0 &&
419 	    clicon_strcmp(prefix0, prefix) == 0){
420 	    if ((ret = xml_bind_yang0_opt(xc, YB_PARENT, xc0, xerr)) < 0)
421 		goto done;
422 	}
423 	else if (xsibling &&
424 		 (xs = xml_find_type(xsibling, prefix, name, CX_ELMNT)) != NULL){
425 	    if ((ret = xml_bind_yang0_opt(xc, YB_PARENT, xs, xerr)) < 0)
426 		goto done;
427 	}
428 	else if ((ret = xml_bind_yang0_opt(xc, YB_PARENT, NULL, xerr)) < 0)
429 	    goto done;
430 	if (ret == 0)
431 	    failed++;
432 	xc0 = xc;
433 	yc0 = xml_spec(xc); /* cache */
434 	name0 = xml_name(xc);
435 	prefix0 = xml_prefix(xc);
436     }
437     if (failed)
438 	goto fail;
439  ok:
440     retval = 1;
441  done:
442     return retval;
443  fail:
444     retval = 0;
445     goto done;
446 }
447 
448 /*! Find yang spec association of tree of XML nodes
449  *
450  * @param[in]   xt     XML tree node
451  * @param[in]   yb     How to bind yang to XML top-level when parsing
452  * @param[in]   yspec  Yang spec
453  * @param[out]  xerr   Reason for failure, or NULL
454  * @retval      1      OK yang assignment made
455  * @retval      0      Partial or no yang assigment made (at least one failed) and xerr set
456  * @retval     -1      Error
457  * Populate xt as top-level node
458  * @see xml_bind_yang  If only children of xt should be populated, not xt itself
459  */
460 int
xml_bind_yang0(cxobj * xt,yang_bind yb,yang_stmt * yspec,cxobj ** xerr)461 xml_bind_yang0(cxobj     *xt,
462 	       yang_bind  yb,
463 	       yang_stmt *yspec,
464 	       cxobj    **xerr)
465 {
466     int        retval = -1;
467     cxobj     *xc;           /* xml child */
468     int        ret;
469     int        failed = 0; /* we continue loop after failure, should we stop at fail?`*/
470 
471     switch (yb){
472     case YB_MODULE:
473 	if ((ret = populate_self_top(xt, yspec, xerr)) < 0)
474 	    goto done;
475 	break;
476     case YB_PARENT:
477 	if ((ret = populate_self_parent(xt, NULL, xerr)) < 0)
478 	    goto done;
479 	break;
480     case YB_NONE:
481 	ret = 1;
482 	break;
483     default:
484 	clicon_err(OE_XML, EINVAL, "Invalid yang binding: %d", yb);
485 	goto done;
486 	break;
487     }
488     if (ret == 0)
489 	goto fail;
490     else if (ret == 2)     /* ret=2 for anyxml from parent^ */
491     	goto ok;
492     strip_whitespace(xt);
493     xc = NULL;     /* Apply on children */
494     while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL) {
495 	if ((ret = xml_bind_yang0_opt(xc, YB_PARENT, NULL, xerr)) < 0)
496 	    goto done;
497 	if (ret == 0)
498 	    failed++;
499     }
500     if (failed)
501 	goto fail;
502  ok:
503     retval = 1;
504  done:
505     return retval;
506  fail:
507     retval = 0;
508     goto done;
509 }
510 
511 /*! Find yang spec association of XML node for incoming RPC starting with <rpc>
512  *
513  * Incoming RPC has an "input" structure that is not taken care of by xml_bind_yang
514  * @param[in]   xrpc   XML rpc node
515  * @param[in]   yspec  Yang spec
516  * @param[out]  xerr   Reason for failure, or NULL
517  * @retval      1      OK yang assignment made
518  * @retval      0      Partial or no yang assigment made (at least one failed) and xerr set
519  * @retval     -1      Error
520  * The
521  * @code
522  *   if (xml_bind_yang_rpc(h, x, NULL) < 0)
523  *      err;
524  * @endcode
525  * @see xml_bind_yang  For other generic cases
526  * @see xml_bind_yang_rpc_reply
527  */
528 int
xml_bind_yang_rpc(cxobj * xrpc,yang_stmt * yspec,cxobj ** xerr)529 xml_bind_yang_rpc(cxobj     *xrpc,
530 		  yang_stmt *yspec,
531 		  cxobj    **xerr)
532 {
533     int        retval = -1;
534     yang_stmt *yrpc = NULL;    /* yang node */
535     yang_stmt *ymod=NULL; /* yang module */
536     yang_stmt *yi = NULL; /* input */
537     cxobj     *x;
538     int        ret;
539     char      *opname;  /* top-level netconf operation */
540     char      *rpcname; /* RPC name */
541     char      *name;
542     cbuf      *cb = NULL;
543     cxobj     *xc;
544 
545     opname = xml_name(xrpc);
546     if ((strcmp(opname, "hello")) == 0) /* Hello: dont bind, dont appear in any yang spec  */
547 	goto ok;
548     else if ((strcmp(opname, "notification")) == 0)
549 	goto ok;
550     else if ((strcmp(opname, "rpc")) == 0)
551 	; /* continue */
552     else {   /* Notify, rpc-reply? */
553 	if (xerr &&
554 	    netconf_unknown_element_xml(xerr, "protocol", opname, "Unrecognized netconf operation") < 0)
555 	    goto done;
556 	goto fail;
557     }
558     x = NULL;
559     while ((x = xml_child_each(xrpc, x, CX_ELMNT)) != NULL) {
560 	rpcname = xml_name(x);
561 	if (ys_module_by_xml(yspec, x, &ymod) < 0)
562 	    goto done;
563 	if (ymod == NULL){
564 	    if (xerr &&
565 		netconf_unknown_element_xml(xerr, "application", rpcname, "Unrecognized RPC (wrong namespace?)") < 0)
566 		goto done;
567 	    goto fail;
568 	}
569 	if ((yrpc = yang_find(ymod, Y_RPC, rpcname)) == NULL){
570 	    if (xerr &&
571 		netconf_unknown_element_xml(xerr, "application", rpcname, "Unrecognized RPC") < 0)
572 		goto done;
573 	    goto fail;
574 	}
575 	xml_spec_set(x, yrpc); /* required for validate */
576 	if ((yi = yang_find(yrpc, Y_INPUT, NULL)) == NULL){
577 	    /* If no yang input spec but RPC has elements, return unknown element */
578 	    if (xml_child_nr_type(x, CX_ELMNT) != 0){
579 		xc = xml_child_i_type(x, 0, CX_ELMNT); /* Pick first */
580 		name = xml_name(xc);
581 		if ((cb = cbuf_new()) == NULL){
582 		    clicon_err(OE_UNIX, errno, "cbuf_new");
583 		    goto done;
584 		}
585 		cprintf(cb, "Unrecognized parameter: %s in rpc: %s", name, rpcname);
586 		if (xerr &&
587 		    netconf_unknown_element_xml(xerr, "application", name, cbuf_get(cb)) < 0)
588 		    goto done;
589 		goto fail;
590 	    }
591 	}
592 	else{
593 	    /* xml_bind_yang need to have parent with yang spec for
594 	     * recursive population to work. Therefore, assign input yang
595 	     * to rpc level although not 100% intuitive */
596 	    xml_spec_set(x, yi);
597 	    if ((ret = xml_bind_yang(x, YB_PARENT, NULL, xerr)) < 0)
598 		goto done;
599 	    if (ret == 0)
600 		goto fail;
601 	}
602     }
603  ok:
604     retval = 1;
605  done:
606     if (cb)
607 	cbuf_free(cb);
608     return retval;
609  fail:
610     retval = 0;
611     goto done;
612 }
613 
614 /*! Find yang spec association of XML node for outgoing RPC starting with <rpc-reply>
615  *
616  * Incoming RPC has an "input" structure that is not taken care of by xml_bind_yang
617  * @param[in]   xrpc  XML rpc node
618  * @param[in]   name  Name of RPC (not seen in output/reply)
619  * @param[in]   yspec  Yang spec
620  * @param[out]  xerr   Reason for failure, or NULL
621  * @retval      1      OK yang assignment made
622  * @retval      0      Partial or no yang assigment made (at least one failed) and xerr set
623  * @retval     -1      Error
624  *
625  * @code
626  *   if (xml_bind_yang_rpc_reply(x, "get-config", yspec, name) < 0)
627  *      err;
628  * @endcode
629  * @see xml_bind_yang  For other generic cases
630  */
631 int
xml_bind_yang_rpc_reply(cxobj * xrpc,char * name,yang_stmt * yspec,cxobj ** xerr)632 xml_bind_yang_rpc_reply(cxobj     *xrpc,
633 			char      *name,
634 			yang_stmt *yspec,
635 			cxobj    **xerr)
636 {
637     int        retval = -1;
638     yang_stmt *yrpc = NULL;    /* yang node */
639     yang_stmt *ymod=NULL;      /* yang module */
640     yang_stmt *yo = NULL;      /* output */
641     cxobj     *x;
642     int        ret;
643 
644     if (strcmp(xml_name(xrpc), "rpc-reply")){
645 	clicon_err(OE_UNIX, EINVAL, "rpc-reply expected");
646 	goto done;
647     }
648     x = NULL;
649     while ((x = xml_child_each(xrpc, x, CX_ELMNT)) != NULL) {
650 	if (ys_module_by_xml(yspec, x, &ymod) < 0)
651 	    goto done;
652 	if (ymod == NULL)
653 	    continue;
654 	if ((yrpc = yang_find(ymod, Y_RPC, name)) == NULL)
655 	    continue;
656 	//	xml_spec_set(xrpc, yrpc);
657 	if ((yo = yang_find(yrpc, Y_OUTPUT, NULL)) == NULL)
658 	    continue;
659 	/* xml_bind_yang need to have parent with yang spec for
660 	 * recursive population to work. Therefore, assign input yang
661 	 * to rpc level although not 100% intuitive */
662 	break;
663     }
664     if (yo != NULL){
665 	xml_spec_set(xrpc, yo);
666 	if ((ret = xml_bind_yang(xrpc, YB_MODULE, yspec, xerr)) < 0)
667 	    goto done;
668 	if (ret == 0)
669 	    goto fail;
670     }
671     retval = 1;
672  done:
673     return retval;
674  fail:
675     retval = 0;
676     goto done;
677 }
678 
679 
680 
681