forgot new data and datastore files

This commit is contained in:
Olof hagsand 2019-03-31 18:20:48 +02:00
parent 98a5ebc76e
commit 65451f1045
4 changed files with 1347 additions and 0 deletions

113
lib/clixon/clixon_data.h Normal file
View file

@ -0,0 +1,113 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
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 *****
*
* Access functions for clixon data.
* Free-typed values for runtime getting and setting.
* Accessed with clicon_data(h).
*/
#ifndef _CLIXON_DATA_H_
#define _CLIXON_DATA_H_
/*
* Constants
*/
/* default group membership to access config unix socket */
#define CLICON_SOCK_GROUP "clicon"
/*
* Types
*/
/* Struct per database in hash */
typedef struct {
int de_pid;
cxobj *de_xml; /* cache */
} db_elmnt;
/*
* Prototypes
*/
yang_spec * clicon_dbspec_yang(clicon_handle h);
int clicon_dbspec_yang_set(clicon_handle h, struct yang_spec *ys);
cxobj * clicon_nacm_ext(clicon_handle h);
int clicon_nacm_ext_set(clicon_handle h, cxobj *xn);
yang_spec * clicon_config_yang(clicon_handle h);
int clicon_config_yang_set(clicon_handle h, struct yang_spec *ys);
cxobj *clicon_conf_xml(clicon_handle h);
int clicon_conf_xml_set(clicon_handle h, cxobj *x);
#ifdef XXX
plghndl_t clicon_xmldb_plugin_get(clicon_handle h);
int clicon_xmldb_plugin_set(clicon_handle h, plghndl_t handle);
void *clicon_xmldb_api_get(clicon_handle h);
int clicon_xmldb_api_set(clicon_handle h, void *xa_api);
void *clicon_xmldb_handle_get(clicon_handle h);
int clicon_xmldb_handle_set(clicon_handle h, void *xh);
#endif
db_elmnt *clicon_db_elmnt_get(clicon_handle h, const char *db);
int clicon_db_elmnt_set(clicon_handle h, const char *db, db_elmnt *xc);
/**/
/* Set and get authorized user name */
char *clicon_username_get(clicon_handle h);
int clicon_username_set(clicon_handle h, void *username);
/* Set and get startup status */
enum startup_status clicon_startup_status_get(clicon_handle h);
int clicon_startup_status_set(clicon_handle h, enum startup_status status);
/* Set and get socket fd (ie backend server socket / restconf fcgx socket */
int clicon_socket_get(clicon_handle h);
int clicon_socket_set(clicon_handle h, int s);
/*! Set and get module state full and brief cached tree */
cxobj *clicon_modst_cache_get(clicon_handle h, int brief);
int clicon_modst_cache_set(clicon_handle h, int brief, cxobj *xms);
/*! Set and get yang/xml module revision changelog */
cxobj *clicon_xml_changelog_get(clicon_handle h);
int clicon_xml_changelog_set(clicon_handle h, cxobj *xchlog);
/*! Set and get user command-line options (after --) */
int clicon_argv_get(clicon_handle h, int *argc, char ***argv);
int clicon_argv_set(clicon_handle h, char *argv0, int argc, char **argv);
#endif /* _CLIXON_DATA_H_ */

595
lib/src/clixon_data.c Normal file
View file

@ -0,0 +1,595 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
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 *****
*
* Access functions for clixon data.
* Free-typed values for runtime getting and setting.
* Accessed with clicon_data(h).
*/
#ifdef HAVE_CONFIG_H
#include "clixon_config.h" /* generated by config & autoconf */
#endif
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <stdint.h>
#include <syslog.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/socket.h>
/* cligen */
#include <cligen/cligen.h>
/* clicon */
#include "clixon_err.h"
#include "clixon_string.h"
#include "clixon_queue.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_log.h"
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_xml_sort.h"
#include "clixon_options.h"
#include "clixon_plugin.h"
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
#include "clixon_data.h"
/*! Get YANG specification for application
* Must use hash functions directly since they are not strings.
*/
yang_spec *
clicon_dbspec_yang(clicon_handle h)
{
clicon_hash_t *cdat = clicon_data(h);
size_t len;
void *p;
if ((p = hash_value(cdat, "dbspec_yang", &len)) != NULL)
return *(yang_spec **)p;
return NULL;
}
/*! Set yang specification for application
* ys must be a malloced pointer
*/
int
clicon_dbspec_yang_set(clicon_handle h,
struct yang_spec *ys)
{
clicon_hash_t *cdat = clicon_data(h);
/* It is the pointer to ys that should be copied by hash,
so we send a ptr to the ptr to indicate what to copy.
*/
if (hash_add(cdat, "dbspec_yang", &ys, sizeof(ys)) == NULL)
return -1;
return 0;
}
/*! Get NACM (rfc 8341) XML parse tree if external not in std xml config
* @param[in] h Clicon handle
* @retval xn XML NACM tree, or NULL
* @note only used if config option CLICON_NACM_MODE is external
* @see clicon_nacm_ext_set
*/
cxobj *
clicon_nacm_ext(clicon_handle h)
{
clicon_hash_t *cdat = clicon_data(h);
size_t len;
void *p;
if ((p = hash_value(cdat, "nacm_xml", &len)) != NULL)
return *(cxobj **)p;
return NULL;
}
/*! Set NACM (rfc 8341) external XML parse tree, free old if any
* @param[in] h Clicon handle
* @param[in] xn XML Nacm tree
* @note only used if config option CLICON_NACM_MODE is external
* @see clicon_nacm_ext
*/
int
clicon_nacm_ext_set(clicon_handle h,
cxobj *xn)
{
clicon_hash_t *cdat = clicon_data(h);
cxobj *xo;
if ((xo = clicon_nacm_ext(h)) != NULL)
xml_free(xo);
/* It is the pointer to xn that should be copied by hash,
so we send a ptr to the ptr to indicate what to copy.
*/
if (hash_add(cdat, "nacm_xml", &xn, sizeof(xn)) == NULL)
return -1;
return 0;
}
#if 1 /* Temporary function until "Top-level Yang symbol cannot be called "config"" is fixed */
/*! Get YANG specification for clixon config
* Must use hash functions directly since they are not strings.
*/
yang_spec *
clicon_config_yang(clicon_handle h)
{
clicon_hash_t *cdat = clicon_data(h);
size_t len;
void *p;
if ((p = hash_value(cdat, "control_yang", &len)) != NULL)
return *(yang_spec **)p;
return NULL;
}
/*! Set yang specification for control
* ys must be a malloced pointer
*/
int
clicon_config_yang_set(clicon_handle h,
struct yang_spec *ys)
{
clicon_hash_t *cdat = clicon_data(h);
/* It is the pointer to ys that should be copied by hash,
so we send a ptr to the ptr to indicate what to copy.
*/
if (hash_add(cdat, "control_yang", &ys, sizeof(ys)) == NULL)
return -1;
return 0;
}
#endif
/*! Get YANG specification for Clixon system options and features
* Must use hash functions directly since they are not strings.
* Example: features are typically accessed directly in the config tree.
*/
cxobj *
clicon_conf_xml(clicon_handle h)
{
clicon_hash_t *cdat = clicon_data(h);
size_t len;
void *p;
if ((p = hash_value(cdat, "clixon_conf", &len)) != NULL)
return *(cxobj **)p;
return NULL;
}
/*! Set YANG specification for Clixon system options and features
* ys must be a malloced pointer
*/
int
clicon_conf_xml_set(clicon_handle h,
cxobj *x)
{
clicon_hash_t *cdat = clicon_data(h);
/* It is the pointer to x that should be copied by hash,
* so we send a ptr to the ptr to indicate what to copy.
*/
if (hash_add(cdat, "clixon_conf", &x, sizeof(x)) == NULL)
return -1;
return 0;
}
#ifdef XXX
/*! Get xmldb datastore plugin handle, as used by dlopen/dlsym/dlclose */
plghndl_t
clicon_xmldb_plugin_get(clicon_handle h)
{
clicon_hash_t *cdat = clicon_data(h);
size_t len;
void *p;
if ((p = hash_value(cdat, "xmldb_plugin", &len)) != NULL)
return *(plghndl_t*)p;
return NULL;
}
/*! Set xmldb datastore plugin handle, as used by dlopen/dlsym/dlclose */
int
clicon_xmldb_plugin_set(clicon_handle h,
plghndl_t handle)
{
clicon_hash_t *cdat = clicon_data(h);
if (hash_add(cdat, "xmldb_plugin", &handle, sizeof(void*)) == NULL)
return -1;
return 0;
}
/*! Get XMLDB API struct pointer
* @param[in] h Clicon handle
* @retval xa XMLDB API struct
* @note xa is really of type struct xmldb_api*
*/
void *
clicon_xmldb_api_get(clicon_handle h)
{
clicon_hash_t *cdat = clicon_data(h);
size_t len;
void *xa;
if ((xa = hash_value(cdat, "xmldb_api", &len)) != NULL)
return *(void**)xa;
return NULL;
}
/*! Set or reset XMLDB API struct pointer
* @param[in] h Clicon handle
* @param[in] xa XMLDB API struct
* @note xa is really of type struct xmldb_api*
*/
int
clicon_xmldb_api_set(clicon_handle h,
void *xa)
{
clicon_hash_t *cdat = clicon_data(h);
/* It is the pointer to xa_api that should be copied by hash,
so we send a ptr to the ptr to indicate what to copy.
*/
if (hash_add(cdat, "xmldb_api", &xa, sizeof(void*)) == NULL)
return -1;
return 0;
}
/*! Get XMLDB storage handle
* @param[in] h Clicon handle
* @retval xh XMLDB storage handle. If not connected return NULL
*/
void *
clicon_xmldb_handle_get(clicon_handle h)
{
clicon_hash_t *cdat = clicon_data(h);
size_t len;
void *xh;
if ((xh = hash_value(cdat, "xmldb_handle", &len)) != NULL)
return *(void**)xh;
return NULL;
}
/*! Set or reset XMLDB storage handle
* @param[in] h Clicon handle
* @param[in] xh XMLDB storage handle. If NULL reset it
* @note Just keep note of it, dont allocate it or so.
*/
int
clicon_xmldb_handle_set(clicon_handle h,
void *xh)
{
clicon_hash_t *cdat = clicon_data(h);
if (hash_add(cdat, "xmldb_handle", &xh, sizeof(void*)) == NULL)
return -1;
return 0;
}
#endif /* XXX */
/*! Get authorized user name
* @param[in] h Clicon handle
* @retval xh XMLDB storage handle. If not connected return NULL
*/
char *
clicon_username_get(clicon_handle h)
{
clicon_hash_t *cdat = clicon_data(h);
return (char*)hash_value(cdat, "username", NULL);
}
/*! Set authorized user name
* @param[in] h Clicon handle
* @param[in] xh XMLDB storage handle. If NULL reset it
* @note Just keep note of it, dont allocate it or so.
*/
int
clicon_username_set(clicon_handle h,
void *username)
{
clicon_hash_t *cdat = clicon_data(h);
if (username == NULL)
return hash_del(cdat, "username");
return hash_add(cdat, "username", username, strlen(username)+1)==NULL?-1:0;
}
/*! Get backend daemon startup status
* @param[in] h Clicon handle
* @retval status Startup status
*/
enum startup_status
clicon_startup_status_get(clicon_handle h)
{
clicon_hash_t *cdat = clicon_data(h);
void *p;
if ((p = hash_value(cdat, "startup_status", NULL)) != NULL)
return *(enum startup_status *)p;
return STARTUP_ERR;
}
/*! Set backend daemon startup status
* @param[in] h Clicon handle
* @param[in] status Startup status
* @retval 0 OK
* @retval -1 Error (when setting value)
*/
int
clicon_startup_status_set(clicon_handle h,
enum startup_status status)
{
clicon_hash_t *cdat = clicon_data(h);
if (hash_add(cdat, "startup_status", &status, sizeof(status))==NULL)
return -1;
return 0;
}
/*! Get socket fd (ie backend server socket / restconf fcgx socket)
* @param[in] h Clicon handle
* @retval -1 No open socket
* @retval s Socket
*/
int
clicon_socket_get(clicon_handle h)
{
clicon_hash_t *cdat = clicon_data(h);
void *p;
if ((p = hash_value(cdat, "socket", NULL)) == NULL)
return -1;
return *(int*)p;
}
/*! Set socket fd (ie backend server socket / restconf fcgx socket)
* @param[in] h Clicon handle
* @param[in] s Open socket (or -1 to close)
* @retval 0 OK
* @retval -1 Error
*/
int
clicon_socket_set(clicon_handle h,
int s)
{
clicon_hash_t *cdat = clicon_data(h);
if (s == -1)
return hash_del(cdat, "socket");
return hash_add(cdat, "socket", &s, sizeof(int))==NULL?-1:0;
}
/*! Get module state cache
* @param[in] h Clicon handle
* @param[in] brief 0: Full module state tree, 1: Brief tree (datastore)
* @retval xms Module state cache XML tree
* xms is on the form: <modules-state>...
*/
cxobj *
clicon_modst_cache_get(clicon_handle h,
int brief)
{
clicon_hash_t *cdat = clicon_data(h);
void *p;
if ((p = hash_value(cdat, brief?"modst_brief":"modst_full", NULL)) != NULL)
return *(cxobj **)p;
return NULL;
}
/*! Set module state cache
* @param[in] h Clicon handle
* @param[in] brief 0: Full module state tree, 1: Brief tree (datastore)
* @param[in] xms Module state cache XML tree
* @retval 0 OK
* @retval -1 Error
*/
int
clicon_modst_cache_set(clicon_handle h,
int brief,
cxobj *xms)
{
clicon_hash_t *cdat = clicon_data(h);
cxobj *x;
if ((x = clicon_modst_cache_get(h, brief)) != NULL)
xml_free(x);
if (xms == NULL)
goto ok;
assert(strcmp(xml_name(xms),"modules-state")==0);
if ((x = xml_dup(xms)) == NULL)
return -1;
if (hash_add(cdat, brief?"modst_brief":"modst_full", &x, sizeof(x))==NULL)
return -1;
ok:
return 0;
}
/*! Get yang module changelog
* @param[in] h Clicon handle
* @retval xch Module revision changelog XML tree
* @see draft-wang-netmod-module-revision-management-01
*/
cxobj *
clicon_xml_changelog_get(clicon_handle h)
{
clicon_hash_t *cdat = clicon_data(h);
void *p;
if ((p = hash_value(cdat, "xml-changelog", NULL)) != NULL)
return *(cxobj **)p;
return NULL;
}
/*! Set xml module changelog
* @param[in] h Clicon handle
* @param[in] s Module revision changelog XML tree
* @retval 0 OK
* @retval -1 Error
* @see draft-wang-netmod-module-revision-management-01
*/
int
clicon_xml_changelog_set(clicon_handle h,
cxobj *xchlog)
{
clicon_hash_t *cdat = clicon_data(h);
if (hash_add(cdat, "xml-changelog", &xchlog, sizeof(xchlog))==NULL)
return -1;
return 0;
}
/*! Get user clicon command-line options argv, argc (after --)
* @param[in] h Clicon handle
* @param[out] argc
* @param[out] argv
* @retval 0 OK
* @retval -1 Error
*/
int
clicon_argv_get(clicon_handle h,
int *argc,
char ***argv)
{
clicon_hash_t *cdat = clicon_data(h);
void *p;
if (argc){
if ((p = hash_value(cdat, "argc", NULL)) == NULL)
return -1;
*argc = *(int*)p;
}
if (argv){
if ((p = hash_value(cdat, "argv", NULL)) == NULL)
return -1;
*argv = (char**)p;
}
return 0;
}
/*! Set clicon user command-line options argv, argc (after --)
* @param[in] h Clicon handle
* @param[in] prog argv[0] - the program name
* @param[in] argc Length of argv
* @param[in] argv Array of command-line options or NULL
* @retval 0 OK
* @retval -1 Error
* @note If argv=NULL deallocate allocated argv vector if exists.
*/
int
clicon_argv_set(clicon_handle h,
char *prgm,
int argc,
char **argv)
{
int retval = -1;
clicon_hash_t *cdat = clicon_data(h);
char **argvv = NULL;
size_t len;
/* add space for null-termination and argv[0] program name */
len = argc+2;
if ((argvv = calloc(len, sizeof(char*))) == NULL){
clicon_err(OE_UNIX, errno, "calloc");
goto done;
}
memcpy(argvv+1, argv, argc*sizeof(char*));
argvv[0] = prgm;
/* Note the value is the argv vector (which is copied) */
if (hash_add(cdat, "argv", argvv, len*sizeof(char*))==NULL)
goto done;
argc += 1;
if (hash_add(cdat, "argc", &argc, sizeof(argc))==NULL)
goto done;
retval = 0;
done:
if (argvv)
free(argvv);
return retval;
}
/*! Get xml database element including pid and xml cache
* @param[in] h Clicon handle
* @param[in] db Name of database
* @retval de Database element
* @retval NULL None found
* @note these use db_elmnt hash, not data
*/
db_elmnt *
clicon_db_elmnt_get(clicon_handle h,
const char *db)
{
clicon_hash_t *cdat = clicon_db_elmnt(h);
void *p;
if ((p = hash_value(cdat, db, NULL)) != NULL)
return (db_elmnt *)p;
return NULL;
}
/*! Set xml database element including pid and xml cache
* @param[in] h Clicon handle
* @param[in] db Name of database
* @param[in] de Database element
* @retval 0 OK
* @retval -1 Error
* XXX add prefix to db to ensure uniqueness?
* @note these use db_elmnt hash, not data
*/
int
clicon_db_elmnt_set(clicon_handle h,
const char *db,
db_elmnt *de)
{
clicon_hash_t *cdat = clicon_db_elmnt(h);
if (hash_add(cdat, db, de, sizeof(*de))==NULL)
return -1;
return 0;
}

View file

@ -0,0 +1,593 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
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 <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <limits.h>
#include <fnmatch.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <assert.h>
#include <syslog.h>
#include <fcntl.h>
/* cligen */
#include <cligen/cligen.h>
/* clixon */
#include "clixon_err.h"
#include "clixon_string.h"
#include "clixon_queue.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_log.h"
#include "clixon_file.h"
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_xml_sort.h"
#include "clixon_options.h"
#include "clixon_data.h"
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
#include "clixon_xml_map.h"
#include "clixon_json.h"
#include "clixon_nacm.h"
#include "clixon_netconf_lib.h"
#include "clixon_yang_module.h"
#include "clixon_datastore.h"
#include "clixon_datastore_read.h"
#define handle(xh) (assert(text_handle_check(xh)==0),(struct text_handle *)(xh))
/*! Ensure that xt only has a single sub-element and that is "config"
*/
static int
singleconfigroot(cxobj *xt,
cxobj **xp)
{
int retval = -1;
cxobj *x = NULL;
int i = 0;
/* There should only be one element and called config */
x = NULL;
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL){
i++;
if (strcmp(xml_name(x), "config")){
clicon_err(OE_DB, ENOENT, "Wrong top-element %s expected config",
xml_name(x));
goto done;
}
}
if (i != 1){
clicon_err(OE_DB, ENOENT, "Top-element is not unique, expecting single config");
goto done;
}
x = NULL;
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL){
if (xml_rm(x) < 0)
goto done;
if (xml_free(xt) < 0)
goto done;
*xp = x;
break;
}
retval = 0;
done:
return retval;
}
/*! Given XML tree x0 with marked nodes, copy marked nodes to new tree x1
* Two marks are used: XML_FLAG_MARK and XML_FLAG_CHANGE
*
* The algorithm works as following:
* (1) Copy individual nodes marked with XML_FLAG_CHANGE
* until nodes marked with XML_FLAG_MARK are reached, where
* (2) the complete subtree of that node is copied.
* (3) Special case: key nodes in lists are copied if any node in list is marked
* @note you may want to check:!yang_config(ys)
*/
static int
xml_copy_marked(cxobj *x0,
cxobj *x1)
{
int retval = -1;
int mark;
cxobj *x;
cxobj *xcopy;
int iskey;
yang_stmt *yt;
char *name;
assert(x0 && x1);
yt = xml_spec(x0); /* can be null */
/* Copy all attributes */
x = NULL;
while ((x = xml_child_each(x0, x, CX_ATTR)) != NULL) {
name = xml_name(x);
if ((xcopy = xml_new(name, x1, xml_spec(x))) == NULL)
goto done;
if (xml_copy(x, xcopy) < 0)
goto done;
}
/* Go through children to detect any marked nodes:
* (3) Special case: key nodes in lists are copied if any
* node in list is marked
*/
mark = 0;
x = NULL;
while ((x = xml_child_each(x0, x, CX_ELMNT)) != NULL) {
if (xml_flag(x, XML_FLAG_MARK|XML_FLAG_CHANGE)){
mark++;
break;
}
}
x = NULL;
while ((x = xml_child_each(x0, x, CX_ELMNT)) != NULL) {
name = xml_name(x);
if (xml_flag(x, XML_FLAG_MARK)){
/* (2) the complete subtree of that node is copied. */
if ((xcopy = xml_new(name, x1, xml_spec(x))) == NULL)
goto done;
if (xml_copy(x, xcopy) < 0)
goto done;
continue;
}
if (xml_flag(x, XML_FLAG_CHANGE)){
/* Copy individual nodes marked with XML_FLAG_CHANGE */
if ((xcopy = xml_new(name, x1, xml_spec(x))) == NULL)
goto done;
if (xml_copy_marked(x, xcopy) < 0) /* */
goto done;
}
/* (3) Special case: key nodes in lists are copied if any
* node in list is marked */
if (mark && yt && yt->ys_keyword == Y_LIST){
/* XXX: I think yang_key_match is suboptimal here */
if ((iskey = yang_key_match((yang_node*)yt, name)) < 0)
goto done;
if (iskey){
if ((xcopy = xml_new(name, x1, xml_spec(x))) == NULL)
goto done;
if (xml_copy(x, xcopy) < 0)
goto done;
}
}
}
retval = 0;
done:
return retval;
}
/*! Read module-state in an XML tree
*
* @param[in] th Datastore text handle
* @param[in] yspec Top-level yang spec
* @param[in] xt XML tree
* @param[out] msd If set, return modules-state differences
*
* Read mst (module-state-tree) from xml tree (if any) and compare it with
* the system state mst.
* This can happen:
* 1) There is no modules-state info in the file
* 2) There is module state info in the file
* 3) For each module state m in the file:
* 3a) There is no such module in the system
* 3b) File module-state matches system
* 3c) File module-state does not match system
*/
static int
text_read_modstate(clicon_handle h,
yang_spec *yspec,
cxobj *xt,
modstate_diff_t *msd)
{
int retval = -1;
cxobj *xmodst;
cxobj *xm = NULL;
cxobj *xm2;
cxobj *xs;
char *name; /* module name */
char *mrev; /* file revision */
char *srev; /* system revision */
cxobj *xmcache = NULL;
xmcache = clicon_modst_cache_get(h, 1);
if ((xmodst = xml_find_type(xt, NULL, "modules-state", CX_ELMNT)) == NULL){
/* 1) There is no modules-state info in the file */
}
else if (xmcache && msd){
/* Create diff trees */
if (xml_parse_string("<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"/>", yspec, &msd->md_del) < 0)
goto done;
if (xml_rootchild(msd->md_del, 0, &msd->md_del) < 0)
goto done;
if (xml_parse_string("<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"/>", yspec, &msd->md_mod) < 0)
goto done;
if (xml_rootchild(msd->md_mod, 0, &msd->md_mod) < 0)
goto done;
/* 3) For each module state m in the file */
while ((xm = xml_child_each(xmodst, xm, CX_ELMNT)) != NULL) {
if (strcmp(xml_name(xm), "module"))
continue; /* ignore other tags, such as module-set-id */
if ((name = xml_find_body(xm, "name")) == NULL)
continue;
/* 3a) There is no such module in the system */
if ((xs = xpath_first(xmcache, "module[name=\"%s\"]", name)) == NULL){
// fprintf(stderr, "%s: Module %s: not in system\n", __FUNCTION__, name);
if ((xm2 = xml_dup(xm)) == NULL)
goto done;
if (xml_addsub(msd->md_del, xm2) < 0)
goto done;
continue;
}
/* These two shouldnt happen since revision is key, just ignore */
if ((mrev = xml_find_body(xm, "revision")) == NULL)
continue;
if ((srev = xml_find_body(xs, "revision")) == NULL)
continue;
if (strcmp(mrev, srev)==0){
/* 3b) File module-state matches system */
// fprintf(stderr, "%s: Module %s: file \"%s\" and system revisions match\n", __FUNCTION__, name, mrev);
}
else{
/* 3c) File module-state does not match system */
// fprintf(stderr, "%s: Module %s: file \"%s\" and system \"%s\" revisions do not match\n", __FUNCTION__, name, mrev, srev);
if ((xm2 = xml_dup(xm)) == NULL)
goto done;
if (xml_addsub(msd->md_mod, xm2) < 0)
goto done;
}
}
}
/* The module-state is removed from the input XML tree. This is done
* in all cases, whether CLICON_XMLDB_MODSTATE is on or not.
* Clixon systems with CLICON_XMLDB_MODSTATE disabled ignores it
*/
if (xmodst){
if (xml_purge(xmodst) < 0)
goto done;
}
retval = 0;
done:
return retval;
}
/*! Common read function that reads an XML tree from file
* @param[in] th Datastore text handle
* @param[in] db Symbolic database name, eg "candidate", "running"
* @param[in] yspec Top-level yang spec
* @param[out] xp XML tree read from file
* @param[out] msd If set, return modules-state differences
*/
int
xmldb_readfile(clicon_handle h,
const char *db,
yang_spec *yspec,
cxobj **xp,
modstate_diff_t *msd)
{
int retval = -1;
cxobj *x0 = NULL;
char *dbfile = NULL;
int fd = -1;
char *format;
if (xmldb_db2file(h, db, &dbfile) < 0)
goto done;
if (dbfile==NULL){
clicon_err(OE_XML, 0, "dbfile NULL");
goto done;
}
if ((fd = open(dbfile, O_RDONLY)) < 0) {
clicon_err(OE_UNIX, errno, "open(%s)", dbfile);
goto done;
}
/* Parse file into XML tree */
format = clicon_option_str(h, "CLICON_XMLDB_FORMAT");
if (format && strcmp(format, "json")==0){
if ((json_parse_file(fd, yspec, &x0)) < 0)
goto done;
}
else if ((xml_parse_file(fd, "</config>", yspec, &x0)) < 0)
goto done;
/* Always assert a top-level called "config".
To ensure that, deal with two cases:
1. File is empty <top/> -> rename top-level to "config" */
if (xml_child_nr(x0) == 0){
if (xml_name_set(x0, "config") < 0)
goto done;
}
/* 2. File is not empty <top><config>...</config></top> -> replace root */
else{
/* There should only be one element and called config */
if (singleconfigroot(x0, &x0) < 0)
goto done;
}
/* From Clixon 3.10,datastore files may contain module-state defining
* which modules are used in the file.
*/
if (text_read_modstate(h, yspec, x0, msd) < 0)
goto done;
if (xp){
*xp = x0;
x0 = NULL;
}
retval = 0;
done:
if (fd != -1)
close(fd);
if (dbfile)
free(dbfile);
if (x0)
xml_free(x0);
return retval;
}
/*! Get content of database using xpath. return a set of matching sub-trees
* The function returns a minimal tree that includes all sub-trees that match
* xpath.
* This is a clixon datastore plugin of the the xmldb api
* @param[in] h Clicon handle
* @param[in] db Name of database to search in (filename including dir path
* @param[in] xpath String with XPATH syntax. or NULL for all
* @param[in] config If set only configuration data, else also state
* @param[out] xret Single return XML tree. Free with xml_free()
* @param[out] msd If set, return modules-state differences
* @retval 0 OK
* @retval -1 Error
* @see xmldb_get the generic API function
*/
int
xmldb_get_nocache(clicon_handle h,
const char *db,
char *xpath,
int config,
cxobj **xtop,
modstate_diff_t *msd)
{
int retval = -1;
char *dbfile = NULL;
yang_spec *yspec;
cxobj *xt = NULL;
cxobj *x;
int fd = -1;
cxobj **xvec = NULL;
size_t xlen;
int i;
char *format;
if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_YANG, ENOENT, "No yang spec");
goto done;
}
if (xmldb_db2file(h, db, &dbfile) < 0)
goto done;
if (dbfile==NULL){
clicon_err(OE_XML, 0, "dbfile NULL");
goto done;
}
if ((fd = open(dbfile, O_RDONLY)) < 0){
clicon_err(OE_UNIX, errno, "open(%s)", dbfile);
goto done;
}
/* Parse file into XML tree */
format = clicon_option_str(h, "CLICON_XMLDB_FORMAT");
if (format && strcmp(format, "json")==0){
if ((json_parse_file(fd, yspec, &xt)) < 0)
goto done;
}
else if ((xml_parse_file(fd, "</config>", yspec, &xt)) < 0)
goto done;
/* Always assert a top-level called "config".
To ensure that, deal with two cases:
1. File is empty <top/> -> rename top-level to "config" */
if (xml_child_nr(xt) == 0){
if (xml_name_set(xt, "config") < 0)
goto done;
}
/* 2. File is not empty <top><config>...</config></top> -> replace root */
else{
/* There should only be one element and called config */
if (singleconfigroot(xt, &xt) < 0)
goto done;
}
/* Here xt looks like: <config>...</config> */
/* Given the xpath, return a vector of matches in xvec */
if (xpath_vec(xt, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
goto done;
/* If vectors are specified then mark the nodes found with all ancestors
* and filter out everything else,
* otherwise return complete tree.
*/
if (xvec != NULL)
for (i=0; i<xlen; i++){
x = xvec[i];
xml_flag_set(x, XML_FLAG_MARK);
}
/* Remove everything that is not marked */
if (!xml_flag(xt, XML_FLAG_MARK))
if (xml_tree_prune_flagged_sub(xt, XML_FLAG_MARK, 1, NULL) < 0)
goto done;
/* reset flag */
if (xml_apply(xt, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0)
goto done;
/* filter out state (operations) data if config not set. Mark all nodes
that are not config data */
if (config){
if (xml_apply(xt, CX_ELMNT, xml_non_config_data, NULL) < 0)
goto done;
/* Remove (prune) nodes that are marked (that does not pass test) */
if (xml_tree_prune_flagged(xt, XML_FLAG_MARK, 1) < 0)
goto done;
}
/* Add default values (if not set) */
if (xml_apply(xt, CX_ELMNT, xml_default, NULL) < 0)
goto done;
#if 0 /* debug */
if (xml_apply0(xt, -1, xml_sort_verify, NULL) < 0)
clicon_log(LOG_NOTICE, "%s: sort verify failed #2", __FUNCTION__);
#endif
if (debug>1)
clicon_xml2file(stderr, xt, 0, 1);
*xtop = xt;
xt = NULL;
retval = 0;
done:
if (xt)
xml_free(xt);
if (dbfile)
free(dbfile);
if (xvec)
free(xvec);
if (fd != -1)
close(fd);
return retval;
}
/*! Get content of database using xpath. return a set of matching sub-trees
* The function returns a minimal tree that includes all sub-trees that match
* xpath.
* This is a clixon datastore plugin of the the xmldb api
* @param[in] h Clicon handle
* @param[in] db Name of database to search in (filename including dir path
* @param[in] xpath String with XPATH syntax. or NULL for all
* @param[in] config If set only configuration data, else also state
* @param[out] xret Single return XML tree. Free with xml_free()
* @param[out] msd If set, return modules-state differences
* @retval 0 OK
* @retval -1 Error
* @see xmldb_get the generic API function
*/
int
xmldb_get_cache(clicon_handle h,
const char *db,
char *xpath,
int config,
cxobj **xtop,
modstate_diff_t *msd)
{
int retval = -1;
yang_spec *yspec;
cxobj *x0t = NULL; /* (cached) top of tree */
cxobj *x0;
cxobj **xvec = NULL;
size_t xlen;
int i;
db_elmnt *de = NULL;
cxobj *x1t = NULL;
db_elmnt de0 = {0,};
if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_YANG, ENOENT, "No yang spec");
goto done;
}
de = clicon_db_elmnt_get(h, db);
if (de == NULL || de->de_xml == NULL){ /* Cache miss, read XML from file */
/* If there is no xml x0 tree (in cache), then read it from file */
if (xmldb_readfile(h, db, yspec, &x0t, msd) < 0)
goto done;
/* XXX: should we validate file if read from disk?
* Argument against: we may want to have a semantically wrong file and wish
* to edit?
*/
de0.de_xml = x0t;
clicon_db_elmnt_set(h, db, &de0);
} /* x0t == NULL */
else
x0t = de->de_xml;
/* Here x0t looks like: <config>...</config> */
/* Given the xpath, return a vector of matches in xvec
* Can we do everything in one go?
* 0) Make a new tree
* 1) make the xpath check
* 2) iterate thru matches (maybe this can be folded into the xpath_vec?)
* a) for every node that is found, copy to new tree
* b) if config dont dont state data
*/
/* Here xt looks like: <config>...</config> */
if (xpath_vec(x0t, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
goto done;
/* Make new tree by copying top-of-tree from x0t to x1t */
if ((x1t = xml_new(xml_name(x0t), NULL, xml_spec(x0t))) == NULL)
goto done;
/* Iterate through the match vector
* For every node found in x0, mark the tree up to t1
*/
for (i=0; i<xlen; i++){
x0 = xvec[i];
xml_flag_set(x0, XML_FLAG_MARK);
xml_apply_ancestor(x0, (xml_applyfn_t*)xml_flag_set, (void*)XML_FLAG_CHANGE);
}
if (xml_copy_marked(x0t, x1t) < 0) /* config */
goto done;
if (xml_apply(x0t, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)(XML_FLAG_MARK|XML_FLAG_CHANGE)) < 0)
goto done;
if (xml_apply(x1t, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)(XML_FLAG_MARK|XML_FLAG_CHANGE)) < 0)
goto done;
/* x1t is wrong here should be <config><system>.. but is <system>.. */
/* XXX where should we apply default values once? */
if (xml_apply(x1t, CX_ELMNT, xml_default, NULL) < 0)
goto done;
/* Copy the matching parts of the (relevant) XML tree.
* If cache was empty, also update to datastore cache
*/
if (debug>1)
clicon_xml2file(stderr, x1t, 0, 1);
*xtop = x1t;
retval = 0;
done:
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
if (xvec)
free(xvec);
return retval;
}

View file

@ -0,0 +1,46 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
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 *****
* Datastore text-based XML read functions
*/
#ifndef _CLIXON_DATASTORE_READ_H
#define _CLIXON_DATASTORE_READ_H
/*
* Prototypes
*/
int xmldb_get_cache(clicon_handle h, const char *db, char *xpath, int config, cxobj **xret, modstate_diff_t *msd);
int xmldb_get_nocache(clicon_handle h, const char *db, char *xpath, int config, cxobj **xret, modstate_diff_t *msd);
int xmldb_readfile(clicon_handle h, const char *db, yang_spec *yspec, cxobj **xp, modstate_diff_t *msd);
#endif /* _CLIXON_DATASTORE_READ_H */