* Corrected client session handling to make internal IPC socket persistent

* Applies to cli/netconf/restconf/client-api code
  * Previous behaviour:
    * Close socket after each rpc, but now keeps the socket open until the client terminates
    * Kept locks over socket life-cycle, but according to RFC 6241 7.5 a lock should be relaeased when session ends
This commit is contained in:
Olof hagsand 2021-01-27 15:07:27 +01:00
parent b41f68b677
commit f5f013c739
18 changed files with 371 additions and 145 deletions

View file

@ -55,6 +55,11 @@ Users may have to change how they access the system
### Minor changes ### Minor changes
* Corrected client session handling to make internal IPC socket persistent
* Applies to cli/netconf/restconf/client-api code
* Previous behaviour:
* Close socket after each rpc, but now keeps the socket open until the client terminates
* Kept locks over socket life-cycle, but according to RFC 6241 7.5 a lock should be relaeased when session ends
* Restconf evhtp using network namespaces implemented * Restconf evhtp using network namespaces implemented
* Added validation of clixon-restconf.yang: server-key-path and server-cert-path must be present if ssl enabled. * Added validation of clixon-restconf.yang: server-key-path and server-cert-path must be present if ssl enabled.
* Only if `CLICON_BACKEND_RESTCONF_PROCESS` is true * Only if `CLICON_BACKEND_RESTCONF_PROCESS` is true

View file

@ -91,7 +91,7 @@ backend_terminate(clicon_handle h)
yang_stmt *yspec; yang_stmt *yspec;
char *pidfile = clicon_backend_pidfile(h); char *pidfile = clicon_backend_pidfile(h);
int sockfamily = clicon_sock_family(h); int sockfamily = clicon_sock_family(h);
char *sockpath = clicon_sock(h); char *sockpath = clicon_sock_str(h);
cxobj *x; cxobj *x;
struct stat st; struct stat st;
int ss; int ss;
@ -552,6 +552,36 @@ restconf_pseudo_process_reg(clicon_handle h,
return retval; return retval;
} }
/* Debug timer */
int
backend_timer_setup(int fd,
void *arg)
{
int retval = -1;
clicon_handle h = (clicon_handle)arg;
struct timeval now;
struct timeval t;
struct timeval t1 = {10, 0};
clicon_debug(1, "%s", __FUNCTION__);
gettimeofday(&now, NULL);
backend_client_print(h, stderr);
xmldb_print(h, stderr);
fprintf(stderr, "\n");
/* Initiate new timer */
timeradd(&now, &t1, &t);
if (clixon_event_reg_timeout(t,
backend_timer_setup, /* this function */
h, /* clicon handle */
"backend timer setup") < 0)
goto done;
retval = 0;
done:
return retval;
}
/*! usage /*! usage
*/ */
static void static void
@ -559,7 +589,7 @@ usage(clicon_handle h,
char *argv0) char *argv0)
{ {
char *plgdir = clicon_backend_dir(h); char *plgdir = clicon_backend_dir(h);
char *confsock = clicon_sock(h); char *confsock = clicon_sock_str(h);
char *confpid = clicon_backend_pidfile(h); char *confpid = clicon_backend_pidfile(h);
char *group = clicon_sock_group(h); char *group = clicon_sock_group(h);
@ -829,7 +859,7 @@ main(int argc,
goto done; goto done;
} }
sockfamily = clicon_sock_family(h); sockfamily = clicon_sock_family(h);
if ((sock = clicon_sock(h)) == NULL){ if ((sock = clicon_sock_str(h)) == NULL){
clicon_err(OE_FATAL, 0, "sock not set"); clicon_err(OE_FATAL, 0, "sock not set");
goto done; goto done;
} }
@ -1132,7 +1162,9 @@ main(int argc,
/* Start session-id for clients */ /* Start session-id for clients */
clicon_session_id_set(h, 0); clicon_session_id_set(h, 0);
if (clicon_debug_get() &&
backend_timer_setup(0, h) < 0)
goto done;
if (stream_timer_setup(0, h) < 0) if (stream_timer_setup(0, h) < 0)
goto done; goto done;
if (clixon_event_loop() < 0) if (clixon_event_loop() < 0)

View file

@ -198,7 +198,7 @@ backend_socket_init(clicon_handle h)
{ {
char *sock; /* unix path or ip address string */ char *sock; /* unix path or ip address string */
if ((sock = clicon_sock(h)) == NULL){ if ((sock = clicon_sock_str(h)) == NULL){
clicon_err(OE_FATAL, 0, "CLICON_SOCK option not set"); clicon_err(OE_FATAL, 0, "CLICON_SOCK option not set");
return -1; return -1;
} }

View file

@ -46,7 +46,7 @@ typedef void *clixon_client_handle;
typedef enum { typedef enum {
CLIXON_CLIENT_IPC, /* Internal IPC API, only experimental use */ CLIXON_CLIENT_IPC, /* Internal IPC API, only experimental use */
CLIXON_CLIENT_NETCONF, /* External Netconf */ CLIXON_CLIENT_NETCONF, /* External Netconf */
CLIXON_CLIENT_SSH /* External Netconf over SSH */ CLIXON_CLIENT_SSH /* NYI External Netconf over SSH */
} clixon_client_type; } clixon_client_type;
/* /*
@ -68,6 +68,9 @@ int clixon_client_get_uint16(clixon_client_handle ch, uint16_t *rval, const ch
int clixon_client_get_uint32(clixon_client_handle ch, uint32_t *rval, const char *xnamespace, const char *xpath); int clixon_client_get_uint32(clixon_client_handle ch, uint32_t *rval, const char *xnamespace, const char *xpath);
int clixon_client_get_uint64(clixon_client_handle ch, uint64_t *rval, const char *xnamespace, const char *xpath); int clixon_client_get_uint64(clixon_client_handle ch, uint64_t *rval, const char *xnamespace, const char *xpath);
/* Access functions */
int clixon_client_socket_get(clixon_client_handle ch);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View file

@ -102,10 +102,14 @@ int clicon_username_set(clicon_handle h, void *username);
enum startup_status clicon_startup_status_get(clicon_handle h); enum startup_status clicon_startup_status_get(clicon_handle h);
int clicon_startup_status_set(clicon_handle h, enum startup_status status); int clicon_startup_status_set(clicon_handle h, enum startup_status status);
/* Set and get socket fd (ie backend server socket / restconf fcgx socket */ /* Set and get server socket fd (ie backend server socket / restconf fcgi socket */
int clicon_socket_get(clicon_handle h); int clicon_socket_get(clicon_handle h);
int clicon_socket_set(clicon_handle h, int s); int clicon_socket_set(clicon_handle h, int s);
/* Set and get client socket fd (ie client cli / netconf / restconf / client-api socket */
int clicon_client_socket_get(clicon_handle h);
int clicon_client_socket_set(clicon_handle h, int s);
/*! Set and get module state full and brief cached tree */ /*! Set and get module state full and brief cached tree */
cxobj *clicon_modst_cache_get(clicon_handle h, int brief); cxobj *clicon_modst_cache_get(clicon_handle h, int brief);
int clicon_modst_cache_set(clicon_handle h, int brief, cxobj *xms); int clicon_modst_cache_set(clicon_handle h, int brief, cxobj *xms);

View file

@ -189,7 +189,7 @@ static inline int clicon_cli_tab_mode(clicon_handle h){
static inline char *clicon_cli_model_treename(clicon_handle h){ static inline char *clicon_cli_model_treename(clicon_handle h){
return clicon_option_str(h, "CLICON_CLI_MODEL_TREENAME"); return clicon_option_str(h, "CLICON_CLI_MODEL_TREENAME");
} }
static inline char *clicon_sock(clicon_handle h){ static inline char *clicon_sock_str(clicon_handle h){
return clicon_option_str(h, "CLICON_SOCK"); return clicon_option_str(h, "CLICON_SOCK");
} }
static inline char *clicon_sock_group(clicon_handle h){ static inline char *clicon_sock_group(clicon_handle h){

View file

@ -49,8 +49,8 @@ typedef int (proc_cb_t)(clicon_handle h, process_entry_t *pe, char **operation);
/* /*
* Prototypes * Prototypes
*/ */
int clixon_proc_socket(char **argv, pid_t *pid, int *fdin, int *fdout); int clixon_proc_socket(char **argv, pid_t *pid, int *sock);
int clixon_proc_socket_close(pid_t pid, int fdin, int fdout); int clixon_proc_socket_close(pid_t pid, int sock);
int clixon_proc_background(char **argv, const char *netns, pid_t *pid); int clixon_proc_background(char **argv, const char *netns, pid_t *pid);
int clixon_process_register(clicon_handle h, const char *name, const char *netns, proc_cb_t *callback, char **argv, int argc); int clixon_process_register(clicon_handle h, const char *name, const char *netns, proc_cb_t *callback, char **argv, int argc);
int clixon_process_delete_all(clicon_handle h); int clixon_process_delete_all(clicon_handle h);

View file

@ -83,9 +83,9 @@ int clicon_rpc_connect_inet(clicon_handle h,
uint16_t port, uint16_t port,
int *sock0); int *sock0);
int clicon_rpc(int fdin, int fdout, struct clicon_msg *msg, char **xret); int clicon_rpc(int sock, struct clicon_msg *msg, char **xret);
int clicon_rpc1(int fdin, int fdout, cbuf *msgin, cbuf *msgret); int clicon_rpc1(int sock, cbuf *msgin, cbuf *msgret);
int clicon_msg_send(int s, struct clicon_msg *msg); int clicon_msg_send(int s, struct clicon_msg *msg);

View file

@ -84,9 +84,9 @@
struct clixon_client_handle{ struct clixon_client_handle{
uint32_t cch_magic; /* magic number */ uint32_t cch_magic; /* magic number */
clixon_client_type cch_type; /* Clixon socket type */ clixon_client_type cch_type; /* Clixon socket type */
int cch_fdin; /* Input file descriptor */ int cch_socket; /* Input/output socket */
int cch_fdout; /* Output file descriptor, Only applies for NETCONF/SSH */
int cch_pid; /* Sub-process-id Only applies for NETCONF/SSH */ 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 /*! Check struct magic number for sanity checks
@ -104,10 +104,9 @@ clixon_client_handle_check(clixon_client_handle ch)
} }
/*! Initialize Clixon client API /*! Initialize Clixon client API
* @param[in] name Name of client (NYI)
* @param[in] estream Error/debug file (NULL: syslog)
* @param[in] debug Clixon debug flag
* @param[in] config_file Clixon configuration file, or NULL for default * @param[in] config_file Clixon configuration file, or NULL for default
* @retval h Clixon handler
* @retval NULL Error
* @see clixon_client_close * @see clixon_client_close
*/ */
clixon_handle clixon_handle
@ -129,6 +128,7 @@ clixon_client_init(const char *config_file)
} }
/*! Deallocate everything from client_init /*! Deallocate everything from client_init
* @param[in] h Clixon handle
* @see clixon_client_init * @see clixon_client_init
*/ */
int int
@ -139,12 +139,67 @@ clixon_client_terminate(clicon_handle h)
return 0; 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\">"
"<%slock><target><%s/></target></%slock></rpc>",
NETCONF_BASE_NAMESPACE,
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;
}
/*! Connect client to clixon backend according to config and return a socket /*! Connect client to clixon backend according to config and return a socket
* @param[in] h Clixon handle * @param[in] h Clixon handle
* @param[out] fdin Open netconf output file descr * @param[in] socktype Type of socket, internal/external/netconf/ssh
* @param[out] fdout Open netconf output file descr. If socket same as fdin * @retval ch Clixon session handler
* @retval cs Clixon session handler * @retval NULL Error
* @retval NULL Error
* @see clixon_client_disconnect Close the socket returned here * @see clixon_client_disconnect Close the socket returned here
*/ */
clixon_client_handle clixon_client_handle
@ -170,9 +225,8 @@ clixon_client_connect(clicon_handle h,
cch->cch_type = socktype; cch->cch_type = socktype;
switch (socktype){ switch (socktype){
case CLIXON_CLIENT_IPC: case CLIXON_CLIENT_IPC:
if (clicon_rpc_connect(h, &cch->cch_fdin) < 0) if (clicon_rpc_connect(h, &cch->cch_socket) < 0)
goto err; goto err;
cch->cch_fdout = cch->cch_fdin;
break; break;
case CLIXON_CLIENT_NETCONF: case CLIXON_CLIENT_NETCONF:
nr = 7; nr = 7;
@ -203,23 +257,29 @@ clixon_client_connect(clicon_handle h,
} }
argv[i++] = NULL; argv[i++] = NULL;
assert(i==nr); assert(i==nr);
if (clixon_proc_socket(argv, &cch->cch_pid, &cch->cch_fdin, &cch->cch_fdout) < 0){ if (clixon_proc_socket(argv, &cch->cch_pid, &cch->cch_socket) < 0){
goto err; goto err;
} }
break; break;
case CLIXON_CLIENT_SSH: case CLIXON_CLIENT_SSH:
break; break;
} /* switch */ } /* switch */
/* lock */
if (clixon_client_lock(cch->cch_socket, 1, "running") < 0)
goto err;
cch->cch_locked = 1;
done: done:
clicon_debug(1, "%s retval:%p", __FUNCTION__, cch);
return cch; return cch;
err: err:
free(cch); if (cch)
clixon_client_disconnect(cch);
cch = NULL; cch = NULL;
goto done; goto done;
} }
/*! Connect client to clixon backend according to config and return a socket /*! Connect client to clixon backend according to config and return a socket
* @param[in] cch Clixon client session handle * @param[in] ch Clixon client session handle
* @see clixon_client_connect where the handle is created * @see clixon_client_connect where the handle is created
* The handle is deallocated * The handle is deallocated
*/ */
@ -234,14 +294,17 @@ clixon_client_disconnect(clixon_client_handle ch)
clicon_err(OE_XML, EINVAL, "Expected cch handle"); clicon_err(OE_XML, EINVAL, "Expected cch handle");
goto done; goto done;
} }
/* unlock (if locked) */
if (cch->cch_locked)
;// (void)clixon_client_lock(cch->cch_socket, 0, "running");
switch(cch->cch_type){ switch(cch->cch_type){
case CLIXON_CLIENT_IPC: case CLIXON_CLIENT_IPC:
close(cch->cch_fdin); close(cch->cch_socket);
break; break;
case CLIXON_CLIENT_NETCONF: case CLIXON_CLIENT_NETCONF:
if (clixon_proc_socket_close(cch->cch_pid, if (clixon_proc_socket_close(cch->cch_pid,
cch->cch_fdin, cch->cch_socket) < 0)
cch->cch_fdout) < 0)
goto done; goto done;
break; break;
case CLIXON_CLIENT_SSH: case CLIXON_CLIENT_SSH:
@ -253,6 +316,11 @@ clixon_client_disconnect(clixon_client_handle ch)
return retval; 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 static int
clixon_xml_bottom(cxobj *xtop, clixon_xml_bottom(cxobj *xtop,
cxobj **xbotp) cxobj **xbotp)
@ -283,14 +351,15 @@ clixon_xml_bottom(cxobj *xtop,
/*! Internal function to construct a get-config and query a value from the backend /*! Internal function to construct a get-config and query a value from the backend
* *
* @param[in] sock Stream socket * @param[in] sock Socket
* @param[in] xpath XPath
* @param[in] namespace Default namespace used for non-prefixed entries in xpath. (Alt use nsc) * @param[in] namespace Default namespace used for non-prefixed entries in xpath. (Alt use nsc)
* @param[out] val String value * @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 static int
clixon_client_get_xdata(int fdin, clixon_client_get_xdata(int sock,
int fdout,
const char *namespace, const char *namespace,
const char *xpath, const char *xpath,
cxobj **xdata) cxobj **xdata)
@ -303,6 +372,7 @@ clixon_client_get_xdata(int fdin,
const char *db = "running"; const char *db = "running";
cvec *nsc = NULL; cvec *nsc = NULL;
clicon_debug(1, "%s", __FUNCTION__);
if ((msg = cbuf_new()) == NULL){ if ((msg = cbuf_new()) == NULL){
clicon_err(OE_PLUGIN, errno, "cbuf_new"); clicon_err(OE_PLUGIN, errno, "cbuf_new");
goto done; goto done;
@ -327,7 +397,7 @@ clixon_client_get_xdata(int fdin,
cprintf(msg, "/>"); cprintf(msg, "/>");
} }
cprintf(msg, "</get-config></rpc>"); cprintf(msg, "</get-config></rpc>");
if (clicon_rpc1(fdin, fdout, msg, msgret) < 0) if (clicon_rpc1(sock, msg, msgret) < 0)
goto done; goto done;
if (clixon_xml_parse_string(cbuf_get(msgret), YB_NONE, NULL, &xret, NULL) < 0) if (clixon_xml_parse_string(cbuf_get(msgret), YB_NONE, NULL, &xret, NULL) < 0)
goto done; goto done;
@ -357,35 +427,61 @@ clixon_client_get_xdata(int fdin,
return retval; 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 static int
clixon_client_get_val(int fdin, clixon_client_get_body_val(int sock,
int fdout, const char *namespace,
const char *namespace, const char *xpath,
const char *xpath, char **val)
char **val)
{ {
int retval = -1; int retval = -1;
cxobj *xdata = NULL; cxobj *xdata = NULL;
cxobj *xobj; cxobj *xobj = NULL;
clicon_debug(1, "%s", __FUNCTION__);
if (val == NULL){ if (val == NULL){
clicon_err(OE_XML, EINVAL, "Expected val"); clicon_err(OE_XML, EINVAL, "Expected val");
goto done; goto done;
} }
if (clixon_client_get_xdata(fdin, fdout, namespace, xpath, &xdata) < 0) if (clixon_client_get_xdata(sock, namespace, xpath, &xdata) < 0)
goto done; goto done;
if (xdata == NULL){
clicon_err(OE_XML, ENODATA, "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, ENODATA, "Value not found");
goto done;
}
if (clixon_xml_bottom(xdata, &xobj) < 0) if (clixon_xml_bottom(xdata, &xobj) < 0)
goto done; goto done;
if (xobj == NULL){ if (xobj == NULL){
clicon_err(OE_XML, EFAULT, "No bottom xml found"); clicon_err(OE_XML, ENODATA, "No xml value found");
goto done; goto done;
} }
*val = xml_body(xobj); *val = xml_body(xobj);
retval = 0; retval = 0;
done: done:
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
return 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 int
clixon_client_get_bool(clixon_client_handle ch, clixon_client_get_bool(clixon_client_handle ch,
int *rval, int *rval,
@ -400,9 +496,8 @@ clixon_client_get_bool(clixon_client_handle ch,
uint8_t val0=0; uint8_t val0=0;
clicon_debug(1, "%s", __FUNCTION__); clicon_debug(1, "%s", __FUNCTION__);
if (clixon_client_get_val(cch->cch_fdin, if (clixon_client_get_body_val(cch->cch_socket,
cch->cch_fdout, namespace, xpath, &val) < 0)
namespace, xpath, &val) < 0)
goto done; goto done;
if ((ret = parse_bool(val, &val0, &reason)) < 0){ if ((ret = parse_bool(val, &val0, &reason)) < 0){
clicon_err(OE_XML, errno, "parse_bool"); clicon_err(OE_XML, errno, "parse_bool");
@ -420,12 +515,14 @@ clixon_client_get_bool(clixon_client_handle ch,
return retval; return retval;
} }
/*! Get string using get-config /*! Client-api get string
* @param[in] sock Stream socket * @param[in] ch Clixon client handle
* @param[out] rval Return value string * @param[out] rval Return value string
* @param[in] n Length of string * @param[in] n Length of string
* @param[in] xpath XPath
* @param[in] namespace Default namespace used for non-prefixed entries in xpath. (Alt use nsc) * @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 int
clixon_client_get_str(clixon_client_handle ch, clixon_client_get_str(clixon_client_handle ch,
@ -439,9 +536,8 @@ clixon_client_get_str(clixon_client_handle ch,
char *val = NULL; char *val = NULL;
clicon_debug(1, "%s", __FUNCTION__); clicon_debug(1, "%s", __FUNCTION__);
if (clixon_client_get_val(cch->cch_fdin, if (clixon_client_get_body_val(cch->cch_socket,
cch->cch_fdout, namespace, xpath, &val) < 0)
namespace, xpath, &val) < 0)
goto done; goto done;
strncpy(rval, val, n-1); strncpy(rval, val, n-1);
rval[n-1]= '\0'; rval[n-1]= '\0';
@ -450,6 +546,14 @@ clixon_client_get_str(clixon_client_handle ch,
return retval; 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 int
clixon_client_get_uint8(clixon_client_handle ch, clixon_client_get_uint8(clixon_client_handle ch,
uint8_t *rval, uint8_t *rval,
@ -463,9 +567,8 @@ clixon_client_get_uint8(clixon_client_handle ch,
int ret; int ret;
clicon_debug(1, "%s", __FUNCTION__); clicon_debug(1, "%s", __FUNCTION__);
if (clixon_client_get_val(cch->cch_fdin, if (clixon_client_get_body_val(cch->cch_socket,
cch->cch_fdout, namespace, xpath, &val) < 0)
namespace, xpath, &val) < 0)
goto done; goto done;
if ((ret = parse_uint8(val, rval, &reason)) < 0){ if ((ret = parse_uint8(val, rval, &reason)) < 0){
clicon_err(OE_XML, errno, "parse_bool"); clicon_err(OE_XML, errno, "parse_bool");
@ -482,6 +585,14 @@ clixon_client_get_uint8(clixon_client_handle ch,
return retval; 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 int
clixon_client_get_uint16(clixon_client_handle ch, clixon_client_get_uint16(clixon_client_handle ch,
uint16_t *rval, uint16_t *rval,
@ -495,9 +606,8 @@ clixon_client_get_uint16(clixon_client_handle ch,
int ret; int ret;
clicon_debug(1, "%s", __FUNCTION__); clicon_debug(1, "%s", __FUNCTION__);
if (clixon_client_get_val(cch->cch_fdin, if (clixon_client_get_body_val(cch->cch_socket,
cch->cch_fdout, namespace, xpath, &val) < 0)
namespace, xpath, &val) < 0)
goto done; goto done;
if ((ret = parse_uint16(val, rval, &reason)) < 0){ if ((ret = parse_uint16(val, rval, &reason)) < 0){
clicon_err(OE_XML, errno, "parse_bool"); clicon_err(OE_XML, errno, "parse_bool");
@ -514,6 +624,14 @@ clixon_client_get_uint16(clixon_client_handle ch,
return retval; 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 int
clixon_client_get_uint32(clixon_client_handle ch, clixon_client_get_uint32(clixon_client_handle ch,
uint32_t *rval, uint32_t *rval,
@ -527,10 +645,13 @@ clixon_client_get_uint32(clixon_client_handle ch,
int ret; int ret;
clicon_debug(1, "%s", __FUNCTION__); clicon_debug(1, "%s", __FUNCTION__);
if (clixon_client_get_val(cch->cch_fdin, if (clixon_client_get_body_val(cch->cch_socket,
cch->cch_fdout, namespace, xpath, &val) < 0)
namespace, xpath, &val) < 0)
goto done; goto done;
if (val == NULL){
clicon_err(OE_XML, EFAULT, "val is NULL");
goto done;
}
if ((ret = parse_uint32(val, rval, &reason)) < 0){ if ((ret = parse_uint32(val, rval, &reason)) < 0){
clicon_err(OE_XML, errno, "parse_bool"); clicon_err(OE_XML, errno, "parse_bool");
goto done; goto done;
@ -541,11 +662,20 @@ clixon_client_get_uint32(clixon_client_handle ch,
} }
retval = 0; retval = 0;
done: done:
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
if (reason) if (reason)
free(reason); free(reason);
return retval; 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 int
clixon_client_get_uint64(clixon_client_handle ch, clixon_client_get_uint64(clixon_client_handle ch,
uint64_t *rval, uint64_t *rval,
@ -559,9 +689,8 @@ clixon_client_get_uint64(clixon_client_handle ch,
int ret; int ret;
clicon_debug(1, "%s", __FUNCTION__); clicon_debug(1, "%s", __FUNCTION__);
if (clixon_client_get_val(cch->cch_fdin, if (clixon_client_get_body_val(cch->cch_socket,
cch->cch_fdout, namespace, xpath, &val) < 0)
namespace, xpath, &val) < 0)
goto done; goto done;
if ((ret = parse_uint64(val, rval, &reason)) < 0){ if ((ret = parse_uint64(val, rval, &reason)) < 0){
clicon_err(OE_XML, errno, "parse_bool"); clicon_err(OE_XML, errno, "parse_bool");
@ -577,3 +706,17 @@ clixon_client_get_uint64(clixon_client_handle ch,
free(reason); free(reason);
return retval; 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;
}

View file

@ -520,7 +520,7 @@ clicon_startup_status_set(clicon_handle h,
return 0; return 0;
} }
/*! Get socket fd (ie backend server socket / restconf fcgx socket) /*! Get server socket fd (ie backend server socket / restconf fcgi socket)
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @retval -1 No open socket * @retval -1 No open socket
* @retval s Socket * @retval s Socket
@ -536,7 +536,7 @@ clicon_socket_get(clicon_handle h)
return *(int*)p; return *(int*)p;
} }
/*! Set socket fd (ie backend server socket / restconf fcgx socket) /*! Set server socket fd (ie backend server socket / restconf fcgi socket)
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] s Open socket (or -1 to close) * @param[in] s Open socket (or -1 to close)
* @retval 0 OK * @retval 0 OK
@ -553,6 +553,39 @@ clicon_socket_set(clicon_handle h,
return clicon_hash_add(cdat, "socket", &s, sizeof(int))==NULL?-1:0; return clicon_hash_add(cdat, "socket", &s, sizeof(int))==NULL?-1:0;
} }
/*! Get client socket fd (ie client cli / netconf / restconf / client-api socket
* @param[in] h Clicon handle
* @retval -1 No open socket
* @retval s Socket
*/
int
clicon_client_socket_get(clicon_handle h)
{
clicon_hash_t *cdat = clicon_data(h);
void *p;
if ((p = clicon_hash_value(cdat, "client-socket", NULL)) == NULL)
return -1;
return *(int*)p;
}
/*! Set client socket fd (ie client cli / netconf / restconf / client-api socket
* @param[in] h Clicon handle
* @param[in] s Open socket (or -1 to close)
* @retval 0 OK
* @retval -1 Error
*/
int
clicon_client_socket_set(clicon_handle h,
int s)
{
clicon_hash_t *cdat = clicon_data(h);
if (s == -1)
return clicon_hash_del(cdat, "client-socket");
return clicon_hash_add(cdat, "client-socket", &s, sizeof(int))==NULL?-1:0;
}
/*! Get module state cache /*! Get module state cache
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] brief 0: Full module state tree, 1: Brief tree (datastore) * @param[in] brief 0: Full module state tree, 1: Brief tree (datastore)

View file

@ -33,6 +33,7 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/param.h> #include <sys/param.h>
#include <sys/wait.h>
#include <netinet/in.h> #include <netinet/in.h>
#include "clixon_err.h" #include "clixon_err.h"
@ -158,6 +159,14 @@ create_socket(struct sockaddr *sa,
return retval; return retval;
} }
/*! Fork a child, create and bind a socket in a separate network namespace and send back to parent
*
* @param[in] netns Network namespace
* @param[in] sa Socketaddress
* @param[in] sa_len Length of sa. Tecynicaliyu to be independent of sockaddr sa_len
* @param[in] backlog Listen backlog, queie of pending connections
* @param[out] sock Server socket (bound for accept)
*/
int int
fork_netns_socket(const char *netns, fork_netns_socket(const char *netns,
struct sockaddr *sa, struct sockaddr *sa,
@ -166,8 +175,9 @@ fork_netns_socket(const char *netns,
int *sock) int *sock)
{ {
int retval = -1; int retval = -1;
int sp[2] = {0,}; int sp[2] = {-1, -1};
pid_t child; pid_t child;
int status = 0;
if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, sp) < 0){ if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, sp) < 0){
clicon_err(OE_UNIX, errno, "socketpair"); clicon_err(OE_UNIX, errno, "socketpair");
@ -209,7 +219,8 @@ fork_netns_socket(const char *netns,
if (get_sock(sp[0], sock) < 0) if (get_sock(sp[0], sock) < 0)
goto done; goto done;
close(sp[0]); close(sp[0]);
retval = 0; if(waitpid(child, &status, 0) == child)
retval = WEXITSTATUS(status);
done: done:
return retval; return retval;
} }

View file

@ -105,23 +105,21 @@ clixon_proc_sigint(int sig)
kill(_clicon_proc_child, SIGINT); kill(_clicon_proc_child, SIGINT);
} }
/*! /*! Fork a child, exec a child and setup socket to child and return to caller
* @param[in] argv NULL-terminated Argument vector * @param[in] argv NULL-terminated Argument vector
* @param[in] doerr If non-zero, stderr will be directed to the pipe as well. * @param[in] doerr If non-zero, stderr will be directed to the pipe as well.
* @param[out] s Socket * @param[out] s Socket
* @retval O OK * @retval O OK
* @retval -1 Error. * @retval -1 Error.
* @note need to cleanup and wait on sub-process * @see clixon_proc_socket_close close sockets, kill child and wait for child termination
*/ */
int int
clixon_proc_socket(char **argv, clixon_proc_socket(char **argv,
pid_t *pid, pid_t *pid,
int *fdin, int *sock)
int *fdout)
{ {
int retval = -1; int retval = -1;
int p2c[2] = { -1, -1 }; /* parent->child */ int sp[2] = {-1, -1};
int c2p[2] = { -1, -1 }; /* child->parent */
pid_t child; pid_t child;
sigfn_t oldhandler = NULL; sigfn_t oldhandler = NULL;
sigset_t oset; sigset_t oset;
@ -131,15 +129,10 @@ clixon_proc_socket(char **argv,
clicon_err(OE_UNIX, EINVAL, "argv is NULL"); clicon_err(OE_UNIX, EINVAL, "argv is NULL");
goto done; goto done;
} }
if (pipe2(p2c, O_DIRECT) == -1){ /* parent->child */ if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, sp) < 0){
clicon_err(OE_UNIX, errno, "pipe"); clicon_err(OE_UNIX, errno, "socketpair");
goto done; goto done;
} }
if (pipe2(c2p, O_DIRECT) == -1){ /* child->parent */
clicon_err(OE_UNIX, errno, "pipe");
goto done;
}
// clicon_debug(1, "%s p2c: %d -> %d c2p: %d <- %d", __FUNCTION__, p2c[1], p2c[0], c2p[0], c2p[1]);
sigprocmask(0, NULL, &oset); sigprocmask(0, NULL, &oset);
set_signal(SIGINT, clixon_proc_sigint, &oldhandler); set_signal(SIGINT, clixon_proc_sigint, &oldhandler);
sig++; sig++;
@ -152,24 +145,18 @@ clixon_proc_socket(char **argv,
clicon_signal_unblock(0); clicon_signal_unblock(0);
signal(SIGTSTP, SIG_IGN); signal(SIGTSTP, SIG_IGN);
close(sp[0]);
close(0); close(0);
if (dup2(sp[1], STDIN_FILENO) < 0){
if (dup2(p2c[0], STDIN_FILENO) < 0){
// if (dup(p2c[0]) < 0){
perror("dup2"); perror("dup2");
return -1; return -1;
} }
close(p2c[1]);
close(p2c[0]);
close(1); close(1);
if (dup2(c2p[1], STDOUT_FILENO) < 0){ if (dup2(sp[1], STDOUT_FILENO) < 0){
// if (dup(c2p[1]) < 0){
perror("dup2"); perror("dup2");
return -1; return -1;
} }
close(c2p[1]); close(sp[1]);
close(c2p[0]);
if (execvp(argv[0], argv) < 0){ if (execvp(argv[0], argv) < 0){
perror("execvp"); perror("execvp");
@ -178,11 +165,9 @@ clixon_proc_socket(char **argv,
exit(-1); /* Shouldnt reach here */ exit(-1); /* Shouldnt reach here */
} }
/* Parent */ /* Parent */
close(p2c[0]); close(sp[1]);
close(c2p[1]);
*pid = child; *pid = child;
*fdout = p2c[1]; *sock = sp[0];
*fdin = c2p[0];
retval = 0; retval = 0;
done: done:
if (sig){ /* Restore sigmask and fn */ if (sig){ /* Restore sigmask and fn */
@ -192,18 +177,18 @@ clixon_proc_socket(char **argv,
return retval; return retval;
} }
/*!
* @see clixon_proc_socket which creates the child and sockets closed and killed here
*/
int int
clixon_proc_socket_close(pid_t pid, clixon_proc_socket_close(pid_t pid,
int fdin, int sock)
int fdout)
{ {
int retval = -1; int retval = -1;
int status; int status;
if (fdin != -1) if (sock != -1)
close(fdin); close(sock); /* usually kills */
if (fdout != -1)
close(fdout); /* Usually kills */
kill(pid, SIGTERM); kill(pid, SIGTERM);
// usleep(100000); /* Wait for child to finish */ // usleep(100000); /* Wait for child to finish */
if(waitpid(pid, &status, 0) == pid) if(waitpid(pid, &status, 0) == pid)

View file

@ -435,6 +435,7 @@ clicon_msg_rcv1(int s,
int poll; int poll;
clicon_debug(1, "%s", __FUNCTION__); clicon_debug(1, "%s", __FUNCTION__);
*eof = 0;
memset(buf, 0, sizeof(buf)); memset(buf, 0, sizeof(buf));
while (1){ while (1){
if ((len = read(s, buf, sizeof(buf))) < 0){ if ((len = read(s, buf, sizeof(buf))) < 0){
@ -446,7 +447,7 @@ clicon_msg_rcv1(int s,
} }
} /* read */ } /* read */
if (len == 0){ /* EOF */ if (len == 0){ /* EOF */
// cc_closed++; *eof = 1;
close(s); close(s);
goto ok; goto ok;
} }
@ -473,8 +474,6 @@ clicon_msg_rcv1(int s,
retval = 0; retval = 0;
done: done:
clicon_debug(1, "%s done", __FUNCTION__); clicon_debug(1, "%s done", __FUNCTION__);
// if (cc_closed)
// retval = -1;
return retval; return retval;
} }
@ -597,31 +596,29 @@ clicon_rpc_connect_inet(clicon_handle h,
* errno set to ENOTCONN which means that socket is now closed probably * errno set to ENOTCONN which means that socket is now closed probably
* due to remote peer disconnecting. The caller may have to do something,... * due to remote peer disconnecting. The caller may have to do something,...
* *
* @param[in] fdin Input file descriptor * @param[in] sock Socket / file descriptor
* @param[in] fdout Output file descriptor (for socket same as fdin)
* @param[in] msg CLICON msg data structure. It has fixed header and variable body. * @param[in] msg CLICON msg data structure. It has fixed header and variable body.
* @param[out] xret Returned data as netconf xml tree. * @param[out] xret Returned data as netconf xml tree.
* @retval 0 OK * @retval 0 OK
* @retval -1 Error * @retval -1 Error
*/ */
int int
clicon_rpc(int fdin, clicon_rpc(int sock,
int fdout, struct clicon_msg *msg,
struct clicon_msg *msg, char **ret)
char **ret)
{ {
int retval = -1; int retval = -1;
struct clicon_msg *reply = NULL; struct clicon_msg *reply = NULL;
int eof; int eof;
char *data = NULL; char *data = NULL;
if (clicon_msg_send(fdout, msg) < 0) if (clicon_msg_send(sock, msg) < 0)
goto done; goto done;
if (clicon_msg_rcv(fdin, &reply, &eof) < 0) if (clicon_msg_rcv(sock, &reply, &eof) < 0)
goto done; goto done;
if (eof){ if (eof){
clicon_err(OE_PROTO, ESHUTDOWN, "Unexpected close of CLICON_SOCK. Clixon backend daemon may have crashed."); clicon_err(OE_PROTO, ESHUTDOWN, "Unexpected close of CLICON_SOCK. Clixon backend daemon may have crashed.");
close(fdin); /* assume socket */ close(sock); /* assume socket */
errno = ESHUTDOWN; errno = ESHUTDOWN;
goto done; goto done;
} }
@ -645,8 +642,7 @@ clicon_rpc(int fdin,
* errno set to ENOTCONN which means that socket is now closed probably * errno set to ENOTCONN which means that socket is now closed probably
* due to remote peer disconnecting. The caller may have to do something,... * due to remote peer disconnecting. The caller may have to do something,...
* *
* @param[in] fdin Input file descriptor * @param[in] sock Socket / file descriptor
* @param[in] fdout Output file descriptor (for socket same as fdin)
* @param[in] msgin CLICON msg data structure. It has fixed header and variable body. * @param[in] msgin CLICON msg data structure. It has fixed header and variable body.
* @param[out] msgret Returned data as netconf xml tree. * @param[out] msgret Returned data as netconf xml tree.
* @retval 0 OK * @retval 0 OK
@ -654,22 +650,21 @@ clicon_rpc(int fdin,
* see clicon_rpc using clicon_msg * see clicon_rpc using clicon_msg
*/ */
int int
clicon_rpc1(int fdin, clicon_rpc1(int sock,
int fdout, cbuf *msg,
cbuf *msg, cbuf *msgret)
cbuf *msgret)
{ {
int retval = -1; int retval = -1;
int eof; int eof;
clicon_debug(1, "%s", __FUNCTION__); clicon_debug(1, "%s", __FUNCTION__);
if (clicon_msg_send1(fdout, msg) < 0) if (clicon_msg_send1(sock, msg) < 0)
goto done; goto done;
if (clicon_msg_rcv1(fdin, msgret, &eof) < 0) if (clicon_msg_rcv1(sock, msgret, &eof) < 0)
goto done; goto done;
if (eof){ if (eof){
clicon_err(OE_PROTO, ESHUTDOWN, "Unexpected close of CLICON_SOCK. Clixon backend daemon may have crashed."); clicon_err(OE_PROTO, ESHUTDOWN, "Unexpected close of CLICON_SOCK. Clixon backend daemon may have crashed.");
close(fdin); /* assume socket */ close(sock);
errno = ESHUTDOWN; errno = ESHUTDOWN;
goto done; goto done;
} }

View file

@ -86,20 +86,20 @@
*/ */
int int
clicon_rpc_connect(clicon_handle h, clicon_rpc_connect(clicon_handle h,
int *sock0) int *sockp)
{ {
int retval = -1; int retval = -1;
char *sock = NULL; char *sockstr = NULL;
int port; int port;
if ((sock = clicon_sock(h)) == NULL){ if ((sockstr = clicon_sock_str(h)) == NULL){
clicon_err(OE_FATAL, 0, "CLICON_SOCK option not set"); clicon_err(OE_FATAL, 0, "CLICON_SOCK option not set");
goto done; goto done;
} }
/* What to do if inet socket? */ /* What to do if inet socket? */
switch (clicon_sock_family(h)){ switch (clicon_sock_family(h)){
case AF_UNIX: case AF_UNIX:
if (clicon_rpc_connect_unix(h, sock, sock0) < 0){ if (clicon_rpc_connect_unix(h, sockstr, sockp) < 0){
#if 0 #if 0
if (errno == ESHUTDOWN) if (errno == ESHUTDOWN)
/* Maybe could reconnect on a higher layer, but lets fail /* Maybe could reconnect on a higher layer, but lets fail
@ -118,7 +118,7 @@ clicon_rpc_connect(clicon_handle h,
clicon_err(OE_FATAL, 0, "CLICON_SOCK_PORT not set"); clicon_err(OE_FATAL, 0, "CLICON_SOCK_PORT not set");
goto done; goto done;
} }
if (clicon_rpc_connect_inet(h, sock, port, sock0) < 0) if (clicon_rpc_connect_inet(h, sockstr, port, sockp) < 0)
goto done; goto done;
break; break;
} }
@ -152,9 +152,12 @@ clicon_rpc_msg(clicon_handle h,
#endif #endif
clicon_debug(1, "%s request:%s", __FUNCTION__, msg->op_body); clicon_debug(1, "%s request:%s", __FUNCTION__, msg->op_body);
/* Create a socket and connect to it, either UNIX, IPv4 or IPv6 per config options */ /* Create a socket and connect to it, either UNIX, IPv4 or IPv6 per config options */
if (clicon_rpc_connect(h, &s) < 0) if ((s = clicon_client_socket_get(h)) < 0){
goto done; if (clicon_rpc_connect(h, &s) < 0)
if (clicon_rpc(s, s, msg, &retdata) < 0) goto done;
clicon_client_socket_set(h, s);
}
if (clicon_rpc(s, msg, &retdata) < 0)
goto done; goto done;
clicon_debug(1, "%s retdata:%s", __FUNCTION__, retdata); clicon_debug(1, "%s retdata:%s", __FUNCTION__, retdata);
@ -177,8 +180,6 @@ clicon_rpc_msg(clicon_handle h,
} }
retval = 0; retval = 0;
done: done:
if (s != -1)
close(s);
if (retdata) if (retdata)
free(retdata); free(retdata);
if (xret) if (xret)
@ -308,7 +309,6 @@ clicon_rpc_netconf_xml(clicon_handle h,
return retval; return retval;
} }
/*! Get database configuration /*! Get database configuration
* Same as clicon_proto_change just with a cvec instead of lvec * Same as clicon_proto_change just with a cvec instead of lvec
* @param[in] h CLICON handle * @param[in] h CLICON handle
@ -786,10 +786,12 @@ clicon_rpc_get(clicon_handle h,
return retval; return retval;
} }
/*! Close a (user) session /*! Send a close a netconf user session. Socket is also closed if still open
* @param[in] h CLICON handle * @param[in] h CLICON handle
* @retval 0 OK * @retval 0 OK
* @retval -1 Error and logged to syslog * @retval -1 Error and logged to syslog
* Session is implicitly created in eg clicon_rpc_netconf
* @note Maybe separate closing session and closing socket.
*/ */
int int
clicon_rpc_close_session(clicon_handle h) clicon_rpc_close_session(clicon_handle h)
@ -800,6 +802,7 @@ clicon_rpc_close_session(clicon_handle h)
cxobj *xerr; cxobj *xerr;
char *username; char *username;
uint32_t session_id; uint32_t session_id;
int s;
if (session_id_check(h, &session_id) < 0) if (session_id_check(h, &session_id) < 0)
goto done; goto done;
@ -810,6 +813,10 @@ clicon_rpc_close_session(clicon_handle h)
goto done; goto done;
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done; goto done;
if ((s = clicon_client_socket_get(h)) >= 0){
close(s);
clicon_client_socket_set(h, -1);
}
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
clixon_netconf_error(xerr, "Close session", NULL); clixon_netconf_error(xerr, "Close session", NULL);
goto done; goto done;

View file

@ -15,6 +15,7 @@ fyang=$dir/example-client.yang
cfile=$dir/example-client.c cfile=$dir/example-client.c
pdir=$dir/plugin pdir=$dir/plugin
app=$dir/clixon-app app=$dir/clixon-app
debug=0
if [ ! -d $pdir ]; then if [ ! -d $pdir ]; then
mkdir $pdir mkdir $pdir
@ -68,19 +69,21 @@ cat<<EOF > $cfile
#include <unistd.h> #include <unistd.h>
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <stdint.h>
#include <syslog.h> // debug
#include <clixon/clixon_log.h> #include <clixon/clixon_log.h> // debug
#include <clixon/clixon_client.h> #include <clixon/clixon_client.h>
int int
main(int argc, main(int argc,
char **argv) char **argv)
{ {
int retval = -1;
clixon_handle h = NULL; /* clixon handle */ clixon_handle h = NULL; /* clixon handle */
clixon_client_handle ch = NULL; /* clixon client handle */ clixon_client_handle ch = NULL; /* clixon client handle */
// clicon_log_init("client", LOG_DEBUG, CLICON_LOG_STDERR); // debug clicon_log_init("client", LOG_DEBUG, CLICON_LOG_STDERR); // debug
// clicon_debug_init(1, NULL); // debug clicon_debug_init($debug, NULL); // debug
/* Provide a clixon config-file, get a clixon handle */ /* Provide a clixon config-file, get a clixon handle */
if ((h = clixon_client_init("$cfg")) == NULL) if ((h = clixon_client_init("$cfg")) == NULL)
@ -94,12 +97,15 @@ main(int argc,
{ {
uint32_t u = 0; uint32_t u = 0;
if (clixon_client_get_uint32(ch, &u, "urn:example:clixon-client", "/table/parameter[name='a']/value") < 0) if (clixon_client_get_uint32(ch, &u, "urn:example:clixon-client", "/table/parameter[name='a']/value") < 0)
return -1; goto done;
printf("%u\n", u); /* for test output */ printf("%u\n", u); /* for test output */
} }
retval = 0;
done:
clixon_client_disconnect(ch); clixon_client_disconnect(ch);
clixon_client_terminate(h); clixon_client_terminate(h);
return 0; printf("done\n"); /* for test output */
return retval;
} }
EOF EOF

View file

@ -236,6 +236,9 @@ expecteof "$clixon_netconf -qf $cfg" 0 "<xx:rpc xmlns:xx=\"urn:ietf:params:xml:n
new "netconf lock/unlock" new "netconf lock/unlock"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><lock><target><candidate/></target></lock></rpc>]]>]]><rpc $DEFAULTNS><unlock><target><candidate/></target></unlock></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]><rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><lock><target><candidate/></target></lock></rpc>]]>]]><rpc $DEFAULTNS><unlock><target><candidate/></target></unlock></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]><rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
new "netconf lock/unlock/lock"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><lock><target><candidate/></target></lock></rpc>]]>]]><rpc $DEFAULTNS><unlock><target><candidate/></target></unlock></rpc>]]>]]><rpc $DEFAULTNS><lock><target><candidate/></target></lock></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]><rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]><rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
new "netconf lock/lock" new "netconf lock/lock"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><lock><target><candidate/></target></lock></rpc>]]>]]><rpc $DEFAULTNS><lock><target><candidate/></target></lock></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]><rpc-reply $DEFAULTNS><rpc-error><error-type>protocol</error-type><error-tag>lock-denied</error-tag><error-info><session-id>" expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><lock><target><candidate/></target></lock></rpc>]]>]]><rpc $DEFAULTNS><lock><target><candidate/></target></lock></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]><rpc-reply $DEFAULTNS><rpc-error><error-type>protocol</error-type><error-tag>lock-denied</error-tag><error-info><session-id>"

View file

@ -167,7 +167,7 @@ expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' http:
# Negative # Negative
new "restconf get config on wrong port in netns:$netns" new "restconf get config on wrong port in netns:$netns"
expectpart "$(sudo ip netns exec $netns curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://$vaddr:8888/restconf/data/clixon-example:table)" 7 expectpart "$(sudo ip netns exec $netns curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://$vaddr:8888/restconf/data/clixon-example:table 2> /dev/null)" 7
if [ $RC -ne 0 ]; then if [ $RC -ne 0 ]; then
new "Kill restconf daemon" new "Kill restconf daemon"
@ -192,4 +192,3 @@ new "endtest"
endtest endtest
rm -rf $dir rm -rf $dir

View file

@ -176,7 +176,7 @@ main(int argc,
else else
if (clicon_rpc_connect_inet(h, sockpath, 4535, &s) < 0) if (clicon_rpc_connect_inet(h, sockpath, 4535, &s) < 0)
goto done; goto done;
if (clicon_rpc(s, s, msg, &retdata) < 0) if (clicon_rpc(s, msg, &retdata) < 0)
goto done; goto done;
close(s); close(s);
fprintf(stdout, "%s\n", retdata); fprintf(stdout, "%s\n", retdata);