458 lines
10 KiB
C
458 lines
10 KiB
C
/*
|
|
*
|
|
***** 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 *****
|
|
|
|
*
|
|
* XML file save
|
|
*/
|
|
|
|
#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 <ctype.h>
|
|
#include <string.h>
|
|
#include <syslog.h>
|
|
#include <fcntl.h>
|
|
#include <assert.h>
|
|
#include <arpa/inet.h>
|
|
#include <sys/param.h>
|
|
#include <netinet/in.h>
|
|
|
|
/* cligen */
|
|
#include <cligen/cligen.h>
|
|
|
|
/* clicon */
|
|
|
|
#include "clixon_string.h"
|
|
#include "clixon_queue.h"
|
|
#include "clixon_hash.h"
|
|
#include "clixon_err.h"
|
|
#include "clixon_log.h"
|
|
#include "clixon_handle.h"
|
|
#include "clixon_yang.h"
|
|
#include "clixon_xml.h"
|
|
|
|
#include "clixon_datastore_tree.h"
|
|
|
|
#define align4(s) (((s)/4)*4 + 4)
|
|
|
|
struct indexlist{
|
|
qelem_t il_q; /* this must be there */
|
|
uint32_t il_i;
|
|
int il_len; /* How many */
|
|
};
|
|
typedef struct indexlist indexlist;
|
|
|
|
struct indexhead{
|
|
indexlist *ih_list;
|
|
};
|
|
typedef struct indexhead indexhead;
|
|
|
|
static int
|
|
indexhead_free(indexhead *ih)
|
|
{
|
|
indexlist *il;
|
|
|
|
while ((il = ih->ih_list) != NULL)
|
|
DELQ(il, ih->ih_list, indexlist*);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
index_get(indexhead *ih,
|
|
size_t len, /* in bytes */
|
|
uint32_t *indexp)
|
|
{
|
|
int retval = -1;
|
|
int nr;
|
|
uint32_t index = 0;
|
|
struct indexlist *il;
|
|
indexlist *new;
|
|
|
|
nr = len/DF_BLOCK + 1;
|
|
if ((new = malloc(sizeof(indexlist))) == NULL){
|
|
clicon_err(OE_XML, errno, "malloc");
|
|
goto done;
|
|
}
|
|
memset(new, 0, sizeof(indexlist));
|
|
// new->il_i = i; Dont know i yet
|
|
new->il_len = nr;
|
|
if ((il = ih->ih_list) != NULL) {
|
|
il = PREVQ(indexlist*, il);
|
|
index = il->il_i + il->il_len;
|
|
}
|
|
ADDQ(new, ih->ih_list);
|
|
new->il_i = index;
|
|
*indexp = index;
|
|
retval = 0;
|
|
done:
|
|
return retval;
|
|
}
|
|
#ifdef NOTYET
|
|
int
|
|
index_get(indexhead *ih,
|
|
size_t len, /* in bytes */
|
|
uint32_t *indexp)
|
|
{
|
|
int retval = -1;
|
|
int nr;
|
|
uint32_t index = 0;
|
|
struct indexlist *il;
|
|
indexlist *new;
|
|
|
|
nr = len/DF_BLOCK + 1;
|
|
if ((new = malloc(sizeof(indexlist))) == NULL){
|
|
clicon_err(OE_XML, errno, "malloc");
|
|
goto done;
|
|
}
|
|
memset(new, 0, sizeof(indexlist));
|
|
// new->il_i = i; Dont know i yet
|
|
new->il_len = nr;
|
|
|
|
/* Find nr conseq blocks */
|
|
if ((il = ih->ih_list) != NULL) {
|
|
do {
|
|
if (il->il_i - index >= nr){ /* Found */
|
|
INSQ(new, il);
|
|
break;
|
|
}
|
|
index = il->il_i + il->il_len;
|
|
il = NEXTQ(indexlist*, il);
|
|
} while (il != ih->ih_list);
|
|
ADDQ(new, il); /* after? */
|
|
}
|
|
else
|
|
INSQ(new, ih->ih_list);
|
|
new->il_i = index;
|
|
*indexp = index;
|
|
retval = 0;
|
|
done:
|
|
return retval;
|
|
}
|
|
#endif
|
|
|
|
int
|
|
index_dump(FILE *f,
|
|
indexhead *ih)
|
|
{
|
|
indexlist *il;
|
|
|
|
if ((il = ih->ih_list) != NULL) {
|
|
do {
|
|
fprintf(f, "%u %d\n", il->il_i, il->il_len);
|
|
il = NEXTQ(indexlist*, il);
|
|
} while (il != ih->ih_list);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* @param[out] xp
|
|
* @param[out] vec If type is CX_ELMNT, points to vector of indexes,...
|
|
* @retval -1 Error
|
|
* @retval 0 Sanity check failed
|
|
* @retval 1 OK xp set (or NULL if empty)
|
|
*/
|
|
static int
|
|
buf2xml(FILE *f,
|
|
uint32_t index,
|
|
cxobj **xp)
|
|
{
|
|
int retval = -1;
|
|
uint8_t ver;
|
|
uint8_t type;
|
|
int ptr;
|
|
uint32_t len;
|
|
cxobj *x = NULL;
|
|
char *name;
|
|
char *prefix = NULL;
|
|
char hdr[8] = {0, };
|
|
char *buf = NULL;
|
|
int vlen;
|
|
int i;
|
|
uint32_t ind;
|
|
cxobj *xc;
|
|
size_t ret;
|
|
|
|
/* Read hdr
|
|
* +-----+-----+-----+-----+-----+-----+-----+-----+
|
|
* | ver | type| de | ad | len |
|
|
* +-----+-----+-----+-----+-----+-----+-----+-----+
|
|
*/
|
|
if (fseek(f, index*DF_BLOCK, SEEK_SET) < 0){
|
|
clicon_err(OE_UNIX, errno, "fseek");
|
|
goto done;
|
|
}
|
|
if ((ret = fread(hdr, sizeof(char), sizeof(hdr), f)) != sizeof(hdr)){
|
|
clicon_err(OE_XML, errno, "fread");
|
|
goto done;
|
|
}
|
|
if ((ver = hdr[0]&0xff) == 0) /* Means empty */
|
|
goto ok;
|
|
if (ver != 1){
|
|
clicon_debug(1, "%s Wrong version: %d", __FUNCTION__, ver);
|
|
goto fail;
|
|
}
|
|
type = hdr[1]&0x0f;
|
|
if (type != CX_BODY && type != CX_ELMNT && type != CX_ATTR){
|
|
clicon_debug(1, "%s Wrong type: %d", __FUNCTION__, type);
|
|
goto fail;
|
|
}
|
|
if ((hdr[2]&0xff) != 0xde){
|
|
clicon_debug(1, "%s Expected 0xde: %x", __FUNCTION__, hdr[2]&0xff);
|
|
goto fail;
|
|
}
|
|
if ((hdr[3]&0xff) != 0xad){
|
|
clicon_debug(1, "%s Expected 0xad: %x", __FUNCTION__, hdr[3]&0xff);
|
|
goto fail;
|
|
}
|
|
/* Copy and byte-swap length field */
|
|
memcpy(&len, &hdr[4], 4);
|
|
len = ntohl(len);
|
|
|
|
/* Read rest
|
|
*/
|
|
ptr = 0;
|
|
if ((buf = malloc(len - sizeof(hdr))) == NULL){
|
|
clicon_err(OE_UNIX, errno, "malloc");
|
|
goto done;
|
|
}
|
|
if ((ret = fread(buf, sizeof(char), len-sizeof(hdr), f)) != len-sizeof(hdr)){
|
|
clicon_err(OE_XML, errno, "fread");
|
|
goto done;
|
|
}
|
|
if ((name = strchr(buf, ':')) != NULL){
|
|
*name = '\0';
|
|
name++;
|
|
prefix = buf;
|
|
ptr += strlen(prefix)+1;
|
|
}
|
|
else
|
|
name = buf;
|
|
ptr += strlen(name) + 1;
|
|
ptr = align4(ptr);
|
|
if ((x = xml_new(name, NULL, NULL)) == NULL)
|
|
goto done;
|
|
if (prefix)
|
|
if (xml_prefix_set(x, prefix) < 0)
|
|
goto done;
|
|
xml_type_set(x, type);
|
|
if (xml_type(x) != CX_ELMNT){
|
|
if (xml_value_set(x, &buf[ptr]) < 0)
|
|
goto done;
|
|
ptr += strlen(xml_value(x)) +1;
|
|
}
|
|
else{
|
|
vlen = (len - sizeof(hdr) - ptr)/ sizeof(uint32_t);
|
|
if (vlen < 0){
|
|
clicon_debug(1, "%s Nr of elements should not be negative: %d", __FUNCTION__, vlen);
|
|
goto fail;
|
|
}
|
|
for (i=0; i<vlen; i++){
|
|
memcpy(&ind, &buf[ptr], sizeof(uint32_t));
|
|
ptr += sizeof(uint32_t);
|
|
if (buf2xml(f, ntohl(ind), &xc) < 0)
|
|
goto done;
|
|
if (xml_addsub(x, xc) < 0)
|
|
goto done;
|
|
}
|
|
}
|
|
assert(len == sizeof(hdr)+ptr);
|
|
ok:
|
|
if (xp)
|
|
*xp = x;
|
|
retval = 1;
|
|
done:
|
|
if (buf)
|
|
free(buf);
|
|
return retval;
|
|
fail:
|
|
retval = 0;
|
|
goto done;
|
|
}
|
|
|
|
/*! Dump xml object to a datastore block
|
|
*/
|
|
static int
|
|
xml2buf(FILE *f,
|
|
cxobj *x,
|
|
indexhead *ih,
|
|
uint32_t *indexp)
|
|
{
|
|
int retval = -1;
|
|
char hdr[8] = {0,};
|
|
int len;
|
|
int len1;
|
|
int ptr;
|
|
int i;
|
|
char *buf0 = NULL;
|
|
char *buf;
|
|
uint32_t myindex;
|
|
|
|
hdr[0] = 1;
|
|
hdr[1] = 0x80 | xml_type(x);
|
|
hdr[2] = 0xde;
|
|
hdr[3] = 0xad;
|
|
len = sizeof(hdr);
|
|
if (xml_prefix(x))
|
|
len += strlen(xml_prefix(x)) + 1; /* colon */
|
|
len += strlen(xml_name(x)) + 1; /* end-of-string */
|
|
len = align4(len);
|
|
switch(xml_type(x)){
|
|
case CX_ELMNT:
|
|
len += xml_child_nr(x)*sizeof(uint32_t);
|
|
break;
|
|
case CX_BODY:
|
|
case CX_ATTR:
|
|
len += strlen(xml_value(x))+1;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (index_get(ih, len, &myindex) < 0)
|
|
goto done;
|
|
|
|
len1 = htonl(len); /* swap */
|
|
memcpy(&hdr[4], &len1, 4);
|
|
/* Write rest
|
|
*/
|
|
if ((buf0 = malloc(len)) == NULL){
|
|
clicon_err(OE_UNIX, errno, "malloc");
|
|
goto done;
|
|
}
|
|
buf = buf0+sizeof(hdr);
|
|
ptr = 0;
|
|
if (xml_prefix(x)){
|
|
strcpy(buf, xml_prefix(x));
|
|
ptr += strlen(xml_prefix(x));
|
|
buf[ptr++] = ':';
|
|
}
|
|
strcpy(&buf[ptr], xml_name(x));
|
|
ptr += strlen(xml_name(x)) + 1;
|
|
for (i=ptr; i< align4(ptr); i++)
|
|
buf[i] = 0;
|
|
ptr = align4(ptr);
|
|
if (xml_type(x) != CX_ELMNT){
|
|
strcpy(&buf[ptr], xml_value(x));
|
|
ptr += strlen(xml_value(x)) +1;
|
|
}
|
|
else{
|
|
uint32_t index;
|
|
for (i=0; i< xml_child_nr(x); i++){
|
|
if (xml2buf(f, xml_child_i(x, i), ih,
|
|
&index) < 0)
|
|
goto done;
|
|
index = htonl(index);
|
|
memcpy(&buf[ptr], &index, sizeof(uint32_t));
|
|
ptr += sizeof(uint32_t);
|
|
}
|
|
}
|
|
assert(len == sizeof(hdr)+ptr);
|
|
/* Note write both here so we dont get an intermediate seek,... */
|
|
if (fseek(f, myindex*DF_BLOCK, SEEK_SET) < 0){
|
|
clicon_err(OE_UNIX, errno, "fseek");
|
|
goto done;
|
|
}
|
|
memcpy(buf0, hdr, sizeof(hdr));
|
|
if (fwrite(buf0, sizeof(char), len, f) == 0){
|
|
clicon_err(OE_XML, errno, "fwrite");
|
|
goto done;
|
|
}
|
|
if (indexp)
|
|
*indexp = myindex;
|
|
retval = 0;
|
|
done:
|
|
if (buf0)
|
|
free(buf0);
|
|
return retval;
|
|
}
|
|
|
|
int
|
|
datastore_tree_write(clicon_handle h,
|
|
char *filename,
|
|
cxobj *xt)
|
|
{
|
|
int retval = -1;
|
|
FILE *f = NULL;
|
|
indexhead ih = {0,};
|
|
|
|
if ((f = fopen(filename, "w+b")) == NULL){
|
|
clicon_err(OE_XML, errno, "Opening file %s", filename);
|
|
goto done;
|
|
}
|
|
if (xml2buf(f, xt, &ih, NULL) < 0)
|
|
goto done;
|
|
if (debug)
|
|
index_dump(stderr, &ih);
|
|
retval = 0;
|
|
done:
|
|
indexhead_free(&ih);
|
|
if (f != NULL)
|
|
fclose(f);
|
|
return retval;
|
|
}
|
|
|
|
/*
|
|
* @retval -1 Error
|
|
* @retval 0 Sanity check failed
|
|
* @retval 1 OK xp set (or NULL if empty)
|
|
*/
|
|
int
|
|
datastore_tree_read(clicon_handle h,
|
|
char *filename,
|
|
cxobj **xt)
|
|
{
|
|
int retval = -1;
|
|
FILE *f = NULL;
|
|
|
|
if ((f = fopen(filename, "r")) == NULL){
|
|
clicon_err(OE_XML, errno, "Opening file %s", filename);
|
|
goto done;
|
|
}
|
|
if ((retval = buf2xml(f, 0, xt)) < 0)
|
|
goto done;
|
|
if (retval == 0 || /* fail */
|
|
*xt == NULL){ /* empty */
|
|
if ((*xt = xml_new("config", NULL, NULL)) == NULL)
|
|
goto done;
|
|
xml_type_set(*xt, CX_ELMNT);
|
|
}
|
|
done:
|
|
if (f != NULL)
|
|
fclose(f);
|
|
return retval;
|
|
}
|