Moved restconf_stream.c -> restconf_stream_fcgi.c, made some generaizations and documented what was hardcoded to FCGI
This commit is contained in:
parent
73bbcded87
commit
e2b3cdb3f6
8 changed files with 148 additions and 94 deletions
|
|
@ -94,7 +94,8 @@ APPSRC += restconf_main_$(with_restconf).c
|
||||||
|
|
||||||
# Fcgi-specific source including main
|
# Fcgi-specific source including main
|
||||||
ifeq ($(with_restconf),fcgi)
|
ifeq ($(with_restconf),fcgi)
|
||||||
APPSRC += restconf_stream.c
|
# Streams notifications have some fcgi specific handling
|
||||||
|
APPSRC += restconf_stream_$(with_restconf).c
|
||||||
endif
|
endif
|
||||||
|
|
||||||
APPOBJ = $(APPSRC:.c=.o)
|
APPOBJ = $(APPSRC:.c=.o)
|
||||||
|
|
|
||||||
|
|
@ -206,6 +206,7 @@ restconf_reply_send(void *req0,
|
||||||
FCGX_FPrintF(req->out, "%s", cbuf_get(cb));
|
FCGX_FPrintF(req->out, "%s", cbuf_get(cb));
|
||||||
FCGX_FPrintF(req->out, "\r\n");
|
FCGX_FPrintF(req->out, "\r\n");
|
||||||
}
|
}
|
||||||
|
FCGX_FFlush(req->out); /* Is this only for notification ? */
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
return retval;
|
return retval;
|
||||||
|
|
|
||||||
|
|
@ -492,7 +492,17 @@ main(int argc,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (strncmp(path+1, stream_path, strlen(stream_path)) == 0) {
|
else if (strncmp(path+1, stream_path, strlen(stream_path)) == 0) {
|
||||||
api_stream(h, req, stream_path, &finish);
|
char *query = NULL;
|
||||||
|
cvec *qvec = NULL;
|
||||||
|
query = restconf_param_get(h, "QUERY_STRING");
|
||||||
|
if (query != NULL && strlen(query))
|
||||||
|
if (str2cvec(query, '&', '=', &qvec) < 0)
|
||||||
|
goto done;
|
||||||
|
api_stream(h, req, qvec, stream_path, &finish);
|
||||||
|
if (qvec){
|
||||||
|
cvec_free(qvec);
|
||||||
|
qvec = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (strncmp(path, RESTCONF_WELL_KNOWN, strlen(RESTCONF_WELL_KNOWN)) == 0) {
|
else if (strncmp(path, RESTCONF_WELL_KNOWN, strlen(RESTCONF_WELL_KNOWN)) == 0) {
|
||||||
api_well_known(h, req); /* */
|
api_well_known(h, req); /* */
|
||||||
|
|
@ -509,7 +519,7 @@ main(int argc,
|
||||||
if (finish)
|
if (finish)
|
||||||
FCGX_Finish_r(req);
|
FCGX_Finish_r(req);
|
||||||
else{ /* A handler is forked so we initiate a new request after instead
|
else{ /* A handler is forked so we initiate a new request after instead
|
||||||
of finnishing the old */
|
of finishing the old */
|
||||||
if (FCGX_InitRequest(req, sock, 0) != 0){
|
if (FCGX_InitRequest(req, sock, 0) != 0){
|
||||||
clicon_err(OE_CFG, errno, "FCGX_InitRequest");
|
clicon_err(OE_CFG, errno, "FCGX_InitRequest");
|
||||||
goto done;
|
goto done;
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,6 @@
|
||||||
*/
|
*/
|
||||||
int stream_child_free(clicon_handle h, int pid);
|
int stream_child_free(clicon_handle h, int pid);
|
||||||
int stream_child_freeall(clicon_handle h);
|
int stream_child_freeall(clicon_handle h);
|
||||||
int api_stream(clicon_handle h, FCGX_Request *r, char *streampath, int *finish);
|
int api_stream(clicon_handle h, void *req, cvec *qvec, char *streampath, int *finish);
|
||||||
|
|
||||||
#endif /* _RESTCONF_STREAM_H_ */
|
#endif /* _RESTCONF_STREAM_H_ */
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,12 @@
|
||||||
query parameters, defined in Section 4.8. Refer to Appendix B.3.6
|
query parameters, defined in Section 4.8. Refer to Appendix B.3.6
|
||||||
for filter parameter examples.
|
for filter parameter examples.
|
||||||
|
|
||||||
|
* Note that this implementation includes some hardcoded things for FCGI.
|
||||||
|
* These are:
|
||||||
|
* - req->listen_sock is used to register incoming fd events from (nginx) fcgi server
|
||||||
|
* - The stream_child struct copies the FCGX_Request by value, so FCGX_Free() can be called
|
||||||
|
* asynchronously
|
||||||
|
* - In the forked variant, FCGX_Finish_r() and FCGX_Free() are called (minor)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
#ifdef HAVE_CONFIG_H
|
||||||
|
|
@ -99,13 +105,13 @@
|
||||||
*/
|
*/
|
||||||
#define STREAM_FORK 1
|
#define STREAM_FORK 1
|
||||||
|
|
||||||
/* Keep track of children - whjen they exit - their FCGX handle needs to be
|
/* Keep track of children - when they exit - their FCGX handle needs to be
|
||||||
* freed with FCGX_Free(&rbk, 0);
|
* freed with FCGX_Free(&rbk, 0);
|
||||||
*/
|
*/
|
||||||
struct stream_child{
|
struct stream_child{
|
||||||
qelem_t sc_q; /* queue header */
|
qelem_t sc_q; /* queue header */
|
||||||
int sc_pid; /* Child process id */
|
int sc_pid; /* Child process id */
|
||||||
FCGX_Request sc_r; /* FCGI stream data */
|
FCGX_Request sc_r; /* FCGI stream data. XXX this is by value */
|
||||||
};
|
};
|
||||||
/* Linked list of children
|
/* Linked list of children
|
||||||
* @note could hang STREAM_CHILD list on clicon handle instead.
|
* @note could hang STREAM_CHILD list on clicon handle instead.
|
||||||
|
|
@ -127,7 +133,7 @@ stream_child_free(clicon_handle h,
|
||||||
do {
|
do {
|
||||||
if (pid == sc->sc_pid){
|
if (pid == sc->sc_pid){
|
||||||
DELQ(sc, STREAM_CHILD, struct stream_child *);
|
DELQ(sc, STREAM_CHILD, struct stream_child *);
|
||||||
FCGX_Free(&sc->sc_r, 0);
|
FCGX_Free(&sc->sc_r, 0); /* XXX pointer to actual copied struct */
|
||||||
free(sc);
|
free(sc);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
@ -145,13 +151,15 @@ stream_child_freeall(clicon_handle h)
|
||||||
|
|
||||||
while ((sc = STREAM_CHILD) != NULL){
|
while ((sc = STREAM_CHILD) != NULL){
|
||||||
DELQ(sc, STREAM_CHILD, struct stream_child *);
|
DELQ(sc, STREAM_CHILD, struct stream_child *);
|
||||||
FCGX_Free(&sc->sc_r, 1);
|
FCGX_Free(&sc->sc_r, 1); /* XXX pointer to actual copied struct */
|
||||||
free(sc);
|
free(sc);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Callback when stream notifications arrive from backend
|
/*! Callback when stream notifications arrive from backend
|
||||||
|
* @param[in] s Socket
|
||||||
|
* @param[in] req Generic Www handle (can be part of clixon handle)
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
restconf_stream_cb(int s,
|
restconf_stream_cb(int s,
|
||||||
|
|
@ -229,14 +237,14 @@ restconf_stream_cb(int s,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Send subsctription to backend
|
/*! Send subsctription to backend
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
* @param[in] r Fastcgi request handle
|
* @param[in] req Generic Www handle (can be part of clixon handle)
|
||||||
* @param[in] name Stream name
|
* @param[in] name Stream name
|
||||||
* @param[out] sp Socket -1 if not set
|
* @param[out] sp Socket -1 if not set
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
restconf_stream(clicon_handle h,
|
restconf_stream(clicon_handle h,
|
||||||
FCGX_Request *r,
|
void *req,
|
||||||
char *name,
|
char *name,
|
||||||
cvec *qvec,
|
cvec *qvec,
|
||||||
int pretty,
|
int pretty,
|
||||||
|
|
@ -279,19 +287,22 @@ restconf_stream(clicon_handle h,
|
||||||
if (clicon_rpc_netconf(h, cbuf_get(cb), &xret, &s) < 0)
|
if (clicon_rpc_netconf(h, cbuf_get(cb), &xret, &s) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((xe = xpath_first(xret, NULL, "rpc-reply/rpc-error")) != NULL){
|
if ((xe = xpath_first(xret, NULL, "rpc-reply/rpc-error")) != NULL){
|
||||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Setting up stream */
|
/* Setting up stream */
|
||||||
FCGX_SetExitStatus(201, r->out); /* Created */
|
if (restconf_reply_header(req, "Content-Type", "text/event-stream") < 0)
|
||||||
FCGX_FPrintF(r->out, "Status: 201 Created\r\n");
|
goto done;
|
||||||
FCGX_FPrintF(r->out, "Content-Type: text/event-stream\r\n");
|
if (restconf_reply_header(req, "Cache-Control", "no-cache") < 0)
|
||||||
FCGX_FPrintF(r->out, "Cache-Control: no-cache\r\n");
|
goto done;
|
||||||
FCGX_FPrintF(r->out, "Connection: keep-alive\r\n");
|
if (restconf_reply_header(req, "Connection", "keep-alive") < 0)
|
||||||
FCGX_FPrintF(r->out, "X-Accel-Buffering: no\r\n");
|
goto done;
|
||||||
FCGX_FPrintF(r->out, "\r\n");
|
if (restconf_reply_header(req, "X-Accel-Buffering", "no") < 0)
|
||||||
FCGX_FFlush(r->out);
|
goto done;
|
||||||
|
if (restconf_reply_send(req, 201, NULL) < 0)
|
||||||
|
goto done;
|
||||||
*sp = s;
|
*sp = s;
|
||||||
ok:
|
ok:
|
||||||
retval = 0;
|
retval = 0;
|
||||||
|
|
@ -308,11 +319,16 @@ restconf_stream(clicon_handle h,
|
||||||
#include "restconf_lib.h"
|
#include "restconf_lib.h"
|
||||||
#include "restconf_stream.h"
|
#include "restconf_stream.h"
|
||||||
|
|
||||||
|
/*! Listen sock callback (from proxy?)
|
||||||
|
* @param[in] s Socket
|
||||||
|
* @param[in] req Generic Www handle (can be part of clixon handle)
|
||||||
|
*/
|
||||||
static int
|
static int
|
||||||
stream_checkuplink(int s,
|
stream_checkuplink(int s,
|
||||||
void *arg)
|
void *arg)
|
||||||
{
|
{
|
||||||
FCGX_Request *r = (FCGX_Request *)arg;
|
FCGX_Request *r = (FCGX_Request *)arg;
|
||||||
|
|
||||||
clicon_debug(1, "%s", __FUNCTION__);
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
if (FCGX_GetError(r->out) != 0){ /* break loop */
|
if (FCGX_GetError(r->out) != 0){ /* break loop */
|
||||||
clicon_debug(1, "%s FCGX_GetError upstream", __FUNCTION__);
|
clicon_debug(1, "%s FCGX_GetError upstream", __FUNCTION__);
|
||||||
|
|
@ -343,78 +359,78 @@ stream_timeout(int s,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Process a FastCGI request
|
/*! Process a stream request
|
||||||
* @param[in] r Fastcgi request handle
|
* @param[in] h Clicon handle
|
||||||
|
* @param[in] req Generic Www handle (can be part of clixon handle)
|
||||||
|
* @param[in] qvec Query parameters, ie the ?<id>=<val>&<id>=<val> stuff
|
||||||
|
* @param[in] streampath URI path for streams, eg /streams, see CLICON_STREAM_PATH
|
||||||
|
* @param[out] finish Set to zero, if request should not be finnished by upper layer
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
api_stream(clicon_handle h,
|
api_stream(clicon_handle h,
|
||||||
FCGX_Request *r,
|
void *req,
|
||||||
|
cvec *qvec,
|
||||||
char *streampath,
|
char *streampath,
|
||||||
int *finish)
|
int *finish)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
char *path;
|
FCGX_Request *rfcgi = (FCGX_Request *)req; /* XXX */
|
||||||
char *query;
|
char *path;
|
||||||
char *method;
|
char *method;
|
||||||
char **pvec = NULL;
|
char **pvec = NULL;
|
||||||
int pn;
|
int pn;
|
||||||
cvec *qvec = NULL;
|
cvec *pcvec = NULL; /* for rest api */
|
||||||
cvec *dvec = NULL;
|
cbuf *cb = NULL;
|
||||||
cvec *pcvec = NULL; /* for rest api */
|
char *indata;
|
||||||
cbuf *cb = NULL;
|
int authenticated = 0;
|
||||||
char *data;
|
int pretty;
|
||||||
int authenticated = 0;
|
|
||||||
int pretty;
|
|
||||||
restconf_media media_out = YANG_DATA_XML; /* XXX default */
|
restconf_media media_out = YANG_DATA_XML; /* XXX default */
|
||||||
cbuf *cbret = NULL;
|
cbuf *cbret = NULL;
|
||||||
cxobj *xret = NULL;
|
cxobj *xret = NULL;
|
||||||
cxobj *xerr;
|
cxobj *xerr;
|
||||||
int s=-1;
|
int s = -1;
|
||||||
#ifdef STREAM_FORK
|
#ifdef STREAM_FORK
|
||||||
int pid;
|
int pid;
|
||||||
struct stream_child *sc;
|
struct stream_child *sc;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
clicon_debug(1, "%s", __FUNCTION__);
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
path = restconf_uripath(h);
|
path = restconf_uripath(h);
|
||||||
query = restconf_param_get(h, "QUERY_STRING");
|
|
||||||
pretty = clicon_option_bool(h, "CLICON_RESTCONF_PRETTY");
|
pretty = clicon_option_bool(h, "CLICON_RESTCONF_PRETTY");
|
||||||
if ((pvec = clicon_strsep(path, "/", &pn)) == NULL)
|
if ((pvec = clicon_strsep(path, "/", &pn)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
/* Sanity check of path. Should be /stream/<name> */
|
/* Sanity check of path. Should be /stream/<name> */
|
||||||
if (pn != 3){
|
if (pn != 3){
|
||||||
restconf_notfound(h, r);
|
restconf_notfound(h, req);
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
if (strlen(pvec[0]) != 0){
|
if (strlen(pvec[0]) != 0){
|
||||||
retval = restconf_notfound(h, r);
|
retval = restconf_notfound(h, req);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if (strcmp(pvec[1], streampath)){
|
if (strcmp(pvec[1], streampath)){
|
||||||
retval = restconf_notfound(h, r);
|
retval = restconf_notfound(h, req);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((method = pvec[2]) == NULL){
|
if ((method = pvec[2]) == NULL){
|
||||||
retval = restconf_notfound(h, r);
|
retval = restconf_notfound(h, req);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
clicon_debug(1, "%s: method=%s", __FUNCTION__, method);
|
clicon_debug(1, "%s: method=%s", __FUNCTION__, method);
|
||||||
if (str2cvec(query, '&', '=', &qvec) < 0)
|
|
||||||
goto done;
|
|
||||||
if (str2cvec(path, '/', '=', &pcvec) < 0) /* rest url eg /album=ricky/foo */
|
if (str2cvec(path, '/', '=', &pcvec) < 0) /* rest url eg /album=ricky/foo */
|
||||||
goto done;
|
goto done;
|
||||||
/* data */
|
/* data */
|
||||||
if ((cb = restconf_get_indata(r)) == NULL)
|
if ((cb = restconf_get_indata(req)) == NULL)
|
||||||
goto done;
|
|
||||||
data = cbuf_get(cb);
|
|
||||||
clicon_debug(1, "%s DATA=%s", __FUNCTION__, data);
|
|
||||||
if (str2cvec(data, '&', '=', &dvec) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
|
indata = cbuf_get(cb);
|
||||||
|
clicon_debug(1, "%s DATA=%s", __FUNCTION__, indata);
|
||||||
|
|
||||||
/* If present, check credentials. See "plugin_credentials" in plugin
|
/* If present, check credentials. See "plugin_credentials" in plugin
|
||||||
* See RFC 8040 section 2.5
|
* See RFC 8040 section 2.5
|
||||||
*/
|
*/
|
||||||
if ((authenticated = clixon_plugin_auth_all(h, r)) < 0)
|
if ((authenticated = clixon_plugin_auth_all(h, req)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
clicon_debug(1, "%s auth:%d %s", __FUNCTION__, authenticated, clicon_username_get(h));
|
clicon_debug(1, "%s auth:%d %s", __FUNCTION__, authenticated, clicon_username_get(h));
|
||||||
|
|
||||||
|
|
@ -427,22 +443,20 @@ api_stream(clicon_handle h,
|
||||||
if (netconf_access_denied_xml(&xret, "protocol", "The requested URL was unauthorized") < 0)
|
if (netconf_access_denied_xml(&xret, "protocol", "The requested URL was unauthorized") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
|
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
|
||||||
if (api_return_err(h, r, xerr, pretty, media_out, 0) < 0)
|
if (api_return_err(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
clicon_debug(1, "%s auth2:%d %s", __FUNCTION__, authenticated, clicon_username_get(h));
|
clicon_debug(1, "%s auth2:%d %s", __FUNCTION__, authenticated, clicon_username_get(h));
|
||||||
if (restconf_stream(h, r, method, qvec, pretty, media_out, &s) < 0)
|
if (restconf_stream(h, req, method, qvec, pretty, media_out, &s) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (s != -1){
|
if (s != -1){
|
||||||
#ifdef STREAM_FORK
|
#ifdef STREAM_FORK
|
||||||
if ((pid = fork()) == 0){ /* child */
|
if ((pid = fork()) == 0){ /* child */
|
||||||
if (pvec)
|
if (pvec)
|
||||||
free(pvec);
|
free(pvec);
|
||||||
if (dvec)
|
|
||||||
cvec_free(dvec);
|
|
||||||
if (qvec)
|
if (qvec)
|
||||||
cvec_free(qvec);
|
cvec_free(qvec);
|
||||||
if (pcvec)
|
if (pcvec)
|
||||||
|
|
@ -457,26 +471,27 @@ api_stream(clicon_handle h,
|
||||||
/* Listen to backend socket */
|
/* Listen to backend socket */
|
||||||
if (clixon_event_reg_fd(s,
|
if (clixon_event_reg_fd(s,
|
||||||
restconf_stream_cb,
|
restconf_stream_cb,
|
||||||
(void*)r,
|
req,
|
||||||
"stream socket") < 0)
|
"stream socket") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (clixon_event_reg_fd(r->listen_sock,
|
if (clixon_event_reg_fd(rfcgi->listen_sock,
|
||||||
stream_checkuplink,
|
stream_checkuplink,
|
||||||
(void*)r,
|
req,
|
||||||
"stream socket") < 0)
|
"stream socket") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* Poll upstream errors */
|
/* Poll upstream errors */
|
||||||
stream_timeout(0, (void*)r);
|
stream_timeout(0, req);
|
||||||
/* Start loop */
|
/* Start loop */
|
||||||
clixon_event_loop();
|
clixon_event_loop();
|
||||||
close(s);
|
close(s);
|
||||||
clixon_event_unreg_fd(s, restconf_stream_cb);
|
clixon_event_unreg_fd(s, restconf_stream_cb);
|
||||||
clixon_event_unreg_fd(r->listen_sock, restconf_stream_cb);
|
clixon_event_unreg_fd(rfcgi->listen_sock,
|
||||||
clixon_event_unreg_timeout(stream_timeout, (void*)r);
|
restconf_stream_cb);
|
||||||
|
clixon_event_unreg_timeout(stream_timeout, (void*)req);
|
||||||
clicon_exit_reset();
|
clicon_exit_reset();
|
||||||
#ifdef STREAM_FORK
|
#ifdef STREAM_FORK
|
||||||
FCGX_Finish_r(r);
|
FCGX_Finish_r(rfcgi);
|
||||||
FCGX_Free(r, 0);
|
FCGX_Free(rfcgi, 0);
|
||||||
restconf_terminate(h);
|
restconf_terminate(h);
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
@ -490,7 +505,8 @@ api_stream(clicon_handle h,
|
||||||
}
|
}
|
||||||
memset(sc, 0, sizeof(struct stream_child));
|
memset(sc, 0, sizeof(struct stream_child));
|
||||||
sc->sc_pid = pid;
|
sc->sc_pid = pid;
|
||||||
sc->sc_r = *r;
|
sc->sc_r = *rfcgi; /* XXX by value */
|
||||||
|
|
||||||
ADDQ(sc, STREAM_CHILD);
|
ADDQ(sc, STREAM_CHILD);
|
||||||
*finish = 0; /* If spawn child, we should not finish this stream */
|
*finish = 0; /* If spawn child, we should not finish this stream */
|
||||||
#endif /* STREAM_FORK */
|
#endif /* STREAM_FORK */
|
||||||
|
|
@ -501,10 +517,6 @@ api_stream(clicon_handle h,
|
||||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||||
if (pvec)
|
if (pvec)
|
||||||
free(pvec);
|
free(pvec);
|
||||||
if (dvec)
|
|
||||||
cvec_free(dvec);
|
|
||||||
if (qvec)
|
|
||||||
cvec_free(qvec);
|
|
||||||
if (pcvec)
|
if (pcvec)
|
||||||
cvec_free(pcvec);
|
cvec_free(pcvec);
|
||||||
if (cb)
|
if (cb)
|
||||||
|
|
@ -37,8 +37,10 @@
|
||||||
* argc/argv after -- in clixon_backend:
|
* argc/argv after -- in clixon_backend:
|
||||||
* -r enable the reset function
|
* -r enable the reset function
|
||||||
* -s enable the state function
|
* -s enable the state function
|
||||||
* -S read state data from file, otherwise construct it programmatically
|
* -S <file> read state data from file, otherwise construct it programmatically (requires -s)
|
||||||
|
* -i read state file on init not by request for optimization (requires -sS <file>)
|
||||||
* -u enable upgrade function - auto-upgrade testing
|
* -u enable upgrade function - auto-upgrade testing
|
||||||
|
* -U general-purpose upgrade
|
||||||
* -t enable transaction logging (cal syslog for every transaction)
|
* -t enable transaction logging (cal syslog for every transaction)
|
||||||
*/
|
*/
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
@ -372,14 +374,26 @@ example_statedata(clicon_handle h,
|
||||||
if (clixon_xml_parse_file(fd, YB_MODULE, yspec, NULL, &xt, NULL) < 0)
|
if (clixon_xml_parse_file(fd, YB_MODULE, yspec, NULL, &xt, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
close(fd);
|
close(fd);
|
||||||
if ((x1 = xpath_first(xt, nsc, "%s", xpath)) != NULL){
|
if (xpath_vec(xt, nsc, "%s", &xvec, &xlen, xpath) < 0)
|
||||||
|
goto done;
|
||||||
|
for (i=0; i<xlen; i++){
|
||||||
|
x1 = xvec[i];
|
||||||
xml_flag_set(x1, XML_FLAG_MARK);
|
xml_flag_set(x1, XML_FLAG_MARK);
|
||||||
/* Remove everything that is not marked */
|
}
|
||||||
if (xml_tree_prune_flagged_sub(xt, XML_FLAG_MARK, 1, NULL) < 0)
|
/* Remove everything that is not marked */
|
||||||
goto done;
|
if (xml_tree_prune_flagged_sub(xt, XML_FLAG_MARK, 1, NULL) < 0)
|
||||||
|
goto done;
|
||||||
|
for (i=0; i<xlen; i++){
|
||||||
|
x1 = xvec[i];
|
||||||
xml_flag_reset(x1, XML_FLAG_MARK);
|
xml_flag_reset(x1, XML_FLAG_MARK);
|
||||||
if (xml_copy(xt, xstate) < 0)
|
}
|
||||||
goto done;
|
if (xml_copy(xt, xstate) < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
if (xvec){
|
||||||
|
free(xvec);
|
||||||
|
xvec = 0;
|
||||||
|
xlen = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -175,8 +175,6 @@ main(int argc,
|
||||||
goto done;
|
goto done;
|
||||||
if (clicon_conf_xml_set(h, xcfg) < 0)
|
if (clicon_conf_xml_set(h, xcfg) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
xcfg = xml_new("clixon-config", NULL, CX_ELMNT);
|
|
||||||
clicon_conf_xml_set(h, xcfg);
|
|
||||||
optind = 1;
|
optind = 1;
|
||||||
opterr = 0;
|
opterr = 0;
|
||||||
while ((c = getopt(argc, argv, UTIL_XML_OPTS)) != -1)
|
while ((c = getopt(argc, argv, UTIL_XML_OPTS)) != -1)
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@
|
||||||
#include "clixon/clixon.h"
|
#include "clixon/clixon.h"
|
||||||
|
|
||||||
/* Command line options passed to getopt(3) */
|
/* Command line options passed to getopt(3) */
|
||||||
#define UTIL_XML_MOD_OPTS "hD:o:y:b:x:p:s"
|
#define UTIL_XML_MOD_OPTS "hD:o:y:Y:b:x:p:s"
|
||||||
|
|
||||||
enum opx{
|
enum opx{
|
||||||
OPX_ERROR = -1,
|
OPX_ERROR = -1,
|
||||||
|
|
@ -95,8 +95,9 @@ usage(char *argv0)
|
||||||
"where options are\n"
|
"where options are\n"
|
||||||
"\t-h \t\tHelp\n"
|
"\t-h \t\tHelp\n"
|
||||||
"\t-D <level>\tDebug\n"
|
"\t-D <level>\tDebug\n"
|
||||||
"\t-o <op> \tOperation: insert or merge\n"
|
"\t-o <op> \tOperation: parent, insert or merge\n"
|
||||||
"\t-y <file> \tYANG spec file\n"
|
"\t-y <file> \tYANG spec file\n"
|
||||||
|
"\t-Y <dir> \tYang dirs (can be several)\n"
|
||||||
"\t-b <base> \tXML base expression\n"
|
"\t-b <base> \tXML base expression\n"
|
||||||
"\t-x <xml> \tXML to insert\n"
|
"\t-x <xml> \tXML to insert\n"
|
||||||
"\t-p <xpath>\tXpath to where in base and XML\n"
|
"\t-p <xpath>\tXpath to where in base and XML\n"
|
||||||
|
|
@ -120,7 +121,7 @@ main(int argc, char **argv)
|
||||||
yang_stmt *yspec = NULL;
|
yang_stmt *yspec = NULL;
|
||||||
cxobj *x0 = NULL;
|
cxobj *x0 = NULL;
|
||||||
cxobj *x1 = NULL;
|
cxobj *x1 = NULL;
|
||||||
cxobj *xb;
|
cxobj *xb = NULL;
|
||||||
cxobj *xi = NULL;
|
cxobj *xi = NULL;
|
||||||
cxobj *xi1 = NULL;
|
cxobj *xi1 = NULL;
|
||||||
cxobj *xerr = NULL;
|
cxobj *xerr = NULL;
|
||||||
|
|
@ -130,10 +131,15 @@ main(int argc, char **argv)
|
||||||
enum opx opx = OPX_ERROR;
|
enum opx opx = OPX_ERROR;
|
||||||
char *reason = NULL;
|
char *reason = NULL;
|
||||||
int dbg = 0;
|
int dbg = 0;
|
||||||
|
cxobj *xcfg = NULL;
|
||||||
|
|
||||||
clicon_log_init("clixon_insert", LOG_DEBUG, CLICON_LOG_STDERR);
|
clicon_log_init("clixon_insert", LOG_DEBUG, CLICON_LOG_STDERR);
|
||||||
if ((h = clicon_handle_init()) == NULL)
|
if ((h = clicon_handle_init()) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
|
if ((xcfg = xml_new("clixon-config", NULL, CX_ELMNT)) == NULL)
|
||||||
|
goto done;
|
||||||
|
if (clicon_conf_xml_set(h, xcfg) < 0)
|
||||||
|
goto done;
|
||||||
optind = 1;
|
optind = 1;
|
||||||
opterr = 0;
|
opterr = 0;
|
||||||
while ((c = getopt(argc, argv, UTIL_XML_MOD_OPTS)) != -1)
|
while ((c = getopt(argc, argv, UTIL_XML_MOD_OPTS)) != -1)
|
||||||
|
|
@ -151,6 +157,10 @@ main(int argc, char **argv)
|
||||||
case 'y': /* YANG spec file */
|
case 'y': /* YANG spec file */
|
||||||
yangfile = optarg;
|
yangfile = optarg;
|
||||||
break;
|
break;
|
||||||
|
case 'Y':
|
||||||
|
if (clicon_option_add(h, "CLICON_YANG_DIR", optarg) < 0)
|
||||||
|
goto done;
|
||||||
|
break;
|
||||||
case 'b': /* Base XML expression */
|
case 'b': /* Base XML expression */
|
||||||
x0str = optarg;
|
x0str = optarg;
|
||||||
break;
|
break;
|
||||||
|
|
@ -187,7 +197,9 @@ main(int argc, char **argv)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
/* Get base subtree by xpath */
|
/* Get base subtree by xpath */
|
||||||
if ((xb = xpath_first(x0, NULL, "%s", xpath)) == NULL){
|
if (xpath == NULL)
|
||||||
|
xb = x0;
|
||||||
|
else if ((xb = xpath_first(x0, NULL, "%s", xpath)) == NULL){
|
||||||
clicon_err(OE_XML, 0, "xpath: %s not found in x0", xpath);
|
clicon_err(OE_XML, 0, "xpath: %s not found in x0", xpath);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
@ -208,7 +220,7 @@ main(int argc, char **argv)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case OPX_MERGE:
|
case OPX_MERGE:
|
||||||
/* Parse insert XML */
|
/* Parse merge XML */
|
||||||
if ((ret = clixon_xml_parse_string(x1str, YB_MODULE, yspec, &x1, &xerr)) < 0){
|
if ((ret = clixon_xml_parse_string(x1str, YB_MODULE, yspec, &x1, &xerr)) < 0){
|
||||||
clicon_err(OE_XML, 0, "Parsing insert xml: %s", x1str);
|
clicon_err(OE_XML, 0, "Parsing insert xml: %s", x1str);
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -217,7 +229,9 @@ main(int argc, char **argv)
|
||||||
clixon_netconf_error(xerr, "Parsing secondary xml", NULL);
|
clixon_netconf_error(xerr, "Parsing secondary xml", NULL);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if ((xi = xpath_first(x1, NULL, "%s", xpath)) == NULL){
|
if (xpath == NULL)
|
||||||
|
xi = x1;
|
||||||
|
else if ((xi = xpath_first(x1, NULL, "%s", xpath)) == NULL){
|
||||||
clicon_err(OE_XML, 0, "xpath: %s not found in xi", xpath);
|
clicon_err(OE_XML, 0, "xpath: %s not found in xi", xpath);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
@ -239,7 +253,9 @@ main(int argc, char **argv)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
/* Get secondary subtree by xpath */
|
/* Get secondary subtree by xpath */
|
||||||
if ((xi = xpath_first(x1, NULL, "%s", xpath)) == NULL){
|
if (xpath == NULL)
|
||||||
|
xi = x1;
|
||||||
|
else if ((xi = xpath_first(x1, NULL, "%s", xpath)) == NULL){
|
||||||
clicon_err(OE_XML, 0, "xpath: %s not found in xi", xpath);
|
clicon_err(OE_XML, 0, "xpath: %s not found in xi", xpath);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
@ -275,6 +291,8 @@ main(int argc, char **argv)
|
||||||
xml_free(x0);
|
xml_free(x0);
|
||||||
if (x1)
|
if (x1)
|
||||||
xml_free(x1);
|
xml_free(x1);
|
||||||
|
if (xcfg)
|
||||||
|
xml_free(xcfg);
|
||||||
if (xerr)
|
if (xerr)
|
||||||
xml_free(xerr);
|
xml_free(xerr);
|
||||||
if (reason)
|
if (reason)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue