xml.c revision f3861e1a2ceec23a5b699c24d814b7775a9e0b52
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <inttypes.h>
#include <assert.h>
#include <libxml/xmlreader.h>
#include <strings.h>
#include <ctype.h>
#include <stdlib.h>
#include <syslog.h>
#include <sys/stat.h>
#include "iscsitgt_impl.h"
/*
* Forward declarations
*/
static char *strip_space(char *value);
static tgt_node_t *node_alloc();
static void node_free(tgt_node_t *x);
static Boolean_t node_name(tgt_node_t *x, const xmlChar *n);
static Boolean_t node_value(tgt_node_t *x, const xmlChar *n, Boolean_t s);
static tgt_node_t *node_parent(tgt_node_t *x);
static tgt_node_t *node_child(tgt_node_t *x);
static tgt_node_t *node_alloc_attr(tgt_node_t *x);
static void buf_add_node_attr(char **b, tgt_node_t *x);
static void buf_add_comment(char **b, char *comment);
static void buf_add_str(char **b, char *str);
#define XML_COMMENT_STR "!--"
#define XML_COMMENT_END "--"
void
tgt_node_free(tgt_node_t *n)
{
tgt_node_t *c,
*c1;
if (n == NULL)
return;
for (c = n->x_child; c; ) {
c1 = c->x_sibling;
tgt_node_free(c);
c = c1;
}
for (c = n->x_attr; c; ) {
c1 = c->x_sibling;
node_free(c);
c = c1;
}
node_free(n);
}
/*
* []----
* | xml_update_config -- dump out in core node tree to XML parsable file
* |
* | The depth argument is only used to pad the output with white space
* | for indentation. This is meant to help the readability of the file
* | for humans.
* []----
*/
static void
xml_update_config(tgt_node_t *t, int depth, FILE *output)
{
int i;
tgt_node_t *c;
if (t == NULL)
return;
/*
* If this node has an attribute which indicates it's incore only
* we don't dump it out to a file.
*/
for (c = t->x_attr; c; c = c->x_sibling)
if ((strcmp(c->x_name, XML_ELEMENT_INCORE) == 0) &&
(strcmp(c->x_value, XML_VALUE_TRUE) == 0))
return;
for (i = 0; i < depth; i++)
(void) fprintf(output, " ");
if (strcmp(t->x_name, XML_COMMENT_STR) == 0) {
(void) fprintf(output, "<%s%s%s>\n", XML_COMMENT_STR,
t->x_value, XML_COMMENT_END);
return;
}
if ((t->x_child == NULL) && (t->x_value != NULL) &&
(t->x_attr == NULL) &&
(((depth * 4) + ((strlen(t->x_name) * 2) + 5) +
strlen(t->x_value)) < 80)) {
(void) fprintf(output, "<%s>%s</%s>\n", t->x_name, t->x_value,
t->x_name);
} else {
(void) fprintf(output, "<%s", t->x_name);
for (c = t->x_attr; c; c = c->x_sibling)
(void) fprintf(output, " %s='%s'", c->x_name,
c->x_value);
(void) fprintf(output, ">\n");
if (t->x_value) {
for (i = 0; i < depth + 1; i++)
(void) fprintf(output, " ");
(void) fprintf(output, "%s\n", t->x_value);
}
for (c = t->x_child; c; c = c->x_sibling)
xml_update_config(c, depth + 1, output);
for (i = 0; i < depth; i++)
(void) fprintf(output, " ");
(void) fprintf(output, "</%s>\n", t->x_name);
}
}
Boolean_t
tgt_dump2file(tgt_node_t *root, char *path)
{
FILE *output;
if ((output = fopen(path, "wb")) == NULL) {
syslog(LOG_ERR, "Cannot open the file: %s\n", path);
return (False);
} else {
xml_update_config(root, 0, output);
(void) fclose(output);
/*
* Make sure only root can access the file since we're
* optionally keeping CHAP secrets located here.
*/
(void) chmod(path, 0600);
return (True);
}
}
/*
* tgt_dump2buf -- dumps node tree to buffer, allocating memory as it goes
*
* It is up to the caller, when finished with 'buf', to call free()
*/
void
tgt_dump2buf(tgt_node_t *n, char **buf)
{
tgt_node_t *c;
if (n == NULL)
return;
if (strcmp(n->x_name, XML_COMMENT_STR) == 0) {
buf_add_comment(buf, n->x_value);
return;
}
buf_add_node_attr(buf, n);
if (n->x_value != NULL)
tgt_buf_add_tag(buf, n->x_value, Tag_String);
for (c = n->x_child; c; c = c->x_sibling)
tgt_dump2buf(c, buf);
tgt_buf_add_tag(buf, n->x_name, Tag_End);
}
char *common_attr_list[] = {
XML_ELEMENT_NAME,
XML_ELEMENT_VERS,
XML_ELEMENT_INCORE,
0
};
Boolean_t
tgt_node_process(xmlTextReaderPtr r, tgt_node_t **node)
{
const xmlChar *name,
*value;
char **ap;
xmlElementType node_type;
tgt_node_t *n,
*an;
n = *node;
if (n == NULL) {
n = node_alloc();
if (n == NULL)
return (False);
*node = n;
}
name = (xmlChar *)xmlTextReaderConstName(r);
if (name == NULL) {
node_free(n);
return (False);
}
node_type = (xmlElementType)xmlTextReaderNodeType(r);
value = (xmlChar *)xmlTextReaderConstValue(r);
if (node_type == XML_ELEMENT_NODE) {
if (n->x_state != NodeAlloc) {
n = node_child(n);
*node = n;
if (n == NULL)
return (False);
}
if (xmlTextReaderAttributeCount(r) > 0) {
for (ap = common_attr_list; *ap; ap++) {
value = xmlTextReaderGetAttribute(r,
(xmlChar *)*ap);
if (value != NULL) {
if ((an = node_alloc_attr(n)) == NULL)
return (False);
if (node_name(an, (xmlChar *)*ap) ==
False) {
node_free(an);
return (False);
}
if (node_value(an, value, True) ==
False) {
node_free(an);
return (False);
}
free((char *)value);
}
}
}
if (node_name(n, name) == False) {
node_free(n);
return (False);
}
} else if ((value != NULL) && (node_type == XML_TEXT_NODE)) {
if (node_value(n, value, True) == False) {
node_free(n);
return (False);
}
} else if (node_type == XML_ELEMENT_DECL) {
n = node_parent(n);
if (n == NULL)
return (False);
*node = n;
} else if (node_type == XML_COMMENT_NODE) {
n = node_child(n);
if (node_name(n, (xmlChar *)XML_COMMENT_STR) == False) {
node_free(n);
return (False);
}
if (node_value(n, (xmlChar *)value, False) == False) {
node_free(n);
return (False);
}
} else if (node_type != XML_DTD_NODE) {
node_free(n);
return (False);
}
return (True);
}
Boolean_t
tgt_find_attr_str(tgt_node_t *n, char *attr, char **value)
{
tgt_node_t *a;
if ((n == NULL) || (n->x_attr == NULL))
return (False);
for (a = n->x_attr; a; a = a->x_sibling)
if (strcmp(a->x_name, attr) == 0) {
*value = a->x_value ? strdup(a->x_value) : NULL;
return (True);
}
return (False);
}
Boolean_t
tgt_find_value_str(tgt_node_t *n, char *name, char **value)
{
tgt_node_t *c;
if ((n == NULL) || (n->x_name == NULL))
return (False);
if (strcmp(n->x_name, name) == 0) {
*value = n->x_value ? strdup(n->x_value) : NULL;
return (True);
}
for (c = n->x_child; c; c = c->x_sibling) {
if (tgt_find_value_str(c, name, value) == True)
return (True);
}
return (False);
}
Boolean_t
tgt_find_value_int(tgt_node_t *n, char *name, int *value)
{
tgt_node_t *c;
if ((n == NULL) || (n->x_name == NULL))
return (False);
if (strcmp(n->x_name, name) == 0) {
if (n->x_value == NULL)
return (False);
*value = strtol(n->x_value, NULL, 0);
return (True);
}
for (c = n->x_child; c; c = c->x_sibling) {
if (tgt_find_value_int(c, name, value) == True)
return (True);
}
return (False);
}
/*
* []----
* | xml_find_value_intchk -- if node exists, check to see if value is okay
* []----
*/
Boolean_t
tgt_find_value_intchk(tgt_node_t *n, char *name, int *value)
{
char *str,
chk[32];
Boolean_t rval;
if (tgt_find_value_str(n, name, &str) == True) {
*value = strtol(str, NULL, 0);
/*
* Validate that the input string hasn't overrun what
* what an integer can handle. This is done by simply
* printing out the result of the conversion into a buffer
* and comparing it to the incoming string. That way when
* someone enters 4294967296 which strtol returns as 0
* we'll catch it.
*/
if ((str[0] == '0') && (str[1] != '\0')) {
if (str[1] == 'x')
(void) snprintf(chk, sizeof (chk), "0x%x",
*value);
else if (str[1] == 'X')
(void) snprintf(chk, sizeof (chk), "0X%x",
*value);
else
(void) snprintf(chk, sizeof (chk), "0%o",
*value);
} else
(void) snprintf(chk, sizeof (chk), "%d", *value);
if (strcmp(chk, str) == 0)
rval = True;
else
rval = False;
free(str);
return (rval);
} else
return (True);
}
Boolean_t
tgt_find_value_boolean(tgt_node_t *n, char *name, Boolean_t *value)
{
tgt_node_t *c;
if ((n == NULL) || (n->x_name == NULL))
return (False);
if (strcmp(n->x_name, name) == 0) {
if (n->x_value == NULL)
return (False);
*value = strcmp(n->x_value, "true") == 0 ? True : False;
return (True);
}
for (c = n->x_child; c; c = c->x_sibling) {
if (tgt_find_value_boolean(c, name, value) == True)
return (True);
}
return (False);
}
tgt_node_t *
tgt_node_next(tgt_node_t *n, char *name, tgt_node_t *cur)
{
tgt_node_t *x,
*p;
if (n == NULL)
return (NULL);
if (cur != NULL) {
for (x = cur->x_sibling; x; x = x->x_sibling)
if (strcmp(x->x_name, name) == 0)
return (x);
return (NULL);
}
if (n->x_name == NULL)
return (NULL);
if (strcmp(n->x_name, name) == 0)
return (n);
for (x = n->x_child; x; x = x->x_sibling)
if ((p = tgt_node_next(x, name, 0)) != NULL)
return (p);
return (NULL);
}
tgt_node_t *
tgt_node_next_child(tgt_node_t *n, char *name, tgt_node_t *cur)
{
if (cur != NULL) {
n = cur->x_sibling;
} else {
if (n != NULL)
n = n->x_child;
else
return (NULL);
}
while (n) {
if (strcmp(n->x_name, name) == 0)
return (n);
n = n->x_sibling;
}
return (NULL);
}
void
tgt_node_add(tgt_node_t *p, tgt_node_t *c)
{
if ((p == NULL) || (c == NULL))
return;
if (p->x_child == NULL)
p->x_child = c;
else {
c->x_sibling = p->x_child;
p->x_child = c;
}
}
void
tgt_node_add_attr(tgt_node_t *p, tgt_node_t *a)
{
if ((p == NULL) || (a == NULL))
return;
if (p->x_attr == NULL)
p->x_attr = a;
else {
a->x_sibling = p->x_attr;
p->x_attr = a;
}
}
tgt_node_t *
tgt_node_alloc(char *name, xml_val_type_t type, void *value)
{
tgt_node_t *d = node_alloc();
int value_len = 0;
char *value_str = NULL;
if (d == NULL)
return (NULL);
switch (type) {
case String:
if (value)
value_len = strlen((char *)value) + 1;
break;
case Int:
value_len = sizeof (int) * 2 + 3;
break;
case Uint64:
value_len = sizeof (uint64_t) * 2 + 3;
break;
}
if (value_len &&
(value_str = (char *)calloc(sizeof (char), value_len)) == NULL)
return (NULL);
if (node_name(d, (xmlChar *)name) == False) {
free(value_str);
return (NULL);
}
if (value_str) {
switch (type) {
case String:
(void) snprintf(value_str, value_len, "%s",
(char *)value);
break;
case Int:
(void) snprintf(value_str, value_len, "0x%x",
*(int *)value);
break;
case Uint64:
(void) snprintf(value_str, value_len, "0x%llx",
*(uint64_t *)value);
break;
}
}
(void) node_value(d, (xmlChar *)value_str, True);
return (d);
}
Boolean_t
tgt_xml_encode(uint8_t *ip, size_t ip_size, char **buf, size_t *buf_size)
{
char *bp;
*buf_size = (ip_size * 2) + 1;
if ((*buf = (char *)malloc(*buf_size)) == NULL) {
*buf_size = 0;
return (False);
}
for (bp = *buf; ip_size; ip_size--) {
(void) sprintf(bp, "%.2x", *ip);
ip++;
bp += 2;
}
return (True);
}
Boolean_t
tgt_xml_decode(char *buf, uint8_t **ip, size_t *ip_size)
{
uint8_t *i;
size_t buf_size = strlen(buf);
*ip_size = buf_size / 2;
if ((*ip = (uint8_t *)malloc(*ip_size)) == NULL) {
*ip_size = 0;
return (False);
}
for (i = *ip; buf_size; buf_size -= 2) {
char x[3];
bcopy(buf, x, 2);
x[2] = 0;
*i++ = strtol(x, NULL, 16);
buf += 2;
}
return (True);
}
Boolean_t
tgt_node_remove(tgt_node_t *parent, tgt_node_t *child, match_type_t m)
{
tgt_node_t *s,
*c = NULL;
if ((parent == NULL) || (child == NULL))
return (False);
for (s = parent->x_child; s; c = s, s = s->x_sibling) {
/*
* See if the new child node matches one of the children
* in the parent.
*/
if ((strcmp(s->x_name, child->x_name) == 0) &&
((m == MatchName) || (strcmp(s->x_value,
child->x_value) == 0))) {
if (parent->x_child == s) {
parent->x_child = s->x_sibling;
} else {
c->x_sibling = s->x_sibling;
}
tgt_node_free(s);
break;
}
}
if (s == NULL)
return (False);
else
return (True);
}
void
tgt_node_replace(tgt_node_t *parent, tgt_node_t *child, match_type_t m)
{
tgt_node_t *s,
*c;
if ((parent == NULL) || (child == NULL))
return;
for (s = parent->x_child; s; s = s->x_sibling) {
/*
* See if the new child node matches one of the children
* in the parent.
*/
if ((strcmp(s->x_name, child->x_name) == 0) &&
((m == MatchName) || (strcmp(s->x_value,
child->x_value) == 0))) {
/*
* We have a match. Now save the values of the new
* child in this current node.
*/
free(s->x_name);
free(s->x_value);
s->x_name = strdup(child->x_name);
s->x_value = strdup(child->x_value);
if (s->x_child) {
tgt_node_free(s->x_child);
s->x_child = NULL;
}
for (c = child->x_child; c; c = c->x_sibling)
(void) tgt_node_add(s, tgt_node_dup(c));
break;
}
}
if (s == NULL) {
/*
* Never found the child so add it
*/
(void) tgt_node_add(parent, tgt_node_dup(child));
}
}
Boolean_t
tgt_update_value_str(tgt_node_t *node, char *name, char *str)
{
if ((node == NULL) || (strcmp(name, node->x_name) != 0))
return (False);
if (node->x_value != NULL)
free(node->x_value);
node->x_value = strdup(str);
node->x_state = NodeValue;
return (True);
}
tgt_node_t *
tgt_node_find(tgt_node_t *n, char *name)
{
tgt_node_t *rval;
for (rval = n->x_child; rval; rval = rval->x_sibling)
if (strcmp(rval->x_name, name) == 0)
break;
return (rval);
}
tgt_node_t *
tgt_node_dup(tgt_node_t *n)
{
tgt_node_t *d = node_alloc(),
*c;
if (d == NULL)
return (NULL);
if (node_name(d, (xmlChar *)n->x_name) == False)
return (NULL);
if (node_value(d, (xmlChar *)n->x_value, True) == False)
return (NULL);
for (c = n->x_child; c; c = c->x_sibling)
(void) tgt_node_add(d, tgt_node_dup(c));
return (d);
}
void
tgt_buf_add(char **b, char *element, const char *cdata)
{
tgt_buf_add_tag(b, element, Tag_Start);
if (cdata != NULL)
tgt_buf_add_tag(b, cdata, Tag_String);
tgt_buf_add_tag(b, element, Tag_End);
}
/*
* []----
* | tgt_buf_add_tag -- adds string to buffer allocating space, sets up tags too
* |
* | Helper function to build a string by allocating memory as we go.
* | If the string argument 'str' is defined to be a start or end tag
* | as declared by 'type' argument add the appropriate characters.
* []----
*/
void
tgt_buf_add_tag(char **b, const char *str, val_type_t type)
{
char *buf;
int len;
/*
* We will add potentially up to 3 extra characters plus the NULL byte
*/
len = strlen(str) + 4;
if ((buf = malloc(len)) == NULL)
return;
(void) snprintf(buf, len, "%s%s%s%s", type == Tag_String ? "" : "<",
type == Tag_End ? "/" : "", str, type == Tag_String ? "" : ">");
buf_add_str(b, buf);
free(buf);
}
/*
* []----
* | tgt_buf_add_tag_and_attr -- variant on tgt_buf_add_tag which also gives
* | attr
* []----
*/
void
tgt_buf_add_tag_and_attr(char **b, char *str, char *attr)
{
char *buf;
int len;
/*
* In addition to the 'str' and 'attr' strings the code will add
* three characters plus a null byte.
*/
len = strlen(str) + strlen(attr) + 4;
if ((buf = malloc(len)) == NULL)
return;
(void) snprintf(buf, len, "<%s %s>", str, attr);
buf_add_str(b, buf);
free(buf);
}
/*
* []----
* | Utility functions
* []----
*/
static tgt_node_t *
node_alloc()
{
tgt_node_t *x = (tgt_node_t *)calloc(sizeof (tgt_node_t), 1);
if (x == NULL)
return (NULL);
x->x_state = NodeAlloc;
return (x);
}
static void
node_free(tgt_node_t *x)
{
x->x_state = NodeFree;
if (x->x_name)
free(x->x_name);
if (x->x_value)
free(x->x_value);
free(x);
}
static Boolean_t
node_name(tgt_node_t *x, const xmlChar *n)
{
assert(x->x_state == NodeAlloc);
if ((n == NULL) || (strlen((char *)n) == 0))
return (False);
x->x_state = NodeName;
x->x_name = strip_space((char *)n);
return (True);
}
static Boolean_t
node_value(tgt_node_t *x, const xmlChar *n, Boolean_t do_strip)
{
assert(x->x_state == NodeName);
if ((n == NULL) || (strlen((char *)n) == NULL))
return (False);
x->x_state = NodeValue;
x->x_value = (do_strip == True) ?
strip_space((char *)n) : strdup((char *)n);
return (True);
}
static tgt_node_t *
node_parent(tgt_node_t *x)
{
return (x->x_parent);
}
static tgt_node_t *
node_child(tgt_node_t *x)
{
tgt_node_t *n;
if ((n = node_alloc()) == NULL)
return (NULL);
if (x->x_child == NULL) {
x->x_child = n;
} else {
n->x_sibling = x->x_child;
x->x_child = n;
}
n->x_parent = x;
return (n);
}
static tgt_node_t *
node_alloc_attr(tgt_node_t *x)
{
tgt_node_t *n,
*next;
n = node_alloc();
if (x->x_attr == NULL) {
x->x_attr = n;
} else {
for (next = x->x_attr; next->x_sibling; next = next->x_sibling)
;
next->x_sibling = n;
}
if (n != NULL)
n->x_parent = x;
return (n);
}
static void
buf_add_str(char **b, char *str)
{
int len,
olen = 0;
char *p = *b;
/*
* Make sure we have enough room for the string and tag characters
* plus a NULL byte.
*/
if (str == NULL)
return;
len = strlen(str) + 1;
if (p == NULL) {
if ((p = malloc(len)) == NULL)
return;
} else {
olen = strlen(p);
p = realloc(p, olen + len);
}
(void) strncpy(p + olen, str, len);
*b = p;
}
static void
buf_add_node_attr(char **b, tgt_node_t *x)
{
char *buf;
tgt_node_t *n;
int len;
/* ---- null byte and starting '<' character ---- */
len = strlen(x->x_name) + 2;
if ((buf = malloc(len)) == NULL)
return;
(void) snprintf(buf, len, "<%s", x->x_name);
buf_add_str(b, buf);
free(buf);
for (n = x->x_attr; n; n = n->x_sibling) {
len = strlen(n->x_name) + strlen(n->x_value) + 5;
if ((buf = malloc(len)) == NULL)
return;
(void) snprintf(buf, len, " %s='%s'", n->x_name, n->x_value);
buf_add_str(b, buf);
free(buf);
}
buf_add_str(b, ">");
}
static void
buf_add_comment(char **b, char *comment)
{
char *p = *b;
int len,
olen;
if (comment == NULL)
return;
/*
* Room for the strings, plus the brackets and NULL byte
*/
len = strlen(comment) + strlen(XML_COMMENT_STR) +
strlen(XML_COMMENT_END) + 3;
if (p == NULL)
p = malloc(len);
else {
olen = strlen(p);
p = realloc(p, olen + len);
}
(void) snprintf(p + olen, len, "<%s%s%s>", XML_COMMENT_STR, comment,
XML_COMMENT_END);
*b = p;
}
static char *
strip_space(char *value)
{
char *p,
*n;
for (p = value; p && *p; p++)
if (!isspace(*p))
break;
if ((p == NULL) || (*p == '\0'))
return (NULL);
p = strdup(p);
for (n = (p + strlen(p) - 1); n >= p; n--)
if (!isspace(*n)) {
n++;
break;
}
*n = '\0';
return (p);
}