930 lines
22 KiB
C
930 lines
22 KiB
C
/*
|
|
*
|
|
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
|
|
<http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "clixon_config.h" /* generated by config & autoconf */
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <stdarg.h>
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
#include <fcntl.h>
|
|
#include <time.h>
|
|
#include <syslog.h>
|
|
#include <sys/time.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/param.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <netinet/in.h>
|
|
#include <sys/un.h>
|
|
|
|
/* cligen */
|
|
#include <cligen/cligen.h>
|
|
|
|
/* clicon */
|
|
#include <clixon/clixon.h>
|
|
|
|
/* 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, "<error>%s</error>", 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
|
|
* <rpc>
|
|
* <get>
|
|
* <source><candidate/></source>
|
|
* <xpath>/</xpath>
|
|
* <vector/> # If set send back list of xpath hits not single tree
|
|
* </get>
|
|
* </rpc>
|
|
* @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<xlen; i++){
|
|
xc = xvec[i];
|
|
if (clicon_xml2cbuf(cb, xc, 0, 0) < 0)
|
|
goto done;
|
|
}
|
|
}
|
|
else{
|
|
if (clicon_xml2cbuf(cb, xt, 0, 0) < 0)
|
|
goto done;
|
|
}
|
|
if (debug)
|
|
fprintf(stderr, "%s: \"%s\" len:%d\n",
|
|
__FUNCTION__, cbuf_get(cb), cbuf_len(cb)+1);
|
|
if (write(s, cbuf_get(cb), cbuf_len(cb)+1) < 0){
|
|
clicon_err(OE_UNIX, errno, "write");
|
|
goto done;
|
|
}
|
|
drop:
|
|
retval = 0;
|
|
done:
|
|
if (xt)
|
|
xml_free(xt);
|
|
if (xvec)
|
|
free(xvec);
|
|
if (cb)
|
|
cbuf_free(cb);
|
|
return retval;
|
|
}
|
|
|
|
/*! Process incoming xmldb put message
|
|
* @param[in] s Stream socket
|
|
* @param[in] cb Packet buffer
|
|
* @param[in] xr XML request node with root in "put"
|
|
* example
|
|
* <rpc>
|
|
* <put>
|
|
* <target><candidate/></target>
|
|
* <default-operation>merge|none|replace</default-operation>
|
|
* <config>
|
|
* ...
|
|
* </config>
|
|
* </put>
|
|
* </rpc>
|
|
* @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, "<ok/>", strlen("<ok/>")+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, "<ok/>", strlen("<ok/>")+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
|
|
* <rpc>
|
|
* <copy>
|
|
* <source><candidate/></source>
|
|
* <target><running/></target>
|
|
* </copy>
|
|
* </rpc>
|
|
* @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, "<ok/>", strlen("<ok/>")+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
|
|
* <rpc>
|
|
* <exists>
|
|
* <target><candidate/></target>
|
|
* <id>43</id>
|
|
* </exists>
|
|
* </rpc>
|
|
* @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
|
|
* <rpc>
|
|
* <unlock>
|
|
* <target><candidate/></target>
|
|
* <id>43</id>
|
|
* </unlock>
|
|
* </rpc>
|
|
* @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
|
|
* <rpc>
|
|
* <islocked>
|
|
* <target><candidate/></target>
|
|
* </islocked>
|
|
* </rpc>
|
|
* @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, "<locked>%u</locked>", ret);
|
|
if (write(s, cbuf_get(cb), cbuf_len(cb)+1) < 0){
|
|
clicon_err(OE_UNIX, errno, "write");
|
|
goto done;
|
|
}
|
|
}
|
|
else{
|
|
if (write(s, "<unlocked/>", strlen("<unlocked/>")+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
|
|
* <rpc>
|
|
* <exists>
|
|
* <target><candidate/></target>
|
|
* </exists>
|
|
* </rpc>
|
|
* @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, "<ok/>", strlen("<ok/>")+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
|
|
* <rpc>
|
|
* <delete>
|
|
* <target><candidate/></target>
|
|
* </delete>
|
|
* </rpc>
|
|
* @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, "<ok/>", strlen("<ok/>")+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
|
|
* <rpc>
|
|
* <init>
|
|
* <target><candidate/></target>
|
|
* </init>
|
|
* </rpc>
|
|
* @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, "<ok/>", strlen("<ok/>")+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: <rpc><get></get></rpc>]]>]]>
|
|
*/
|
|
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)
|
|
xml_print(stderr, xrq);
|
|
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<len; i++){
|
|
if (buf[i] == 0)
|
|
continue; /* Skip NULL chars (eg from terminals) */
|
|
cprintf(cb, "%c", buf[i]);
|
|
if (detect_endtag("]]>]]>",
|
|
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 <file>\tCLICON config file\n"
|
|
"\t-a <addr>\tIP address\n"
|
|
"\t-p <port>\tTCP port\n"
|
|
"\t-y <dir>\tYang dir\n"
|
|
"\t-m <mod>\tYang main module name\n"
|
|
"\t-r <rev>\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;
|
|
}
|