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