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 
38 #ifdef HAVE_CONFIG_H
39 #include "clixon_config.h" /* generated by config & autoconf */
40 #endif
41 
42 #include <stdio.h>
43 #include <string.h>
44 #include <stdlib.h>
45 #include <unistd.h>
46 #include <stdarg.h>
47 #include <inttypes.h>
48 #include <errno.h>
49 #include <signal.h>
50 #include <fcntl.h>
51 #include <time.h>
52 #include <syslog.h>
53 #include <sys/stat.h>
54 #include <sys/time.h>
55 #include <sys/socket.h>
56 #include <sys/param.h>
57 #include <sys/types.h>
58 #include <netinet/in.h>
59 #include <arpa/inet.h>
60 #include <assert.h>
61 #include <netinet/in.h>
62 
63 /* cligen */
64 #include <cligen/cligen.h>
65 
66 /* clicon */
67 #include <clixon/clixon.h>
68 
69 #include "clixon_backend_handle.h"
70 #include "backend_plugin.h"
71 #include "backend_commit.h"
72 #include "backend_client.h"
73 #include "backend_handle.h"
74 
75 /*! Find client by session-id
76  * @param[in] ce_list   List of clients
77  * @param[in] id        Session id
78  */
79 static struct client_entry *
ce_find_byid(struct client_entry * ce_list,uint32_t id)80 ce_find_byid(struct client_entry *ce_list,
81 	     uint32_t            id)
82 {
83     struct client_entry *ce;
84 
85     for (ce = ce_list; ce; ce = ce->ce_next)
86 	if (ce->ce_id == id)
87 	    return ce;
88     return NULL;
89 }
90 
91 /*! Stream callback for netconf stream notification (RFC 5277)
92  * @param[in]  h     Clicon handle
93  * @param[in]  op    0:event, 1:rm
94  * @param[in]  event Event as XML
95  * @param[in]  arg   Extra argument provided in stream_ss_add
96  * @see stream_ss_add
97  */
98 int
ce_event_cb(clicon_handle h,int op,cxobj * event,void * arg)99 ce_event_cb(clicon_handle h,
100 	    int           op,
101 	    cxobj        *event,
102 	    void         *arg)
103 {
104     struct client_entry *ce = (struct client_entry *)arg;
105 
106     clicon_debug(1, "%s op:%d", __FUNCTION__, op);
107     switch (op){
108     case 1:
109 	/* Risk of recursion here */
110 	if (ce->ce_s)
111 	    backend_client_rm(h, ce);
112 	break;
113     default:
114 	if (send_msg_notify_xml(h, ce->ce_s, event) < 0){
115 	    if (errno == ECONNRESET || errno == EPIPE){
116 		clicon_log(LOG_WARNING, "client %d reset", ce->ce_nr);
117 	    }
118 	    break;
119 	}
120     }
121     return 0;
122 }
123 
124 /*! Remove client entry state
125  * Close down everything wrt clients (eg sockets, subscriptions)
126  * Finally actually remove client struct in handle
127  * @param[in]  h   Clicon handle
128  * @param[in]  ce  Client handle
129  * @see backend_client_delete for actual deallocation of client entry struct
130  */
131 int
backend_client_rm(clicon_handle h,struct client_entry * ce)132 backend_client_rm(clicon_handle        h,
133 		  struct client_entry *ce)
134 {
135     struct client_entry   *c;
136     struct client_entry   *c0;
137     struct client_entry  **ce_prev;
138 
139     clicon_debug(1, "%s", __FUNCTION__);
140     /* for all streams: XXX better to do it top-level? */
141     stream_ss_delete_all(h, ce_event_cb, (void*)ce);
142     c0 = backend_client_list(h);
143     ce_prev = &c0; /* this points to stack and is not real backpointer */
144     for (c = *ce_prev; c; c = c->ce_next){
145 	if (c == ce){
146 	    if (ce->ce_s){
147 		clixon_event_unreg_fd(ce->ce_s, from_client);
148 		close(ce->ce_s);
149 		ce->ce_s = 0;
150 	    }
151 	    break;
152 	}
153 	ce_prev = &c->ce_next;
154     }
155     return backend_client_delete(h, ce); /* actually purge it */
156 }
157 
158 /*!
159  * Maybe should be in the restconf client instead of backend?
160  * @param[in]     h       Clicon handle
161  * @param[in]     yspec   Yang spec
162  * @param[in]     xpath   Xpath selection, not used but may be to filter early
163  * @param[out]    xrs     XML restconf-state node
164  * @see netconf_hello_server
165  * @see rfc8040 Sections 9.1
166  */
167 static int
client_get_capabilities(clicon_handle h,yang_stmt * yspec,char * xpath,cxobj ** xret)168 client_get_capabilities(clicon_handle h,
169 			yang_stmt    *yspec,
170 			char         *xpath,
171 			cxobj       **xret)
172 {
173     int     retval = -1;
174     cxobj  *xrstate = NULL; /* xml restconf-state node */
175     cbuf   *cb = NULL;
176 
177     if ((xrstate = xpath_first(*xret, NULL, "restconf-state")) == NULL){
178 	clicon_err(OE_YANG, ENOENT, "restconf-state not found in config node");
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, "<capabilities>");
186     cprintf(cb, "<capability>urn:ietf:params:restconf:capability:defaults:1.0?basic-mode=explicit</capability>");
187     cprintf(cb, "<capability>urn:ietf:params:restconf:capability:depth:1.0</capability>");
188     cprintf(cb, "</capabilities>");
189     if (clixon_xml_parse_string(cbuf_get(cb), YB_PARENT, NULL, &xrstate, NULL) < 0)
190 	goto done;
191     retval = 0;
192  done:
193     if (cb)
194 	cbuf_free(cb);
195     return retval;
196 }
197 
198 /*! Get streams state according to RFC 8040 or RFC5277 common function
199  * @param[in]     h       Clicon handle
200  * @param[in]     yspec   Yang spec
201  * @param[in]     xpath   Xpath selection, not used but may be to filter early
202  * @param[in]     module  Name of yang module
203  * @param[in]     top     Top symbol, ie netconf or restconf-state
204  * @param[in,out] xret    Existing XML tree, merge x into this
205  * @retval       -1       Error (fatal)
206  * @retval        0       Statedata callback failed
207  * @retval        1       OK
208  */
209 static int
client_get_streams(clicon_handle h,yang_stmt * yspec,char * xpath,yang_stmt * ymod,char * top,cxobj ** xret)210 client_get_streams(clicon_handle   h,
211 		   yang_stmt      *yspec,
212 		   char           *xpath,
213 		   yang_stmt      *ymod,
214 		   char           *top,
215 		   cxobj         **xret)
216 {
217     int            retval = -1;
218     yang_stmt     *yns = NULL;  /* yang namespace */
219     cxobj         *x = NULL;
220     cbuf          *cb = NULL;
221     int            ret;
222 
223     if ((yns = yang_find(ymod, Y_NAMESPACE, NULL)) == NULL){
224 	clicon_err(OE_YANG, 0, "%s yang namespace not found", yang_argument_get(ymod));
225 	goto done;
226     }
227     if ((cb = cbuf_new()) == NULL){
228 	clicon_err(OE_UNIX, errno, "cbuf_new");
229 	goto done;
230     }
231     cprintf(cb, "<%s xmlns=\"%s\">", top, yang_argument_get(yns));
232     /* Second argument is a hack to have the same function for the
233      * RFC5277 and 8040 stream cases
234      */
235     if (stream_get_xml(h, strcmp(top,"restconf-state")==0, cb) < 0)
236 	goto done;
237     cprintf(cb,"</%s>", top);
238 
239     if (clixon_xml_parse_string(cbuf_get(cb), YB_MODULE, yspec, &x, NULL) < 0){
240 	if (netconf_operation_failed_xml(xret, "protocol", clicon_err_reason)< 0)
241 	    goto done;
242 	goto fail;
243     }
244     if ((ret = netconf_trymerge(x, yspec, xret)) < 0)
245 	goto done;
246     if (ret == 0)
247 	goto fail;
248     retval = 1;
249  done:
250     if (cb)
251 	cbuf_free(cb);
252     if (x)
253 	xml_free(x);
254     return retval;
255  fail:
256     retval = 0;
257     goto done;
258 }
259 
260 /*! Get clixon per datastore stats
261  * @param[in]     h       Clicon handle
262  * @param[in]     dbname  Datastore name
263  * @param[in,out] cb      Cligen buf
264  * @retval        0       OK
265  * @retval       -1       Error
266  */
267 static int
clixon_stats_get_db(clicon_handle h,char * dbname,cbuf * cb)268 clixon_stats_get_db(clicon_handle h,
269 		    char         *dbname,
270 		    cbuf         *cb)
271 {
272     int       retval = -1;
273     cxobj    *xt = NULL;
274     uint64_t  nr = 0;
275     size_t    sz = 0;
276 
277     /* This is the db cache */
278     if ((xt = xmldb_cache_get(h, dbname)) == NULL){
279 
280 	cprintf(cb, "<datastore><name>%s</name><nr>0</nr><size>0</size></datastore>", dbname);
281     }
282     else{
283 	if (xml_stats(xt, &nr, &sz) < 0)
284 	    goto done;
285 	cprintf(cb, "<datastore><name>%s</name><nr>%" PRIu64 "</nr>"
286 		"<size>%zu</size></datastore>",
287 		dbname, nr, sz);
288     }
289     retval = 0;
290  done:
291     return retval;
292 }
293 
294 /*! Get system state-data, including streams and plugins
295  * @param[in]     h       Clicon handle
296  * @param[in]     xpath   XPath selection, may be used to filter early
297  * @param[in]     nsc     XML Namespace context for xpath
298  * @param[in]     content config/state or both
299  * @param[in,out] xret    Existing XML tree, merge x into this
300  * @retval       -1       Error (fatal)
301  * @retval        0       Statedata callback failed (clicon_err called)
302  * @retval        1       OK
303  */
304 static int
client_statedata(clicon_handle h,char * xpath,cvec * nsc,netconf_content content,cxobj ** xret)305 client_statedata(clicon_handle h,
306 		 char         *xpath,
307 		 cvec         *nsc,
308 		 netconf_content content,
309 		 cxobj       **xret)
310 {
311     int        retval = -1;
312     yang_stmt *yspec;
313     yang_stmt *ymod;
314     int        ret;
315     char      *namespace;
316     cbuf      *cb = NULL;
317 
318     clicon_debug(1, "%s", __FUNCTION__);
319     if ((yspec = clicon_dbspec_yang(h)) == NULL){
320 	clicon_err(OE_YANG, ENOENT, "No yang spec");
321 	goto done;
322     }
323     if ((cb = cbuf_new()) == NULL){
324 	clicon_err(OE_UNIX, errno, "cbuf_new");
325 	goto done;
326     }
327     /* Add default state to config if present */
328     if (xml_default_recurse(*xret, 1) < 0)
329 	goto done;
330     /* Add default global state */
331     if (xml_global_defaults(h, *xret, nsc, xpath, yspec, 1) < 0)
332 	goto done;
333 
334     if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC5277")){
335 	if ((ymod = yang_find_module_by_name(yspec, "clixon-rfc5277")) == NULL){
336 	    clicon_err(OE_YANG, ENOENT, "yang module clixon-rfc5277 not found");
337 	    goto done;
338 	}
339 	if ((namespace = yang_find_mynamespace(ymod)) == NULL){
340 	    clicon_err(OE_YANG, ENOENT, "clixon-rfc5277 namespace not found");
341 	    goto done;
342 	}
343 	cprintf(cb, "<netconf xmlns=\"%s\"/>", namespace);
344 	if (clixon_xml_parse_string(cbuf_get(cb), YB_MODULE, yspec, xret, NULL) < 0)
345 	    goto done;
346 	if ((ret = client_get_streams(h, yspec, xpath, ymod, "netconf", xret)) < 0)
347 	    goto done;
348 	if (ret == 0)
349 	    goto fail;
350     }
351     if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC8040")){
352 	if ((ymod = yang_find_module_by_name(yspec, "ietf-restconf-monitoring")) == NULL){
353 	    clicon_err(OE_YANG, ENOENT, "yang module ietf-restconf-monitoring not found");
354 	    goto done;
355 	}
356 	if ((namespace = yang_find_mynamespace(ymod)) == NULL){
357 	    clicon_err(OE_YANG, ENOENT, "ietf-restconf-monitoring namespace not found");
358 	    goto done;
359 	}
360 	cbuf_reset(cb);
361 	cprintf(cb, "<restconf-state xmlns=\"%s\"/>", namespace);
362 	if (clixon_xml_parse_string(cbuf_get(cb), YB_MODULE, yspec, xret, NULL) < 0)
363 	    goto done;
364 	if ((ret = client_get_streams(h, yspec, xpath, ymod, "restconf-state", xret)) < 0)
365 	    goto done;
366 	if (ret == 0)
367 	    goto fail;
368 	if ((ret = client_get_capabilities(h, yspec, xpath, xret)) < 0)
369 	    goto done;
370     }
371     if (clicon_option_bool(h, "CLICON_MODULE_LIBRARY_RFC7895")){
372 	if ((ret = yang_modules_state_get(h, yspec, xpath, nsc, 0, xret)) < 0)
373 	    goto done;
374 	if (ret == 0)
375 	    goto fail;
376     }
377     /* Use plugin state callbacks */
378     if ((ret = clixon_plugin_statedata_all(h, yspec, nsc, xpath, xret)) < 0)
379 	goto done;
380     if (ret == 0)
381 	goto fail;
382     retval = 1; /* OK */
383  done:
384     clicon_debug(1, "%s %d", __FUNCTION__, retval);
385     if (cb)
386 	cbuf_free(cb);
387     return retval;
388  fail:
389     retval = 0;
390     goto done;
391 }
392 
393 /*! Retrieve all or part of a specified configuration.
394  *
395  * Function reused from both from_client_get() and from_client_get_config
396  * @param[in]  yspec
397  * @param[in]  db
398  * @param[in]  xpath
399  * @param[in]  username
400  * @param[in]  content
401  * @param[in]  depth
402  * @param[out] cbret   Return xml tree, eg <rpc-reply>..., <rpc-error..
403  * @retval     0       OK
404  * @retval    -1       Error
405  * @see from_client_get
406  */
407 static int
client_get_config_only(clicon_handle h,cvec * nsc,yang_stmt * yspec,char * db,char * xpath,char * username,int32_t depth,cbuf * cbret)408 client_get_config_only(clicon_handle h,
409 		       cvec         *nsc,
410 		       yang_stmt    *yspec,
411 		       char         *db,
412 		       char         *xpath,
413 		       char         *username,
414 		       int32_t       depth,
415 		       cbuf         *cbret)
416 {
417     int     retval = -1;
418     cxobj  *xret = NULL;
419     cxobj  *xnacm = NULL;
420     cxobj **xvec = NULL;
421     size_t  xlen;
422 
423     /* Note xret can be pruned by nacm below (and change name),
424      * so zero-copy cant be used
425      * Also, must use external namespace context here due to <filter stmt
426      */
427     if (xmldb_get0(h, db, YB_MODULE, nsc, xpath, 1, &xret, NULL) < 0) {
428 	if (netconf_operation_failed(cbret, "application", "read registry")< 0)
429 	    goto done;
430 	goto ok;
431     }
432     /* Pre-NACM access step */
433     xnacm = clicon_nacm_cache(h);
434     if (xnacm != NULL){ /* Do NACM validation */
435 	if (xpath_vec(xret, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
436 	    goto done;
437 	/* NACM datanode/module read validation */
438 	if (nacm_datanode_read(h, xret, xvec, xlen, username, xnacm) < 0)
439 	    goto done;
440     }
441     cprintf(cbret, "<rpc-reply xmlns=\"%s\">", NETCONF_BASE_NAMESPACE);
442     if (xret==NULL)
443 	cprintf(cbret, "<data/>");
444     else{
445 	if (xml_name_set(xret, "data") < 0)
446 	    goto done;
447 	if (clicon_xml2cbuf(cbret, xret, 0, 0, depth>0?depth+1:depth) < 0)
448 	    goto done;
449     }
450     cprintf(cbret, "</rpc-reply>");
451  ok:
452     retval = 0;
453  done:
454     if (xvec)
455 	free(xvec);
456     if (xret)
457 	xml_free(xret);
458     return retval;
459 }
460 
461 /*! Retrieve all or part of a specified configuration.
462  *
463  * @param[in]  h       Clicon handle
464  * @param[in]  xe      Request: <rpc><xn></rpc>
465  * @param[out] cbret   Return xml tree, eg <rpc-reply>..., <rpc-error..
466  * @param[in]  arg     client-entry
467  * @param[in]  regarg  User argument given at rpc_callback_register()
468  * @retval     0       OK
469  * @retval    -1       Error
470  * @see from_client_get
471  */
472 static int
from_client_get_config(clicon_handle h,cxobj * xe,cbuf * cbret,void * arg,void * regarg)473 from_client_get_config(clicon_handle h,
474 		       cxobj        *xe,
475 		       cbuf         *cbret,
476 		       void         *arg,
477 		       void         *regarg)
478 {
479     int        retval = -1;
480     char      *db;
481     cxobj     *xfilter;
482     char      *xpath = NULL;
483     cbuf      *cbx = NULL; /* Assist cbuf */
484     int        ret;
485     char      *username;
486     cvec      *nsc = NULL; /* Create a netconf namespace context from filter */
487     yang_stmt *yspec;
488     int32_t    depth = -1; /* Nr of levels to print, -1 is all, 0 is none */
489     char      *attr;
490     char      *xpath0;
491     cvec      *nsc1 = NULL;
492 
493     username = clicon_username_get(h);
494     if ((yspec =  clicon_dbspec_yang(h)) == NULL){
495 	clicon_err(OE_YANG, ENOENT, "No yang spec9");
496 	goto done;
497     }
498     if ((db = netconf_db_find(xe, "source")) == NULL){
499 	clicon_err(OE_XML, 0, "db not found");
500 	goto done;
501     }
502     if (xmldb_validate_db(db) < 0){
503 	if ((cbx = cbuf_new()) == NULL){
504 	    clicon_err(OE_XML, errno, "cbuf_new");
505 	    goto done;
506 	}
507 	cprintf(cbx, "No such database: %s", db);
508 	if (netconf_invalid_value(cbret, "protocol", cbuf_get(cbx))< 0)
509 	    goto done;
510 	goto ok;
511     }
512     if ((xfilter = xml_find(xe, "filter")) != NULL){
513 	if ((xpath0 = xml_find_value(xfilter, "select"))==NULL)
514 	    xpath0="/";
515 	/* Create namespace context for xpath from <filter>
516 	 *  The set of namespace declarations are those in scope on the
517 	 * <filter> element.
518 	 */
519 	else
520 	    if (xml_nsctx_node(xfilter, &nsc) < 0)
521 		goto done;
522 	if (xpath2canonical(xpath0, nsc, yspec, &xpath, &nsc1) < 0)
523 	    goto done;
524 	if (nsc)
525 	    xml_nsctx_free(nsc);
526 	nsc = nsc1;
527     }
528     /* Clixon extensions: depth */
529     if ((attr = xml_find_value(xe, "depth")) != NULL){
530 	    char *reason = NULL;
531 	if ((ret = parse_int32(attr, &depth, &reason)) < 0){
532 	    clicon_err(OE_XML, errno, "parse_int32");
533 	    goto done;
534 	}
535 	if (ret == 0){
536 	    if (netconf_bad_attribute(cbret, "application",
537 				      "<bad-attribute>depth</bad-attribute>", "Unrecognized value of depth attribute") < 0)
538 		goto done;
539 	    goto ok;
540 	}
541     }
542     if ((ret = client_get_config_only(h, nsc, yspec, db, xpath, username, -1, cbret)) < 0)
543 	goto done;
544  ok:
545     retval = 0;
546  done:
547     if (xpath)
548 	free(xpath);
549     if (nsc)
550 	xml_nsctx_free(nsc);
551     if (cbx)
552 	cbuf_free(cbx);
553     return retval;
554 }
555 
556 /*! Loads all or part of a specified configuration to target configuration
557  *
558  * @param[in]  h       Clicon handle
559  * @param[in]  xe      Request: <rpc><xn></rpc>
560  * @param[out] cbret   Return xml tree, eg <rpc-reply>..., <rpc-error..
561  * @param[in]  arg     client-entry
562  * @param[in]  regarg  User argument given at rpc_callback_register()
563  * @retval     0       OK
564  * @retval    -1       Error
565  */
566 static int
from_client_edit_config(clicon_handle h,cxobj * xn,cbuf * cbret,void * arg,void * regarg)567 from_client_edit_config(clicon_handle h,
568 			cxobj        *xn,
569 			cbuf         *cbret,
570 			void         *arg,
571 			void         *regarg)
572 {
573     int                 retval = -1;
574     struct client_entry *ce = (struct client_entry *)arg;
575     uint32_t            myid = ce->ce_id;
576     uint32_t            iddb;
577     char               *target;
578     cxobj              *xc;
579     cxobj              *x;
580     enum operation_type operation = OP_MERGE;
581     int                 non_config = 0;
582     yang_stmt          *yspec;
583     cbuf               *cbx = NULL; /* Assist cbuf */
584     int                 ret;
585     char               *username;
586     cxobj              *xret = NULL;
587     char               *attr;
588     int                 autocommit = 0;
589     char               *val = NULL;
590 
591     username = clicon_username_get(h);
592     if ((yspec =  clicon_dbspec_yang(h)) == NULL){
593 	clicon_err(OE_YANG, ENOENT, "No yang spec9");
594 	goto done;
595     }
596     if ((target = netconf_db_find(xn, "target")) == NULL){
597 	if (netconf_missing_element(cbret, "protocol", "target", NULL) < 0)
598 	    goto done;
599 	goto ok;
600     }
601     if ((cbx = cbuf_new()) == NULL){
602 	clicon_err(OE_XML, errno, "cbuf_new");
603 	goto done;
604     }
605     if (xmldb_validate_db(target) < 0){
606 	cprintf(cbx, "No such database: %s", target);
607 	if (netconf_invalid_value(cbret, "protocol", cbuf_get(cbx))< 0)
608 	    goto done;
609 	goto ok;
610     }
611     /* Check if target locked by other client */
612     iddb = xmldb_islocked(h, target);
613     if (iddb && myid != iddb){
614 	cprintf(cbx, "<session-id>%u</session-id>", iddb);
615 	if (netconf_lock_denied(cbret, cbuf_get(cbx), "Operation failed, lock is already held") < 0)
616 	    goto done;
617 	goto ok;
618     }
619     if ((x = xpath_first(xn, NULL, "default-operation")) != NULL){
620 	if (xml_operation(xml_body(x), &operation) < 0){
621 	    if (netconf_invalid_value(cbret, "protocol", "Wrong operation")< 0)
622 		goto done;
623 	    goto ok;
624 	}
625     }
626     if ((xc = xpath_first(xn, NULL, "config")) == NULL){
627 	if (netconf_missing_element(cbret, "protocol", "config", NULL) < 0)
628 	    goto done;
629 	goto ok;
630     }
631     /* <config> yang spec may be set to anyxml by ingress yang check,...*/
632     if (xml_spec(xc) != NULL)
633 	xml_spec_set(xc, NULL);
634     /* Populate XML with Yang spec (why not do this in parser?)
635      */
636     if ((ret = xml_bind_yang(xc, YB_MODULE, yspec, &xret)) < 0)
637 	goto done;
638     if (ret == 0){
639 	if (clicon_xml2cbuf(cbret, xret, 0, 0, -1) < 0)
640 	    goto done;
641 	goto ok;
642     }
643     /* (Mark all nodes that are not configure data and) set return */
644     if ((ret = xml_non_config_data(xc, &xret)) < 0)
645 	goto done;
646     if (ret == 0){
647 	if (clicon_xml2cbuf(cbret, xret, 0, 0, -1) < 0)
648 	    goto done;
649 	goto ok;
650     }
651     if (non_config){
652 	if (netconf_invalid_value(cbret, "protocol", "State data not allowed")< 0)
653 	    goto done;
654 	goto ok;
655     }
656     /* xmldb_put (difflist handling) requires list keys */
657     if ((ret = xml_yang_validate_list_key_only(xc, &xret)) < 0)
658 	goto done;
659     if (ret == 0){
660 	if (clicon_xml2cbuf(cbret, xret, 0, 0, -1) < 0)
661 	    goto done;
662 	goto ok;
663     }
664     /* Cant do this earlier since we dont have a yang spec to
665      * the upper part of the tree, until we get the "config" tree.
666      */
667     if (xml_sort_recurse(xc) < 0)
668 	goto done;
669     if ((ret = xmldb_put(h, target, operation, xc, username, cbret)) < 0){
670 	clicon_debug(1, "%s ERROR PUT", __FUNCTION__);
671 	if (netconf_operation_failed(cbret, "protocol", clicon_err_reason)< 0)
672 	    goto done;
673 	goto ok;
674     }
675     if (ret == 0)
676 	goto ok;
677     xmldb_modified_set(h, target, 1); /* mark as dirty */
678     /* Clixon extension: autocommit */
679     if ((attr = xml_find_value(xn, "autocommit")) != NULL &&
680 	strcmp(attr,"true")==0)
681 	autocommit = 1;
682     /* If autocommit option is set or requested by client */
683     if (clicon_autocommit(h) || autocommit) {
684 	if ((ret = candidate_commit(h, "candidate", cbret)) < 0){ /* Assume validation fail, nofatal */
685 	    if (netconf_operation_failed(cbret, "application", clicon_err_reason)< 0)
686 		goto done;
687 	    xmldb_copy(h, "running", "candidate");
688 	    goto ok;
689 	}
690 	if (ret == 0){ /* discard */
691 	    if (xmldb_copy(h, "running", "candidate") < 0){
692 		if (netconf_operation_failed(cbret, "application", clicon_err_reason)< 0)
693 		    goto done;
694 		goto ok;
695 	    }
696 	    goto ok;
697 	}
698     }
699     /* Clixon extension: copy */
700     if ((attr = xml_find_value(xn, "copystartup")) != NULL &&
701 	strcmp(attr,"true") == 0){
702 	if (xmldb_copy(h, "running", "startup") < 0){
703 	    if (netconf_operation_failed(cbret, "application", clicon_err_reason)< 0)
704 		goto done;
705 	    goto ok;
706 	}
707     }
708     assert(cbuf_len(cbret) == 0);
709     cprintf(cbret, "<rpc-reply xmlns=\"%s\"><ok", NETCONF_BASE_NAMESPACE);
710     if (clicon_data_get(h, "objectexisted", &val) == 0)
711 	cprintf(cbret, " objectexisted=\"%s\"", val);
712     cprintf(cbret, "/></rpc-reply>");
713  ok:
714     retval = 0;
715  done:
716     if (xret)
717 	xml_free(xret);
718     if (cbx)
719 	cbuf_free(cbx);
720     clicon_debug(1, "%s done cbret:%s", __FUNCTION__, cbuf_get(cbret));
721     return retval;
722 
723 } /* from_client_edit_config */
724 
725 /*! Create or replace an entire config with another complete config db
726  * @param[in]  h       Clicon handle
727  * @param[in]  xe      Request: <rpc><xn></rpc>
728  * @param[out] cbret   Return xml tree, eg <rpc-reply>..., <rpc-error..
729  * @param[in]  arg     client-entry
730  * @param[in]  regarg  User argument given at rpc_callback_register()
731  * @retval     0       OK
732  * @retval    -1       Error
733  * NACM: If source running and target startup --> only exec permission
734  * else:
735  * - omit data nodes to which the client does not have read access
736  * - access denied if user lacks create/delete/update
737  */
738 static int
from_client_copy_config(clicon_handle h,cxobj * xe,cbuf * cbret,void * arg,void * regarg)739 from_client_copy_config(clicon_handle h,
740 			cxobj        *xe,
741 			cbuf         *cbret,
742 			void         *arg,
743 			void         *regarg)
744 {
745     int      retval = -1;
746     struct client_entry *ce = (struct client_entry *)arg;
747     char    *source;
748     char    *target;
749     uint32_t iddb;
750     uint32_t myid = ce->ce_id;
751     cbuf    *cbx = NULL; /* Assist cbuf */
752 
753     if ((source = netconf_db_find(xe, "source")) == NULL){
754 	if (netconf_missing_element(cbret, "protocol", "source", NULL) < 0)
755 	    goto done;
756 	goto ok;
757     }
758     if ((cbx = cbuf_new()) == NULL){
759 	clicon_err(OE_XML, errno, "cbuf_new");
760 	goto done;
761     }
762     if (xmldb_validate_db(source) < 0){
763 	cprintf(cbx, "No such database: %s", source);
764 	if (netconf_invalid_value(cbret, "protocol", cbuf_get(cbx))< 0)
765 	    goto done;
766 	goto ok;
767     }
768     if ((target = netconf_db_find(xe, "target")) == NULL){
769 	if (netconf_missing_element(cbret, "protocol", "target", NULL) < 0)
770 	    goto done;
771 	goto ok;
772     }
773     if (xmldb_validate_db(target) < 0){
774 	cprintf(cbx, "No such database: %s", target);
775 	if (netconf_invalid_value(cbret, "protocol", cbuf_get(cbx))< 0)
776 	    goto done;
777 	goto ok;
778     }
779     /* Check if target locked by other client */
780     iddb = xmldb_islocked(h, target);
781     if (iddb && myid != iddb){
782 	cprintf(cbx, "<session-id>%u</session-id>", iddb);
783 	if (netconf_lock_denied(cbret, cbuf_get(cbx), "Copy failed, lock is already held") < 0)
784 	    goto done;
785 	goto ok;
786     }
787     if (xmldb_copy(h, source, target) < 0){
788 	if (netconf_operation_failed(cbret, "application", clicon_err_reason)< 0)
789 	    goto done;
790 	goto ok;
791     }
792     xmldb_modified_set(h, target, 1); /* mark as dirty */
793     cprintf(cbret, "<rpc-reply xmlns=\"%s\"><ok/></rpc-reply>", NETCONF_BASE_NAMESPACE);
794  ok:
795     retval = 0;
796  done:
797     if (cbx)
798 	cbuf_free(cbx);
799     return retval;
800 }
801 
802 /*! Delete a configuration datastore.
803  * @param[in]  h       Clicon handle
804  * @param[in]  xe      Request: <rpc><xn></rpc>
805  * @param[out] cbret   Return xml tree, eg <rpc-reply>..., <rpc-error..
806  * @param[in]  arg     client-entry
807  * @param[in]  regarg  User argument given at rpc_callback_register()
808  * @retval     0       OK
809  * @retval    -1       Error
810  */
811 static int
from_client_delete_config(clicon_handle h,cxobj * xe,cbuf * cbret,void * arg,void * regarg)812 from_client_delete_config(clicon_handle h,
813 			  cxobj        *xe,
814 			  cbuf         *cbret,
815 			  void         *arg,
816 			  void         *regarg)
817 {
818     int                  retval = -1;
819     struct client_entry *ce = (struct client_entry *)arg;
820     char                *target;
821     uint32_t             iddb;
822     uint32_t             myid = ce->ce_id;
823     cbuf                *cbx = NULL; /* Assist cbuf */
824 
825     if ((target = netconf_db_find(xe, "target")) == NULL ||
826 	strcmp(target, "running")==0){
827 	if (netconf_missing_element(cbret, "protocol", "target", NULL) < 0)
828 	    goto done;
829 	goto ok;
830     }
831     if ((cbx = cbuf_new()) == NULL){
832 	clicon_err(OE_XML, errno, "cbuf_new");
833 	goto done;
834     }
835     if (xmldb_validate_db(target) < 0){
836 	cprintf(cbx, "No such database: %s", target);
837 	if (netconf_invalid_value(cbret, "protocol", cbuf_get(cbx))< 0)
838 	    goto done;
839 	goto ok;
840     }
841     /* Check if target locked by other client */
842     iddb = xmldb_islocked(h, target);
843     if (iddb && myid != iddb){
844 	cprintf(cbx, "<session-id>%u</session-id>", iddb);
845 	if (netconf_lock_denied(cbret, cbuf_get(cbx), "Operation failed, lock is already held") < 0)
846 	    goto done;
847 	goto ok;
848     }
849     if (xmldb_delete(h, target) < 0){
850 	if (netconf_operation_failed(cbret, "protocol", clicon_err_reason)< 0)
851 	    goto done;
852 	goto ok;
853     }
854     if (xmldb_create(h, target) < 0){
855 	if (netconf_operation_failed(cbret, "protocol", clicon_err_reason)< 0)
856 	    goto done;
857 	goto ok;
858     }
859     xmldb_modified_set(h, target, 1); /* mark as dirty */
860     cprintf(cbret, "<rpc-reply xmlns=\"%s\"><ok/></rpc-reply>", NETCONF_BASE_NAMESPACE);
861  ok:
862     retval = 0;
863   done:
864     if (cbx)
865 	cbuf_free(cbx);
866     return retval;
867 }
868 
869 /*! Lock the configuration system of a device
870  *
871  * @param[in]  h       Clicon handle
872  * @param[in]  xe      Request: <rpc><xn></rpc>
873  * @param[out] cbret   Return xml tree, eg <rpc-reply>..., <rpc-error..
874  * @param[in]  arg     client-entry
875  * @param[in]  regarg  User argument given at rpc_callback_register()
876  * @retval     0       OK
877  * @retval    -1       Error
878  */
879 static int
from_client_lock(clicon_handle h,cxobj * xe,cbuf * cbret,void * arg,void * regarg)880 from_client_lock(clicon_handle h,
881 		 cxobj        *xe,
882 		 cbuf         *cbret,
883 		 void         *arg,
884 		 void         *regarg)
885 {
886     int                  retval = -1;
887     struct client_entry *ce = (struct client_entry *)arg;
888     uint32_t             id = ce->ce_id;
889     uint32_t             iddb;
890     char                *db;
891     cbuf                *cbx = NULL; /* Assist cbuf */
892 
893     if ((db = netconf_db_find(xe, "target")) == NULL){
894 	if (netconf_missing_element(cbret, "protocol", "target", NULL) < 0)
895 	    goto done;
896 	goto ok;
897     }
898     if ((cbx = cbuf_new()) == NULL){
899 	clicon_err(OE_XML, errno, "cbuf_new");
900 	goto done;
901     }
902     if (xmldb_validate_db(db) < 0){
903 	cprintf(cbx, "No such database: %s", db);
904 	if (netconf_invalid_value(cbret, "protocol", cbuf_get(cbx))< 0)
905 	    goto done;
906 	goto ok;
907     }
908     /*
909      * A lock MUST not be granted if either of the following conditions is true:
910      * 1) A lock is already held by any NETCONF session or another entity.
911      */
912     if ((iddb = xmldb_islocked(h, db)) != 0){
913 	cprintf(cbx, "<session-id>%u</session-id>", iddb);
914 	if (netconf_lock_denied(cbret, cbuf_get(cbx), "Operation failed, lock is already held") < 0)
915 	    goto done;
916 	goto ok;
917     }
918     /* 2) The target configuration is <candidate>, it has already been modified, and
919      *    these changes have not been committed or rolled back.
920      */
921     if (strcmp(db, "candidate") == 0 &&
922 	xmldb_modified_get(h, db)){
923 	if (netconf_lock_denied(cbret, "<session-id>0</session-id>",
924 				"Operation failed, candidate has already been modified and the changes have not been committed or rolled back (RFC 6241 7.5)") < 0)
925 	    goto done;
926 	goto ok;
927     }
928     if (xmldb_lock(h, db, id) < 0)
929 	goto done;
930     cprintf(cbret, "<rpc-reply xmlns=\"%s\"><ok/></rpc-reply>", NETCONF_BASE_NAMESPACE);
931  ok:
932     retval = 0;
933  done:
934     if (cbx)
935 	cbuf_free(cbx);
936     return retval;
937 }
938 
939 /*! Release a configuration lock previously obtained with the 'lock' operation
940  *
941  * @param[in]  h       Clicon handle
942  * @param[in]  xe      Request: <rpc><xn></rpc>
943  * @param[out] cbret   Return xml tree, eg <rpc-reply>..., <rpc-error..
944  * @param[in]  arg     client-entry
945  * @param[in]  regarg  User argument given at rpc_callback_register()
946  * @retval     0       OK
947  * @retval    -1       Error
948  */
949 static int
from_client_unlock(clicon_handle h,cxobj * xe,cbuf * cbret,void * arg,void * regarg)950 from_client_unlock(clicon_handle h,
951 		   cxobj        *xe,
952 		   cbuf         *cbret,
953 		   void         *arg,
954 		   void         *regarg)
955 {
956     int                  retval = -1;
957     struct client_entry *ce = (struct client_entry *)arg;
958     uint32_t             id = ce->ce_id;
959     uint32_t             iddb; /* DBs lock, if any */
960     char                *db;
961     cbuf                *cbx = NULL; /* Assist cbuf */
962 
963     if ((db = netconf_db_find(xe, "target")) == NULL){
964 	if (netconf_missing_element(cbret, "protocol", "target", NULL) < 0)
965 	    goto done;
966 	goto ok;
967     }
968     if ((cbx = cbuf_new()) == NULL){
969 	clicon_err(OE_XML, errno, "cbuf_new");
970 	goto done;
971     }
972     if (xmldb_validate_db(db) < 0){
973 	cprintf(cbx, "No such database: %s", db);
974 	if (netconf_invalid_value(cbret, "protocol", cbuf_get(cbx))< 0)
975 	    goto done;
976 	goto ok;
977     }
978     iddb = xmldb_islocked(h, db);
979     /*
980      * An unlock operation will not succeed if any of the following
981      * conditions are true:
982      * 1) the specified lock is not currently active
983      */
984     if (iddb == 0){
985 	cprintf(cbx, "<session-id>0</session-id>");
986 	if (netconf_lock_denied(cbret, cbuf_get(cbx), "Unlock failed, lock is not currently active") < 0)
987 	    goto done;
988 	goto ok;
989     }
990     /* 2) the session issuing the <unlock> operation is not the same
991      *    session that obtained the lock
992      */
993     else if (iddb != id){
994 	cprintf(cbx, "<session-id>%u</session-id>", iddb);
995 	if (netconf_lock_denied(cbret, cbuf_get(cbx), "Unlock failed, lock held by other session") < 0)
996 	    goto done;
997 	goto ok;
998     }
999     else{
1000 	xmldb_unlock(h, db);
1001 	if (cprintf(cbret, "<rpc-reply xmlns=\"%s\"><ok/></rpc-reply>", NETCONF_BASE_NAMESPACE) < 0)
1002 	    goto done;
1003     }
1004  ok:
1005     retval = 0;
1006  done:
1007     if (cbx)
1008 	cbuf_free(cbx);
1009     return retval;
1010 }
1011 
1012 /*! Retrieve running configuration and device state information.
1013  *
1014  * @param[in]  h       Clicon handle
1015  * @param[in]  xe      Request: <rpc><xn></rpc>
1016  * @param[out] cbret   Return xml tree, eg <rpc-reply>..., <rpc-error..
1017  * @param[in]  arg     client-entry
1018  * @param[in]  regarg  User argument given at rpc_callback_register()
1019  * @retval     0       OK
1020  * @retval    -1       Error
1021  *
1022  * @see from_client_get_config
1023  */
1024 static int
from_client_get(clicon_handle h,cxobj * xe,cbuf * cbret,void * arg,void * regarg)1025 from_client_get(clicon_handle h,
1026 		cxobj        *xe,
1027 		cbuf         *cbret,
1028 		void         *arg,
1029 		void         *regarg)
1030 {
1031     int             retval = -1;
1032     cxobj          *xfilter;
1033     char           *xpath = NULL;
1034     cxobj          *xret = NULL;
1035     cxobj         **xvec = NULL;
1036     size_t          xlen;
1037     cxobj          *xnacm = NULL;
1038     char           *username;
1039     cvec           *nsc = NULL; /* Create a netconf namespace context from filter */
1040     char           *attr;
1041     netconf_content content = CONTENT_ALL;
1042     int32_t         depth = -1; /* Nr of levels to print, -1 is all, 0 is none */
1043     yang_stmt      *yspec;
1044     int             i;
1045     cxobj          *xerr = NULL;
1046     int             ret;
1047 
1048     clicon_debug(1, "%s", __FUNCTION__);
1049     username = clicon_username_get(h);
1050     if ((yspec =  clicon_dbspec_yang(h)) == NULL){
1051 	clicon_err(OE_YANG, ENOENT, "No yang spec9");
1052 	goto done;
1053     }
1054     if ((xfilter = xml_find(xe, "filter")) != NULL){
1055 	char *xpath0;
1056 	cvec *nsc1 = NULL;
1057 	if ((xpath0 = xml_find_value(xfilter, "select"))==NULL)
1058 	    xpath0 = "/";
1059 	/* Create namespace context for xpath from <filter>
1060 	 *  The set of namespace declarations are those in scope on the
1061 	 * <filter> element.
1062 	 */
1063 	else
1064 	    if (xml_nsctx_node(xfilter, &nsc) < 0)
1065 		goto done;
1066 	if (xpath2canonical(xpath0, nsc, yspec, &xpath, &nsc1) < 0)
1067 	    goto done;
1068 	if (nsc)
1069 	    xml_nsctx_free(nsc);
1070 	nsc = nsc1;
1071     }
1072     /* Clixon extensions: content */
1073     if ((attr = xml_find_value(xe, "content")) != NULL)
1074 	content = netconf_content_str2int(attr);
1075     /* Clixon extensions: depth */
1076     if ((attr = xml_find_value(xe, "depth")) != NULL){
1077 	char *reason = NULL;
1078 	if ((ret = parse_int32(attr, &depth, &reason)) < 0){
1079 	    clicon_err(OE_XML, errno, "parse_int32");
1080 	    goto done;
1081 	}
1082 	if (ret == 0){
1083 	    if (netconf_bad_attribute(cbret, "application",
1084 				      "<bad-attribute>depth</bad-attribute>", "Unrecognized value of depth attribute") < 0)
1085 		goto done;
1086 	    goto ok;
1087 	}
1088     }
1089     if (content == CONTENT_CONFIG){ /* config only, no state */
1090 	if (client_get_config_only(h, nsc, yspec, "running", xpath, username, depth, cbret) < 0)
1091 	    goto done;
1092 	goto ok;
1093     }
1094     /* If not only-state, then read running config
1095      * Note xret can be pruned by nacm below and change name and
1096      * merged with state data, so zero-copy cant be used
1097      * Also, must use external namespace context here due to <filter> stmt
1098      */
1099     if (clicon_option_bool(h, "CLICON_VALIDATE_STATE_XML")){
1100 	if (xmldb_get0(h, "running", YB_MODULE, nsc, NULL, 1, &xret, NULL) < 0) {
1101 	    if (netconf_operation_failed(cbret, "application", "read registry")< 0)
1102 		goto done;
1103 	    goto ok;
1104 	}
1105     }
1106     else{
1107 	if (xmldb_get0(h, "running", YB_MODULE, nsc, xpath, 1, &xret, NULL) < 0) {
1108 	    if (netconf_operation_failed(cbret, "application", "read registry")< 0)
1109 		goto done;
1110 	    goto ok;
1111 	}
1112     }
1113     /* If not only config,
1114      * get state data from plugins as defined by plugin_statedata(), if any
1115      */
1116     clicon_err_reset();
1117     if ((ret = client_statedata(h, xpath?xpath:"/", nsc, content, &xret)) < 0)
1118 	goto done;
1119     if (ret == 0){ /* Error from callback (error in xret) */
1120 	if (clicon_xml2cbuf(cbret, xret, 0, 0, -1) < 0)
1121 	    goto done;
1122 	goto ok;
1123     }
1124     if (clicon_option_bool(h, "CLICON_VALIDATE_STATE_XML")){
1125 	/* Check XML  by validating it. return internal error with error cause
1126 	 * Primarily intended for user-supplied state-data.
1127 	 * The whole config tree must be present in case the state data references config data
1128 	 */
1129 	if ((ret = xml_yang_validate_all_top(h, xret, &xerr)) < 0)
1130 	    goto done;
1131 	if (ret > 0 &&
1132 	    (ret = xml_yang_validate_add(h, xret, &xerr)) < 0)
1133 	    goto done;
1134 	if (ret == 0){
1135 	    if (clicon_debug_get())
1136 		clicon_log_xml(LOG_DEBUG, xret, "VALIDATE_STATE");
1137 	    if (clixon_netconf_internal_error(xerr,
1138 					      ". Internal error, state callback returned invalid XML",
1139 					      NULL) < 0)
1140 		goto done;
1141 	    if (clicon_xml2cbuf(cbret, xerr, 0, 0, -1) < 0)
1142 		goto done;
1143 	    goto ok;
1144 	}
1145     } /* CLICON_VALIDATE_STATE_XML */
1146 
1147     if (content == CONTENT_NONCONFIG){ /* state only, all config should be removed now */
1148 	/* Keep state data only, remove everything that is not config. Note that state data
1149 	 * may be a sub-part in a config tree, we need to traverse to find all
1150 	 */
1151 	if (xml_non_config_data(xret, NULL) < 0)
1152 	    goto done;
1153 	if (xml_tree_prune_flagged_sub(xret, XML_FLAG_MARK, 1, NULL) < 0)
1154 	    goto done;
1155 	if (xml_apply(xret, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0)
1156 	    goto done;
1157     }
1158     /* Code complex to filter out anything that is outside of xpath
1159      * Actually this is a safety catch, should really be done in plugins
1160      * and modules_state functions.
1161      */
1162     if (xpath_vec(xret, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
1163 	goto done;
1164     /* If vectors are specified then mark the nodes found and
1165      * then filter out everything else,
1166      * otherwise return complete tree.
1167      */
1168     if (xvec != NULL){
1169 	for (i=0; i<xlen; i++)
1170 	    xml_flag_set(xvec[i], XML_FLAG_MARK);
1171     }
1172     if (xvec){
1173 	free(xvec);
1174 	xvec = NULL;
1175     }
1176 
1177     /* Remove everything that is not marked */
1178     if (!xml_flag(xret, XML_FLAG_MARK))
1179 	if (xml_tree_prune_flagged_sub(xret, XML_FLAG_MARK, 1, NULL) < 0)
1180 	    goto done;
1181     /* reset flag */
1182     if (xml_apply(xret, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0)
1183 	goto done;
1184 
1185     /* Pre-NACM access step */
1186     xnacm = clicon_nacm_cache(h);
1187     if (xnacm != NULL){ /* Do NACM validation */
1188 	if (xpath_vec(xret, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
1189 	    goto done;
1190 	/* NACM datanode/module read validation */
1191 	if (nacm_datanode_read(h, xret, xvec, xlen, username, xnacm) < 0)
1192 	    goto done;
1193     }
1194     cprintf(cbret, "<rpc-reply xmlns=\"%s\">", NETCONF_BASE_NAMESPACE);     /* OK */
1195     if (xret==NULL)
1196 	cprintf(cbret, "<data/>");
1197     else{
1198 	if (xml_name_set(xret, "data") < 0)
1199 	    goto done;
1200 	/* Top level is data, so add 1 to depth if significant */
1201 	if (clicon_xml2cbuf(cbret, xret, 0, 0, depth>0?depth+1:depth) < 0)
1202 	    goto done;
1203     }
1204     cprintf(cbret, "</rpc-reply>");
1205  ok:
1206     retval = 0;
1207  done:
1208     clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
1209     if (xerr)
1210 	xml_free(xerr);
1211     if (xpath)
1212 	free(xpath);
1213     if (xvec)
1214 	free(xvec);
1215     if (nsc)
1216 	xml_nsctx_free(nsc);
1217     if (xret)
1218 	xml_free(xret);
1219     return retval;
1220 }
1221 
1222 /*! Request graceful termination of a NETCONF session.
1223  * @param[in]  h       Clicon handle
1224  * @param[in]  xe      Request: <rpc><xn></rpc>
1225  * @param[out] cbret   Return xml tree, eg <rpc-reply>..., <rpc-error..
1226  * @param[in]  arg     client-entry
1227  * @param[in]  regarg  User argument given at rpc_callback_register()
1228  * @retval     0       OK
1229  * @retval    -1       Error
1230  */
1231 static int
from_client_close_session(clicon_handle h,cxobj * xe,cbuf * cbret,void * arg,void * regarg)1232 from_client_close_session(clicon_handle h,
1233 			 cxobj        *xe,
1234 			 cbuf         *cbret,
1235 			 void         *arg,
1236 			 void         *regarg)
1237 {
1238     struct client_entry *ce = (struct client_entry *)arg;
1239     uint32_t             id = ce->ce_id;
1240 
1241     xmldb_unlock_all(h, id);
1242     stream_ss_delete_all(h, ce_event_cb, (void*)ce);
1243     cprintf(cbret, "<rpc-reply xmlns=\"%s\"><ok/></rpc-reply>", NETCONF_BASE_NAMESPACE);
1244     return 0;
1245 }
1246 
1247 /*! Internal message:  Force the termination of a NETCONF session.
1248  *
1249  * @param[in]  h       Clicon handle
1250  * @param[in]  xe      Request: <rpc><xn></rpc>
1251  * @param[out] cbret   Return xml tree, eg <rpc-reply>..., <rpc-error..
1252  * @param[in]  arg     client-entry
1253  * @param[in]  regarg  User argument given at rpc_callback_register()
1254  * @retval     0       OK
1255  * @retval    -1       Error
1256  */
1257 static int
from_client_kill_session(clicon_handle h,cxobj * xe,cbuf * cbret,void * arg,void * regarg)1258 from_client_kill_session(clicon_handle h,
1259 			 cxobj        *xe,
1260 			 cbuf         *cbret,
1261 			 void         *arg,
1262 			 void         *regarg)
1263 {
1264     int                  retval = -1;
1265     uint32_t             id; /* session id */
1266     char                *str;
1267     struct client_entry *ce;
1268     char                *db = "running"; /* XXX */
1269     cxobj               *x;
1270     int                  ret;
1271     char                *reason = NULL;
1272 
1273     if ((x = xml_find(xe, "session-id")) == NULL ||
1274 	(str = xml_find_value(x, "body")) == NULL){
1275 	if (netconf_missing_element(cbret, "protocol", "session-id", NULL) < 0)
1276 	    goto done;
1277 	goto ok;
1278     }
1279     if ((ret = parse_uint32(str, &id, &reason)) < 0){
1280 	clicon_err(OE_XML, errno, "parse_uint32");
1281 	goto done;
1282     }
1283     if (ret == 0){
1284 	if (netconf_bad_element(cbret, "protocol", "session-id", reason) < 0)
1285 	    goto done;
1286 	goto done;
1287     }
1288     /* may or may not be in active client list, probably not */
1289     if ((ce = ce_find_byid(backend_client_list(h), id)) != NULL){
1290 	xmldb_unlock_all(h, id);
1291 	backend_client_rm(h, ce);
1292     }
1293     if (xmldb_islocked(h, db) == id)
1294 	xmldb_unlock(h, db);
1295     cprintf(cbret, "<rpc-reply xmlns=\"%s\"><ok/></rpc-reply>", NETCONF_BASE_NAMESPACE);
1296  ok:
1297     retval = 0;
1298  done:
1299     if (reason)
1300 	free(reason);
1301     return retval;
1302 }
1303 
1304 /*! Create a notification subscription
1305  * @param[in]  h       Clicon handle
1306  * @param[in]  xe      Request: <rpc><xn></rpc>
1307  * @param[out] cbret   Return xml tree, eg <rpc-reply>..., <rpc-error..
1308  * @param[in]  arg     client-entry
1309  * @param[in]  regarg  User argument given at rpc_callback_register()
1310  * @retval     0       OK
1311  * @retval    -1       Error
1312  * @see RFC5277 2.1
1313  * @example:
1314  *    <create-subscription>
1315  *       <stream>RESULT</stream> # If not present, events in the default NETCONF stream will be sent.
1316  *       <filter type="xpath" select="XPATH-EXPR"/>
1317  *       <startTime></startTime>
1318  *       <stopTime></stopTime>
1319  *    </create-subscription>
1320  */
1321 static int
from_client_create_subscription(clicon_handle h,cxobj * xe,cbuf * cbret,void * arg,void * regarg)1322 from_client_create_subscription(clicon_handle h,
1323 				cxobj        *xe,
1324 				cbuf         *cbret,
1325 				void         *arg,
1326 				void         *regarg)
1327 {
1328     int                  retval = -1;
1329     struct client_entry *ce = (struct client_entry *)arg;
1330     char                *stream = "NETCONF";
1331     cxobj               *x; /* Generic xml tree */
1332     cxobj               *xfilter; /* Filter xml tree */
1333     char                *ftype;
1334     char                *starttime = NULL;
1335     char                *stoptime = NULL;
1336     char                *selector = NULL;
1337     struct timeval       start;
1338     struct timeval       stop;
1339     cvec                *nsc = NULL;
1340 
1341     if ((nsc = xml_nsctx_init(NULL, EVENT_RFC5277_NAMESPACE)) == NULL)
1342 	goto done;
1343     if ((x = xpath_first(xe, nsc, "//stream")) != NULL)
1344 	stream = xml_find_value(x, "body");
1345     if ((x = xpath_first(xe, nsc, "//stopTime")) != NULL){
1346 	if ((stoptime = xml_find_value(x, "body")) != NULL &&
1347 	    str2time(stoptime, &stop) < 0){
1348 	    if (netconf_bad_element(cbret, "application", "stopTime", "Expected timestamp") < 0)
1349 		goto done;
1350 	    goto ok;
1351 	}
1352     }
1353     if ((x = xpath_first(xe, nsc, "//startTime")) != NULL){
1354 	if ((starttime = xml_find_value(x, "body")) != NULL &&
1355 	    str2time(starttime, &start) < 0){
1356 	    if (netconf_bad_element(cbret, "application", "startTime", "Expected timestamp") < 0)
1357 		goto done;
1358 	    goto ok;
1359 	}
1360     }
1361     if ((xfilter = xpath_first(xe, nsc, "//filter")) != NULL){
1362 	if ((ftype = xml_find_value(xfilter, "type")) != NULL){
1363 	    /* Only accept xpath as filter type */
1364 	    if (strcmp(ftype, "xpath") != 0){
1365 		if (netconf_operation_failed(cbret, "application", "Only xpath filter type supported")< 0)
1366 		    goto done;
1367 		goto ok;
1368 	    }
1369 	    if ((selector = xml_find_value(xfilter, "select")) == NULL)
1370 		goto done;
1371 	}
1372     }
1373     if ((stream_find(h, stream)) == NULL){
1374 	if (netconf_invalid_value(cbret, "application", "No such stream") < 0)
1375 	    goto done;
1376 	goto ok;
1377     }
1378     /* Add subscriber to stream - to make notifications for this client */
1379     if (stream_ss_add(h, stream, selector,
1380 		      starttime?&start:NULL, stoptime?&stop:NULL,
1381 		      ce_event_cb, (void*)ce) < 0)
1382 	goto done;
1383     /* Replay of this stream to specific subscription according to start and
1384      * stop (if present).
1385      * RFC 5277: If <startTime> is not present, this is not a replay
1386      * subscription.
1387      * Schedule the replay to occur right after this RPC completes, eg "now"
1388      */
1389     if (starttime){
1390 	if (stream_replay_trigger(h, stream, ce_event_cb, (void*)ce) < 0)
1391 	    goto done;
1392     }
1393     cprintf(cbret, "<rpc-reply xmlns=\"%s\"><ok/></rpc-reply>", NETCONF_BASE_NAMESPACE);
1394  ok:
1395     retval = 0;
1396   done:
1397     if (nsc)
1398 	xml_nsctx_free(nsc);
1399     return retval;
1400 }
1401 
1402 /*! Set debug level.
1403  * @param[in]  h       Clicon handle
1404  * @param[in]  xe      Request: <rpc><xn></rpc>
1405  * @param[out] cbret   Return xml tree, eg <rpc-reply>..., <rpc-error..
1406  * @param[in]  arg     client-entry
1407  * @param[in]  regarg  User argument given at rpc_callback_register()
1408  * @retval     0       OK
1409  * @retval    -1       Error
1410  */
1411 static int
from_client_debug(clicon_handle h,cxobj * xe,cbuf * cbret,void * arg,void * regarg)1412 from_client_debug(clicon_handle h,
1413 		  cxobj        *xe,
1414 		  cbuf         *cbret,
1415 		  void         *arg,
1416 		  void         *regarg)
1417 {
1418     int      retval = -1;
1419     uint32_t level;
1420     char    *valstr;
1421 
1422     if ((valstr = xml_find_body(xe, "level")) == NULL){
1423 	if (netconf_missing_element(cbret, "application", "level", NULL) < 0)
1424 	    goto done;
1425 	goto ok;
1426     }
1427     level = atoi(valstr);
1428 
1429     clicon_debug_init(level, NULL); /* 0: dont debug, 1:debug */
1430     setlogmask(LOG_UPTO(level?LOG_DEBUG:LOG_INFO)); /* for syslog */
1431     clicon_log(LOG_NOTICE, "%s debug:%d", __FUNCTION__, clicon_debug_get());
1432     cprintf(cbret, "<rpc-reply xmlns=\"%s\"><ok/></rpc-reply>", NETCONF_BASE_NAMESPACE);
1433  ok:
1434     retval = 0;
1435  done:
1436     return retval;
1437 }
1438 
1439 /*! Check liveness of backend daemon,  just send a reply
1440  * @param[in]  h       Clicon handle
1441  * @param[in]  xe      Request: <rpc><xn></rpc>
1442  * @param[out] cbret   Return xml tree, eg <rpc-reply>..., <rpc-error..
1443  * @param[in]  arg     client-entry
1444  * @param[in]  regarg  User argument given at rpc_callback_register()
1445  * @retval     0       OK
1446  * @retval    -1       Error
1447  */
1448 static int
from_client_ping(clicon_handle h,cxobj * xe,cbuf * cbret,void * arg,void * regarg)1449 from_client_ping(clicon_handle h,
1450 		 cxobj        *xe,
1451 		 cbuf         *cbret,
1452 		 void         *arg,
1453 		 void         *regarg)
1454 {
1455     cprintf(cbret, "<rpc-reply xmlns=\"%s\"><ok/></rpc-reply>", NETCONF_BASE_NAMESPACE);
1456     return 0;
1457 }
1458 
1459 /*! Check liveness of backend daemon,  just send a reply
1460  * @param[in]  h       Clicon handle
1461  * @param[in]  xe      Request: <rpc><xn></rpc>
1462  * @param[out] cbret   Return xml tree, eg <rpc-reply>..., <rpc-error..
1463  * @param[in]  arg     client-entry
1464  * @param[in]  regarg  User argument given at rpc_callback_register()
1465  * @retval     0       OK
1466  * @retval    -1       Error
1467  */
1468 static int
from_client_stats(clicon_handle h,cxobj * xe,cbuf * cbret,void * arg,void * regarg)1469 from_client_stats(clicon_handle h,
1470 		  cxobj        *xe,
1471 		  cbuf         *cbret,
1472 		  void         *arg,
1473 		  void         *regarg)
1474 {
1475     int      retval = -1;
1476     uint64_t nr;
1477 
1478     cprintf(cbret, "<rpc-reply xmlns=\"%s\">", NETCONF_BASE_NAMESPACE);
1479     nr=0;
1480     xml_stats_global(&nr);
1481     cprintf(cbret, "<global><xmlnr>%" PRIu64 "</xmlnr></global>", nr);
1482     if (clixon_stats_get_db(h, "running", cbret) < 0)
1483 	goto done;
1484     if (clixon_stats_get_db(h, "candidate", cbret) < 0)
1485 	goto done;
1486     if (clixon_stats_get_db(h, "startup", cbret) < 0)
1487 	goto done;
1488     cprintf(cbret, "</rpc-reply>");
1489     retval = 0;
1490  done:
1491     return retval;
1492 }
1493 
1494 /*! Request restart of specific plugins
1495  * @param[in]  h       Clicon handle
1496  * @param[in]  xe      Request: <rpc><xn></rpc>
1497  * @param[out] cbret   Return xml tree, eg <rpc-reply>..., <rpc-error..
1498  * @param[in]  arg     client-entry
1499  * @param[in]  regarg  User argument given at rpc_callback_register()
1500  * @retval     0       OK
1501  * @retval    -1       Error
1502  */
1503 static int
from_client_restart_plugin(clicon_handle h,cxobj * xe,cbuf * cbret,void * arg,void * regarg)1504 from_client_restart_plugin(clicon_handle h,
1505 			   cxobj        *xe,
1506 			   cbuf         *cbret,
1507 			   void         *arg,
1508 			   void         *regarg)
1509 {
1510     int            retval = -1;
1511     char          *name;
1512     cxobj        **vec = NULL;
1513     size_t         veclen;
1514     int            i;
1515     clixon_plugin *cp;
1516     int            ret;
1517 
1518     if (xpath_vec(xe, NULL, "plugin", &vec, &veclen) < 0)
1519 	goto done;
1520     for (i=0; i<veclen; i++){
1521 	name = xml_body(vec[i]);
1522 	if ((cp = clixon_plugin_find(h, name)) == NULL){
1523 	    if (netconf_bad_element(cbret, "application", "plugin", "No such plugin") < 0)
1524 		goto done;
1525 	    goto ok;
1526 	}
1527 	if ((ret = from_client_restart_one(h, cp, cbret)) < 0)
1528 	    goto done;
1529 	if (ret == 0)
1530 	    goto ok; /* cbret set */
1531    }
1532     cprintf(cbret, "<rpc-reply xmlns=\"%s\"><ok/></rpc-reply>", NETCONF_BASE_NAMESPACE);
1533  ok:
1534     retval = 0;
1535  done:
1536     if (vec)
1537 	free(vec);
1538     return retval;
1539 }
1540 
1541 /*!
1542  * @retval     0       OK
1543  * @retval    -1       Error
1544  */
1545 static int
from_client_hello(clicon_handle h,cxobj * x,struct client_entry * ce,cbuf * cbret)1546 from_client_hello(clicon_handle       h,
1547 		  cxobj               *x,
1548 		  struct client_entry *ce,
1549 		  cbuf                *cbret)
1550 
1551 {
1552     int      retval = -1;
1553     uint32_t id;
1554 
1555     if (clicon_session_id_get(h, &id) < 0){
1556 	clicon_err(OE_NETCONF, ENOENT, "session_id not set");
1557 	goto done;
1558     }
1559     id++;
1560     clicon_session_id_set(h, id);
1561     cprintf(cbret, "<hello xmlns=\"%s\"><session-id>%u</session-id></hello>",
1562 	    NETCONF_BASE_NAMESPACE, id);
1563     retval = 0;
1564  done:
1565     return retval;
1566 }
1567 
1568 /*! An internal clicon message has arrived from a client. Receive and dispatch.
1569  * @param[in]   h    Clicon handle
1570  * @param[in]   s    Socket where message arrived. read from this.
1571  * @param[in]   arg  Client entry (from).
1572  * @retval      0    OK
1573  * @retval      -1   Error Terminates backend and is never called). Instead errors are
1574  *                   propagated back to client.
1575  */
1576 static int
from_client_msg(clicon_handle h,struct client_entry * ce,struct clicon_msg * msg)1577 from_client_msg(clicon_handle        h,
1578 		struct client_entry *ce,
1579 		struct clicon_msg   *msg)
1580 {
1581     int                  retval = -1;
1582     cxobj               *xt = NULL;
1583     cxobj               *x;
1584     cxobj               *xe;
1585     char                *rpc = NULL;
1586     char                *module = NULL;
1587     cbuf                *cbret = NULL; /* return message */
1588     int                  ret;
1589     char                *username;
1590     yang_stmt           *yspec;
1591     yang_stmt           *ye;
1592     yang_stmt           *ymod;
1593     cxobj               *xnacm = NULL;
1594     cxobj               *xret = NULL;
1595     uint32_t             id;
1596     enum nacm_credentials_t creds;
1597     char                *rpcname;
1598     char                *rpcprefix;
1599     char                *namespace;
1600 
1601     clicon_debug(1, "%s", __FUNCTION__);
1602     yspec = clicon_dbspec_yang(h);
1603     /* Return netconf message. Should be filled in by the dispatch(sub) functions
1604      * as wither rpc-error or by positive response.
1605      */
1606     if ((cbret = cbuf_new()) == NULL){
1607 	clicon_err(OE_XML, errno, "cbuf_new");
1608 	goto done;
1609     }
1610     /* Decode msg from client -> xml top (ct) and session id */
1611     if ((ret = clicon_msg_decode(msg, yspec, &id, &xt, &xret)) < 0){
1612 	if (netconf_malformed_message(cbret, "XML parse error") < 0)
1613 	    goto done;
1614 	goto reply;
1615     }
1616     if (ret == 0){
1617 	if (clicon_xml2cbuf(cbret, xret, 0, 0, -1) < 0)
1618 	    goto done;
1619 	goto reply;
1620     }
1621     /* Check for empty frame (no mesaages), return empty message, not clear from RFC what to do */
1622     if (xml_child_nr_type(xt, CX_ELMNT) == 0){
1623 	if (netconf_malformed_message(cbret, "Empty message in netconf rpc frame")< 0)
1624 	    goto done;
1625 	goto reply;
1626     }
1627     /* Check for multi-messages in frame */
1628     if (xml_child_nr_type(xt, CX_ELMNT) != 1){
1629 	if (netconf_malformed_message(cbret, "More than one message in netconf rpc frame")< 0)
1630 	    goto done;
1631 	goto reply;
1632     }
1633     if ((x = xml_child_i_type(xt, 0, CX_ELMNT)) == NULL){ /* Shouldnt happen */
1634 	clicon_err(OE_XML, EFAULT, "No xml req (shouldnt happen)");
1635 	goto done;
1636     }
1637     rpcname = xml_name(x);
1638     rpcprefix = xml_prefix(x);
1639     if (0) { /* XXX notyet (4.8) restconf seems not to produce right namespace */
1640 	if (xml2ns(x, rpcprefix, &namespace) < 0)
1641 	    goto done;
1642 	/* Only accept resolved NETCONF base namespace */
1643 	if (namespace == NULL || strcmp(namespace, NETCONF_BASE_NAMESPACE) != 0){
1644 	    if (netconf_unknown_namespace(cbret, "protocol", rpcprefix, "No appropriate namespace associated with prefix")< 0)
1645 		goto done;
1646 	    goto reply;
1647 	}
1648     }
1649     if (strcmp(rpcname, "rpc") == 0){
1650 	; /* continue  below */
1651     }
1652     else if (strcmp(rpcname, "hello") == 0){
1653 	if ((ret = from_client_hello(h, x, ce, cbret)) <0)
1654 	    goto done;
1655 	goto reply;
1656     }
1657     else{
1658 	if (netconf_unknown_element(cbret, "protocol", rpcname, "Unrecognized netconf operation")< 0)
1659 	    goto done;
1660 	goto reply;
1661     }
1662     ce->ce_id = id;
1663     if ((ret = xml_yang_validate_rpc(h, x, &xret)) < 0)
1664 	goto done;
1665     if (ret == 0){
1666 	if (clicon_xml2cbuf(cbret, xret, 0, 0, -1) < 0)
1667 	    goto done;
1668 	goto reply;
1669     }
1670     xe = NULL;
1671     username = xml_find_value(x, "username");
1672     /* May be used by callbacks, etc */
1673     clicon_username_set(h, username);
1674     while ((xe = xml_child_each(x, xe, CX_ELMNT)) != NULL) {
1675 	rpc = xml_name(xe);
1676 	if ((ye = xml_spec(xe)) == NULL){
1677 	    if (netconf_operation_not_supported(cbret, "protocol", rpc) < 0)
1678 		goto done;
1679 	    goto reply;
1680 	}
1681 	if ((ymod = ys_module(ye)) == NULL){
1682 	    clicon_err(OE_XML, ENOENT, "rpc yang does not have module");
1683 	    goto done;
1684 	}
1685 	module = yang_argument_get(ymod);
1686 	clicon_debug(1, "%s module:%s rpc:%s", __FUNCTION__, module, rpc);
1687 	/* Pre-NACM access step */
1688 	xnacm = NULL;
1689 
1690 	/* NACM intial pre- access control enforcements. Retval:
1691 	 * 0: Use NACM validation and xnacm is set.
1692 	 * 1: Permit, skip NACM
1693 	 * Therefore, xnacm=NULL means no NACM checks needed.
1694 	 */
1695 	if ((ret = nacm_access_pre(h, ce->ce_username, username, &xnacm)) < 0)
1696 	    goto done;
1697 	/* Cache XML NACM tree here. Use with caution, only valid on from_client_msg stack
1698 	 */
1699 	if (clicon_nacm_cache_set(h, xnacm) < 0)
1700 	    goto done;
1701 	if (ret == 0){ /* Do NACM RPC validation */
1702 	    creds = clicon_nacm_credentials(h);
1703 	    if ((ret = verify_nacm_user(creds, ce->ce_username, username, cbret)) < 0)
1704 		goto done;
1705 	    if (ret == 0) /* credentials fail */
1706 		goto reply;
1707 	    /* NACM rpc operation exec validation */
1708 	    if ((ret = nacm_rpc(rpc, module, username, xnacm, cbret)) < 0)
1709 		goto done;
1710 	    if (ret == 0) /* Not permitted and cbret set */
1711 		goto reply;
1712 	}
1713 	clicon_err_reset();
1714 	if ((ret = rpc_callback_call(h, xe, cbret, ce)) < 0){
1715 	    if (netconf_operation_failed(cbret, "application", clicon_err_reason)< 0)
1716 		goto done;
1717 	    clicon_log(LOG_NOTICE, "%s Error in rpc_callback_call:%s", __FUNCTION__, xml_name(xe));
1718 	    goto reply; /* Dont quit here on user callbacks */
1719 	}
1720 	if (ret == 0){ /* not handled by callback */
1721 	    if (netconf_operation_not_supported(cbret, "application", "RPC operation not supported")< 0)
1722 		goto done;
1723 	    goto reply;
1724 	}
1725 	if (xnacm){
1726 	    xml_free(xnacm);
1727 	    xnacm = NULL;
1728 	    if (clicon_nacm_cache_set(h, NULL) < 0)
1729 		goto done;
1730 	}
1731     } /* while */
1732  reply:
1733     if (cbuf_len(cbret) == 0)
1734 	if (netconf_operation_failed(cbret, "application", clicon_errno?clicon_err_reason:"unknown")< 0)
1735 	    goto done;
1736     clicon_debug(1, "%s cbret:%s", __FUNCTION__, cbuf_get(cbret));
1737     /* XXX problem here is that cbret has not been parsed so may contain
1738        parse errors */
1739     if (send_msg_reply(ce->ce_s, cbuf_get(cbret), cbuf_len(cbret)+1) < 0){
1740 	switch (errno){
1741 	case EPIPE:
1742 	    /* man (2) write:
1743 	     * EPIPE  fd is connected to a pipe or socket whose reading end is
1744 	     * closed.  When this happens the writing process will also receive
1745 	     * a SIGPIPE signal.
1746 	     * In Clixon this means a client, eg restconf, netconf or cli closes
1747 	     * the (UNIX domain) socket.
1748 	     */
1749 	case ECONNRESET:
1750 	    clicon_log(LOG_WARNING, "client rpc reset");
1751 	    break;
1752 	default:
1753 	    goto done;
1754 	}
1755     }
1756     // ok:
1757     retval = 0;
1758   done:
1759     clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
1760     if (xnacm){
1761 	xml_free(xnacm);
1762 	if (clicon_nacm_cache_set(h, NULL) < 0)
1763 	    goto done;
1764     }
1765     if (xret)
1766 	xml_free(xret);
1767     if (xt)
1768 	xml_free(xt);
1769     if (cbret)
1770 	cbuf_free(cbret);
1771     /* Sanity: log if clicon_err() is not called ! */
1772     if (retval < 0 && clicon_errno < 0)
1773 	clicon_log(LOG_NOTICE, "%s: Internal error: No clicon_err call on RPC error (message: %s)",
1774 		   __FUNCTION__, rpc?rpc:"");
1775     //    clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
1776     return retval;// -1 here terminates backend
1777 }
1778 
1779 /*! An internal clicon message has arrived from a client. Receive and dispatch.
1780  * @param[in]   s    Socket where message arrived. read from this.
1781  * @param[in]   arg  Client entry (from).
1782  * @retval      0    OK
1783  * @retval      -1   Error Terminates backend and is never called). Instead errors are
1784  *                   propagated back to client.
1785  */
1786 int
from_client(int s,void * arg)1787 from_client(int   s,
1788 	    void* arg)
1789 {
1790     int                  retval = -1;
1791     struct clicon_msg   *msg = NULL;
1792     struct client_entry *ce = (struct client_entry *)arg;
1793     clicon_handle        h = ce->ce_handle;
1794     int                  eof;
1795 
1796     clicon_debug(1, "%s", __FUNCTION__);
1797     // assert(s == ce->ce_s);
1798     if (clicon_msg_rcv(ce->ce_s, &msg, &eof) < 0)
1799 	goto done;
1800     if (eof)
1801 	backend_client_rm(h, ce);
1802     else
1803 	if (from_client_msg(h, ce, msg) < 0)
1804 	    goto done;
1805     retval = 0;
1806   done:
1807     clicon_debug(1, "%s retval=%d", __FUNCTION__, retval);
1808     if (msg)
1809 	free(msg);
1810     return retval; /* -1 here terminates backend */
1811 }
1812 
1813 /*! Init backend rpc: Set up standard netconf rpc callbacks
1814  * @param[in]  h     Clicon handle
1815  * @retval       -1       Error (fatal)
1816  * @retval        0       OK
1817  * @see ietf-netconf@2011-06-01.yang
1818  */
1819 int
backend_rpc_init(clicon_handle h)1820 backend_rpc_init(clicon_handle h)
1821 {
1822     int retval = -1;
1823 
1824     /* In backend_client.? RFC 6241 */
1825     if (rpc_callback_register(h, from_client_get_config, NULL,
1826 		      NETCONF_BASE_NAMESPACE, "get-config") < 0)
1827 	goto done;
1828     if (rpc_callback_register(h, from_client_edit_config, NULL,
1829 		      NETCONF_BASE_NAMESPACE, "edit-config") < 0)
1830 	goto done;
1831     if (rpc_callback_register(h, from_client_copy_config, NULL,
1832 		      NETCONF_BASE_NAMESPACE, "copy-config") < 0)
1833 	goto done;
1834     if (rpc_callback_register(h, from_client_delete_config, NULL,
1835 		      NETCONF_BASE_NAMESPACE, "delete-config") < 0)
1836 	goto done;
1837     if (rpc_callback_register(h, from_client_lock, NULL,
1838 		      NETCONF_BASE_NAMESPACE, "lock") < 0)
1839 	goto done;
1840     if (rpc_callback_register(h, from_client_unlock, NULL,
1841 		      NETCONF_BASE_NAMESPACE, "unlock") < 0)
1842 	goto done;
1843     if (rpc_callback_register(h, from_client_get, NULL,
1844 		      NETCONF_BASE_NAMESPACE, "get") < 0)
1845 	goto done;
1846     if (rpc_callback_register(h, from_client_close_session, NULL,
1847 		      NETCONF_BASE_NAMESPACE, "close-session") < 0)
1848 	goto done;
1849     if (rpc_callback_register(h, from_client_kill_session, NULL,
1850 		      NETCONF_BASE_NAMESPACE, "kill-session") < 0)
1851 	goto done;
1852     /* In backend_commit.? */
1853     if (rpc_callback_register(h, from_client_commit, NULL,
1854 		      NETCONF_BASE_NAMESPACE, "commit") < 0)
1855 	goto done;
1856     if (rpc_callback_register(h, from_client_discard_changes, NULL,
1857 		      NETCONF_BASE_NAMESPACE, "discard-changes") < 0)
1858 	goto done;
1859     /* if-feature confirmed-commit */
1860     if (rpc_callback_register(h, from_client_cancel_commit, NULL,
1861 		      NETCONF_BASE_NAMESPACE, "cancel-commit") < 0)
1862 	goto done;
1863     /* if-feature validate */
1864     if (rpc_callback_register(h, from_client_validate, NULL,
1865 		      NETCONF_BASE_NAMESPACE, "validate") < 0)
1866 	goto done;
1867 
1868     /* In backend_client.? RPC from RFC 5277 */
1869     if (rpc_callback_register(h, from_client_create_subscription, NULL,
1870 		      EVENT_RFC5277_NAMESPACE, "create-subscription") < 0)
1871 	goto done;
1872     /* Clixon RPC */
1873     if (rpc_callback_register(h, from_client_debug, NULL,
1874 			      CLIXON_LIB_NS, "debug") < 0)
1875 	goto done;
1876     if (rpc_callback_register(h, from_client_ping, NULL,
1877 			      CLIXON_LIB_NS, "ping") < 0)
1878 	goto done;
1879     if (rpc_callback_register(h, from_client_stats, NULL,
1880 			      CLIXON_LIB_NS, "stats") < 0)
1881 	goto done;
1882     if (rpc_callback_register(h, from_client_restart_plugin, NULL,
1883 			      CLIXON_LIB_NS, "restart-plugin") < 0)
1884 	goto done;
1885     retval =0;
1886  done:
1887     return retval;
1888 }
1889