diff --git a/apps/xmldb/xmldb_main.c b/apps/xmldb/xmldb_main.c
new file mode 100644
index 00000000..93368d8f
--- /dev/null
+++ b/apps/xmldb/xmldb_main.c
@@ -0,0 +1,930 @@
+/*
+ *
+ Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
+
+ This file is part of CLIXON.
+
+ CLIXON is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ CLIXON is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with CLIXON; see the file LICENSE. If not, see
+ .
+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "clixon_config.h" /* generated by config & autoconf */
+#endif
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* cligen */
+#include
+
+/* clicon */
+#include
+
+/* Command line options to be passed to getopt(3) */
+#define XMLDB_OPTS "hDSf:a:p:y:m:r:"
+
+#define DEFAULT_PORT 7878
+#define DEFAULT_ADDR "127.0.0.1"
+
+static int
+xmldb_send_error(int s,
+ char *reason)
+{
+ int retval = -1;
+ cbuf *cb = NULL; /* Outgoing return message */
+
+ clicon_log(LOG_NOTICE, "%s", reason);
+ if ((cb = cbuf_new()) == NULL){
+ clicon_err(OE_UNIX, errno, "cbuf_new");
+ goto done;
+ }
+ cprintf(cb, "%s", reason);
+ if (write(s, cbuf_get(cb), cbuf_len(cb)+1) < 0){
+ clicon_err(OE_UNIX, errno, "write");
+ goto done;
+ }
+ retval = 0;
+ done:
+ if (cb)
+ cbuf_free(cb);
+ return retval;
+}
+
+/*! Process incoming xmldb get message
+ * @param[in] s Stream socket
+ * @param[in] cb Packet buffer
+ * @param[in] xr XML request node with root in "get"
+ * example
+ *
+ *
+ *
+ * /
+ * # If set send back list of xpath hits not single tree
+ *
+ *
+ * @note restrictions on using only databases called candidate and running
+ *
+ */
+static int
+xmldb_from_get(clicon_handle h,
+ int s,
+ cxobj *xr)
+{
+ int retval = -1;
+ cxobj *x;
+ cbuf *cb = NULL; /* Outgoing return message */
+ char *db;
+ int vector = 0;
+ char *xpath = "/";
+ cxobj *xt = NULL; /* Top of return tree */
+ cxobj *xc; /* Child */
+ cxobj **xvec = NULL;
+ size_t xlen = 0;
+ int i;
+
+ if (xpath_first(xr, "source/candidate") != NULL)
+ db = "candidate";
+ else if (xpath_first(xr, "source/running") != NULL)
+ db = "running";
+ else {
+ xmldb_send_error(s, "Get request: Expected candidate or running as source");
+ goto drop;
+ }
+ if ((x = xpath_first(xr, "xpath")) != NULL)
+ xpath = xml_body(x);
+ if (xpath_first(xr, "vector") != NULL)
+ vector++;
+ /* Actual get call */
+ if (xmldb_get(h, db, xpath, vector, &xt, &xvec, &xlen) < 0)
+ goto done;
+ xml_name_set(xt, "config");
+ if ((cb = cbuf_new()) == NULL){
+ clicon_err(OE_UNIX, errno, "cbuf_new");
+ goto done;
+ }
+ if (vector){
+ for (i=0; i
+ *
+ *
+ * merge|none|replace
+ *
+ * ...
+ *
+ *
+ *
+ * @note restrictions on using only databases called candidate and running
+ * @note key,val see xmldb_put_xkey
+ */
+static int
+xmldb_from_put(clicon_handle h,
+ int s,
+ cxobj *xr)
+{
+ int retval = -1;
+ cxobj *x;
+ char *db;
+ cxobj *xc; /* Child */
+ char *opstr;
+ enum operation_type op = OP_REPLACE;
+
+ if (xpath_first(xr, "target/candidate") != NULL)
+ db = "candidate";
+ else if (xpath_first(xr, "target/running") != NULL)
+ db = "running";
+ else {
+ xmldb_send_error(s, "Put request: Expected candidate or running as source");
+ goto drop;
+ }
+ if ((x = xpath_first(xr, "default-operation")) != NULL)
+ if ((opstr = xml_body(x)) != NULL){
+ if (strcmp(opstr, "replace") == 0)
+ op = OP_REPLACE;
+ else
+ if (strcmp(opstr, "merge") == 0)
+ op = OP_MERGE;
+ else
+ if (strcmp(opstr, "none") == 0)
+ op = OP_NONE;
+ else{
+ xmldb_send_error(s, "Put request: unrecognized default-operation");
+ goto drop;
+ }
+ }
+ if ((xc = xpath_first(xr, "config")) != NULL){
+ /* Actual put call */
+ if (xmldb_put(h, db, xc, op) < 0)
+ goto done;
+ }
+ if (write(s, "", strlen("")+1) < 0){
+ clicon_err(OE_UNIX, errno, "write");
+ goto done;
+ }
+ drop:
+ retval = 0;
+ done:
+ return retval;
+}
+
+static int
+xmldb_from_put_xkey(clicon_handle h,
+ int s,
+ cxobj *xr)
+{
+ int retval = -1;
+ cxobj *x;
+ char *db;
+ char *xkey;
+ char *val;
+ char *opstr;
+ enum operation_type op = OP_REPLACE;
+
+ if (xpath_first(xr, "target/candidate") != NULL)
+ db = "candidate";
+ else if (xpath_first(xr, "target/running") != NULL)
+ db = "running";
+ else {
+ xmldb_send_error(s, "Put request: Expected candidate or running as source");
+ goto drop;
+ }
+ if ((x = xpath_first(xr, "default-operation")) != NULL)
+ if ((opstr = xml_body(x)) != NULL){
+ if (strcmp(opstr, "replace") == 0)
+ op = OP_REPLACE;
+ else
+ if (strcmp(opstr, "merge") == 0)
+ op = OP_MERGE;
+ else
+ if (strcmp(opstr, "none") == 0)
+ op = OP_NONE;
+ else{
+ xmldb_send_error(s, "Put xkey request: unrecognized default-operation");
+ goto drop;
+ }
+ }
+ if ((x = xpath_first(xr, "xkey")) == NULL){
+ xmldb_send_error(s, "Put xkey request: no xkey");
+ goto drop;
+ }
+ xkey = xml_body(x);
+ if ((x = xpath_first(xr, "value")) == NULL){
+ xmldb_send_error(s, "Put xkey request: no value");
+ goto drop;
+ }
+ val = xml_body(x);
+ if (xmldb_put_xkey(h, db, xkey, val, op) < 0)
+ goto done;
+ if (write(s, "", strlen("")+1) < 0){
+ clicon_err(OE_UNIX, errno, "write");
+ goto done;
+ }
+ drop:
+ retval = 0;
+ done:
+ return retval;
+}
+
+/*! Process incoming copy message
+ * @param[in] s Stream socket
+ * @param[in] cb Packet buffer
+ * @param[in] xr XML request node with root in "exists"
+ * example
+ *
+ *
+ *
+ *
+ *
+ *
+ * @note restrictions on using only databases called candidate and running
+ */
+static int
+xmldb_from_copy(clicon_handle h,
+ int s,
+ cxobj *xr)
+{
+ int retval = -1;
+ char *source;
+ char *target;
+
+ if (xpath_first(xr, "source/candidate") != NULL)
+ source = "candidate";
+ else if (xpath_first(xr, "source/running") != NULL)
+ source = "running";
+ else {
+ xmldb_send_error(s, "Copy request: Expected candidate or running as source");
+ goto drop;
+ }
+ if (xpath_first(xr, "target/candidate") != NULL)
+ target = "candidate";
+ else if (xpath_first(xr, "target/running") != NULL)
+ target = "running";
+ else {
+ xmldb_send_error(s, "Copy request: Expected candidate or running as target");
+ goto drop;
+ }
+ if (xmldb_copy(h, source, target) < 0)
+ goto done;
+ if (write(s, "", strlen("")+1) < 0){
+ clicon_err(OE_UNIX, errno, "write");
+ goto done;
+ }
+ drop:
+ retval = 0;
+ done:
+ return retval;
+}
+
+/*! Process incoming lock message
+ * @param[in] s Stream socket
+ * @param[in] cb Packet buffer
+ * @param[in] xr XML request node with root in "exists"
+ * example
+ *
+ *
+ *
+ * 43
+ *
+ *
+ * @note restrictions on using only databases called candidate and running
+ */
+static int
+xmldb_from_lock(clicon_handle h,
+ int s,
+ cxobj *xr)
+{
+ int retval = -1;
+ char *target;
+ char *idstr = NULL;
+ cxobj *x;
+
+ if (xpath_first(xr, "target/candidate") != NULL)
+ target = "candidate";
+ else if (xpath_first(xr, "target/running") != NULL)
+ target = "running";
+ else {
+ xmldb_send_error(s, "Lock request: Expected candidate or running as target");
+ goto drop;
+ }
+ if ((x = xpath_first(xr, "id")) != NULL){
+ xmldb_send_error(s, "Lock request: mandatory id not found");
+ goto drop;
+ }
+ idstr = xml_body(x);
+ if (xmldb_lock(h, target, atoi(idstr)) < 0)
+ goto done;
+ drop:
+ retval = 0;
+ done:
+ return retval;
+}
+
+
+/*! Process incoming unlock message
+ * @param[in] s Stream socket
+ * @param[in] cb Packet buffer
+ * @param[in] xr XML request node with root in "exists"
+ * example
+ *
+ *
+ *
+ * 43
+ *
+ *
+ * @note restrictions on using only databases called candidate and running
+ */
+static int
+xmldb_from_unlock(clicon_handle h,
+ int s,
+ cxobj *xr)
+{
+ int retval = -1;
+ char *target;
+ char *idstr = NULL;
+ cxobj *x;
+
+ if (xpath_first(xr, "target/candidate") != NULL)
+ target = "candidate";
+ else if (xpath_first(xr, "target/running") != NULL)
+ target = "running";
+ else {
+ xmldb_send_error(s, "Unlock request: Expected candidate or running as target");
+ goto drop;
+ }
+ if ((x = xpath_first(xr, "id")) != NULL){
+ xmldb_send_error(s, "Unlock request: mandatory id not found");
+ goto drop;
+ }
+ idstr = xml_body(x);
+ if (xmldb_unlock(h, target, atoi(idstr)) < 0)
+ goto done;
+ drop:
+ retval = 0;
+ done:
+ return retval;
+
+}
+
+/*! Process incoming islocked message
+ * @param[in] s Stream socket
+ * @param[in] cb Packet buffer
+ * @param[in] xr XML request node with root in "exists"
+ * example
+ *
+ *
+ *
+ *
+ *
+ * @note restrictions on using only databases called candidate and running
+ */
+static int
+xmldb_from_islocked(clicon_handle h,
+ int s,
+ cxobj *xr)
+{
+ int retval = -1;
+ char *db;
+ int ret;
+ cbuf *cb = NULL;
+
+ if (xpath_first(xr, "target/candidate") != NULL)
+ db = "candidate";
+ else if (xpath_first(xr, "target/running") != NULL)
+ db = "running";
+ else {
+ xmldb_send_error(s, "Islocked request: Expected candidate or running as source");
+ goto drop;
+ }
+ if ((ret = xmldb_islocked(h, db)) < 0)
+ goto done;
+ if (ret > 0){
+ if ((cb = cbuf_new()) == NULL){
+ clicon_err(OE_UNIX, errno, "cbuf_new");
+ goto done;
+ }
+ cprintf(cb, "%u", ret);
+ if (write(s, cbuf_get(cb), cbuf_len(cb)+1) < 0){
+ clicon_err(OE_UNIX, errno, "write");
+ goto done;
+ }
+ }
+ else{
+ if (write(s, "", strlen("")+1) < 0){
+ clicon_err(OE_UNIX, errno, "write");
+ goto done;
+ }
+ }
+ drop:
+ retval = 0;
+ done:
+ if (cb)
+ cbuf_free(cb);
+ return retval;
+}
+
+
+/*! Process incoming exists? message
+ * @param[in] s Stream socket
+ * @param[in] cb Packet buffer
+ * @param[in] xr XML request node with root in "exists"
+ * example
+ *
+ *
+ *
+ *
+ *
+ * @note restrictions on using only databases called candidate and running
+ */
+static int
+xmldb_from_exists(clicon_handle h,
+ int s,
+ cxobj *xr)
+{
+ int retval = -1;
+ char *db;
+
+ if (xpath_first(xr, "target/candidate") != NULL)
+ db = "candidate";
+ else if (xpath_first(xr, "target/running") != NULL)
+ db = "running";
+ else {
+ xmldb_send_error(s, "Exists request: Expected candidate or running as source");
+ goto drop;
+ }
+ /* XXX error and non-exist treated same */
+ if (xmldb_exists(h, db) == 1){
+ if (write(s, "", strlen("")+1) < 0){
+ clicon_err(OE_UNIX, errno, "write");
+ goto done;
+ }
+ }
+ else{
+ xmldb_send_error(s, "DB does not exist");
+ goto drop;
+ }
+ drop:
+ retval = 0;
+ done:
+ return retval;
+}
+
+/*! Process incoming xmldb delete message
+ * @param[in] s Stream socket
+ * @param[in] cb Packet buffer
+ * @param[in] xr XML request node with root in "delete"
+ * example
+ *
+ *
+ *
+ *
+ *
+ * @note restrictions on using only databases called candidate and running
+ */
+static int
+xmldb_from_delete(clicon_handle h,
+ int s,
+ cxobj *xr)
+{
+ int retval = -1;
+ char *db;
+
+ if (xpath_first(xr, "target/candidate") != NULL)
+ db = "candidate";
+ else if (xpath_first(xr, "target/running") != NULL)
+ db = "running";
+ else {
+ xmldb_send_error(s, "Delete request: Expected candidate or running as source");
+ goto drop;
+ }
+ if (xmldb_delete(h, db) < 0)
+ ; /* ignore */
+ if (write(s, "", strlen("")+1) < 0){
+ clicon_err(OE_UNIX, errno, "write");
+ goto done;
+ }
+ drop:
+ retval = 0;
+ done:
+ return retval;
+}
+
+
+/*! Process incoming xmldb init message
+ * @param[in] s Stream socket
+ * @param[in] cb Packet buffer
+ * @param[in] xr XML request node with root in "init"
+ * example
+ *
+ *
+ *
+ *
+ *
+ * @note restrictions on using only databases called candidate and running
+ */
+static int
+xmldb_from_init(clicon_handle h,
+ int s,
+ cxobj *xr)
+{
+ int retval = -1;
+ char *db;
+
+ if (xpath_first(xr, "target/candidate") != NULL)
+ db = "candidate";
+ else if (xpath_first(xr, "target/running") != NULL)
+ db = "running";
+ else {
+ xmldb_send_error(s, "Init request: Expected candidate or running as source");
+ goto drop;
+ }
+ if (xmldb_init(h, db) < 0)
+ goto done;
+ if (write(s, "", strlen("")+1) < 0){
+ clicon_err(OE_UNIX, errno, "write");
+ goto done;
+ }
+ drop:
+ retval = 0;
+ done:
+ return retval;
+}
+
+/*! Process incoming xmldb packet
+ * @param[in] h Clicon handle
+ * @param[in] s Stream socket
+ * @param[in] cbin Incoming packet buffer
+ * example: ]]>]]>
+ */
+static int
+xmldb_from_client(clicon_handle h,
+ int s,
+ cbuf *cbin)
+{
+ int retval = -1;
+ char *str;
+ cxobj *xrq = NULL; /* Request (in) */
+ cxobj *xr;
+ cxobj *x;
+ cxobj *xt = NULL;
+
+ clicon_debug(1, "xmldb message: \"%s\"", cbuf_get(cbin));
+ str = cbuf_get(cbin);
+ str[strlen(str)-strlen("]]>]]>")] = '\0';
+ /* Parse incoming XML message */
+ if (clicon_xml_parse_string(&str, &xrq) < 0)
+ goto done;
+ if (debug)
+ clicon_xml2file(stderr, xrq, 0, 1);
+ if ((xr = xpath_first(xrq, "rpc")) != NULL){
+ if ((x = xpath_first(xr, "get")) != NULL){
+ if (xmldb_from_get(h, s, x) < 0)
+ goto done;
+ }
+ else if ((x = xpath_first(xr, "put")) != NULL){
+ if (xmldb_from_put(h, s, x) < 0)
+ goto done;
+ }
+ else if ((x = xpath_first(xr, "put-xkey")) != NULL){
+ if (xmldb_from_put_xkey(h, s, x) < 0)
+ goto done;
+ }
+ else if ((x = xpath_first(xr, "copy")) != NULL){
+ if (xmldb_from_copy(h, s, x) < 0)
+ goto done;
+ }
+ else if ((x = xpath_first(xr, "lock")) != NULL){
+ if (xmldb_from_lock(h, s, x) < 0)
+ goto done;
+ }
+ else if ((x = xpath_first(xr, "unlock")) != NULL){
+ if (xmldb_from_unlock(h, s, x) < 0)
+ goto done;
+ }
+ else if ((x = xpath_first(xr, "islocked")) != NULL){
+ if (xmldb_from_islocked(h, s, x) < 0)
+ goto done;
+ }
+ else if ((x = xpath_first(xr, "exists")) != NULL){
+ if (xmldb_from_exists(h, s, x) < 0)
+ goto done;
+ }
+ else if ((x = xpath_first(xr, "init")) != NULL){
+ if (xmldb_from_init(h, s, x) < 0)
+ goto done;
+ }
+ else if ((x = xpath_first(xr, "delete")) != NULL){
+ if (xmldb_from_delete(h, s, x) < 0)
+ goto done;
+ }
+ }
+ else{
+ xmldb_send_error(s, "Expected rpc as top xml msg");
+ goto drop;
+ }
+ drop:
+ retval = 0;
+ done:
+ if (xrq)
+ xml_free(xrq);
+ if (xt)
+ xml_free(xt);
+ return retval;
+}
+
+/*! stolen from netconf_lib.c */
+static int
+detect_endtag(char *tag, char ch, int *state)
+{
+ int retval = 0;
+
+ if (tag[*state] == ch){
+ (*state)++;
+ if (*state == strlen(tag)){
+ *state = 0;
+ retval = 1;
+ }
+ }
+ else
+ *state = 0;
+ return retval;
+}
+
+/*! config_accept_client
+ */
+int
+config_accept_client(int fd,
+ void *arg)
+{
+ int retval = -1;
+ clicon_handle h = (clicon_handle)arg;
+ int s = -1;
+ struct sockaddr_un from;
+ socklen_t slen;
+ ssize_t len;
+ unsigned char buf[BUFSIZ];
+ int i;
+ cbuf *cb = NULL;
+ int xml_state = 0;
+
+ clicon_debug(1, "Accepting client request");
+ if ((cb = cbuf_new()) == NULL){
+ clicon_err(OE_XML, errno, "cbuf_new");
+ return retval;
+ }
+ len = sizeof(from);
+ if ((s = accept(fd, (struct sockaddr*)&from, &slen)) < 0){
+ clicon_err(OE_UNIX, errno, "%s: accept", __FUNCTION__);
+ goto done;
+ }
+ if ((len = read(s, buf, sizeof(buf))) < 0){
+ clicon_err(OE_UNIX, errno, "read");
+ goto done;
+ }
+ for (i=0; i]]>",
+ buf[i],
+ &xml_state)) {
+ if (xmldb_from_client(h, s, cb) < 0){
+ goto done;
+ }
+ cbuf_reset(cb);
+ }
+ }
+ retval = 0;
+ done:
+ if (cb)
+ cbuf_free(cb);
+ if (s != -1)
+ close(s);
+ return retval;
+}
+
+/*! Create tcp server socket and register callback
+ */
+static int
+server_socket(clicon_handle h,
+ char *ipv4addr,
+ uint16_t port)
+{
+ int retval = -1;
+ int s;
+ struct sockaddr_in addr;
+
+ /* Open control socket */
+ if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ clicon_err(OE_UNIX, errno, "socket");
+ goto done;
+ }
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ if (inet_pton(addr.sin_family, ipv4addr, &addr.sin_addr) != 1){
+ clicon_err(OE_UNIX, errno, "inet_pton: %s (Expected IPv4 address. Check settings of CLICON_SOCK_FAMILY and CLICON_SOCK)", ipv4addr);
+ goto done;
+ }
+ if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0){
+ clicon_err(OE_UNIX, errno, "%s: bind", __FUNCTION__);
+ goto done;
+ }
+ clicon_debug(1, "Listen on server socket at %s:%hu", ipv4addr, port);
+ if (listen(s, 5) < 0){
+ clicon_err(OE_UNIX, errno, "%s: listen", __FUNCTION__);
+ goto done;
+ }
+ if (event_reg_fd(s, config_accept_client, h, "server socket") < 0) {
+ close(s);
+ goto done;
+ }
+ retval = 0;
+ done:
+ return retval;
+}
+
+/*
+ * usage
+ */
+static void
+usage(char *argv0)
+{
+ fprintf(stderr, "usage:%s\n"
+ "where options are\n"
+ "\t-h\t\tHelp\n"
+ "\t-D\t\tDebug\n"
+ "\t-S\t\tLog on syslog\n"
+ "\t-f \tCLICON config file\n"
+ "\t-a \tIP address\n"
+ "\t-p \tTCP port\n"
+ "\t-y \tYang dir\n"
+ "\t-m \tYang main module name\n"
+ "\t-r \tYang module revision\n",
+ argv0
+ );
+ exit(0);
+}
+
+int
+main(int argc, char **argv)
+{
+ char c;
+ int use_syslog;
+ clicon_handle h;
+ uint16_t port;
+ char *addr = DEFAULT_ADDR;
+
+ /* In the startup, logs to stderr & debug flag set later */
+ clicon_log_init(__PROGRAM__, LOG_INFO, CLICON_LOG_STDERR);
+
+ /* Defaults */
+ use_syslog = 0;
+ port = DEFAULT_PORT;
+
+ if ((h = clicon_handle_init()) == NULL)
+ goto done;
+ /* getopt in two steps, first find config-file before over-riding options. */
+ while ((c = getopt(argc, argv, XMLDB_OPTS)) != -1)
+ switch (c) {
+ case '?' :
+ case 'h' : /* help */
+ usage(argv[0]);
+ break;
+ case 'D' : /* debug */
+ debug = 1;
+ break;
+ case 'f': /* config file */
+ if (!strlen(optarg))
+ usage(argv[0]);
+ clicon_option_str_set(h, "CLICON_CONFIGFILE", optarg);
+ break;
+ case 'S': /* Log on syslog */
+ use_syslog = 1;
+ break;
+ }
+ /*
+ * Logs, error and debug to stderr or syslog, set debug level
+ */
+ clicon_log_init(__PROGRAM__, debug?LOG_DEBUG:LOG_INFO,
+ use_syslog?CLICON_LOG_SYSLOG:CLICON_LOG_STDERR);
+ clicon_debug_init(debug, NULL);
+
+ /* Find and read configfile */
+ if (clicon_options_main(h) < 0){
+ usage(argv[0]);
+ goto done;
+ }
+
+ /* Now rest of options */
+ optind = 1;
+ while ((c = getopt(argc, argv, XMLDB_OPTS)) != -1)
+ switch (c) {
+ case 'D': /* Processed earlier, ignore now. */
+ case 'S':
+ case 'f':
+ break;
+ case 'a': /* address */
+ clicon_option_str_set(h, "CLICON_XMLDB_ADDR", optarg);
+ break;
+ case 'p': /* port */
+ clicon_option_str_set(h, "CLICON_XMLDB_PORT", optarg);
+ break;
+ case 'y': /* yang dir */
+ clicon_option_str_set(h, "CLICON_YANG_DIR", optarg);
+ break;
+ case 'm': /* yang module */
+ clicon_option_str_set(h, "CLICON_YANG_MODULE_MAIN", optarg);
+ break;
+ case 'r': /* yang revision */
+ clicon_option_str_set(h, "CLICON_YANG_MODULE_REVISION", optarg);
+ break;
+ default:
+ usage(argv[0]);
+ break;
+ }
+ argc -= optind;
+ argv += optind;
+ clicon_option_str_set(h, "CLICON_XMLDB_RPC", "0");
+ if (clicon_yang_dir(h) == NULL){
+ clicon_err(OE_UNIX, errno, "yang dir not set");
+ goto done;
+ }
+ if (clicon_yang_module_main(h) == NULL){
+ clicon_err(OE_UNIX, errno, "yang main module not set");
+ goto done;
+ }
+ if (yang_spec_main(h, NULL, 0) < 0)
+ goto done;
+ addr = clicon_xmldb_addr(h);
+ port = clicon_xmldb_port(h);
+ if (server_socket(h, addr, port) < 0)
+ goto done;
+
+ if (event_loop() < 0)
+ goto done;
+
+ done:
+ return 0;
+}