* Made NETCONF message handling more strict according to RFC 6241 * Set `CLICON_NETCONF_MESSAGE_ID_OPTIONAL` to true to accept omission of message-id attribute * Fixed: [need make sure message-id exist in rpc validate #240](https://github.com/clicon/clixon/issues/240)
762 lines
21 KiB
C
762 lines
21 KiB
C
/*
|
|
*
|
|
***** BEGIN LICENSE BLOCK *****
|
|
|
|
Copyright (C) 2020-2021 Olof Hagsand and Rubicon Communications, LLC(Netgate)
|
|
|
|
This file is part of CLIXON.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
|
|
Alternatively, the contents of this file may be used under the terms of
|
|
the GNU General Public License Version 3 or later (the "GPL"),
|
|
in which case the provisions of the GPL are applicable instead
|
|
of those above. If you wish to allow use of your version of this file only
|
|
under the terms of the GPL, and not to allow others to
|
|
use your version of this file under the terms of Apache License version 2, indicate
|
|
your decision by deleting the provisions above and replace them with the
|
|
notice and other provisions required by the GPL. If you do not delete
|
|
the provisions above, a recipient may use your version of this file under
|
|
the terms of any one of the Apache License version 2 or the GPL.
|
|
|
|
***** END LICENSE BLOCK *****
|
|
*
|
|
*/
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "clixon_config.h" /* generated by config & autoconf */
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <syslog.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <assert.h>
|
|
#include <sys/stat.h>
|
|
#include <netinet/in.h>
|
|
|
|
/* cligen */
|
|
#include <cligen/cligen.h>
|
|
|
|
#include "clixon_queue.h"
|
|
#include "clixon_string.h"
|
|
#include "clixon_hash.h"
|
|
#include "clixon_handle.h"
|
|
#include "clixon_log.h"
|
|
#include "clixon_err.h"
|
|
#include "clixon_yang.h"
|
|
#include "clixon_options.h"
|
|
#include "clixon_proc.h"
|
|
#include "clixon_xml.h"
|
|
#include "clixon_xml_nsctx.h"
|
|
#include "clixon_xml_io.h"
|
|
#include "clixon_xpath_ctx.h"
|
|
#include "clixon_xpath.h"
|
|
#include "clixon_netconf_lib.h"
|
|
#include "clixon_proto.h"
|
|
#include "clixon_proto_client.h"
|
|
#include "clixon_client.h"
|
|
|
|
/*
|
|
* Constants
|
|
*/
|
|
/* Netconf binary default, override with environment variable: CLIXON_NETCONF_BIN
|
|
* Could try to get path from install/makefile data
|
|
*/
|
|
#define CLIXON_NETCONF_BIN "/usr/local/bin/clixon_netconf"
|
|
|
|
#define CLIXON_CLIENT_MAGIC 0x54fe649a
|
|
|
|
#define chandle(ch) (assert(clixon_client_handle_check(ch)==0),(struct clixon_client_handle *)(ch))
|
|
|
|
/*! Internal structure of clixon client handle.
|
|
*/
|
|
struct clixon_client_handle{
|
|
uint32_t cch_magic; /* magic number */
|
|
clixon_client_type cch_type; /* Clixon socket type */
|
|
int cch_socket; /* Input/output socket */
|
|
int cch_pid; /* Sub-process-id Only applies for NETCONF/SSH */
|
|
int cch_locked; /* State variable: 1 means locked */
|
|
};
|
|
|
|
/*! Check struct magic number for sanity checks
|
|
* @param[in] h Clicon client handle
|
|
* @retval 0 Sanity check OK
|
|
* @retval -1 Sanity check failed
|
|
*/
|
|
static int
|
|
clixon_client_handle_check(clixon_client_handle ch)
|
|
{
|
|
/* Dont use handle macro to avoid recursion */
|
|
struct clixon_client_handle *cch = (struct clixon_client_handle *)(ch);
|
|
|
|
return cch->cch_magic == CLIXON_CLIENT_MAGIC ? 0 : -1;
|
|
}
|
|
|
|
/*! Initialize Clixon client API
|
|
* @param[in] config_file Clixon configuration file, or NULL for default
|
|
* @retval h Clixon handler
|
|
* @retval NULL Error
|
|
* @see clixon_client_close
|
|
*/
|
|
clixon_handle
|
|
clixon_client_init(const char *config_file)
|
|
{
|
|
clicon_handle h;
|
|
|
|
clicon_debug(1, "%s", __FUNCTION__);
|
|
/* Initiate CLICON handle. CLIgen is also initialized */
|
|
if ((h = clicon_handle_init()) == NULL)
|
|
return NULL;
|
|
/* Set clixon config file - reuse the one in the main installation */
|
|
clicon_option_str_set(h, "CLICON_CONFIGFILE",
|
|
config_file?(char*)config_file:CLIXON_DEFAULT_CONFIG);
|
|
/* Find, read and parse configfile */
|
|
if (clicon_options_main(h) < 0)
|
|
return NULL;
|
|
return h;
|
|
}
|
|
|
|
/*! Deallocate everything from client_init
|
|
* @param[in] h Clixon handle
|
|
* @see clixon_client_init
|
|
*/
|
|
int
|
|
clixon_client_terminate(clicon_handle h)
|
|
{
|
|
clicon_debug(1, "%s", __FUNCTION__);
|
|
clicon_handle_exit(h);
|
|
return 0;
|
|
}
|
|
|
|
/*! Send a lock request (internal)
|
|
* @param[in] sock Open socket
|
|
* @param[in] lock 0: unlock, 1: lock
|
|
* @param[in] db Datastore name
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
static int
|
|
clixon_client_lock(int sock,
|
|
const int lock,
|
|
const char *db)
|
|
{
|
|
int retval = -1;
|
|
cxobj *xret = NULL;
|
|
cxobj *xd;
|
|
cbuf *msg = NULL;
|
|
cbuf *msgret = NULL;
|
|
|
|
clicon_debug(1, "%s", __FUNCTION__);
|
|
if (db == NULL){
|
|
clicon_err(OE_XML, EINVAL, "Expected db");
|
|
goto done;
|
|
}
|
|
if ((msg = cbuf_new()) == NULL){
|
|
clicon_err(OE_PLUGIN, errno, "cbuf_new");
|
|
goto done;
|
|
}
|
|
if ((msgret = cbuf_new()) == NULL){
|
|
clicon_err(OE_PLUGIN, errno, "cbuf_new");
|
|
goto done;
|
|
}
|
|
cprintf(msg, "<rpc xmlns=\"%s\" %s>"
|
|
"<%slock><target><%s/></target></%slock></rpc>",
|
|
NETCONF_BASE_NAMESPACE,
|
|
NETCONF_MESSAGE_ID_ATTR,
|
|
lock?"":"un", db, lock?"":"un");
|
|
if (clicon_rpc1(sock, msg, msgret) < 0)
|
|
goto done;
|
|
if (clixon_xml_parse_string(cbuf_get(msgret), YB_NONE, NULL, &xret, NULL) < 0)
|
|
goto done;
|
|
if ((xd = xpath_first(xret, NULL, "/rpc-reply/rpc-error")) != NULL){
|
|
xd = xml_parent(xd); /* point to rpc-reply */
|
|
clixon_netconf_error(xd, "Get config", NULL);
|
|
goto done; /* Not fatal */
|
|
}
|
|
retval = 0;
|
|
done:
|
|
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
|
if (xret)
|
|
xml_free(xret);
|
|
if (msg)
|
|
cbuf_free(msg);
|
|
if (msgret)
|
|
cbuf_free(msgret);
|
|
return retval;
|
|
}
|
|
|
|
/*! Internal function to construct the encoding and hello message
|
|
*
|
|
* @param[in] sock Socket
|
|
* @param[in] namespace Default namespace used for non-prefixed entries in xpath. (Alt use nsc)
|
|
* @param[in] xpath XPath
|
|
* @param[out] xdata XML data tree (may or may not include the intended data)
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
static int
|
|
clixon_client_hello(int sock)
|
|
{
|
|
int retval = -1;
|
|
cbuf *msg = NULL;
|
|
|
|
clicon_debug(1, "%s", __FUNCTION__);
|
|
if ((msg = cbuf_new()) == NULL){
|
|
clicon_err(OE_PLUGIN, errno, "cbuf_new");
|
|
goto done;
|
|
}
|
|
cprintf(msg, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
|
|
cprintf(msg, "<hello xmlns=\"%s\">", NETCONF_BASE_NAMESPACE);
|
|
cprintf(msg, "<capabilities><capability>%s</capability></capabilities>", NETCONF_BASE_CAPABILITY_1_1);
|
|
cprintf(msg, "</hello>");
|
|
if (clicon_msg_send1(sock, msg) < 0)
|
|
goto done;
|
|
retval = 0;
|
|
done:
|
|
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
|
if (msg)
|
|
cbuf_free(msg);
|
|
return retval;
|
|
}
|
|
|
|
/*! Connect client to clixon backend according to config and return a socket
|
|
* @param[in] h Clixon handle
|
|
* @param[in] socktype Type of socket, internal/external/netconf/ssh
|
|
* @retval ch Clixon session handler
|
|
* @retval NULL Error
|
|
* @see clixon_client_disconnect Close the socket returned here
|
|
*/
|
|
clixon_client_handle
|
|
clixon_client_connect(clicon_handle h,
|
|
clixon_client_type socktype)
|
|
{
|
|
struct clixon_client_handle *cch = NULL;
|
|
char **argv = NULL;
|
|
int nr;
|
|
int i;
|
|
char *netconf_bin = NULL;
|
|
struct stat st = {0,};
|
|
size_t sz = sizeof(struct clixon_client_handle);
|
|
char dbgstr[8];
|
|
|
|
clicon_debug(1, "%s", __FUNCTION__);
|
|
if ((cch = malloc(sz)) == NULL){
|
|
clicon_err(OE_NETCONF, errno, "malloc");
|
|
goto done;
|
|
}
|
|
memset(cch, 0, sz);
|
|
cch->cch_magic = CLIXON_CLIENT_MAGIC;
|
|
cch->cch_type = socktype;
|
|
switch (socktype){
|
|
case CLIXON_CLIENT_IPC:
|
|
if (clicon_rpc_connect(h, &cch->cch_socket) < 0)
|
|
goto err;
|
|
break;
|
|
case CLIXON_CLIENT_NETCONF:
|
|
nr = 7;
|
|
if (clicon_debug_get() != 0)
|
|
nr += 2;
|
|
if ((argv = calloc(nr, sizeof(char *))) == NULL){
|
|
clicon_err(OE_UNIX, errno, "calloc");
|
|
goto err;
|
|
}
|
|
i = 0;
|
|
if ((netconf_bin = getenv("CLIXON_NETCONF_BIN")) == NULL)
|
|
netconf_bin = CLIXON_NETCONF_BIN;
|
|
if (stat(netconf_bin, &st) < 0){
|
|
clicon_err(OE_NETCONF, errno, "netconf binary %s. Set with CLIXON_NETCONF_BIN=",
|
|
netconf_bin);
|
|
goto err;
|
|
}
|
|
argv[i++] = netconf_bin;
|
|
argv[i++] = "-q";
|
|
argv[i++] = "-f";
|
|
argv[i++] = clicon_option_str(h, "CLICON_CONFIGFILE");
|
|
argv[i++] = "-l"; /* log to syslog */
|
|
argv[i++] = "s";
|
|
if (clicon_debug_get() != 0){
|
|
argv[i++] = "-D";
|
|
snprintf(dbgstr, sizeof(dbgstr)-1, "%d", clicon_debug_get());
|
|
argv[i++] = dbgstr;
|
|
}
|
|
argv[i++] = NULL;
|
|
assert(i==nr);
|
|
if (clixon_proc_socket(argv, &cch->cch_pid, &cch->cch_socket) < 0){
|
|
goto err;
|
|
}
|
|
/* Start with encoding and hello message */
|
|
if (clixon_client_hello(cch->cch_socket) < 0)
|
|
goto err;
|
|
break;
|
|
case CLIXON_CLIENT_SSH:
|
|
break;
|
|
} /* switch */
|
|
/* lock */
|
|
if (clixon_client_lock(cch->cch_socket, 1, "running") < 0)
|
|
goto err;
|
|
cch->cch_locked = 1;
|
|
done:
|
|
clicon_debug(1, "%s retval:%p", __FUNCTION__, cch);
|
|
return cch;
|
|
err:
|
|
if (cch)
|
|
clixon_client_disconnect(cch);
|
|
cch = NULL;
|
|
goto done;
|
|
}
|
|
|
|
/*! Connect client to clixon backend according to config and return a socket
|
|
* @param[in] ch Clixon client session handle
|
|
* @see clixon_client_connect where the handle is created
|
|
* The handle is deallocated
|
|
*/
|
|
int
|
|
clixon_client_disconnect(clixon_client_handle ch)
|
|
{
|
|
int retval = -1;
|
|
struct clixon_client_handle *cch = chandle(ch);
|
|
|
|
clicon_debug(1, "%s", __FUNCTION__);
|
|
if (cch == NULL){
|
|
clicon_err(OE_XML, EINVAL, "Expected cch handle");
|
|
goto done;
|
|
}
|
|
/* unlock (if locked) */
|
|
if (cch->cch_locked)
|
|
;// (void)clixon_client_lock(cch->cch_socket, 0, "running");
|
|
|
|
switch(cch->cch_type){
|
|
case CLIXON_CLIENT_IPC:
|
|
close(cch->cch_socket);
|
|
break;
|
|
case CLIXON_CLIENT_NETCONF:
|
|
if (clixon_proc_socket_close(cch->cch_pid,
|
|
cch->cch_socket) < 0)
|
|
goto done;
|
|
break;
|
|
case CLIXON_CLIENT_SSH:
|
|
break;
|
|
}
|
|
free(cch);
|
|
retval = 0;
|
|
done:
|
|
return retval;
|
|
}
|
|
|
|
/*! Get the bottom-most leaf in an xml tree being a result of xpath
|
|
* @param[in] xtop Pointer to XML top-of-tree
|
|
* @param[out] xbotp Pointer to XML bottom node
|
|
* @retval 0 OK
|
|
*/
|
|
static int
|
|
clixon_xml_bottom(cxobj *xtop,
|
|
cxobj **xbotp)
|
|
{
|
|
int retval = -1;
|
|
cxobj *x;
|
|
cxobj *xp;
|
|
cxobj *xc = NULL;
|
|
|
|
xp = xtop;
|
|
while (xp != NULL){
|
|
/* Find child, if many, one which is not a key, if any */
|
|
xc = NULL;
|
|
x = NULL;
|
|
while ((x = xml_child_each(xp, x, CX_ELMNT)) != NULL)
|
|
xc = x;
|
|
/* xc is last XXX */
|
|
if (xc == NULL)
|
|
break;
|
|
xp = xc;
|
|
}
|
|
retval = 0;
|
|
*xbotp = xp;
|
|
retval = 0;
|
|
// done:
|
|
return retval;
|
|
}
|
|
|
|
|
|
/*! Internal function to construct a get-config and query a value from the backend
|
|
*
|
|
* @param[in] sock Socket
|
|
* @param[in] namespace Default namespace used for non-prefixed entries in xpath. (Alt use nsc)
|
|
* @param[in] xpath XPath
|
|
* @param[out] xdata XML data tree (may or may not include the intended data)
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
static int
|
|
clixon_client_get_xdata(int sock,
|
|
const char *namespace,
|
|
const char *xpath,
|
|
cxobj **xdata)
|
|
{
|
|
int retval = -1;
|
|
cxobj *xret = NULL;
|
|
cxobj *xd;
|
|
cbuf *msg = NULL;
|
|
cbuf *msgret = NULL;
|
|
const char *db = "running";
|
|
cvec *nsc = NULL;
|
|
|
|
clicon_debug(1, "%s", __FUNCTION__);
|
|
if ((msg = cbuf_new()) == NULL){
|
|
clicon_err(OE_PLUGIN, errno, "cbuf_new");
|
|
goto done;
|
|
}
|
|
if ((msgret = cbuf_new()) == NULL){
|
|
clicon_err(OE_PLUGIN, errno, "cbuf_new");
|
|
goto done;
|
|
}
|
|
cprintf(msg, "<rpc xmlns=\"%s\"", NETCONF_BASE_NAMESPACE);
|
|
cprintf(msg, " xmlns:%s=\"%s\"",
|
|
NETCONF_BASE_PREFIX, NETCONF_BASE_NAMESPACE);
|
|
cprintf(msg, " %s", NETCONF_MESSAGE_ID_ATTR);
|
|
cprintf(msg, "><get-config><source><%s/></source>", db);
|
|
if (xpath && strlen(xpath)){
|
|
cprintf(msg, "<%s:filter %s:type=\"xpath\" xmlns=\"%s\" %s:select=\"%s\"",
|
|
NETCONF_BASE_PREFIX,
|
|
NETCONF_BASE_PREFIX,
|
|
namespace,
|
|
NETCONF_BASE_PREFIX,
|
|
xpath);
|
|
if (xml_nsctx_cbuf(msg, nsc) < 0)
|
|
goto done;
|
|
cprintf(msg, "/>");
|
|
}
|
|
cprintf(msg, "</get-config></rpc>");
|
|
if (clicon_rpc1(sock, msg, msgret) < 0)
|
|
goto done;
|
|
if (clixon_xml_parse_string(cbuf_get(msgret), YB_NONE, NULL, &xret, NULL) < 0)
|
|
goto done;
|
|
if ((xd = xpath_first(xret, NULL, "/rpc-reply/rpc-error")) != NULL){
|
|
xd = xml_parent(xd); /* point to rpc-reply */
|
|
clixon_netconf_error(xd, "Get config", NULL);
|
|
goto done; /* Not fatal */
|
|
}
|
|
else if ((xd = xpath_first(xret, NULL, "/rpc-reply/data")) == NULL){
|
|
if ((xd = xml_new(NETCONF_OUTPUT_DATA, NULL, CX_ELMNT)) == NULL)
|
|
goto done;
|
|
}
|
|
else{
|
|
if (xml_rm(xd) < 0)
|
|
goto done;
|
|
}
|
|
*xdata = xd;
|
|
retval = 0;
|
|
done:
|
|
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
|
if (xret)
|
|
xml_free(xret);
|
|
if (msg)
|
|
cbuf_free(msg);
|
|
if (msgret)
|
|
cbuf_free(msgret);
|
|
return retval;
|
|
}
|
|
|
|
/*! Generic get value of body
|
|
* @param[in] sock Open socket
|
|
* @param[in] namespace Default namespace used for non-prefixed entries in xpath.
|
|
* @param[in] xpath XPath
|
|
* @param[out] val Output value
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
static int
|
|
clixon_client_get_body_val(int sock,
|
|
const char *namespace,
|
|
const char *xpath,
|
|
char **val)
|
|
{
|
|
int retval = -1;
|
|
cxobj *xdata = NULL;
|
|
cxobj *xobj = NULL;
|
|
|
|
clicon_debug(1, "%s", __FUNCTION__);
|
|
if (val == NULL){
|
|
clicon_err(OE_XML, EINVAL, "Expected val");
|
|
goto done;
|
|
}
|
|
if (clixon_client_get_xdata(sock, namespace, xpath, &xdata) < 0)
|
|
goto done;
|
|
if (xdata == NULL){
|
|
clicon_err(OE_XML, EINVAL, "No xml obj found");
|
|
goto done;
|
|
}
|
|
/* Is this an error, maybe an "unset" retval ? */
|
|
if (xml_child_nr_type(xdata, CX_ELMNT) == 0){
|
|
clicon_err(OE_XML, EINVAL, "Value not found");
|
|
goto done;
|
|
}
|
|
if (clixon_xml_bottom(xdata, &xobj) < 0)
|
|
goto done;
|
|
if (xobj == NULL){
|
|
clicon_err(OE_XML, EINVAL, "No xml value found");
|
|
goto done;
|
|
}
|
|
*val = xml_body(xobj);
|
|
retval = 0;
|
|
done:
|
|
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
|
return retval;
|
|
}
|
|
|
|
/*! Client-api get boolean
|
|
* @param[in] ch Clixon client handle
|
|
* @param[out] rval Return value
|
|
* @param[in] namespace Default namespace used for non-prefixed entries in xpath. (Alt use nsc)
|
|
* @param[in] xpath XPath
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
clixon_client_get_bool(clixon_client_handle ch,
|
|
int *rval,
|
|
const char *namespace,
|
|
const char *xpath)
|
|
{
|
|
int retval = -1;
|
|
struct clixon_client_handle *cch = chandle(ch);
|
|
char *val = NULL;
|
|
char *reason = NULL;
|
|
int ret;
|
|
uint8_t val0=0;
|
|
|
|
clicon_debug(1, "%s", __FUNCTION__);
|
|
if (clixon_client_get_body_val(cch->cch_socket,
|
|
namespace, xpath, &val) < 0)
|
|
goto done;
|
|
if ((ret = parse_bool(val, &val0, &reason)) < 0){
|
|
clicon_err(OE_XML, errno, "parse_bool");
|
|
goto done;
|
|
}
|
|
if (ret == 0){
|
|
clicon_err(OE_XML, EINVAL, "%s", reason);
|
|
goto done;
|
|
}
|
|
*rval = (int)val0;
|
|
retval = 0;
|
|
done:
|
|
if (reason)
|
|
free(reason);
|
|
return retval;
|
|
}
|
|
|
|
/*! Client-api get string
|
|
* @param[in] ch Clixon client handle
|
|
* @param[out] rval Return value string
|
|
* @param[in] n Length of string
|
|
* @param[in] namespace Default namespace used for non-prefixed entries in xpath. (Alt use nsc)
|
|
* @param[in] xpath XPath
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
clixon_client_get_str(clixon_client_handle ch,
|
|
char *rval,
|
|
int n,
|
|
const char *namespace,
|
|
const char *xpath)
|
|
{
|
|
int retval = -1;
|
|
struct clixon_client_handle *cch = chandle(ch);
|
|
char *val = NULL;
|
|
|
|
clicon_debug(1, "%s", __FUNCTION__);
|
|
if (clixon_client_get_body_val(cch->cch_socket,
|
|
namespace, xpath, &val) < 0)
|
|
goto done;
|
|
strncpy(rval, val, n-1);
|
|
rval[n-1]= '\0';
|
|
retval = 0;
|
|
done:
|
|
return retval;
|
|
}
|
|
|
|
/*! Client-api get uint8
|
|
* @param[in] ch Clixon client handle
|
|
* @param[out] rval Return value
|
|
* @param[in] namespace Default namespace used for non-prefixed entries in xpath. (Alt use nsc)
|
|
* @param[in] xpath XPath
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
clixon_client_get_uint8(clixon_client_handle ch,
|
|
uint8_t *rval,
|
|
const char *namespace,
|
|
const char *xpath)
|
|
{
|
|
int retval = -1;
|
|
struct clixon_client_handle *cch = chandle(ch);
|
|
char *val = NULL;
|
|
char *reason = NULL;
|
|
int ret;
|
|
|
|
clicon_debug(1, "%s", __FUNCTION__);
|
|
if (clixon_client_get_body_val(cch->cch_socket,
|
|
namespace, xpath, &val) < 0)
|
|
goto done;
|
|
if ((ret = parse_uint8(val, rval, &reason)) < 0){
|
|
clicon_err(OE_XML, errno, "parse_bool");
|
|
goto done;
|
|
}
|
|
if (ret == 0){
|
|
clicon_err(OE_XML, EINVAL, "%s", reason);
|
|
goto done;
|
|
}
|
|
retval = 0;
|
|
done:
|
|
if (reason)
|
|
free(reason);
|
|
return retval;
|
|
}
|
|
|
|
/*! Client-api get uint16
|
|
* @param[in] ch Clixon client handle
|
|
* @param[out] rval Return value
|
|
* @param[in] namespace Default namespace used for non-prefixed entries in xpath. (Alt use nsc)
|
|
* @param[in] xpath XPath
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
clixon_client_get_uint16(clixon_client_handle ch,
|
|
uint16_t *rval,
|
|
const char *namespace,
|
|
const char *xpath)
|
|
{
|
|
int retval = -1;
|
|
struct clixon_client_handle *cch = chandle(ch);
|
|
char *val = NULL;
|
|
char *reason = NULL;
|
|
int ret;
|
|
|
|
clicon_debug(1, "%s", __FUNCTION__);
|
|
if (clixon_client_get_body_val(cch->cch_socket,
|
|
namespace, xpath, &val) < 0)
|
|
goto done;
|
|
if ((ret = parse_uint16(val, rval, &reason)) < 0){
|
|
clicon_err(OE_XML, errno, "parse_bool");
|
|
goto done;
|
|
}
|
|
if (ret == 0){
|
|
clicon_err(OE_XML, EINVAL, "%s", reason);
|
|
goto done;
|
|
}
|
|
retval = 0;
|
|
done:
|
|
if (reason)
|
|
free(reason);
|
|
return retval;
|
|
}
|
|
|
|
/*! Client-api get uint32
|
|
* @param[in] ch Clixon client handle
|
|
* @param[out] rval Return value
|
|
* @param[in] namespace Default namespace used for non-prefixed entries in xpath. (Alt use nsc)
|
|
* @param[in] xpath XPath
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
clixon_client_get_uint32(clixon_client_handle ch,
|
|
uint32_t *rval,
|
|
const char *namespace,
|
|
const char *xpath)
|
|
{
|
|
int retval = -1;
|
|
struct clixon_client_handle *cch = chandle(ch);
|
|
char *val = NULL;
|
|
char *reason = NULL;
|
|
int ret;
|
|
|
|
clicon_debug(1, "%s", __FUNCTION__);
|
|
if (clixon_client_get_body_val(cch->cch_socket,
|
|
namespace, xpath, &val) < 0)
|
|
goto done;
|
|
if (val == NULL){
|
|
clicon_err(OE_XML, EFAULT, "val is NULL");
|
|
goto done;
|
|
}
|
|
if ((ret = parse_uint32(val, rval, &reason)) < 0){
|
|
clicon_err(OE_XML, errno, "parse_bool");
|
|
goto done;
|
|
}
|
|
if (ret == 0){
|
|
clicon_err(OE_XML, EINVAL, "%s", reason);
|
|
goto done;
|
|
}
|
|
retval = 0;
|
|
done:
|
|
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
|
if (reason)
|
|
free(reason);
|
|
return retval;
|
|
}
|
|
|
|
/*! Client-api get uint64
|
|
* @param[in] ch Clixon client handle
|
|
* @param[out] rval Return value
|
|
* @param[in] namespace Default namespace used for non-prefixed entries in xpath. (Alt use nsc)
|
|
* @param[in] xpath XPath
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
clixon_client_get_uint64(clixon_client_handle ch,
|
|
uint64_t *rval,
|
|
const char *namespace,
|
|
const char *xpath)
|
|
{
|
|
int retval = -1;
|
|
struct clixon_client_handle *cch = chandle(ch);
|
|
char *val = NULL;
|
|
char *reason = NULL;
|
|
int ret;
|
|
|
|
clicon_debug(1, "%s", __FUNCTION__);
|
|
if (clixon_client_get_body_val(cch->cch_socket,
|
|
namespace, xpath, &val) < 0)
|
|
goto done;
|
|
if ((ret = parse_uint64(val, rval, &reason)) < 0){
|
|
clicon_err(OE_XML, errno, "parse_bool");
|
|
goto done;
|
|
}
|
|
if (ret == 0){
|
|
clicon_err(OE_XML, EINVAL, "%s", reason);
|
|
goto done;
|
|
}
|
|
retval = 0;
|
|
done:
|
|
if (reason)
|
|
free(reason);
|
|
return retval;
|
|
}
|
|
|
|
/* Access functions */
|
|
/*! Client-api get uint64
|
|
* @param[in] ch Clixon client handle
|
|
* @retval s Open socket
|
|
* @retval -1 No/closed socket
|
|
*/
|
|
int
|
|
clixon_client_socket_get(clixon_client_handle ch)
|
|
{
|
|
struct clixon_client_handle *cch = chandle(ch);
|
|
|
|
return cch->cch_socket;
|
|
}
|