/*
 * 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 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <libgen.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <libxml/parser.h>
#include <libxml/xpath.h>

#include "isns_server.h"
#include "isns_obj.h"
#include "isns_log.h"

/*
 * external variables
 */
extern const int NUM_OF_ATTRS[MAX_OBJ_TYPE_FOR_SIZE];
extern const int TYPE_OF_PARENT[MAX_OBJ_TYPE_FOR_SIZE];
extern const int UID_ATTR_INDEX[MAX_OBJ_TYPE_FOR_SIZE];

extern char data_store[MAXPATHLEN];

/*
 * local variables
 */
static xmlDocPtr xml_doc = NULL;
static char *xml_file = NULL;
static char *xml_tmp_file = NULL;
static char *xml_bak_file = NULL;

static const int OBJ_DTD_ORDER[MAX_OBJ_TYPE_FOR_SIZE] = {
	0,
	1,	/* OBJ_ENTITY */
	2,	/* OBJ_ISCSI */
	3,	/* OBJ_PORTAL */
	4,	/* OBJ_PG */
	5,	/* OBJ_DD */
	6,	/* OBJ_DDS */
	0,	/* MAX_OBJ_TYPE */
	0,	/* OBJ_DUMMY1 */
	0,	/* OBJ_DUMMY2 */
	0,	/* OBJ_DUMMY3 */
	0,	/* OBJ_DUMMY4 */
	12,	/* OBJ_ASSOC_ISCSI */
	14,	/* OBJ_ASSOC_DD */
};

#define	DEF_XML_ROOT(ISNS_DATA, VENDOR, SMI, VERSION, ONE_DOT_O) \
	(xmlChar *)ISNS_DATA, \
	(xmlChar *)VENDOR, \
	(xmlChar *)SMI, \
	(xmlChar *)VERSION, \
	(xmlChar *)ONE_DOT_O
static const xmlChar *xml_root[] = {
#include "data.def"
};

#define	DEF_XML_DATA(TAG, TYPE, ARG1, ARG2) (xmlChar *)TAG,
static const xmlChar* xmlTag[] = {
#include "data.def"
};

#define	DEF_XML_DATA(TAG, TYPE, ARG1, ARG2) TYPE,
static const char *xmlType[] = {
#include "data.def"
};

#define	DEF_XML_DATA(TAG, TYPE, ARG1, ARG2) ARG1,
static const int xmlArg1[] = {
#include "data.def"
};

#define	DEF_XML_DATA(TAG, TYPE, ARG1, ARG2) ARG2,
static const int xmlArg2[] = {
#include "data.def"
};

#define	DEF_XML_PROP(INDEX, TYPE, NAME, TAG, ID) TYPE,
static const unsigned char xmlPropType[] = {
#include "data.def"
};

#define	DEF_XML_PROP(INDEX, TYPE, NAME, TAG, ID) (xmlChar *)NAME,
static const xmlChar *xmlPropName[] = {
#include "data.def"
};

#define	DEF_XML_PROP(INDEX, TYPE, NAME, TAG, ID) TAG,
static const int xmlPropTag[] = {
#include "data.def"
};

#define	DEF_XML_PROP(INDEX, TYPE, NAME, TAG, ID) ID,
static const int xmlPropID[] = {
#include "data.def"
};

#define	ARRAY_LENGTH(ARRAY) (sizeof (ARRAY) / sizeof (ARRAY[0]))

/*
 * ****************************************************************************
 *
 * get_index_by_name:
 *	find the index in the global tables for the name of an attribute.
 *
 * name - the name of an attribute.
 * return - index or -1 for error.
 *
 * ****************************************************************************
 */
static int
get_index_by_name(
	const xmlChar *name
)
{
	int i;
	for (i = 0; i < ARRAY_LENGTH(xmlTag); i++) {
		if (xmlStrEqual(xmlTag[i], name)) {
			return (i);
		}
	}
	return (-1);
}

/*
 * ****************************************************************************
 *
 * get_index_by_otype:
 *	find the index in the global tables for the type of an object.
 *
 * name - the type of an object.
 * return - index or -1 for error.
 *
 * ****************************************************************************
 */
static int
get_index_by_otype(
	int otype
)
{
	int i;
	for (i = 0; i < ARRAY_LENGTH(xmlTag); i++) {
		if (xmlArg1[i] == otype && xmlType[i][0] == 'o') {
			return (i);
		}
	}
	return (-1);
}

/*
 * ****************************************************************************
 *
 * get_index_by_tag:
 *	find the index in the global tables for the tag of an attribute.
 *
 * name - the tag of an attribute.
 * return - index or -1 for error.
 *
 * ****************************************************************************
 */
static int
get_index_by_tag(
	int tag
)
{
	int i;
	for (i = 0; i < ARRAY_LENGTH(xmlTag); i++) {
		if (xmlArg1[i] == tag &&
		    xmlType[i][0] != 'o' &&
		    xmlType[i][0] != 'a') {
			return (i);
		}
	}
	return (-1);
}

/*
 * ****************************************************************************
 *
 * get_xml_doc:
 *	open the xml file and assign the global xml doc if the xml file
 *	is not opened, set the doc pointer with the opened xml file for
 *	returnning.
 *
 * docp - the doc pointer for returning.
 * return - error code.
 *
 * ****************************************************************************
 */
static int
get_xml_doc(
	xmlDocPtr *docp
)
{
	int ec = 0;

	if (xml_doc == NULL) {
		/* validate the xml file */

		/* open the xml file */
		xml_doc = xmlParseFile(xml_file);
	}

	*docp = xml_doc;

	if (xml_doc == NULL) {
		ec = ISNS_RSP_INTERNAL_ERROR;
	}

	return (ec);
}

/*
 * ****************************************************************************
 *
 * close_xml_doc:
 *	close the global xml doc and ignore any changes that has been
 *	made in it.
 *
 * ****************************************************************************
 */
static void
close_xml_doc(
)
{
	if (xml_doc) {
		/* just close it */
		xmlFreeDoc(xml_doc);
		xml_doc = NULL;
	}
}

/*
 * ****************************************************************************
 *
 * convert_xml2attr:
 *	convert a xml data to a TLV format isns attribute.
 *
 * tag - the tag of attribute.
 * type - the data type of the xml data.
 * value - the xml data.
 * attr - TLV format attribute for returning.
 * return - error code.
 *
 * ****************************************************************************
 */
static int
convert_xml2attr(
	const int tag,
	const unsigned char type,
	xmlChar *value,
	isns_attr_t *attr
)
{
	uint32_t len;
	int ec = 0;

	attr->tag = tag;
	switch (type) {
		case 'u':
			/* 4-bytes non-negative integer */
			attr->len = 4;
			attr->value.ui = atoi((const char *)value);
			break;
		case 's':
			/* literal string */
			len = strlen((char *)value);
			len += 4 - (len % 4);
			attr->len = len;
			attr->value.ptr = (uchar_t *)malloc(attr->len);
			if (attr->value.ptr != NULL) {
				(void) strcpy((char *)attr->value.ptr,
				    (char *)value);
			} else {
				ec = ISNS_RSP_INTERNAL_ERROR;
			}
			break;
		case 'p':
			/* IPv6 block data */
			attr->len = sizeof (in6_addr_t);
			attr->value.ip = (in6_addr_t *)malloc(attr->len);
			if (attr->value.ip != NULL) {
				(void) inet_pton(AF_INET6,
				    (char *)value,
				    attr->value.ip);
			} else {
				ec = ISNS_RSP_INTERNAL_ERROR;
			}
			break;
		default:
			break;
	}

	return (ec);
}

/*
 * ****************************************************************************
 *
 * convert_attr2xml:
 *	convert a TLV format isns attribute to xml node format.
 *
 * node - the xml node where the new node is being added to.
 * attr - the TLV format attribute.
 * name - the name of the attribute in xml node.
 * type - the data type of the attribute.
 * elm_flag - 0: adding a xml attlist.
 *	      1: adding a xml child node.
 *	      2: adding a previous sibling node.
 *	      3: adding a xml content node.
 *	      4: adding a xml attribute.
 * return - xml node.
 *
 * ****************************************************************************
 */
static xmlNodePtr
convert_attr2xml(
	xmlNodePtr node,
	const isns_attr_t *attr,
	const xmlChar *name,
	const char type,
	const int elm_flag
)
{
	xmlChar buff[INET6_ADDRSTRLEN + 1] = { 0 };
	xmlChar *value = NULL;
	xmlNodePtr child = NULL;

	switch (type) {
		case 'u':
			/* 4-bytes non-negative integer */
			if (xmlStrPrintf(buff, sizeof (buff),
			    (const unsigned char *)"%u",
			    attr->value.ui) > 0) {
				value = (xmlChar *)&buff;
			}
			break;
		case 's':
			/* literal string */
			value = (xmlChar *)attr->value.ptr;
			break;
		case 'p':
			/* IPv6 block data */
			value = (xmlChar *)inet_ntop(AF_INET6,
			    (char *)attr->value.ip,
			    (char *)buff,
			    sizeof (buff));
			break;
		default:
			break;
	}

	if (!value) {
		return (NULL);
	}

	switch (elm_flag) {
		case 0: /* attlist */
			if (xmlSetProp(node, name, value)) {
				child = node;
			}
			break;
		case 1: /* child element */
			child = xmlNewChild(node, NULL, name, value);
			break;
		case 2: /* prev sibling element */
			child = xmlNewNode(NULL, name);
			if (child != NULL &&
			    xmlAddPrevSibling(node, child) == NULL) {
				xmlFreeNode(child);
				node = NULL;
			} else {
				node = child;
			}
			/* LINTED E_CASE_FALLTHRU */
		case 3: /* set content */
			if (node) {
				xmlNodeSetContent(node, value);
			}
			child = node;
			break;
		case 4: /* new attr value */
			if (xmlSetProp(node, name, value)) {
				child = node;
			}
			break;
		default:
			ASSERT(0);
			break;
	}

	return (child);
}

/*
 * ****************************************************************************
 *
 * parse_xml_prop:
 *	parse the properties of a xml node and convert them to the attributes
 *	of an isns object, these xml properties are the UID attribute and
 *	key attributes of the isns object.
 *
 * node - the xml node that contains the properties.
 * obj - the isns object.
 * i  - the index of the attribute in the global tables.
 * return - error code.
 *
 * ****************************************************************************
 */
static int
parse_xml_prop(
	xmlNodePtr node,
	isns_obj_t *obj,
	int i
)
{
	int ec = 0;
	const char *props = &xmlType[i][1];
	const xmlChar *prop_name;
	xmlChar *prop_value;
	unsigned char prop_type;
	int prop_tag;
	int prop_id;
	char prop;
	int j;

	j = 0;
	prop = props[j ++];
	while (ec == 0 &&
	    prop >= 'a' && prop <= 'z') {
		prop -= 'a';
		prop_id = xmlPropID[prop];
		prop_tag = xmlPropTag[prop];
		prop_name = xmlPropName[prop];
		prop_type = xmlPropType[prop];
		prop_value = xmlGetProp(node, prop_name);

		if (prop_value) {
			ec = convert_xml2attr(
			    prop_tag,
			    prop_type,
			    prop_value,
			    &(obj->attrs[prop_id]));
			xmlFree(prop_value);
		}
		prop = props[j ++];
	}

	return (ec);
}

/*
 * ****************************************************************************
 *
 * parse_xml_attr:
 *	parse a xml node and convert it to one isns object attribute.
 *	this attribute is the non-key attribute of the isns object.
 *
 * node - the xml node.
 * obj - the isns object.
 * i  - the index of the attribute in the global tables.
 * return - error code.
 *
 * ****************************************************************************
 */
static int
parse_xml_attr(
	xmlNodePtr node,
	isns_obj_t *obj,
	int i
)
{
	int ec = 0;
	const unsigned char attr_type = xmlType[i][0];
	const int attr_tag = xmlArg1[i];
	const int attr_id = xmlArg2[i];
	xmlChar *attr_value;

	attr_value = xmlNodeGetContent(node);

	if (attr_value) {
		ec = convert_xml2attr(
		    attr_tag,
		    attr_type,
		    attr_value,
		    &(obj->attrs[attr_id]));
		xmlFree(attr_value);
	}

	return (ec);
}

/*
 * ****************************************************************************
 *
 * parse_xml_obj:
 *	parse one isns object from the xml doc.
 *
 * nodep - the pointer of the xml node for parsing.
 * objp - the pointer of isns object for returning.
 * return - error code.
 *
 * ****************************************************************************
 */
static int
parse_xml_obj(
	xmlNodePtr *nodep,
	isns_obj_t **objp
)
{
	int ec = 0;
	int i, j;

	xmlNodePtr node = *nodep;
	xmlNodePtr children;

	isns_obj_t *obj = *objp;

	while (node && ec == 0) {
		if (node->type == XML_ELEMENT_NODE) {
			children = node->children;
			i = get_index_by_name(node->name);
			ASSERT(i >= 0);
			j = xmlType[i][0];
			if (j == 'o' && obj == NULL) {
				obj = obj_calloc(xmlArg1[i]);
				if (obj == NULL) {
					ec = ISNS_RSP_INTERNAL_ERROR;
					break;
				}
				if ((ec = parse_xml_prop(node, obj, i)) == 0 &&
				    (children == NULL ||
				    (ec = parse_xml_obj(&children, &obj)) ==
				    0)) {
					if (children != NULL &&
					    children != node->children) {
						*nodep = children;
					}
					*objp = obj;
				} else {
					free_object(obj);
				}
				break;
				/* LINTED E_NOP_IF_STMT */
			} else if (j == 'o') {
			} else if (j != 0) {
				ASSERT(obj);
				if (children != NULL) {
					ec = parse_xml_attr(children, obj, i);
					*nodep = children;
				} else {
					/* assign a default value */
					*nodep = node;
				}
			} else {
				/* unknown xml node */
				break;
			}
			/* LINTED E_NOP_ELSE_STMT */
		} else {
			/* carry return or blank spaces, skip it */
		}
		node = node->next;
	}

	return (ec);
}

/*
 * ****************************************************************************
 *
 * locate_xml_node:
 *	locate the xml node from xml doc by matching the object UID.
 *
 * doc - the xml doc.
 * otype - the matching object type.
 * match_uid - the matching object UID.
 * node - the pointer of matched xml node for returning.
 * context - the xml context for matching process.
 * result - the xml result for matching process.
 * return - error code.
 *
 * ****************************************************************************
 */
static int
locate_xml_node(
	xmlDocPtr doc,
	int otype,
	int match_uid,
	xmlNodePtr *node,
	xmlXPathContextPtr *context,
	xmlXPathObjectPtr *result
)
{
	int ec = 0;

	xmlNodeSetPtr nodeset;
	xmlNodePtr curr;
	xmlChar expr[32] = { (xmlChar)'/', (xmlChar)'/', 0 };

	char prop;
	const xmlChar *prop_name;
	xmlChar *prop_value;
	int uid;

	int i, j;

	*node = NULL;

	i = get_index_by_otype(otype);
	ASSERT(i >= 0);

	*context = xmlXPathNewContext(doc);

	if (*context &&
	    xmlStrPrintf(&expr[2], 30, (const unsigned char *)"%s",
	    xmlTag[i]) != -1) {
		*result = xmlXPathEvalExpression(expr, *context);
		if (*result) {
			prop = xmlArg2[i] - 'a';
			prop_name = xmlPropName[prop];
			ASSERT(xmlPropType[prop] == 'u');
			nodeset = (*result)->nodesetval;
			for (j = 0;
			    nodeset && (j < nodeset->nodeNr);
			    j++) {
				curr = nodeset->nodeTab[j];
				prop_value = xmlGetProp(curr, prop_name);
				if (prop_value) {
					uid = atoi((const char *)prop_value);
					xmlFree(prop_value);
					if (uid == match_uid) {
						/* found it */
						*node = curr;
						return (ec);
					}
				}
			}
		} else {
			ec = ISNS_RSP_INTERNAL_ERROR;
		}
	} else {
		ec = ISNS_RSP_INTERNAL_ERROR;
	}

	if (*result) {
		xmlXPathFreeObject(*result);
		*result = NULL;
	}
	if (*context) {
		xmlXPathFreeContext(*context);
		*context = NULL;
	}

	return (ec);
}

/*
 * ****************************************************************************
 *
 * make_xml_node:
 *	generate a xml node for presenting an isns object.
 *
 * obj - an isns object.
 * return - the xml node.
 *
 * ****************************************************************************
 */
static xmlNodePtr
make_xml_node(
	const isns_obj_t *obj
)
{
	const isns_attr_t *attr;

	xmlNodePtr node;
	const char *props;
	char prop;
	const xmlChar *name;
	unsigned char type;
	int prop_id;
	int i, j;

	i = get_index_by_otype(obj->type);
	ASSERT(i >= 0);
	node = xmlNewNode(NULL, xmlTag[i]);
	if (!node) {
		return (NULL);
	}

	/* generate xml attributes of the node */
	props = &xmlType[i][1];
	prop = *(props ++);
	while (prop >= 'a' && prop <= 'z') {
		prop -= 'a';
		prop_id = xmlPropID[prop];
		name = xmlPropName[prop];
		type = xmlPropType[prop];
		attr = &obj->attrs[prop_id];
		if (!convert_attr2xml(node, attr, name, type, 0)) {
			xmlFreeNode(node);
			return (NULL);
		}
		/* attr->tag = 0; */
		prop = *(props ++);
	}

	/* generate sub elements for isns attributes of the object */
	i = 0;
	while (i < NUM_OF_ATTRS[obj->type]) {
		attr = &obj->attrs[i ++];
		j = get_index_by_tag(attr->tag);
		if (j >= 0) {
			name = xmlTag[j];
			type = xmlType[j][0];
			if (!convert_attr2xml(node, attr, name, type, 1)) {
				xmlFreeNode(node);
				return (NULL);
			}
		}
	}

	return (node);
}

/*
 * ****************************************************************************
 *
 * xml_init_data:
 *	initialization of the xml data store.
 *
 * return - error code.
 *
 * ****************************************************************************
 */
static int
xml_init_data(
)
{
#define	XML_PATH	"/etc/isns"
#define	XML_FILE_NAME	"/isnsdata.xml"
#define	XML_DOT_TMP	".tmp"
#define	XML_DOT_BAK	".bak"

	int fd;
	xmlDocPtr doc;
	xmlNodePtr root;

	int len;
	char *xml_path, *p = NULL;

	char *cwd = NULL;

	int has_bak = 0;

	/* cannot reset the xml file when server is running */
	if (xml_file != NULL) {
		return (1);
	}

	/* set the data store file name along with the backup */
	/* file name and temporary file name */
	len = strlen(data_store);
	if (len > 0) {
		xml_file = data_store;
		p = strdup(xml_file);
		xml_bak_file = (char *)malloc(len + 5);
		xml_tmp_file = (char *)malloc(len + 5);
		if (p != NULL &&
		    xml_bak_file != NULL &&
		    xml_tmp_file != NULL) {
			xml_path = dirname(p);
			(void) strcpy(xml_bak_file, xml_file);
			(void) strcat(xml_bak_file, XML_DOT_BAK);
			(void) strcpy(xml_tmp_file, xml_file);
			(void) strcat(xml_tmp_file, XML_DOT_TMP);
		} else {
			return (1);
		}
	} else {
		xml_path = XML_PATH;
		xml_file = XML_PATH XML_FILE_NAME;
		xml_bak_file = XML_PATH XML_FILE_NAME XML_DOT_BAK;
		xml_tmp_file = XML_PATH XML_FILE_NAME XML_DOT_TMP;
	}

	/* save current working directory */
	cwd = getcwd(NULL, MAXPATHLEN);
	if (cwd == NULL) {
		return (1);
	}
	/* check access permission on data store directory */
	if (chdir(xml_path) != 0) {
		if (errno == ENOENT) {
			if (mkdir(xml_path, S_IRWXU) != 0 ||
			    chdir(xml_path) != 0) {
				return (1);
			}
		} else {
			return (1);
		}
	}
	/* go back to original working directory */
	(void) chdir(cwd);
	free(cwd);
	free(p);

	/* do not keep blank spaces */
	(void) xmlKeepBlanksDefault(0);

	/* remove the tmp file if it exists */
	if (access(xml_tmp_file, F_OK) == 0) {
		(void) remove(xml_tmp_file);
	}

	/* test if we can write the bak file */
	fd = open(xml_bak_file, O_RDWR);
	if (fd == -1) {
		fd = open(xml_bak_file, O_RDWR | O_CREAT,
		    S_IRUSR | S_IWUSR);
		if (fd == -1) {
			return (1);
		} else {
			(void) close(fd);
			(void) remove(xml_bak_file);
		}
	} else {
		has_bak = 1;
		(void) close(fd);
	}

	/* Test if we have the data store file, create an empty */
	/* data store if we do not have the data store file and */
	/* the backup data store. */
	fd = open(xml_file, O_RDWR);
	if (fd == -1) {
		if (has_bak == 0) {
			doc = xmlNewDoc(BAD_CAST "1.0");
			root = xmlNewNode(NULL, xml_root[0]);
			if (doc != NULL &&
			    root != NULL &&
			    xmlSetProp(root, xml_root[1], xml_root[2]) !=
			    NULL &&
			    xmlSetProp(root, xml_root[3], xml_root[4]) !=
			    NULL) {
				(void) xmlDocSetRootElement(doc, root);
				if (xmlSaveFormatFile(xml_file, doc, 1) == -1) {
					xmlFreeDoc(doc);
					return (-1);
				}
				xmlFreeDoc(doc);
			} else {
				if (doc != NULL) {
					xmlFreeDoc(doc);
				}
				if (root != NULL) {
					xmlFreeNode(root);
				}
				return (1);
			}
		} else {
			isnslog(LOG_WARNING, "get_xml_doc",
			    "initializing with backup data");
			if (rename(xml_bak_file, xml_file) != 0) {
				return (1);
			}
		}
	} else {
		(void) close(fd);
	}

	return (0);
}

/*
 * ****************************************************************************
 *
 * xml_load_obj:
 *	load an isns object from the xml data store.
 *
 * p - the pointer of current xml node.
 * objp - the pointer of the object for returning.
 * level - the direction of xml parsing for returning.
 * return - error code.
 *
 * ****************************************************************************
 */
static int
xml_load_obj(
	void **p,
	isns_obj_t **objp,
	uchar_t *level
)
{
	xmlDocPtr doc = NULL;
	xmlNodePtr node = (xmlNodePtr)*p;
	int ec = 0;

	*objp = NULL;

	if (node == NULL) {
		*level = '^';
		ec = get_xml_doc(&doc);
		if (doc == NULL) {
			return (ec);
		}
		node = xmlDocGetRootElement(doc);
		if (node != NULL) {
			node = node->children;
		}
	} else if (node->children != NULL) {
		*level = '>';
		node = node->children;
	} else if (node->next != NULL) {
		*level = 'v';
		node = node->next;
	} else {
		*level = 'v';
		while (node != NULL && node->next == NULL) {
			if (node->type == XML_ELEMENT_NODE) {
				*level = '<';
			}
			node = node->parent;
		}
		if (node != NULL) {
			node = node->next;
		}
	}

	/* there is a node, parse it */
	if (node) {
		ec = parse_xml_obj(&node, objp);
		*p = (void *)node;
	}

	if (ec == 0 && *objp != NULL) {
		ec = update_deref_obj(*objp);
		if (ec != 0) {
			free_object(*objp);
			*objp = NULL;
		}
	}

	/* no object available, close the xml doc */
	if (*objp == NULL) {
		(void) close_xml_doc();
	}

	return (ec);
}

/*
 * ****************************************************************************
 *
 * xml_add_obj:
 *	add an isns object to the xml data store.
 *
 * obj - the object being added.
 * return - error code.
 *
 * ****************************************************************************
 */
static int
xml_add_obj(
	const isns_obj_t *obj
)
{
	int ec = 0;

	xmlDocPtr doc;
	xmlXPathContextPtr context = NULL;
	xmlXPathObjectPtr result = NULL;
	xmlNodePtr node, prev;
	xmlNodePtr candidate;

	uint32_t puid, parent_type;

	int i;

	/* get the xml doc */
	ec = get_xml_doc(&doc);
	if (doc == NULL) {
		goto add_done;
	}

	/* create the candidate node */
	candidate = make_xml_node(obj);
	if (candidate == NULL) {
		ec = ISNS_RSP_INTERNAL_ERROR;
		goto add_done;
	}

	/* locate the position */
	parent_type = TYPE_OF_PARENT[obj->type];
	if (parent_type > 0) {
		puid = get_parent_uid(obj);
		ec = locate_xml_node(doc, parent_type, puid,
		    &node, &context, &result);
	} else {
		node = xmlDocGetRootElement(doc);
	}

	/* cannot locate the point for inserting the node */
	if (node == NULL) {
		xmlFreeNode(candidate);
		ec = ISNS_RSP_INTERNAL_ERROR;
		goto add_done;
	}

	/* add it with the apporiate child order */
	if (node->children) {
		node = node->children;
		while (node) {
			if (node->type == XML_ELEMENT_NODE) {
				i = get_index_by_name(node->name);
				ASSERT(i >= 0);
				if (xmlType[i][0] == 'o' &&
				    OBJ_DTD_ORDER[xmlArg1[i]] >=
				    OBJ_DTD_ORDER[obj->type]) {
					break;
				}
			}
			prev = node;
			node = node->next;
		}
		if (node == NULL) {
			node = xmlAddNextSibling(prev, candidate);
		} else {
			node = xmlAddPrevSibling(node, candidate);
		}
	} else {
		node = xmlAddChild(node, candidate);
	}

	if (node == NULL) {
		/* Failed, free the candidate node. */
		xmlFreeNode(candidate);
		ec = ISNS_RSP_INTERNAL_ERROR;
	}

add_done:
	if (result) {
		xmlXPathFreeObject(result);
	}
	if (context) {
		xmlXPathFreeContext(context);
	}

	return (ec);
}

/*
 * ****************************************************************************
 *
 * xml_modify_obj:
 *	modify an isns object in the xml data store.
 *
 * obj - the new object.
 * return - error code.
 *
 * ****************************************************************************
 */
static int
xml_modify_obj(
	const isns_obj_t *obj
)
{
	int ec = 0;
	xmlDocPtr doc;
	xmlXPathContextPtr context = NULL;
	xmlXPathObjectPtr result = NULL;
	xmlNodePtr node, child;
	const char *props;
	char prop;
	int prop_id;
	int prop_tag;
	const xmlChar *name;
	unsigned char type;
	const isns_attr_t *attr;
	int i, j, k;
	int make_child;

	/* get the doc pointer */
	ec = get_xml_doc(&doc);
	if (doc == NULL) {
		return (ec);
	}

	/* locate the node for the object */
	i = get_index_by_otype(obj->type);
	ASSERT(i >= 0);
	prop = xmlArg2[i] - 'a';
	prop_id = xmlPropID[prop];
	attr = &obj->attrs[prop_id];
	ec = locate_xml_node(doc,
	    obj->type,
	    attr->value.ui,
	    &node, &context, &result);

	/* modify it */
	if (node != NULL) {
		props = &xmlType[i][1];
		prop = *(props ++);
		while (prop >= 'a' && prop <= 'z') {
			prop -= 'a';
			prop_id = xmlPropID[prop];
			prop_tag = xmlPropTag[prop];
			attr = &obj->attrs[prop_id];
			/* no need to update the key attributes, skip it. */
			/* btw, dd and dd-set names are non-key attributes. */
			if (prop_tag == ISNS_DD_NAME_ATTR_ID ||
			    prop_tag == ISNS_DD_SET_NAME_ATTR_ID) {
				name = xmlPropName[prop];
				type = xmlPropType[prop];
				if (!convert_attr2xml(node,
				    attr, name, type, 4)) {
					ec = ISNS_RSP_INTERNAL_ERROR;
					goto modify_done;
				}
			}
			/* attr->tag = 0; */
			prop = *(props ++);
		}
		/* set the child */
		child = node->children;
		if (child == NULL) {
			make_child = 1;
		} else {
			make_child = 0;
		}
		for (i = 0; i < NUM_OF_ATTRS[obj->type]; i++) {
			attr = &obj->attrs[i];
			j = get_index_by_tag(attr->tag);
			if (j < 0) {
				continue;
			}
			name = xmlTag[j];
			type = xmlType[j][0];
			if (make_child == 1) {
				/* make a child node */
				if (!convert_attr2xml(node, attr,
				    name, type, 1)) {
					ec = ISNS_RSP_INTERNAL_ERROR;
					goto modify_done;
				}
				continue;
			}
			while (child) {
				if (child->type == XML_ELEMENT_NODE) {
					k = get_index_by_name(child->name);
					ASSERT(k >= 0);
					if (xmlType[k][0] == 'o' ||
					    xmlType[k][0] == 'a' ||
					    xmlArg1[k] > attr->tag) {
						if (!convert_attr2xml(child,
						    attr, name, type, 2)) {
							/* internal error */
							ec = 11;
							goto modify_done;
						}
						break;
					} else if (xmlArg1[k] == attr->tag) {
						/* replace content */
						if (!convert_attr2xml(child,
						    attr, name, type, 3)) {
							/* internal error */
							ec = 11;
							goto modify_done;
						}
						break;
					}
				}
				child = child->next;
			}
			if (child == NULL) {
				/* make a child node */
				if (!convert_attr2xml(node, attr,
				    name, type, 1)) {
					ec = ISNS_RSP_INTERNAL_ERROR;
					goto modify_done;
				}
			}
		}
	} else {
		/* This case is for registering a node which has */
		/* membership in one or more non-default DD(s). */
		ec = xml_add_obj(obj);
	}

modify_done:
	if (result) {
		xmlXPathFreeObject(result);
	}
	if (context) {
		xmlXPathFreeContext(context);
	}

	return (ec);
}

/*
 * ****************************************************************************
 *
 * xml_delete_obj:
 *	delete an isns object from the xml data store.
 *
 * obj - the object being deleted.
 * return - error code.
 *
 * ****************************************************************************
 */
static int
xml_delete_obj(
	const isns_obj_t *obj
)
{
	int ec = 0;
	xmlDocPtr doc;
	xmlXPathContextPtr context = NULL;
	xmlXPathObjectPtr result = NULL;
	xmlNodePtr node;

	isns_type_t otype;
	uint32_t uid;

	/* get the xml doc */
	ec = get_xml_doc(&doc);
	if (doc == NULL) {
		return (ec);
	}

	otype = obj->type;
#ifdef WRITE_DATA_ASYNC
	/* it is a thin clone */
	uid = obj->attrs[0].value.ui;
#else
	uid = get_obj_uid(obj);
#endif

	/* locate the object */
	ec = locate_xml_node(doc,
	    otype,
	    uid,
	    &node, &context, &result);

	/* destroy it */
	if (node) {
		xmlUnlinkNode(node);
		xmlFreeNode(node);
	}

	if (result) {
		xmlXPathFreeObject(result);
	}
	if (context) {
		xmlXPathFreeContext(context);
	}

	return (ec);
}

/*
 * ****************************************************************************
 *
 * xml_delete_assoc:
 *	delete a DD or DD-set membership from the xml data store.
 *
 * assoc - the membership being deleted.
 * return - error code.
 *
 * ****************************************************************************
 */
static int
xml_delete_assoc(
	const isns_obj_t *assoc
)
{
	int ec = 0;
	xmlDocPtr doc;
	xmlXPathContextPtr context = NULL;
	xmlXPathObjectPtr result = NULL;
	xmlNodePtr node;

	uint32_t puid, parent_type;
	uint32_t uid, match_uid;

	char prop;
	const xmlChar *prop_name;
	xmlChar *prop_value;
	int i;

	/* get the xml doc */
	ec = get_xml_doc(&doc);
	if (doc == NULL) {
		return (ec);
	}

	/* get the container object UID */
	parent_type = TYPE_OF_PARENT[assoc->type];
	ASSERT(parent_type != 0);
	puid = get_parent_uid(assoc);
	ASSERT(puid != 0);

	/* get the member object UID */
	i = get_index_by_otype(assoc->type);
	prop = xmlArg2[i] - 'a';
	prop_name = xmlPropName[prop];
	match_uid = assoc->attrs[UID_ATTR_INDEX[assoc->type]].value.ui;

	/* locate the container object */
	ec = locate_xml_node(doc, parent_type, puid,
	    &node, &context, &result);

	/* get the membership nodes */
	if (node != NULL) {
		node = node->children;
	}

	/* get the matching membership node */
	while (node) {
		if (node->type == XML_ELEMENT_NODE) {
			i = get_index_by_name(node->name);
			ASSERT(i >= 0);
			if (xmlType[i][0] == 'o' &&
			    xmlArg1[i] == assoc->type) {
				prop_value = xmlGetProp(node, prop_name);
				if (prop_value) {
					uid = atoi((const char *)prop_value);
					xmlFree(prop_value);
					if (uid == match_uid) {
						break;
					}
				}
			}
		}
		node = node->next;
	}

	/* destroy it */
	if (node) {
		xmlUnlinkNode(node);
		xmlFreeNode(node);
	}

	if (result) {
		xmlXPathFreeObject(result);
	}
	if (context) {
		xmlXPathFreeContext(context);
	}

	return (ec);
}

/*
 * ****************************************************************************
 *
 * xml_update_commit:
 *	backup the current written file and commit all updates from
 *	the xml doc to the written file.
 *
 * return - error code.
 *
 * ****************************************************************************
 */
static int
xml_update_commit(
)
{
	int ec = 0;

	if (xml_doc) {
		/* write to tmp file */
		if (xmlSaveFormatFile(xml_tmp_file, xml_doc, 1) == -1 ||
		    /* backup the current file */
		    rename(xml_file, xml_bak_file) != 0 ||
		    /* rename the tmp file to the current file */
		    rename(xml_tmp_file, xml_file) != 0) {
			/* failed saving file */
			ec = ISNS_RSP_INTERNAL_ERROR;
		}
		/* close the xml_doc */
		xmlFreeDoc(xml_doc);
		xml_doc = NULL;
	}

	return (ec);
}

/*
 * ****************************************************************************
 *
 * xml_update_retreat:
 *	ignore all of updates in the xml doc.
 *
 * return - 0: always successful.
 *
 * ****************************************************************************
 */
static int
xml_update_retreat(
)
{
	if (xml_doc) {
		/* close the xml_doc */
		xmlFreeDoc(xml_doc);
		xml_doc = NULL;
	}

	return (0);
}