text: /*
   * 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 <string.h>
  #include <pthread.h>
  #include <sys/types.h>
  #include <sys/socket.h>
  #include <netinet/in.h>
  #include <arpa/inet.h>
  
  #include "isns_server.h"
  #include "isns_msgq.h"
  #include "isns_htab.h"
  #include "isns_cache.h"
  #include "isns_pdu.h"
  #include "isns_obj.h"
  #include "isns_dd.h"
  #include "isns_func.h"
  #include "isns_dseng.h"
  #include "isns_log.h"
  #include "isns_scn.h"
  #include "isns_utils.h"
  #include "isns_esi.h"
  
  /*
   * external variables
   */
  #ifdef DEBUG
  extern int verbose_mc;
  extern void print_object(char *, isns_obj_t *);
  #endif
  
  extern msg_queue_t *sys_q;
  extern msg_queue_t *scn_q;
  
  extern pthread_mutex_t el_mtx;
  
  extern int cache_flag;
  
  /*
   * global data
   */
  
  /*
   * local variables
   */
  /* type of parent object */
  const int TYPE_OF_PARENT[MAX_OBJ_TYPE_FOR_SIZE] = {
  	0,
  	0,
  	ISCSI_PARENT_TYPE,
  	PORTAL_PARENT_TYPE,
  	PG_PARENT_TYPE,
  	0,	/* OBJ_DD */
  	0,	/* OBJ_DDS */
  	0,	/* MAX_OBJ_TYPE */
  	0,	/* OBJ_DUMMY1 */
  	0,	/* OBJ_DUMMY2 */
  	0,	/* OBJ_DUMMY3 */
  	0,	/* OBJ_DUMMY4 */
  	ASSOC_ISCSI_PARENT_TYPE,
  	ASSOC_DD_PARENT_TYPE
  };
  
  /* number of children object type */
  const int NUM_OF_CHILD[MAX_OBJ_TYPE] = {
  	0,
  	MAX_ENTITY_CHILD,
  	MAX_ISCSI_CHILD,
  	MAX_PORTAL_CHILD,
  	MAX_PG_CHILD,
  	0,
  	0
  };
  
  /* type of a child object */
  const int TYPE_OF_CHILD[MAX_OBJ_TYPE][MAX_CHILD_TYPE] = {
  	{ 0, 0 },
  	{ OBJ_ISCSI, OBJ_PORTAL },
  	{ 0, 0 },
  	{ 0, 0 },
  	{ 0, 0 },
  	{ 0, 0 },
  	{ 0, 0 }
  };
  
  /* number of attributes of certain type of object */
  const int NUM_OF_ATTRS[MAX_OBJ_TYPE_FOR_SIZE] = {
  	0,
  	NUM_OF_ENTITY_ATTRS,
  	NUM_OF_ISCSI_ATTRS,
  	NUM_OF_PORTAL_ATTRS,
  	NUM_OF_PG_ATTRS,
  	NUM_OF_DD_ATTRS,
  	NUM_OF_DDS_ATTRS,
  	0,			/* MAX_OBJ_TYPE */
  	0,			/* OBJ_DUMMY1 */
  	0,			/* OBJ_DUMMY2 */
  	0,			/* OBJ_DUMMY3 */
  	0,			/* OBJ_DUMMY4 */
  	NUM_OF_ASSOC_ISCSI_ATTRS,
  	NUM_OF_ASSOC_DD_ATTRS
  };
  
  /* the tag of UID of each type of object */
  static const int UID_TAG[MAX_OBJ_TYPE_FOR_SIZE] = {
  	0,
  	ISNS_ENTITY_INDEX_ATTR_ID,
  	ISNS_ISCSI_NODE_INDEX_ATTR_ID,
  	ISNS_PORTAL_INDEX_ATTR_ID,
  	ISNS_PG_INDEX_ATTR_ID,
  	ISNS_DD_ID_ATTR_ID,
  	ISNS_DD_SET_ID_ATTR_ID,
  	0,			/* MAX_OBJ_TYPE */
  	0,			/* OBJ_DUMMY1 */
  	0,			/* OBJ_DUMMY2 */
  	0,			/* OBJ_DUMMY3 */
  	0,			/* OBJ_DUMMY4 */
  	ISNS_DD_ISCSI_INDEX_ATTR_ID,
  	ISNS_DD_ID_ATTR_ID
  };
  
  /* the index of UID of each type of object */
  const int UID_ATTR_INDEX[MAX_OBJ_TYPE_FOR_SIZE] = {
  	0,
  	ATTR_INDEX_ENTITY(ISNS_ENTITY_INDEX_ATTR_ID),
  	ATTR_INDEX_ISCSI(ISNS_ISCSI_NODE_INDEX_ATTR_ID),
  	ATTR_INDEX_PORTAL(ISNS_PORTAL_INDEX_ATTR_ID),
  	ATTR_INDEX_PG(ISNS_PG_INDEX_ATTR_ID),
  	ATTR_INDEX_DD(ISNS_DD_ID_ATTR_ID),
  	ATTR_INDEX_DDS(ISNS_DD_SET_ID_ATTR_ID),
  	0,			/* MAX_OBJ_TYPE */
  	0,			/* OBJ_DUMMY1 */
  	0,			/* OBJ_DUMMY2 */
  	0,			/* OBJ_DUMMY3 */
  	0,			/* OBJ_DUMMY4 */
  	ATTR_INDEX_ASSOC_ISCSI(ISNS_DD_ISCSI_INDEX_ATTR_ID),
  	ATTR_INDEX_ASSOC_DD(ISNS_DD_ID_ATTR_ID)
  };
  
  /* the index of the key attributes of each type of object */
  static const int KEY_ATTR_INDEX[MAX_OBJ_TYPE][MAX_KEY_ATTRS] = {
  	{ 0 },
  	{ ATTR_INDEX_ENTITY(ISNS_EID_ATTR_ID), 0 },
  	{ ATTR_INDEX_ISCSI(ISNS_ISCSI_NAME_ATTR_ID),
  		0 },
  	{ ATTR_INDEX_PORTAL(ISNS_PORTAL_IP_ADDR_ATTR_ID),
  		ATTR_INDEX_PORTAL(ISNS_PORTAL_PORT_ATTR_ID),
  		0 },
  	{ ATTR_INDEX_PG(ISNS_PG_ISCSI_NAME_ATTR_ID),
  		ATTR_INDEX_PG(ISNS_PG_PORTAL_IP_ADDR_ATTR_ID),
  		ATTR_INDEX_PG(ISNS_PG_PORTAL_PORT_ATTR_ID) },
  	{ ATTR_INDEX_DD(ISNS_DD_NAME_ATTR_ID), 0 },
  	{ ATTR_INDEX_DDS(ISNS_DD_SET_NAME_ATTR_ID), 0 }
  };
  
  /* the operating methods for key attributes of each type of object */
  static const int KEY_ATTR_OP[MAX_OBJ_TYPE][MAX_KEY_ATTRS] = {
  	{ 0 },
  	{ OP_STRING, 0 },
  	{ OP_STRING, 0 },
  	{ OP_MEMORY_IP6, OP_INTEGER, 0 },
  	{ OP_STRING, OP_MEMORY_IP6, OP_INTEGER },
  	{ OP_STRING, 0 },
  	{ OP_STRING, 0 }
  };
  
  /* the size of each type of object */
  static const int SIZEOF_OBJ[MAX_OBJ_TYPE_FOR_SIZE] = {
  	0,
  	sizeof (isns_entity_t),
  	sizeof (isns_iscsi_t),
  	sizeof (isns_portal_t),
  	sizeof (isns_pg_t),
  	sizeof (isns_dd_t),
  	sizeof (isns_dds_t),
  	0,
  	0,
  	0,
  	0,
  	0,
  	sizeof (isns_assoc_iscsi_t),
  	sizeof (isns_assoc_dd_t)
  };
  
  #ifdef DEBUG
  const int NUM_OF_REF[MAX_OBJ_TYPE_FOR_SIZE] = {
  #else
  static const int NUM_OF_REF[MAX_OBJ_TYPE_FOR_SIZE] = {
  #endif
  	0,
  	0,
  	0,
  	0,
  	PG_REF_COUNT,
  	0,
  	0,
  	0,
  	0,
  	0,
  	0,
  	0,
  	0,
  	0
  };
  
  /* the type of the reference object */
  static const int TYPE_OF_REF[MAX_OBJ_TYPE][MAX_REF_COUNT + 1] = {
  	{ 0 },
  	{ 0 },
  	{ OBJ_PG, OBJ_PORTAL, 0 },
  	{ OBJ_PG, OBJ_ISCSI, 0 },
  	{ 0, OBJ_ISCSI, OBJ_PORTAL },
  	{ 0 },
  	{ 0 }
  };
  
  /* the operating method for match operation of the reference object */
  #define	MAX_REF_MATCH	(2)
  static const int REF_MATCH_OPS[MAX_OBJ_TYPE][MAX_REF_MATCH] = {
  	{ 0, 0 },
  	{ 0, 0 },
  	{ OP_STRING, 0 },
  	{ OP_MEMORY_IP6, OP_INTEGER },
  	{ 0, 0 },
  	{ 0, 0 },
  	{ 0, 0 }
  };
  
  /* the index of the attribute of being matched object */
  static const int REF_MATCH_ID1[MAX_OBJ_TYPE][MAX_REF_MATCH] = {
  	{ 0, 0 },
  	{ 0, 0 },
  	{ ATTR_INDEX_ISCSI(ISNS_ISCSI_NAME_ATTR_ID), 0 },
  	{ ATTR_INDEX_PORTAL(ISNS_PORTAL_IP_ADDR_ATTR_ID),
  		ATTR_INDEX_PORTAL(ISNS_PORTAL_PORT_ATTR_ID) },
  	{ 0, 0 },
  	{ 0, 0 },
  	{ 0, 0 }
  };
  
  /* the index of the attribute of matching object */
  static const int REF_MATCH_ID2[MAX_OBJ_TYPE][MAX_REF_MATCH] = {
  	{ 0, 0 },
  	{ 0, 0 },
  	{ ATTR_INDEX_PG(ISNS_PG_ISCSI_NAME_ATTR_ID), 0 },
  	{ ATTR_INDEX_PG(ISNS_PG_PORTAL_IP_ADDR_ATTR_ID),
  		ATTR_INDEX_PG(ISNS_PG_PORTAL_PORT_ATTR_ID) },
  	{ 0, 0 },
  	{ 0, 0 },
  	{ 0, 0 }
  };
  
  /*
   * local functions.
   */
  static uint32_t get_reg_period();
  static char *make_unique_name(int *, uint32_t);
  static lookup_ctrl_t *set_lookup_ctrl(lookup_ctrl_t *, isns_obj_t *);
  static int setup_ref_lcp(lookup_ctrl_t *,
  	const isns_obj_t *, const isns_obj_t *);
  static int setup_deref_lcp(lookup_ctrl_t *,
  	const isns_obj_t *, isns_type_t);
  static int cb_get_parent(void *, void *);
  static int cb_node_child(void *, void *);
  static int cb_set_ref(void *, void *);
  static int cb_clear_ref(void *, void *);
  static int cb_add_child(void *, void *);
  static int cb_remove_child(void *, void *);
  static int cb_verify_ref(void *, void *);
  static int cb_ref_new2old(void *, void *);
  static int cb_new_ref(void *, void *);
  static int ref_new2old(
  	lookup_ctrl_t *, isns_type_t, uint32_t, const isns_obj_t *);
  static int ref_new2new(
  	lookup_ctrl_t *, const isns_obj_t *, const isns_obj_t *);
  static int new_ref(const isns_obj_t *, const isns_obj_t *);
  static uint32_t setup_parent_lcp(lookup_ctrl_t *, isns_obj_t *);
  static int set_obj_offline(isns_obj_t *);
  static int copy_attrs(isns_obj_t *, const isns_obj_t *);
  
  static isns_obj_t *make_default_pg(const isns_obj_t *, const isns_obj_t *);
  static isns_obj_t *(*const make_ref[MAX_OBJ_TYPE])
  	(const isns_obj_t *, const isns_obj_t *) = {
  		NULL,
  		NULL,
  		&make_default_pg,
  		&make_default_pg,
  		NULL,
  		NULL,
  		NULL
  };
  
  static uint32_t entity_hval(void *, uint16_t, uint32_t *);
  static uint32_t iscsi_hval(void *, uint16_t, uint32_t *);
  static uint32_t portal_hval(void *, uint16_t, uint32_t *);
  static uint32_t pg_hval(void *, uint16_t, uint32_t *);
  static uint32_t dd_hval(void *, uint16_t, uint32_t *);
  static uint32_t dds_hval(void *, uint16_t, uint32_t *);
  static uint32_t (*const hval_func[MAX_OBJ_TYPE])
  	(void *, uint16_t, uint32_t *) = {
  		NULL,
  		&entity_hval,
  		&iscsi_hval,
  		&portal_hval,
  		&pg_hval,
  		&dd_hval,
  		&dds_hval
  };
  
  /*
   * ****************************************************************************
   *
   * entity_hval:
   *	caculate the hash value of a network entity object.
   *
   * p	- the pointer pointers to network entity object or
   *	  the lookup control data, both have the key attribute
   *	  of a network entity object.
   * chunk- which chunk of the hash table.
   * flags- pointer to flags.
   * return - the hash value.
   *
   * ****************************************************************************
   */
  static uint32_t
  entity_hval(
  	void *p,
  	/* LINTED E_FUNC_ARG_UNUSED */
  	uint16_t chunk,
  	uint32_t *flags
  )
  {
  	uchar_t *key;
  	isns_obj_t *obj;
  	lookup_ctrl_t *lcp;
  
  	if ((*flags & FLAGS_CTRL_MASK) == 0) {
  		/* p pointers to a network entity object */
  		obj = (isns_obj_t *)p;
  		key = obj->attrs[ATTR_INDEX_ENTITY(ISNS_EID_ATTR_ID)].
  		    value.ptr;
  	} else {
  		/* p is lookup control data */
  		lcp = (lookup_ctrl_t *)p;
  		key = lcp->data[0].ptr;
  	}
  
  	return (htab_compute_hval(key));
  }
  
  /*
   * ****************************************************************************
   *
   * iscsi_hval:
   *	caculate the hash value of an iscsi storage node object.
   *
   * p	- the pointer pointers to iscsi storage node object or
   *	  the lookup control data, both have the key attribute
   *	  of an iscsi storage node object.
   * chunk- which chunk of the hash table.
   * flags- pointer to flags.
   * return - the hash value.
   *
   * ****************************************************************************
   */
  static uint32_t
  iscsi_hval(
  	void *p,
  	/* LINTED E_FUNC_ARG_UNUSED */
  	uint16_t chunk,
  	uint32_t *flags
  )
  {
  	uchar_t *key;
  	isns_obj_t *obj;
  	lookup_ctrl_t *lcp;
  
  	if ((*flags & FLAGS_CTRL_MASK) == 0) {
  		/* p pointers to an iscsi storage node object */
  		obj = (isns_obj_t *)p;
  		key = obj->attrs[ATTR_INDEX_ISCSI(ISNS_ISCSI_NAME_ATTR_ID)].
  		    value.ptr;
  	} else {
  		/* p is lookup control data */
  		lcp = (lookup_ctrl_t *)p;
  		key = lcp->data[0].ptr;
  	}
  
  	return (htab_compute_hval(key));
  }
  
  /*
   * ****************************************************************************
   *
   * portal_hval:
   *	caculate the hash value of a portal object.
   *
   * p	- the pointer pointers to a portal object or the lookup control
   *	  data, both have the key attributes of a portal object.
   * chunk- which chunk of the hash table.
   * flags- pointer to flags.
   * return - the hash value.
   *
   * ****************************************************************************
   */
  static uint32_t
  portal_hval(
  	void *p,
  	/* LINTED E_FUNC_ARG_UNUSED */
  	uint16_t chunk,
  	uint32_t *flags
  )
  {
  	char buff[INET6_ADDRSTRLEN + 8] = { 0 };
  	char buff2[8] = { 0 };
  	uchar_t *key;
  	isns_obj_t *obj;
  	lookup_ctrl_t *lcp;
  
  	in6_addr_t *ip;
  	uint32_t port;
  
  	if ((*flags & FLAGS_CTRL_MASK) == 0) {
  		/* p pointers to a portal object */
  		obj = (isns_obj_t *)p;
  		ip = obj->attrs[ATTR_INDEX_PORTAL
  		    (ISNS_PORTAL_IP_ADDR_ATTR_ID)].value.ip;
  		port = obj->attrs[ATTR_INDEX_PORTAL
  		    (ISNS_PORTAL_PORT_ATTR_ID)].value.ui;
  	} else {
  		/* p is lookup control data */
  		lcp = (lookup_ctrl_t *)p;
  		ip = lcp->data[0].ip;
  		port = lcp->data[1].ui;
  	}
  
  	key = (uchar_t *)inet_ntop(AF_INET6, (void *)ip,
  	    buff, sizeof (buff));
  	(void) snprintf(buff2, sizeof (buff2), "%d", port);
  	(void) strcat((char *)key, buff2);
  
  	return (htab_compute_hval(key));
  }
  
  /*
   * ****************************************************************************
   *
   * pg_hval:
   *	caculate the hash value of a portal group object.
   *
   * p	- the pointer pointers to a portal group object or the lookup
   *	  control data, both have the key attributes of a portal object.
   * chunk- which chunk of the hash table.
   * flags- pointer to flags.
   * return - the hash value.
   *
   * ****************************************************************************
   */
  static uint32_t
  pg_hval(
  	void *p,
  	uint16_t chunk,
  	uint32_t *flags
  )
  {
  	char buff[INET6_ADDRSTRLEN + 8] = { 0 };
  	char buff2[8] = { 0 };
  	uchar_t *key = NULL;
  	isns_obj_t *obj;
  	lookup_ctrl_t *lcp;
  
  	in6_addr_t *ip = NULL;
  	uint32_t port;
  
  	if ((*flags & FLAGS_CTRL_MASK) == 0) {
  		/* p is a portal group object */
  		obj = (isns_obj_t *)p;
  		if (chunk == 0) {
  			/* the first chunk */
  			key = obj->attrs[ATTR_INDEX_PG
  			    (ISNS_PG_ISCSI_NAME_ATTR_ID)].value.ptr;
  		} else {
  			/* another chunk */
  			ip = obj->attrs[ATTR_INDEX_PG
  			    (ISNS_PG_PORTAL_IP_ADDR_ATTR_ID)].value.ip;
  			port = obj->attrs[ATTR_INDEX_PG
  			    (ISNS_PG_PORTAL_PORT_ATTR_ID)].value.ui;
  		}
  	} else {
  		/* p is a lookup control data */
  		lcp = (lookup_ctrl_t *)p;
  		/* clear the chunk flags */
  		*flags &= ~FLAGS_CHUNK_MASK;
  		if (lcp->op[0] == OP_STRING) {
  			/* the first chunk */
  			key = lcp->data[0].ptr;
  		} else {
  			/* another chunk */
  			ip = lcp->data[0].ip;
  			port = lcp->data[1].ui;
  			*flags |= 1;
  		}
  	}
  
  	if (key == NULL) {
  		key = (uchar_t *)inet_ntop(AF_INET6, (void *)ip,
  		    buff, sizeof (buff));
  		(void) snprintf(buff2, sizeof (buff2), "%d", port);
  		(void) strcat((char *)key, buff2);
  	}
  
  	return (htab_compute_hval(key));
  }
  
  /*
   * ****************************************************************************
   *
   * dd_hval:
   *	caculate the hash value of a DD object.
   *
   * p	- the pointer pointers to a DD object or the lookup control data,
   *	  both have the key attributes of a DD object.
   * chunk- which chunk of the hash table.
   * flags- pointer to flags.
   * return - the hash value.
   *
   * ****************************************************************************
   */
  static uint32_t
  dd_hval(
  	void *p,
  	/* LINTED E_FUNC_ARG_UNUSED */
  	uint16_t chunk,
  	uint32_t *flags
  )
  {
  	uchar_t *key;
  	isns_obj_t *obj;
  	lookup_ctrl_t *lcp;
  
  	if ((*flags & FLAGS_CTRL_MASK) == 0) {
  		/* p is a DD object */
  		obj = (isns_obj_t *)p;
  		key = obj->attrs[ATTR_INDEX_DD(ISNS_DD_NAME_ATTR_ID)].
  		    value.ptr;
  	} else {
  		/* p is a lookup control data */
  		lcp = (lookup_ctrl_t *)p;
  		key = lcp->data[0].ptr;
  	}
  
  	return (htab_compute_hval(key));
  }
  
  /*
   * ****************************************************************************
   *
   * dds_hval:
   *	caculate the hash value of a DD-set object.
   *
   * p	- the pointer pointers to a DD-set object or the lookup control data,
   *	  both have the key attributes of a DD-set object.
   * chunk- which chunk of the hash table.
   * flags- pointer to flags.
   * return - the hash value.
   *
   * ****************************************************************************
   */
  static uint32_t
  dds_hval(
  	void *p,
  	/* LINTED E_FUNC_ARG_UNUSED */
  	uint16_t chunk,
  	uint32_t *flags
  )
  {
  	uchar_t *key;
  	isns_obj_t *obj;
  	lookup_ctrl_t *lcp;
  
  	if ((*flags & FLAGS_CTRL_MASK) == 0) {
  		/* p is a DD-set object */
  		obj = (isns_obj_t *)p;
  		key = obj->attrs[ATTR_INDEX_DDS(ISNS_DD_SET_NAME_ATTR_ID)].
  		    value.ptr;
  	} else {
  		/* p is lookup control data */
  		lcp = (lookup_ctrl_t *)p;
  		key = lcp->data[0].ptr;
  	}
  
  	return (htab_compute_hval(key));
  }
  
  /*
   * ****************************************************************************
   *
   * obj_hval:
   *	caculate the hash value of an object.
   *
   * p	- the pointer pointers to an object or lookup control data,
   *	  both has the object type and the key attributes of an object.
   * chunk- which chunk of the hash table.
   * flags- pointer to flags.
   * return - the hash value.
   *
   * ****************************************************************************
   */
  uint32_t
  obj_hval(
  	void *p,
  	uint16_t chunk,
  	uint32_t *flags
  )
  {
  	isns_type_t type = ((isns_obj_t *)p)->type;
  
  	return (hval_func[type](p, chunk, flags));
  }
  
  /*
   * ****************************************************************************
   *
   * get_obj_uid:
   *	get the UID of an object.
   *
   * p	- the pointer pointers to an object.
   * return - the UID.
   *
   * ****************************************************************************
   */
  uint32_t
  get_obj_uid(
  	const void *p
  )
  {
  	isns_obj_t *obj = (isns_obj_t *)p;
  	isns_attr_t *attr = &obj->attrs[UID_ATTR_INDEX[obj->type]];
  	uint32_t uid = attr->value.ui;
  	return (uid);
  }
  
  /*
   * ****************************************************************************
   *
   * set_obj_uid:
   *	set the UID of an object.
   *
   * p	- the pointer pointers to an object.
   * uid	- the UID.
   * return - the UID.
   *
   * ****************************************************************************
   */
  uint32_t
  set_obj_uid(
  	void *p,
  	uint32_t uid
  )
  {
  	isns_obj_t *obj = (isns_obj_t *)p;
  	isns_attr_t *attr = &obj->attrs[UID_ATTR_INDEX[obj->type]];
  
  	/* set the tag, len and value */
  	attr->tag = UID_TAG[obj->type];
  	attr->len = 4;
  	attr->value.ui = uid;
  
  	return (uid);
  }
  
  /*
   * ****************************************************************************
   *
   * obj_cmp:
   *	compare between two objects or an object with a lookup control data.
   *
   * p1	- the pointer points to an object.
   * p2	- the pointer points to an object or a lookup control data.
   * flags- 0: p2 is an object; otherwise p2 is a lookup control data.
   * return - the comparsion result.
   *
   * ****************************************************************************
   */
  int
  obj_cmp(
  	void *p1,
  	void *p2,
  	int flags
  )
  {
  	isns_obj_t *obj = (isns_obj_t *)p1;
  	lookup_ctrl_t buff = { 0 };
  	lookup_ctrl_t *lcp;
  	uint32_t uid;
  
  	if (flags == 0) {
  		lcp = set_lookup_ctrl(&buff, (isns_obj_t *)p2);
  	} else {
  		lcp = (lookup_ctrl_t *)p2;
  		uid = get_obj_uid(obj);
  		/* the object are linked with decending order by */
  		/* the object UID, if the object UID is greater than */
  		/* or equal to the current UID, it needs to compare */
  		/* for the next one. */
  		if (lcp->curr_uid != 0 && uid >= lcp->curr_uid) {
  			return (-1);
  		}
  	}
  
  	return (key_cmp(lcp, obj));
  }
  
  /*
   * ****************************************************************************
   *
   * replace_object:
   *	replace an existing object with the new one.
   *
   * p1	- the pointer points to an object being replaced.
   * p2	- the pointer points to a new object.
   * uid_p- points to uid for returning.
   * flag	- 0: do not free the source object, otherwise free it.
   * return - error code.
   *
   * ****************************************************************************
   */
  int
  replace_object(
  	void *p1,
  	void *p2,
  	uint32_t *uid_p,
  	int flag
  )
  {
  	int ec = 0;
  
  #ifndef SKIP_SRC_AUTH
  	uint32_t *pp_dst, *pp_src, swap;
  #endif
  	int online;
  
  	isns_obj_t *dst = (isns_obj_t *)p1;
  	isns_obj_t *src = (isns_obj_t *)p2;
  
  	if (src->type == OBJ_DD || src->type == OBJ_DDS) {
  		/* replace not allowed */
  		return (ERR_NAME_IN_USE);
  	}
  
  	online = is_obj_online(dst);
  
  	/* set cache update flag */
  	SET_CACHE_UPDATED();
  
  	/* update parent uid */
  #ifndef SKIP_SRC_AUTH
  	pp_dst = get_parent_p(dst);
  	if (pp_dst != NULL) {
  		pp_src = get_parent_p(src);
  		swap = *pp_dst;
  		*pp_dst = *pp_src;
  		if (swap != 0) {
  			*pp_src = swap;
  		}
  	}
  #endif
  
  	/* update all of attributes */
  	if (copy_attrs(dst, src) != 0) {
  		return (ISNS_RSP_INTERNAL_ERROR);
  	}
  
  	/* free up the src object */
  	if (flag != 0) {
  		(void) free_object(src);
  	} else if (online == 0) {
  		(void) set_obj_uid(src, get_obj_uid(dst));
  		(void) set_obj_offline(src);
  	}
  
  	/* update data store */
  	if (sys_q != NULL) {
  		ec = write_data(DATA_UPDATE, dst);
  	} else {
  		/* we should never have duplicated entry in data store */
  		ec = ISNS_RSP_INTERNAL_ERROR;
  	}
  
  	/* trigger a scn */
  	if (ec == 0) {
  		if (scn_q != NULL) {
  			(void) make_scn((online == 0) ?
  			    ISNS_OBJECT_ADDED :
  			    ISNS_OBJECT_UPDATED,
  			    dst);
  		}
  		if (uid_p != NULL) {
  			*uid_p = get_obj_uid(dst);
  		}
  	}
  
  	return (ec);
  }
  
  /*
   * ****************************************************************************
   *
   * add_object:
   *	post function after adding a new object.
   *
   * p	- object which has been added.
   * return - error code.
   *
   * ****************************************************************************
   */
  int
  add_object(
  	void *p
  )
  {
  	int ec = 0;
  
  	isns_obj_t *obj = (isns_obj_t *)p;
  
  	/* add the new object to data store */
  	if (sys_q != NULL) {
  		ec = write_data(DATA_ADD, obj);
  	}
  
  	/* trigger a scn */
  	if (ec == 0 && scn_q != NULL) {
  		(void) make_scn(ISNS_OBJECT_ADDED, obj);
  	}
  
  	return (ec);
  }
  
  /*
   * ****************************************************************************
   *
   * obj_tab_init:
   *	initialize the object hash tables.
   *
   * c	- points to the cache.
   * return - error code.
   *
   * ****************************************************************************
   */
  int
  obj_tab_init(
  	struct cache *c
  )
  {
  	htab_t *t;
  
  	htab_init();
  
  	/*
  	 * allocate an array of pointer for the object hash tables.
  	 */
  	c->t = (struct htab **)calloc(sizeof (struct htab *), MAX_OBJ_TYPE);
  	if (c->t == NULL) {
  		return (1);
  	}
  
  	/*
  	 * hash table for network entity objects.
  	 */
  	t = htab_create(UID_FLAGS_SEQ, 8, 1);
  	if (t != NULL) {
  		t->c = c;
  		c->t[OBJ_ENTITY] = t;
  	} else {
  		return (1);
  	}
  
  	/*
  	 * hash table for iscsi storage node objects.
  	 */
  	t = htab_create(UID_FLAGS_SEQ, 8, 1);
  	if (t != NULL) {
  		t->c = c;
  		c->t[OBJ_ISCSI] = t;
  	} else {
  		return (1);
  	}
  
  	/*
  	 * hash table for portal objects.
  	 */
  	t = htab_create(UID_FLAGS_SEQ, 8, 1);
  	if (t != NULL) {
  		t->c = c;
  		c->t[OBJ_PORTAL] = t;
  	} else {
  		return (1);
  	}
  
  	/*
  	 * hash table for portal group objects.
  	 */
  	t = htab_create(UID_FLAGS_SEQ, 8, 2);
  	if (t != NULL) {
  		t->c = c;
  		c->t[OBJ_PG] = t;
  	} else {
  		return (1);
  	}
  
  	/*
  	 * hash table for discovery domain objects.
  	 */
  	t = htab_create(0, 6, 1);
  	if (t != NULL) {
  		t->c = c;
  		c->t[OBJ_DD] = t;
  	} else {
  		return (1);
  	}
  
  	/*
  	 * hash table for discovery domain set objects.
  	 */
  	t = htab_create(0, 4, 1);
  	if (t != NULL) {
  		t->c = c;
  		c->t[OBJ_DDS] = t;
  	} else {
  		return (1);
  	}
  
  	return (0);
  }
  
  /*
   * ****************************************************************************
   *
   * get_ref_np:
   *	get the ref pointer of the portal group object.
   *
   * obj	- portal group object.
   * return - ref pointer.
   *
   * ****************************************************************************
   */
  static uint32_t *
  get_ref_np(
  	isns_obj_t *obj,
  	int n
  )
  {
  	uint32_t *refp =
  	    obj->type == OBJ_PG ? &((isns_pg_t *)obj)->ref[n] : NULL;
  
  	return (refp);
  }
  
  #ifdef DEBUG
  uint32_t
  #else
  static uint32_t
  #endif
  get_ref_n(
  	isns_obj_t *obj,
  	int n
  )
  {
  	return (*get_ref_np(obj, n));
  }
  
  static uint32_t *
  get_ref_p(
  	isns_obj_t *obj,
  	isns_type_t rt
  )
  {
  	isns_type_t t = obj->type;
  
  	int i = 0;
  	while (i < NUM_OF_REF[t]) {
  		if (rt == TYPE_OF_REF[t][i + 1]) {
  			return (get_ref_np(obj, i));
  		}
  		i ++;
  	}
  
  	return (NULL);
  }
  
  uint32_t
  get_ref_t(
  	isns_obj_t *obj,
  	isns_type_t type
  )
  {
  	uint32_t *refp = get_ref_p(obj, type);
  
  	if (refp != NULL) {
  		return (*refp);
  	/* LINTED E_NOP_ELSE_STMT */
  	} else {
  		ASSERT(0);
  	}
  
  	return (0);
  }
  
  /*
   * ****************************************************************************
   *
   * get_parent_p:
   *	get the pointer of the parent object.
   *
   * obj	- an object.
   * return - parent object pointer.
   *
   * ****************************************************************************
   */
  uint32_t *const
  get_parent_p(
  	const isns_obj_t *obj
  )
  {
  	uint32_t *pp;
  	switch (obj->type) {
  	case OBJ_ISCSI:
  		pp = &((isns_iscsi_t *)obj)->puid;
  		break;
  	case OBJ_PORTAL:
  		pp = &((isns_portal_t *)obj)->puid;
  		break;
  	case OBJ_PG:
  		pp = &((isns_pg_t *)obj)->puid;
  		break;
  	case OBJ_ASSOC_ISCSI:
  		pp = &((isns_assoc_iscsi_t *)obj)->puid;
  		break;
  	case OBJ_ASSOC_DD:
  		pp = &((isns_assoc_dd_t *)obj)->puid;
  		break;
  	default:
  		pp = NULL;
  		break;
  	}
  
  	return (pp);
  }
  
  uint32_t
  get_parent_uid(
  	const isns_obj_t *obj
  )
  {
  	uint32_t *pp = get_parent_p(obj);
  	if (pp != NULL) {
  		return (*pp);
  	}
  
  	return (0);
  }
  
  /*
   * ****************************************************************************
   *
   * get_child_np:
   *	get the pointer of the UID array of the n'th child of an object.
   *
   * obj	- an object.
   * n	- the child index.
   * return - the pointer of the UID array.
   *
   * ****************************************************************************
   */
  static uint32_t **
  get_child_np(
  	isns_obj_t *obj,
  	int n
  )
  {
  	uint32_t **pp =
  	    obj->type == OBJ_ENTITY ? &((isns_entity_t *)obj)->cuid[n] : NULL;
  
  	return (pp);
  }
  
  /*
   * ****************************************************************************
   *
   * get_child_n:
   *	get the UID array of the n'th child of an object.
   *
   * obj	- an object.
   * n	- the child index.
   * return - the UID array.
   *
   * ****************************************************************************
   */
  #ifdef DEBUG
  uint32_t *
  #else
  static uint32_t *
  #endif
  get_child_n(
  	isns_obj_t *obj,
  	int n
  )
  {
  	uint32_t **pp = get_child_np(obj, n);
  
  	if (pp != NULL) {
  		return (*pp);
  	}
  
  	ASSERT(0);
  	return (NULL);
  }
  
  /*
   * ****************************************************************************
   *
   * get_child_p:
   *	get the pointer of the UID array of the child matching the type.
   *
   * base	- an object.
   * child_type	- the child object type.
   * return - the pointer of the UID array.
   *
   * ****************************************************************************
   */
  static uint32_t **
  get_child_p(
  	isns_obj_t *base,
  	int child_type
  )
  {
  	uint32_t **pp = NULL;
  	int i = 0;
  	while (i < NUM_OF_CHILD[base->type]) {
  		if (child_type == TYPE_OF_CHILD[base->type][i]) {
  			pp = get_child_np(base, i);
  			break;
  		}
  		i ++;
  	}
  
  	return (pp);
  }
  
  /*
   * ****************************************************************************
   *
   * get_child_t:
   *	get the UID array of the child object matching the type.
   *
   * base	- an object.
   * child_type	- the child object type.
   * return - the UID array.
   *
   * ****************************************************************************
   */
  uint32_t *
  get_child_t(
  	isns_obj_t *base,
  	int child_type
  )
  {
  	uint32_t **pp = get_child_p(base, child_type);
  
  	if (pp != NULL) {
  		return (*pp);
  	} else {
  		return (NULL);
  	}
  }
  
  /*
   * ****************************************************************************
   *
   * key_cmp:
   *	compare the object against the lookup control data.
   *
   * lcp	- the lookup control data.
   * obj	- an object.
   * return - comparison result.
   *
   * ****************************************************************************
   */
  int
  key_cmp(
  	lookup_ctrl_t *lcp,
  	isns_obj_t *obj
  )
  {
  	int i = 0;
  	int match = 1;
  	while (i < MAX_LOOKUP_CTRL && lcp->op[i] > 0 && match) {
  		isns_attr_t *attr = &obj->attrs[lcp->id[i]];
  		switch (lcp->op[i]) {
  			case OP_STRING:
  				match = (strcmp((const char *)lcp->data[i].ptr,
  				    (const char *)attr->value.ptr) == 0);
  				break;
  			case OP_INTEGER:
  				match = (lcp->data[i].ui == attr->value.ui);
  				break;
  			case OP_MEMORY_IP6:
  				match = !memcmp((void *)lcp->data[i].ip,
  				    (void *)attr->value.ip,
  				    sizeof (in6_addr_t));
  				break;
  			default:
  				ASSERT(0);
  				match = 0;
  				break;
  		}
  		i ++;
  	}
  
  	if (i && match) {
  		return (0);
  	} else {
  		return (1);
  	}
  }
  
  /*
   * ****************************************************************************
   *
   * set_lookup_ctrl:
   *	fill in the lookup control data for an object.
   *
   * lcp	- the lookup control data.
   * obj	- an object.
   * return - the lookup control data.
   *
   * ****************************************************************************
   */
  static lookup_ctrl_t *
  set_lookup_ctrl(
  	lookup_ctrl_t *lcp,
  	isns_obj_t *obj
  )
  {
  	isns_type_t type = obj->type;
  	uint32_t id, op;
  	int i = 0;
  
  	lcp->type = type;
  	while (i < MAX_KEY_ATTRS) {
  		op = KEY_ATTR_OP[type][i];
  		if (op != 0) {
  			id = KEY_ATTR_INDEX[type][i];
  			lcp->id[i] = id;
  			lcp->op[i] = op;
  			lcp->data[i].ui = obj->attrs[id].value.ui;
  		} else {
  			break;
  		}
  		i ++;
  	}
  
  	return (lcp);
  }
  
  /*
   * ****************************************************************************
   *
   * assign_attr:
   *	assign an attribute.
   *
   * attr	- the attribute being assigned.
   * tmp	- the attribute.
   * return - error code.
   *
   * ****************************************************************************
   */
  int
  assign_attr(
  	isns_attr_t *attr,
  	const isns_attr_t *tmp
  )
  {
  	uint32_t t;
  
  	switch (tmp->tag) {
  	case ISNS_EID_ATTR_ID:
  	case ISNS_DD_SET_NAME_ATTR_ID:
  	case ISNS_DD_NAME_ATTR_ID:
  		if (tmp->len == 0 && attr->len == 0) {
  			int len;
  			char *name = make_unique_name(&len, tmp->tag);
  			if (name != NULL) {
  				attr->value.ptr = (uchar_t *)name;
  				attr->tag = tmp->tag;
  				attr->len = len;
  			} else {
  				/* memory exhausted */
  				return (1);
  			}
  		}
  		/* FALLTHROUGH */
  	case ISNS_PORTAL_NAME_ATTR_ID:
  	case ISNS_ISCSI_NAME_ATTR_ID:
  	case ISNS_ISCSI_ALIAS_ATTR_ID:
  	case ISNS_ISCSI_AUTH_METHOD_ATTR_ID:
  	case ISNS_PG_ISCSI_NAME_ATTR_ID:
  	case ISNS_DD_ISCSI_NAME_ATTR_ID:
  		if (tmp->len == 0) {
  			return (0);
  		} else if (tmp->len >= attr->len) {
  			attr->value.ptr = realloc(
  			    attr->value.ptr, tmp->len + 1);
  		}
  		if (attr->value.ptr != NULL) {
  			(void) strncpy((char *)attr->value.ptr,
  			    (char *)tmp->value.ptr, tmp->len);
  			attr->value.ptr[tmp->len] = 0;
  			attr->tag = tmp->tag;
  			attr->len = tmp->len;
  		} else {
  			/* memory exhausted */
  			return (1);
  		}
  		break;
  	case ISNS_MGMT_IP_ADDR_ATTR_ID:
  	case ISNS_PORTAL_IP_ADDR_ATTR_ID:
  	case ISNS_PG_PORTAL_IP_ADDR_ATTR_ID:
  		if (attr->value.ip == NULL) {
  			attr->value.ip = (in6_addr_t *)calloc(1, tmp->len);
  		}
  		if (attr->value.ip != NULL) {
  			(void) memcpy((void *)attr->value.ip,
  			    (void *)tmp->value.ip, tmp->len);
  			attr->tag = tmp->tag;
  			attr->len = tmp->len;
  		} else {
  			/* memory exhausted */
  			return (1);
  		}
  		break;
  	case ISNS_ENTITY_INDEX_ATTR_ID:
  	case ISNS_PORTAL_INDEX_ATTR_ID:
  	case ISNS_ISCSI_NODE_INDEX_ATTR_ID:
  	case ISNS_PG_INDEX_ATTR_ID:
  	case ISNS_DD_SET_ID_ATTR_ID:
  	case ISNS_DD_ID_ATTR_ID:
  		if (attr->value.ui != 0) {
  			break;
  		}
  		/* FALLTHROUGH */
  	case ISNS_ENTITY_PROTOCOL_ATTR_ID:
  	case ISNS_VERSION_RANGE_ATTR_ID:
  
  	case ISNS_PORTAL_PORT_ATTR_ID:
  	case ISNS_ESI_PORT_ATTR_ID:
  	case ISNS_SCN_PORT_ATTR_ID:
  
  	case ISNS_ISCSI_NODE_TYPE_ATTR_ID:
  	case ISNS_ISCSI_SCN_BITMAP_ATTR_ID:
  
  	case ISNS_PG_PORTAL_PORT_ATTR_ID:
  	case ISNS_PG_TAG_ATTR_ID:
  
  	case ISNS_DD_SET_STATUS_ATTR_ID:
  	case ISNS_DD_ISCSI_INDEX_ATTR_ID:
  		attr->tag = tmp->tag;
  		attr->len = tmp->len;
  		attr->value.ui = tmp->value.ui;
  		break;
  	case ISNS_ENTITY_REG_PERIOD_ATTR_ID:
  		attr->tag = tmp->tag;
  		attr->len = tmp->len;
  		attr->value.ui = tmp->value.ui;
  		t = get_reg_period();
  		if (attr->value.ui > t) {
  			attr->value.ui = t;
  		} else if (attr->value.ui < ONE_DAY) {
  			attr->value.ui = ONE_DAY;
  		}
  		break;
  	case ISNS_ESI_INTERVAL_ATTR_ID:
  		attr->tag = tmp->tag;
  		attr->len = tmp->len;
  		attr->value.ui = tmp->value.ui;
  		if (attr->value.ui > ONE_DAY) {
  			attr->value.ui = ONE_DAY;
  		} else if (attr->value.ui < MIN_ESI_INTVAL) {
  			attr->value.ui = MIN_ESI_INTVAL; /* 20 seconds */
  		}
  		break;
  	default:
  		ASSERT(0);
  		/* don't assign the attribute */
  		break;
  	}
  	return (0);
  }
  
  /*
   * ****************************************************************************
   *
   * copy_attrs:
   *	copy all of attributes from one object to another.
   *
   * dst	- the destination object.
   * tmp	- the source object.
   * return - error code.
   *
   * ****************************************************************************
   */
  static int
  copy_attrs(
  	isns_obj_t *dst,
  	const isns_obj_t *src
  )
  {
  	int i = 0;
  	int n = NUM_OF_ATTRS[dst->type];
  
  	isns_attr_t *dst_attr;
  	const isns_attr_t *src_attr;
  
  	while (i < n) {
  		src_attr = &(src->attrs[i]);
  		if (src_attr->tag != 0) {
  			dst_attr = &(dst->attrs[i]);
  			if (assign_attr(dst_attr, src_attr) != 0) {
  				return (1);
  			}
  		}
  		i ++;
  	}
  
  	return (0);
  }
  
  /*
   * ****************************************************************************
   *
   * extract_attr:
   *	extract an attribute from a TLV format data.
   *
   * attr	- the attribute.
   * tlv	- the TLV format data.
   * return - error code.
   *
   * ****************************************************************************
   */
  int
  extract_attr(
  	isns_attr_t *attr,
  	const isns_tlv_t *tlv,
  	int flag
  )
  {
  	int ec = 0;
  
  	uint32_t min_len = 4, max_len = 224;
  
  	switch (tlv->attr_id) {
  	case ISNS_EID_ATTR_ID:
  		min_len = 0;
  		/* FALLTHROUGH */
  	case ISNS_PORTAL_NAME_ATTR_ID:
  	case ISNS_ISCSI_ALIAS_ATTR_ID:
  	case ISNS_DD_SET_NAME_ATTR_ID:
  	case ISNS_DD_NAME_ATTR_ID:
  		max_len = 256;
  		/* FALLTHROUGH */
  	case ISNS_ISCSI_NAME_ATTR_ID:
  	case ISNS_PG_ISCSI_NAME_ATTR_ID:
  		if (tlv->attr_len < min_len || tlv->attr_len > max_len) {
  			ec = ISNS_RSP_MSG_FORMAT_ERROR;
  		} else {
  			attr->tag = tlv->attr_id;
  			attr->len = tlv->attr_len;
  			attr->value.ptr = (uchar_t *)&(tlv->attr_value[0]);
  		}
  		break;
  	case ISNS_ISCSI_AUTH_METHOD_ATTR_ID:
  		attr->tag = tlv->attr_id;
  		attr->len = tlv->attr_len;
  		attr->value.ptr = (uchar_t *)&(tlv->attr_value[0]);
  		break;
  	case ISNS_MGMT_IP_ADDR_ATTR_ID:
  	case ISNS_PORTAL_IP_ADDR_ATTR_ID:
  	case ISNS_PG_PORTAL_IP_ADDR_ATTR_ID:
  		if (tlv->attr_len != 16) {
  			ec = ISNS_RSP_MSG_FORMAT_ERROR;
  		} else {
  			attr->tag = tlv->attr_id;
  			attr->len = tlv->attr_len;
  			attr->value.ip = (void *)&(tlv->attr_value[0]);
  		}
  		break;
  	case ISNS_ENTITY_PROTOCOL_ATTR_ID:
  	case ISNS_VERSION_RANGE_ATTR_ID:
  	case ISNS_ENTITY_REG_PERIOD_ATTR_ID:
  		/* fall throught */
  	case ISNS_PORTAL_PORT_ATTR_ID:
  	case ISNS_ESI_INTERVAL_ATTR_ID:
  	case ISNS_ESI_PORT_ATTR_ID:
  	case ISNS_SCN_PORT_ATTR_ID:
  		/* fall throught */
  	case ISNS_ISCSI_NODE_TYPE_ATTR_ID:
  		/* fall throught */
  	case ISNS_PG_PORTAL_PORT_ATTR_ID:
  		/* fall throught */
  	case ISNS_DD_SET_ID_ATTR_ID:
  	case ISNS_DD_SET_STATUS_ATTR_ID:
  		/* fall throught */
  	case ISNS_DD_ID_ATTR_ID:
  		if (tlv->attr_len != 4) {
  			ec = ISNS_RSP_MSG_FORMAT_ERROR;
  			break;
  		}
  		/* FALLTHROUGH */
  	case ISNS_PG_TAG_ATTR_ID:
  		attr->tag = tlv->attr_id;
  		attr->len = tlv->attr_len;
  		if (tlv->attr_len == 4) {
  			attr->value.ui = ntohl(*(uint32_t *)
  			    &(tlv->attr_value[0]));
  		} else {
  			attr->value.ui = 0;
  		}
  		break;
  	case ISNS_ISCSI_SCN_BITMAP_ATTR_ID:
  		/* ignore scn bitmap attribute during object registration, */
  		/* it is registered by scn_reg message. */
  	case ISNS_ENTITY_ISAKMP_P1_ATTR_ID:
  	case ISNS_ENTITY_CERT_ATTR_ID:
  	case ISNS_PORTAL_SEC_BMP_ATTR_ID:
  	case ISNS_PORTAL_ISAKMP_P1_ATTR_ID:
  	case ISNS_PORTAL_ISAKMP_P2_ATTR_ID:
  	case ISNS_PORTAL_CERT_ATTR_ID:
  		break;
  	case ISNS_PORTAL_INDEX_ATTR_ID:
  	case ISNS_ISCSI_NODE_INDEX_ATTR_ID:
  	case ISNS_PG_INDEX_ATTR_ID:
  		if (flag == 0) {
  			if (tlv->attr_len != 4) {
  				ec = ISNS_RSP_MSG_FORMAT_ERROR;
  			} else {
  				attr->tag = tlv->attr_id;
  				attr->len = tlv->attr_len;
  				attr->value.ui = ntohl(*(uint32_t *)
  				    &(tlv->attr_value[0]));
  			}
  			break;
  		}
  		/* FALLTHROUGH */
  	case ISNS_ENTITY_INDEX_ATTR_ID:
  	case ISNS_TIMESTAMP_ATTR_ID:
  	default:
  		if (flag == 0) {
  			ec = ISNS_RSP_INVALID_QRY;
  		} else {
  			ec = ISNS_RSP_INVALID_REGIS;
  		}
  		break;
  	}
  
  	return (ec);
  }
  
  /*
   * ****************************************************************************
   *
   * copy_attr:
   *	copy an attribute from a TLV format data.
   *
   * attr	- the attribute.
   * tlv	- the TLV format data.
   * return - error code.
   *
   * ****************************************************************************
   */
  static int
  copy_attr(
  	isns_attr_t *attr,
  	const isns_tlv_t *tlv
  )
  {
  	int ec = 0;
  
  	isns_attr_t tmp = { 0 };
  
  	/* extract the attribute first */
  	ec = extract_attr(&tmp, tlv, 1);
  
  	/* assign the attribute */
  	if (ec == 0 && tmp.tag != 0) {
  		if (assign_attr(attr, &tmp) != 0) {
  			ec = ISNS_RSP_INTERNAL_ERROR;
  		}
  	}
  
  	return (ec);
  }
  
  /*
   * ****************************************************************************
   *
   * get_timestamp:
   *	get current timestamp.
   *
   * return - current timestamp.
   *
   * ****************************************************************************
   */
  uint32_t
  get_timestamp(
  )
  {
  	uint32_t t;
  	int flag;
  
  	/* block the scheduler */
  	(void) pthread_mutex_lock(&el_mtx);
  
  	/* get most current time */
  	if (sys_q != NULL) {
  		/* need to wakeup idle */
  		flag = 1;
  	} else {
  		flag = 0;
  	}
  	t = get_stopwatch(flag);
  
  	/* unblock it */
  	(void) pthread_mutex_unlock(&el_mtx);
  
  	return (t);
  }
  
  /*
   * ****************************************************************************
   *
   * get_reg_period:
   *	get the longest registration period.
   *
   * return - the longest registration period.
   *
   * ****************************************************************************
   */
  static uint32_t
  get_reg_period(
  )
  {
  	uint32_t t;
  	uint32_t period;
  
  	/* get most current time */
  	t = get_timestamp();
  
  	/* just one second before the end of the world */
  	period = INFINITY - t - 1;
  
  	return (period);
  }
  
  /*
   * ****************************************************************************
   *
   * obj_calloc:
   *	allocate memory space for an object.
   *
   * type	- the object type.
   * return - pointer of the object being allocated.
   *
   * ****************************************************************************
   */
  isns_obj_t *
  obj_calloc(
  	int type
  )
  {
  	isns_obj_t *obj = NULL;
  
  	obj = (isns_obj_t *)calloc(1, SIZEOF_OBJ[type]);
  	if (obj != NULL) {
  		obj->type = type;
  #ifdef DEBUG
  	if (verbose_mc) {
  		printf("object(%d) allocated\n", type);
  	}
  #endif
  	}
  
  	return (obj);
  }
  
  /*
   * ****************************************************************************
   *
   * make_default_entity:
   *	generate a default network entity object.
   *
   * return - pointer of the default network entity object.
   *
   * ****************************************************************************
   */
  isns_obj_t *
  make_default_entity(
  )
  {
  	uint32_t t;
  
  	isns_obj_t *obj = obj_calloc(OBJ_ENTITY);
  	isns_attr_t *attr;
  	if (obj != NULL) {
  		int len;
  		char *eid = make_unique_name(&len, ISNS_EID_ATTR_ID);
  		if (!eid) {
  			free(obj);
  			return (NULL);
  		}
  		attr = &obj->attrs[ATTR_INDEX_ENTITY(ISNS_EID_ATTR_ID)];
  
  		/* set default entity name */
  		attr->tag = ISNS_EID_ATTR_ID;
  		attr->len = len;
  		attr->value.ptr = (uchar_t *)eid;
  
  		/* set default registration period */
  		attr = &obj->attrs[
  		    ATTR_INDEX_ENTITY(ISNS_ENTITY_REG_PERIOD_ATTR_ID)];
  		if (attr->tag == 0) {
  			attr->tag = ISNS_ENTITY_REG_PERIOD_ATTR_ID;
  			attr->len = 4;
  			t = get_reg_period();
  			attr->value.ui = t;
  		}
  	}
  
  	return (obj);
  }
  
  /*
   * ****************************************************************************
   *
   * make_default_pg:
   *	generate a default portal group object.
   *
   * iscsi  - the iscsi storage node object.
   * portal - the portal object.
   * return - pointer of the default portal group object.
   *
   * ****************************************************************************
   */
  static isns_obj_t *
  make_default_pg(
  	const isns_obj_t *p1,
  	const isns_obj_t *p2
  )
  {
  	const isns_obj_t *iscsi, *portal;
  	const isns_attr_t *name, *addr, *port;
  	isns_obj_t *pg;
  
  	uchar_t *pg_name;
  	in6_addr_t *pg_addr;
  
  	isns_attr_t *attr;
  
  	uint32_t *refp;
  
  	if (p1->type == OBJ_ISCSI) {
  		iscsi = p1;
  		portal = p2;
  	} else {
  		iscsi = p2;
  		portal = p1;
  	}
  	name = &iscsi->attrs[ATTR_INDEX_ISCSI(ISNS_ISCSI_NAME_ATTR_ID)];
  	addr = &portal->attrs[ATTR_INDEX_PORTAL(ISNS_PORTAL_IP_ADDR_ATTR_ID)];
  	port = &portal->attrs[ATTR_INDEX_PORTAL(ISNS_PORTAL_PORT_ATTR_ID)];
  
  	pg = obj_calloc(OBJ_PG);
  	pg_name = (uchar_t *)malloc(name->len);
  	pg_addr = (in6_addr_t *)malloc(addr->len);
  	if (pg != NULL && pg_name != NULL && pg_addr != NULL) {
  		(void) strcpy((char *)pg_name, (char *)name->value.ptr);
  		attr = &pg->attrs[ATTR_INDEX_PG(ISNS_PG_ISCSI_NAME_ATTR_ID)];
  		attr->tag = ISNS_PG_ISCSI_NAME_ATTR_ID;
  		attr->len = name->len;
  		attr->value.ptr = pg_name;
  
  		(void) memcpy((void *)pg_addr,
  		    (void *)addr->value.ip, addr->len);
  		attr = &pg->attrs[ATTR_INDEX_PG(
  		    ISNS_PG_PORTAL_IP_ADDR_ATTR_ID)];
  		attr->tag = ISNS_PG_PORTAL_IP_ADDR_ATTR_ID;
  		attr->len = addr->len;
  		attr->value.ip = pg_addr;
  
  		attr = &pg->attrs[ATTR_INDEX_PG(
  		    ISNS_PG_PORTAL_PORT_ATTR_ID)];
  		attr->tag = ISNS_PG_PORTAL_PORT_ATTR_ID;
  		attr->len = port->len;
  		attr->value.ui = port->value.ui;
  
  		attr = &pg->attrs[ATTR_INDEX_PG(
  		    ISNS_PG_TAG_ATTR_ID)];
  		attr->tag = ISNS_PG_TAG_ATTR_ID;
  		attr->len = 4;
  		attr->value.ui = ISNS_DEFAULT_PGT;
  
  		refp = get_ref_p(pg, OBJ_ISCSI);
  		*refp = get_obj_uid(iscsi);
  
  		refp = get_ref_p(pg, OBJ_PORTAL);
  		*refp = get_obj_uid(portal);
  
  		(void) set_parent_obj(pg, get_parent_uid(iscsi));
  	} else {
  		free(pg);
  		free(pg_name);
  		free(pg_addr);
  		pg = NULL;
  	}
  
  	return (pg);
  }
  
  /*
   * ****************************************************************************
   *
   * reg_get_entity:
   *	parse the Operating Attributes of the DevAttrReg message and
   *	create the Network Entity object if it has one.
   *
   * p	- the pointer of the object for returning.
   * op	- the operating attributes.
   * op_len - the length of the operating attributes.
   * return - error code.
   *
   * ****************************************************************************
   */
  int
  reg_get_entity(
  	isns_obj_t **p,
  	isns_tlv_t **op,
  	uint16_t *op_len
  )
  {
  	int ec = 0;
  
  	isns_tlv_t *tmp;
  	uint16_t tmp_len;
  	isns_attr_t *attr;
  
  	isns_obj_t *entity = NULL;
  
  	tmp = *op;
  	tmp_len = *op_len;
  
  	/* parse the entity object */
  	if (tmp_len >= 8 && IS_ENTITY_KEY(tmp->attr_id)) {
  		entity = obj_calloc(OBJ_ENTITY);
  		if (entity != NULL) {
  			do {
  				attr = &entity->attrs[
  				    ATTR_INDEX_ENTITY(tmp->attr_id)];
  				ec = copy_attr(attr, tmp);
  				NEXT_TLV(tmp, tmp_len);
  			} while (ec == 0 &&
  			    tmp_len >= 8 &&
  			    IS_ENTITY_ATTR(tmp->attr_id));
  		} else {
  			ec = ISNS_RSP_INTERNAL_ERROR;
  		}
  
  		if (ec == 0) {
  			/* set default registration period */
  			attr = &entity->attrs[
  			    ATTR_INDEX_ENTITY(ISNS_ENTITY_REG_PERIOD_ATTR_ID)];
  			if (attr->tag == 0) {
  				attr->tag = ISNS_ENTITY_REG_PERIOD_ATTR_ID;
  				attr->len = 4;
  				attr->value.ui = get_reg_period();
  			}
  		} else if (entity != NULL) {
  			free(entity);
  			entity = NULL;
  		}
  	}
  
  	*p = entity;
  	*op = tmp;
  	*op_len = tmp_len;
  
  	return (ec);
  }
  
  /*
   * ****************************************************************************
   *
   * reg_get_iscsi:
   *	parse the Operating Attributes of the DevAttrReg message and
   *	create an iSCSI Storage Node object.
   *
   * p	- the pointer of the object for returning.
   * pg_key1 - the pointer of iscsi storage node name for returning.
   * op	- the operating attributes.
   * op_len - the length of the operating attributes.
   * return - error code.
   *
   * ****************************************************************************
   */
  static int
  reg_get_iscsi(
  	isns_obj_t **p,
  	isns_attr_t *pg_key1,
  	isns_tlv_t **op,
  	uint16_t *op_len
  )
  {
  	int ec = 0;
  
  	isns_tlv_t *tmp;
  	uint16_t tmp_len;
  	isns_attr_t *attr;
  
  	isns_obj_t *obj = NULL;
  
  	tmp = *op;
  	tmp_len = *op_len;
  
  	/* keep the iscsi storage node name for */
  	/* parsing a pg object which is immediately */
  	/* followed with a PGT by the iscsi storage node */
  	pg_key1->tag = PG_KEY1;
  	pg_key1->len = tmp->attr_len;
  	pg_key1->value.ptr = (uchar_t *)&tmp->attr_value[0];
  
  	/* parse one iscsi storage node object */
  	obj = obj_calloc(OBJ_ISCSI);
  	if (obj != NULL) {
  		/* parse key & non-key attributes */
  		do {
  			attr = &obj->attrs[
  			    ATTR_INDEX_ISCSI(tmp->attr_id)];
  			ec = copy_attr(attr, tmp);
  			NEXT_TLV(tmp, tmp_len);
  		} while (ec == 0 &&
  		    tmp_len >= 8 &&
  		    IS_ISCSI_ATTR(tmp->attr_id));
  	} else {
  		/* no memory */
  		ec = ISNS_RSP_INTERNAL_ERROR;
  	}
  
  	*p = obj;
  	*op = tmp;
  	*op_len = tmp_len;
  
  	return (ec);
  }
  
  /*
   * ****************************************************************************
   *
   * reg_get_portal:
   *	parse the Operating Attributes of the DevAttrReg message and
   *	create a Portal object.
   *
   * p	- the pointer of the object for returning.
   * pg_key1 - the pointer of portal ip addr for returning.
   * pg_key2 - the pointer of portal port for returning.
   * op	- the operating attributes.
   * op_len - the length of the operating attributes.
   * return - error code.
   *
   * ****************************************************************************
   */
  static int
  reg_get_portal(
  	isns_obj_t **p,
  	isns_attr_t *pg_key1,
  	isns_attr_t *pg_key2,
  	isns_tlv_t **op,
  	uint16_t *op_len
  )
  {
  	int ec = 0;
  
  	isns_tlv_t *tmp;
  	uint16_t tmp_len;
  	isns_attr_t *attr;
  
  	isns_obj_t *obj = NULL;
  
  	isns_tlv_t *ip;
  
  	tmp = *op;
  	tmp_len = *op_len;
  
  	/* keep the portal ip addr */
  	pg_key1->tag = PG_KEY2;
  	pg_key1->len = tmp->attr_len;
  	pg_key1->value.ip = (void *)&tmp->attr_value[0];
  	ip = tmp;
  
  	NEXT_TLV(tmp, tmp_len);
  	if (tmp_len > 8 &&
  	    tmp->attr_id == PORTAL_KEY2 &&
  	    tmp->attr_len == 4) {
  		/* keep the portal port */
  		pg_key2->tag = PG_KEY3;
  		pg_key2->len = tmp->attr_len;
  		pg_key2->value.ui = ntohl(*(uint32_t *)&tmp->attr_value[0]);
  
  		/* parse one portal object */
  		obj = obj_calloc(OBJ_PORTAL);
  		if (obj != NULL) {
  			/* copy ip addr attribute */
  			attr = &obj->attrs[
  			    ATTR_INDEX_PORTAL(ip->attr_id)];
  			ec = copy_attr(attr, ip);
  			/* copy port attribute */
  			if (ec == 0) {
  				attr = &obj->attrs[
  				    ATTR_INDEX_PORTAL(tmp->attr_id)];
  				ec = copy_attr(attr, tmp);
  			}
  			/* parse non-key attributes */
  			NEXT_TLV(tmp, tmp_len);
  			while (ec == 0 &&
  			    tmp_len >= 8 &&
  			    IS_PORTAL_ATTR(tmp->attr_id)) {
  				attr = &obj->attrs[
  				    ATTR_INDEX_PORTAL(
  				    tmp->attr_id)];
  				ec = copy_attr(attr, tmp);
  				NEXT_TLV(tmp, tmp_len);
  			}
  		} else {
  			/* no memory */
  			ec = ISNS_RSP_INTERNAL_ERROR;
  		}
  	} else {
  		/* ip address is not followed by port */
  		ec = ISNS_RSP_MSG_FORMAT_ERROR;
  	}
  
  	*p = obj;
  	*op = tmp;
  	*op_len = tmp_len;
  
  	return (ec);
  }
  
  /*
   * ****************************************************************************
   *
   * reg_get_pg:
   *	parse the Operating Attributes of the DevAttrReg message and
   *	create a Portal Group object.
   *
   * p	- the pointer of the object for returning.
   * op	- the operating attributes.
   * op_len - the length of the operating attributes.
   * return - error code.
   *
   * ****************************************************************************
   */
  static int
  reg_get_pg(
  	isns_obj_t **p,
  	isns_tlv_t **op,
  	uint16_t *op_len
  )
  {
  	int ec = 0;
  
  	isns_tlv_t *tmp;
  	uint16_t tmp_len;
  	isns_attr_t *attr;
  
  	isns_obj_t *obj = NULL;
  
  	tmp = *op;
  	tmp_len = *op_len;
  
  	/* parse a complete pg object */
  	obj = obj_calloc(OBJ_PG);
  	if (obj != NULL) {
  		/* parse attributes */
  		do {
  			attr = &obj->attrs[
  			    ATTR_INDEX_PG(tmp->attr_id)];
  			ec = copy_attr(attr, tmp);
  			NEXT_TLV(tmp, tmp_len);
  		} while (ec == 0 &&
  		    tmp_len >= 8 &&
  		    IS_PG_ATTR(tmp->attr_id));
  	} else {
  		ec = ISNS_RSP_INTERNAL_ERROR;
  	}
  
  	*p = obj;
  	*op = tmp;
  	*op_len = tmp_len;
  
  	return (ec);
  }
  
  /*
   * ****************************************************************************
   *
   * reg_get_pg1:
   *	parse the Operating Attributes of the DevAttrReg message and
   *	create a Portal Group object which is followed to a Portal object.
   *
   * p	- the pointer of the object for returning.
   * pgt	- the size-3 array of pointers which have the pg portal ip addr, port
   *	  and the pg tag attributes.
   * op	- the operating attributes.
   * op_len - the length of the operating attributes.
   * return - error code.
   *
   * ****************************************************************************
   */
  static int
  reg_get_pg1(
  	isns_obj_t **p,
  	isns_attr_t const *pgt,
  	isns_tlv_t **op,
  	uint16_t *op_len
  )
  {
  	int ec = 0;
  
  	isns_tlv_t *tmp;
  	uint16_t tmp_len;
  	isns_attr_t *attr;
  
  	isns_obj_t *obj = NULL;
  	int i = 0;
  
  	tmp = *op;
  	tmp_len = *op_len;
  
  	if (pgt[0].tag == PG_KEY2 &&
  	    pgt[1].tag == PG_KEY3) {
  		/* the pg iscsi storage node name is */
  		/* followed to a portal group tag */
  		obj = obj_calloc(OBJ_PG);
  		if (obj != NULL) {
  			/* copy pg iscsi storage node name */
  			attr = &obj->attrs[
  			    ATTR_INDEX_PG(tmp->attr_id)];
  			ec = copy_attr(attr, tmp);
  			/* copy pg ip addr, pg port & pgt */
  			while (ec == 0 && i < 3) {
  				attr = &obj->attrs[
  				    ATTR_INDEX_PG(pgt[i].tag)];
  				ec = assign_attr(attr, &pgt[i]);
  				i ++;
  			}
  			NEXT_TLV(tmp, tmp_len);
  		} else {
  			/* no memory */
  			ec = ISNS_RSP_INTERNAL_ERROR;
  		}
  	} else {
  		ec = ISNS_RSP_MSG_FORMAT_ERROR;
  	}
  
  	*p = obj;
  	*op = tmp;
  	*op_len = tmp_len;
  
  	return (ec);
  }
  
  /*
   * ****************************************************************************
   *
   * reg_get_pg2:
   *	parse the Operating Attributes of the DevAttrReg message and
   *	create a Portal Group object which is followed to a iSCSI
   *	Storage Node object.
   *
   * p	- the pointer of the object for returning.
   * pgt	- the size-3 array of pointers which have the pg iscsi storage
   *	  node name and the pg tag attributes.
   * op	- the operating attributes.
   * op_len - the length of the operating attributes.
   * return - error code.
   *
   * ****************************************************************************
   */
  static int
  reg_get_pg2(
  	isns_obj_t **p,
  	isns_attr_t const *pgt,
  	isns_tlv_t **op,
  	uint16_t *op_len
  )
  {
  	int ec = 0;
  
  	isns_tlv_t *tmp;
  	uint16_t tmp_len;
  	isns_attr_t *attr;
  
  	isns_obj_t *obj = NULL;
  	int i = 0;
  
  	isns_tlv_t *ip;
  
  	tmp = *op;
  	tmp_len = *op_len;
  
  	/* keep ip address */
  	ip = tmp;
  	NEXT_TLV(tmp, tmp_len);
  
  	if (tmp_len > 8 &&
  	    /* expect pg portal port */
  	    tmp->attr_id == PG_KEY3 &&
  	    tmp->attr_len == 4 &&
  	    /* expect pg tag */
  	    pgt[2].tag == PG_PGT &&
  	    /* expect pg iscsi storage node name only */
  	    pgt[1].tag == 0 &&
  	    pgt[0].tag == PG_KEY1) {
  		/* the pg portal ip addr & port is followed */
  		/* to a pg tag and we have the iscsi storage */
  		/* node parsed previously */
  		obj = obj_calloc(OBJ_PG);
  		if (obj != NULL) {
  			/* copy the pg ip addr */
  			attr = &obj->attrs[
  			    ATTR_INDEX_PG(ip->attr_id)];
  			ec = copy_attr(attr, ip);
  			/* copy the pg port */
  			if (ec == 0) {
  				attr = &obj->attrs[
  				    ATTR_INDEX_PG(tmp->attr_id)];
  				ec = copy_attr(attr, tmp);
  			}
  			/* copy pg iscsi storage node name & pgt */
  			while (ec == 0 && i < 3) {
  				attr = &obj->attrs[
  				    ATTR_INDEX_PG(pgt[i].tag)];
  				ec = assign_attr(attr, &pgt[i]);
  				i += 2;
  			}
  			NEXT_TLV(tmp, tmp_len);
  		} else {
  			ec = ISNS_RSP_INTERNAL_ERROR;
  		}
  	} else {
  		ec = ISNS_RSP_MSG_FORMAT_ERROR;
  	}
  
  	*p = obj;
  	*op = tmp;
  	*op_len = tmp_len;
  
  	return (ec);
  }
  
  /*
   * ****************************************************************************
   *
   * reg_get_obj:
   *	parse and create one object from the rest of Operating Attributes
   *	of the DevAttrReg message, the object can be iSCSI Storage Node,
   *	Portal or Portal Group.
   *
   * p	- the pointer of the object for returning.
   * pgt	- an attribute array with size 3, the elements are:
   *	  0: the first pg key attribute, it is either the name of an
   *	     iscsi storage node object or the ip addr of a portal object.
   *	  1: the second pg key attribute, i.e. the portal port.
   *	  2: the portal group tag attribute.
   * op	- the operating attributes.
   * op_len - the length of the operating attributes.
   * return - error code.
   *
   * ****************************************************************************
   */
  int
  reg_get_obj(
  	isns_obj_t **p,
  	isns_attr_t *pgt,
  	isns_tlv_t **op,
  	uint16_t *op_len
  )
  {
  	int ec = 0;
  
  	int derefd = 0;
  
  	uint32_t pg_tag;
  
  	if (*op_len == 0) {
  		*p = NULL;
  		return (0);
  	}
  
  	switch ((*op)->attr_id) {
  	case ISCSI_KEY:
  		ec = reg_get_iscsi(p, &pgt[0], op, op_len);
  		pgt[1].tag = 0;
  		pgt[2].tag = 0;
  		break;
  	case PORTAL_KEY1:
  		ec = reg_get_portal(p, &pgt[0], &pgt[1], op, op_len);
  		pgt[2].tag = 0;
  		break;
  	case PG_KEY1:
  		if (pgt[2].tag == PG_PGT) {
  			/* pg iscsi storage node name is */
  			/* followed to a pgt */
  			ec = reg_get_pg1(p, pgt, op, op_len);
  		} else {
  			/* a complete pg object */
  			ec = reg_get_pg(p, op, op_len);
  			pgt[0].tag = 0;
  			pgt[1].tag = 0;
  			pgt[2].tag = 0;
  		}
  		break;
  	case PG_KEY2:
  		/* pg portal ip addr is followed to a pgt */
  		ec = reg_get_pg2(p, pgt, op, op_len);
  		break;
  	case PG_PGT:
  		switch (pgt[0].tag) {
  		case 0:
  			/* portal group tag does not follow */
  			/* iscsi storage node or portal object */
  			*p = NULL;
  			ec = ISNS_RSP_MSG_FORMAT_ERROR;
  			break;
  		case PG_KEY1:
  		case PG_KEY2:
  			pgt[2].tag = PG_PGT;
  			pgt[2].len = (*op)->attr_len;
  			pg_tag = 0;
  			switch ((*op)->attr_len) {
  			case 4:
  				pg_tag = ntohl(*(uint32_t *)
  				    &(*op)->attr_value[0]);
  				/* FALLTHROUGH */
  			case 0:
  				pgt[2].value.ui = pg_tag;
  				break;
  			default:
  				*p = NULL;
  				ec = ISNS_RSP_MSG_FORMAT_ERROR;
  				break;
  			}
  			if (ec == 0) {
  				derefd = 1;
  				NEXT_TLV(*op, *op_len);
  				ec = reg_get_obj(p, pgt, op, op_len);
  			}
  			break;
  		default:
  			/* should never happen */
  			ASSERT(0);
  			*p = NULL;
  			ec = ISNS_RSP_INTERNAL_ERROR;
  			break;
  		}
  		break;
  	default:
  		*p = NULL;
  		ec = ISNS_RSP_MSG_FORMAT_ERROR;
  		break;
  	}
  
  	if (ec == 0 && derefd == 0) {
  		ec = update_deref_obj(*p);
  	}
  
  	if (ec != 0 && *p != NULL) {
  		free_one_object(*p);
  		*p = NULL;
  	}
  
  	return (ec);
  }
  
  /*
   * ****************************************************************************
   *
   * reg_auth_src:
   *	Authorize the source attribute the DevAttrReg message.
   *	The update can only performed by the node who has the owenership.
   *
   * p	- the pointer of the object for returning.
   * pgt	- an attribute array with size 3, the elements are:
   *	  0: the first pg key attribute, it is either the name of an
   *	     iscsi storage node object or the ip addr of a portal object.
   *	  1: the second pg key attribute, i.e. the portal port.
   *	  2: the portal group tag attribute.
   * op	- the operating attributes.
   * op_len - the length of the operating attributes.
   * return - error code.
   *
   * ****************************************************************************
   */
  int
  reg_auth_src(
  	isns_type_t type,
  	uint32_t uid,
  	uchar_t *src
  )
  {
  	lookup_ctrl_t lc;
  	uint32_t puid;
  
  	puid = is_parent_there(src);
  
  	if (TYPE_OF_PARENT[type] != 0) {
  		SET_UID_LCP(&lc, type, uid);
  		uid = cache_lookup(&lc, NULL, cb_get_parent);
  		type = TYPE_OF_PARENT[type];
  	}
  
  	if (uid != 0 && puid == 0) {
  		SET_UID_LCP(&lc, type, uid);
  		uid = cache_lookup(&lc, NULL, cb_node_child);
  	}
  
  	if (puid != uid) {
  		return (0);
  	}
  
  	return (1);
  }
  
  /*
   * ****************************************************************************
   *
   * is_obj_online:
   *	determine if the object is currently registered with the server.
   *
   * obj - the object being checked.
   * return - 0: not registered, otherwise registered.
   *
   * ****************************************************************************
   */
  int
  is_obj_online(
  	const isns_obj_t *obj
  )
  {
  	int online = 1;
  
  	switch (obj->type) {
  	case OBJ_ISCSI:
  		online = obj->attrs[ATTR_INDEX_ISCSI(
  		    ISNS_ISCSI_NODE_TYPE_ATTR_ID)].value.ui == 0 ? 0 : 1;
  		break;
  	default:
  		break;
  	}
  
  	return (online);
  }
  
  static int
  set_obj_offline(
  	isns_obj_t *obj
  )
  {
  	switch (obj->type) {
  	case OBJ_ISCSI:
  		obj->attrs[ATTR_INDEX_ISCSI(
  		    ISNS_ISCSI_NODE_TYPE_ATTR_ID)].value.ui = 0;
  		break;
  	default:
  		break;
  	}
  
  	return (0);
  }
  
  /*
   * ****************************************************************************
   *
   * assoc_clone:
   *	clone the association object.
   *
   * p - the object being cloned.
   * clone_flag - 0: the object is being removed;
   *		1: only the association is being removed.
   * return - the clone object.
   *
   * ****************************************************************************
   */
  void *
  assoc_clone(
  	void *p,
  	int clone_flag
  )
  {
  	isns_type_t type;
  	isns_obj_t *clone;
  	const isns_attr_t *src_attr;
  	isns_attr_t *dst_attr;
  	uint32_t id, op;
  	int i = 0;
  
  	const isns_obj_t *obj;
  	uint32_t dd_flag;
  	int online;
  
  	int state;
  
  	obj = (isns_obj_t *)p;
  
  	if (obj->type != OBJ_ISCSI) {
  		return (NULL);
  	}
  
  	dd_flag = (get_dd_id(get_obj_uid(obj), ISNS_DEFAULT_DD_ID) == 0) ?
  	    0 : 1;
  	online = is_obj_online(obj);
  
  	state = (clone_flag << 2) | (dd_flag  << 1) | online;
  
  	/* clone_flag	dd_flag	online	action		*/
  	/* 0		0	0	ASSERT(0)	*/
  	/* 0		0	1	NULL		*/
  	/* 0		1	0	itself		*/
  	/* 0		1	1	clone it	*/
  	/* 1		0	0	NULL		*/
  	/* 1		0	1	itself		*/
  	/* 1		1	0	itself		*/
  	/* 1		1	1	itself		*/
  
  	switch (state) {
  	case 0:
  		ASSERT(0);
  	case 1:
  	case 4:
  		return (NULL);
  	case 2:
  	case 5:
  	case 6:
  	case 7:
  		return (p);
  	case 3:
  	default:
  		break;
  	}
  
  	type = obj->type;
  	clone = obj_calloc(type);
  
  	if (clone != NULL) {
  		id = UID_ATTR_INDEX[type];
  		src_attr = &(obj->attrs[id]);
  		dst_attr = &(clone->attrs[id]);
  		if (assign_attr(dst_attr, src_attr) != 0) {
  			free_one_object(clone);
  			return (NULL);
  		}
  
  		while (i < MAX_KEY_ATTRS) {
  			op = KEY_ATTR_OP[type][i];
  			if (op != 0) {
  				id = KEY_ATTR_INDEX[type][i];
  				src_attr = &(obj->attrs[id]);
  				dst_attr = &(clone->attrs[id]);
  				if (assign_attr(dst_attr, src_attr) != 0) {
  					free_one_object(clone);
  					return (NULL);
  				}
  			} else {
  				break;
  			}
  			i ++;
  		}
  	}
  
  	return ((void *)clone);
  }
  
  /*
   * ****************************************************************************
   *
   * free_one_object:
   *	free up one object.
   *
   * obj - the object being freed.
   *
   * ****************************************************************************
   */
  void
  free_one_object(
  	isns_obj_t *obj
  )
  {
  	int i;
  	uint32_t *cuid;
  	if (obj == NULL) {
  		return;
  	}
  	for (i = 0; i < NUM_OF_ATTRS[obj->type]; i++) {
  		isns_attr_t *attr = &obj->attrs[i];
  		switch (attr->tag) {
  			case ISNS_EID_ATTR_ID:
  			case ISNS_ISCSI_NAME_ATTR_ID:
  			case ISNS_ISCSI_ALIAS_ATTR_ID:
  			case ISNS_ISCSI_AUTH_METHOD_ATTR_ID:
  			case ISNS_PG_ISCSI_NAME_ATTR_ID:
  			case ISNS_PORTAL_IP_ADDR_ATTR_ID:
  			case ISNS_PORTAL_NAME_ATTR_ID:
  			case ISNS_PG_PORTAL_IP_ADDR_ATTR_ID:
  			case ISNS_DD_SET_NAME_ATTR_ID:
  			case ISNS_DD_NAME_ATTR_ID:
  			case ISNS_DD_ISCSI_NAME_ATTR_ID:
  			case ISNS_DD_FC_PORT_NAME_ATTR_ID:
  			case ISNS_DD_PORTAL_IP_ADDR_ATTR_ID:
  #ifdef DEBUG
  				if (verbose_mc) {
  					printf("memory(%d) deallocated\n",
  					    attr->len);
  				}
  #endif
  				free(attr->value.ptr);
  				attr->value.ptr = NULL;
  				break;
  			default:
  				break;
  		}
  	}
  
  	/* free child uids */
  	i = 0;
  	while (i < NUM_OF_CHILD[obj->type]) {
  		cuid = get_child_n(obj, i);
  		free(cuid);
  		i ++;
  	}
  
  	/* at last, free the object itself */
  #ifdef DEBUG
  	if (verbose_mc) {
  		printf("object(%d) deallocated\n", obj->type);
  	}
  #endif
  	free(obj);
  }
  
  /*
   * ****************************************************************************
   *
   * free_object:
   *	free up one object.
   *
   * obj - the object being freed.
   *
   * ****************************************************************************
   */
  void
  free_object(
  	isns_obj_t *obj
  )
  {
  	free_one_object(obj);
  }
  
  /*
   * ****************************************************************************
   *
   * set_parent_obj:
   *	set the parent object UID.
   *
   * obj - the child object.
   * puid- the parent object UID.
   * return - error code.
   *
   * ****************************************************************************
   */
  int
  set_parent_obj(
  	isns_obj_t *obj,
  	uint32_t puid
  )
  {
  	uint32_t *const p = get_parent_p(obj);
  	if (p != NULL) {
  		*p = puid;
  	}
  
  	return (0);
  }
  
  /*
   * ****************************************************************************
   *
   * buff_child_obj:
   *	add a child object UID to the child object array.
   *
   * obj - the parent object.
   * child_type - the type of the child object.
   * number  - the number of the child object.
   * return - the length of the child object UID array.
   *
   * ****************************************************************************
   */
  int
  buff_child_obj(
  	const isns_type_t ptype,
  	const isns_type_t ctype,
  	const void *c,
  	void const ***child
  )
  {
  	int ec = 0;
  
  	int i = 0;
  	void const ***pp, **p;
  	uint32_t num, new_num;
  
  	pp = NULL;
  	/* get the pointer of the array which the child belongs to */
  	while (i < NUM_OF_CHILD[ptype]) {
  		if (TYPE_OF_CHILD[ptype][i] == ctype) {
  			pp = &child[i];
  			break;
  		}
  		i ++;
  	}
  
  	/* the child type is not applicable */
  	if (pp == NULL) {
  		return (ec);
  	}
  
  	p = *pp;
  	/* get an empty slot from the uid array for this child */
  	if (p != NULL) {
  		num = (uint32_t)*p;
  		i = 0;
  		while (i < num) {
  			if (p[++i] == NULL) {
  				/* found it */
  				p[i] = c;
  				return (ec);
  			}
  		}
  		p = *pp;
  		new_num = num + 1;
  	} else {
  		num = 0;
  		new_num = 1;
  	}
  
  	/* the array is full, enlarge the child uid array */
  	p = (void const **)realloc(p, (new_num + 1) * sizeof (void *));
  	if (p != NULL) {
  		*pp = p;
  		*p = (void *)new_num;
  		p[new_num] = c;
  	} else {
  		ec = ISNS_RSP_INTERNAL_ERROR;
  	}
  
  	return (ec);
  }
  
  /*
   * ****************************************************************************
   *
   * update_child_object:
   *	update the child object of a network entity object.
   *
   * puid - the UID of the parent object, i.e. the network entity object.
   * child_type - the type of the child object.
   * child_uid  - the uid of the child object.
   * return - error code.
   *
   * ****************************************************************************
   */
  int
  update_child_obj(
  	const isns_type_t ptype,
  	const uint32_t puid,
  	void const ***child,
  	int child_flag
  )
  {
  	int ec = 0;
  
  	lookup_ctrl_t lc;
  
  	SET_UID_LCP(&lc, ptype, puid);
  
  	lc.data[1].ptr = (uchar_t *)child;
  	lc.data[2].ui = child_flag;
  
  	ec = cache_lookup(&lc, NULL, cb_add_child);
  
  	return (ec);
  }
  
  int
  update_ref_obj(
  	const isns_obj_t *obj
  )
  {
  	uint32_t uid;
  	lookup_ctrl_t lc;
  	isns_type_t t;
  
  	t = obj->type;
  
  	if (TYPE_OF_REF[t][0] != 0) {
  		(void) setup_ref_lcp(&lc, obj, NULL);
  
  		lc.id[2] = t;
  		lc.data[2].ui = get_obj_uid(obj);
  
  		uid = 0;
  		do {
  			lc.curr_uid = uid;
  			(void) cache_lookup(&lc, &uid, cb_set_ref);
  		} while (uid != 0);
  	}
  
  	return (0);
  }
  
  /*
   * ****************************************************************************
   *
   * verify_ref_obj:
   *	update the reference bit of a portal group object.
   *
   * obj - the object being ref'ed.
   * return - error code.
   *
   * ****************************************************************************
   */
  int
  verify_ref_obj(
  	const isns_type_t ptype,
  	const uint32_t puid,
  	void const ***child
  )
  {
  	int ec = 0;
  
  	lookup_ctrl_t lc;
  
  	SET_UID_LCP(&lc, ptype, puid);
  
  	lc.data[1].ptr = (uchar_t *)child;
  
  	ec = cache_lookup(&lc, NULL, cb_verify_ref);
  
  	return (ec);
  }
  
  int
  update_deref_obj(
  	isns_obj_t *obj
  )
  {
  	int ec = 0;
  
  	isns_type_t t, rt;
  	lookup_ctrl_t lc;
  	int i, ref_count;
  
  	uint32_t uid, *refp;
  
  	t = obj->type;
  	i = ref_count = 0;
  	while (i < NUM_OF_REF[t]) {
  		rt = TYPE_OF_REF[t][i + 1];
  		(void) setup_deref_lcp(&lc, obj, rt);
  		uid = is_obj_there(&lc);
  		if (uid != 0) {
  			refp = get_ref_p(obj, lc.type);
  			*refp = uid;
  			ref_count ++;
  		}
  		i ++;
  	}
  
  	if (i > 0 && ref_count == 0) {
  		ec = ISNS_RSP_INVALID_REGIS;
  	}
  
  	return (ec);
  }
  
  /*
   * ****************************************************************************
   *
   * register_object:
   *	add one object to the object container.
   *
   * obj	- the object being added.
   * uid_p- the pointer for returning object UID.
   * update_p- the pointer for returning flag which indicates if the object
   *		is newly registered or updated with an existing one.
   * return - error code.
   *
   * ****************************************************************************
   */
  int
  register_object(
  	isns_obj_t *obj,
  	uint32_t *uid_p,
  	int *update_p
  )
  {
  	return (cache_add(obj, 0, uid_p, update_p));
  }
  
  /*
   * ****************************************************************************
   *
   * register_assoc:
   *	add one association object to the object container, the association
   *	object has only the information for discovery domain membership, i.e.
   *	a name and UID only.
   *
   * obj	- the association object being added.
   * uid_p- the pointer for returning object UID.
   * return - error code.
   *
   * ****************************************************************************
   */
  int
  register_assoc(
  	isns_obj_t *obj,
  	uint32_t *uid_p
  )
  {
  	return (cache_add(obj, 1, uid_p, NULL));
  }
  
  /*
   * ****************************************************************************
   *
   * is_obj_there:
   *	check if the object is registered or not.
   *
   * lcp	- the lookup control data.
   * return - the object UID.
   *
   * ****************************************************************************
   */
  uint32_t
  is_obj_there(
  	lookup_ctrl_t *lcp
  )
  {
  	uint32_t uid;
  
  	(void) cache_lookup(lcp, &uid, NULL);
  
  	return (uid);
  }
  
  uint32_t
  is_parent_there(
  	uchar_t *src
  )
  {
  	lookup_ctrl_t lc;
  
  	lc.curr_uid = 0;
  	lc.type = OBJ_ISCSI;
  	lc.id[0] = ATTR_INDEX_ISCSI(ISNS_ISCSI_NAME_ATTR_ID);
  	lc.op[0] = OP_STRING;
  	lc.data[0].ptr = src;
  	lc.op[1] = 0;
  
  	return (cache_lookup(&lc, NULL, cb_get_parent));
  }
  
  /*
   * ****************************************************************************
   *
   * setup_ref_lcp:
   *	prepare the lookup control data for looking up a portal group
   *	object which references to a iscsi stroage node and/or a portal
   *	object.
   *
   * lcp	- the lookup control data.
   * iscsi- the ref'ed iscsi storage node object.
   * portal- the ref'ed portal object.
   * return - error code.
   *
   * ****************************************************************************
   */
  static int
  setup_ref_lcp(
  	lookup_ctrl_t *lcp,
  	const isns_obj_t *iscsi,
  	const isns_obj_t *portal
  )
  {
  	int i = 0, j = 0;
  
  	lcp->curr_uid = 0;
  	lcp->type = TYPE_OF_REF[iscsi->type][0];
  
  	/* extrace the matching attributes from iscsi storage node object */
  	while (iscsi != NULL &&
  	    i < MAX_REF_MATCH &&
  	    REF_MATCH_OPS[iscsi->type][i] > 0) {
  		lcp->id[i] = REF_MATCH_ID2[iscsi->type][i];
  		lcp->op[i] = REF_MATCH_OPS[iscsi->type][i];
  		lcp->data[i].ptr = iscsi->attrs[
  		    REF_MATCH_ID1[iscsi->type][i]].value.ptr;
  		i ++;
  	}
  
  	/* extrace the matching attributes from portal object */
  	while (portal != NULL &&
  	    i < MAX_LOOKUP_CTRL &&
  	    j < MAX_REF_MATCH &&
  	    REF_MATCH_OPS[portal->type][j] > 0) {
  		lcp->id[i] = REF_MATCH_ID2[portal->type][j];
  		lcp->op[i] = REF_MATCH_OPS[portal->type][j];
  		lcp->data[i].ptr = portal->attrs[
  		    REF_MATCH_ID1[portal->type][j]].value.ptr;
  		j ++;
  		i ++;
  	}
  
  	if (i < MAX_LOOKUP_CTRL) {
  		lcp->op[i] = 0;
  	}
  
  	return (0);
  }
  
  static int
  setup_deref_lcp(
  	lookup_ctrl_t *lcp,
  	const isns_obj_t *pg,
  	isns_type_t t
  )
  {
  	int i = 0;
  
  	lcp->curr_uid = 0;
  	lcp->type = t;
  
  	/* extrace the matching attributes from iscsi storage node object */
  	while (i < MAX_REF_MATCH &&
  	    REF_MATCH_OPS[t][i] > 0) {
  		lcp->id[i] = REF_MATCH_ID1[t][i];
  		lcp->op[i] = REF_MATCH_OPS[t][i];
  		lcp->data[i].ptr = pg->attrs[
  		    REF_MATCH_ID2[t][i]].value.ptr;
  		i ++;
  	}
  
  	if (i < MAX_LOOKUP_CTRL) {
  		lcp->op[i] = 0;
  	}
  
  	return (0);
  }
  
  /*
   * ****************************************************************************
   *
   * setup_parent_lcp:
   *	prepare the lookup control data for looking up parent object
   *	with a child object.
   *
   * lcp	- the lookup control data.
   * obj	- the child object.
   * return - parent object UID.
   *
   * ****************************************************************************
   */
  static uint32_t
  setup_parent_lcp(
  	lookup_ctrl_t *lcp,
  	isns_obj_t *obj
  )
  {
  	isns_type_t ptype;
  	uint32_t puid;
  
  	puid = get_parent_uid(obj);
  	if (puid != 0) {
  		ptype = TYPE_OF_PARENT[obj->type];
  		SET_UID_LCP(lcp, ptype, puid);
  		lcp->data[1].ui = obj->type;
  		lcp->data[2].ui = get_obj_uid(obj);
  	}
  
  	return (puid);
  }
  
  static int
  cb_get_parent(
  	void *p1,
  	/* LINTED E_FUNC_ARG_UNUSED */
  	void *p2
  )
  {
  	return (get_parent_uid(p1));
  }
  
  static int
  cb_node_child(
  	void *p1,
  	/* LINTED E_FUNC_ARG_UNUSED */
  	void *p2
  )
  {
  	isns_obj_t *obj = (isns_obj_t *)p1;
  
  	uint32_t num, uid;
  
  	uint32_t *cuid = get_child_t(obj, OBJ_ISCSI);
  
  	if (cuid != NULL) {
  		num = *cuid;
  	} else {
  		num = 0;
  	}
  
  	while (num > 0) {
  		uid = *++cuid;
  		if (uid != 0) {
  			return (uid);
  		}
  		num --;
  	}
  
  	return (0);
  }
  
  /*
   * ****************************************************************************
   *
   * cb_set_ref:
   *	callback function which sets the reference bit to 1 according to
   *	the type of object.
   *
   * p1	- the object.
   * p2	- the lcp.
   * return - error code.
   *
   * ****************************************************************************
   */
  static int
  cb_set_ref(
  	void *p1,
  	void *p2
  )
  {
  	isns_obj_t *obj = (isns_obj_t *)p1;
  	lookup_ctrl_t *lcp = (lookup_ctrl_t *)p2;
  
  	isns_type_t t;
  	uint32_t u;
  
  	uint32_t *refp;
  
  	t = lcp->id[2];
  	u = lcp->data[2].ui;
  	refp = get_ref_p(obj, t);
  	*refp = u;
  
  	/* successful */
  	return (0);
  }
  
  /*
   * ****************************************************************************
   *
   * cb_clear_ref:
   *	callback function which clears the reference bit according to
   *	the type of object.
   *
   * p1	- the object.
   * p2	- the lcp.
   * return - 1: the object is no longer ref'ed, 0: otherwise.
   *
   * ****************************************************************************
   */
  static int
  cb_clear_ref(
  	void *p1,
  	void *p2
  )
  {
  	isns_obj_t *obj = (isns_obj_t *)p1;
  	lookup_ctrl_t *lcp = (lookup_ctrl_t *)p2;
  
  	isns_type_t t;
  	uint32_t *refp;
  
  	int i = 0;
  	uint32_t ref;
  
  	t = lcp->data[2].ui;
  	refp = get_ref_p(obj, t);
  	*refp = 0;
  
  	while (i < NUM_OF_REF[obj->type]) {
  		ref = get_ref_n(obj, i);
  		if (ref != 0) {
  			return (0);
  		}
  		i ++;
  	}
  
  	return (1);
  }
  
  static int
  cb_add_child(
  	void *p1,
  	void *p2
  )
  {
  	isns_obj_t *obj = (isns_obj_t *)p1;
  	lookup_ctrl_t *lcp = (lookup_ctrl_t *)p2;
  
  	const void ***child;
  	const void **vpp;
  	uint32_t vnum;
  	int child_flag;
  
  	uint32_t **upp, *up;
  	uint32_t num;
  
  	isns_obj_t *o;
  
  	int i = 0;
  
  	child = (const void ***)lcp->data[1].ptr;
  	child_flag = lcp->data[2].ui;
  
  	while (i < NUM_OF_CHILD[obj->type]) {
  		vpp = child[i];
  		if (vpp != NULL &&
  		    (vnum = (uint32_t)*vpp) > 0 &&
  		    *(vpp + 1) != NULL) {
  			upp = get_child_np(obj, i);
  			if (*upp == NULL) {
  				if (child_flag == 0 &&
  				    sizeof (typeof (**upp)) ==
  				    sizeof (typeof (**child))) {
  					*upp = (uint32_t *)vpp;
  					vpp = NULL;
  					child[i] = NULL;
  				}
  				num = vnum;
  			} else {
  				num = **upp + vnum;
  			}
  			if (vpp != NULL) {
  				/* copy required */
  				up = (uint32_t *)realloc(*upp,
  				    (num + 1) * sizeof (uint32_t));
  				if (up == NULL) {
  					return (ISNS_RSP_INTERNAL_ERROR);
  				}
  				*upp = up;
  				*up = num;
  				up += num;
  				vpp += vnum;
  				while (vnum > 0) {
  					if (*vpp == NULL) {
  						*up = 0;
  					} else if (child_flag == 0) {
  						*up = (uint32_t)*vpp;
  						*vpp = NULL;
  					} else {
  						o = (isns_obj_t *)*vpp;
  						*up = get_obj_uid(o);
  						if (is_obj_online(o) == 0) {
  							free_object(o);
  						}
  						*vpp = NULL;
  					}
  					up --;
  					vpp --;
  					vnum --;
  				}
  			}
  		}
  		i ++;
  	}
  
  	return (0);
  }
  
  /*
   * ****************************************************************************
   *
   * cb_remove_child:
   *	callback function which removes a child object UID from the
   *	children objet UID array of the parent object.
   *
   * p1	- the object.
   * p2	- the lcp.
   * return - 1: no more such type of child object, 0: otherwise.
   *
   * ****************************************************************************
   */
  static int
  cb_remove_child(
  	void *p1,
  	void *p2
  )
  {
  	isns_obj_t *obj = (isns_obj_t *)p1;
  	lookup_ctrl_t *lcp = (lookup_ctrl_t *)p2;
  	uint32_t child_type = lcp->data[1].ui;
  	uint32_t child_uid = lcp->data[2].ui;
  	uint32_t *cuidp, cuid, num_of_child = 0;
  	int i;
  
  	/* get the children object UID array */
  	cuidp = get_child_t(obj, child_type);
  	if (cuidp != NULL) {
  		num_of_child = *cuidp;
  	}
  
  	/* remove it */
  	while (num_of_child > 0) {
  		cuid = *++cuidp;
  		if (cuid == child_uid) {
  			*cuidp = 0;
  			break;
  		}
  		num_of_child --;
  	}
  
  	/* check if all of child object UIDs are removed */
  	i = 0;
  	while (i < NUM_OF_CHILD[obj->type]) {
  		cuidp = get_child_n(obj, i);
  		if (cuidp != NULL) {
  			num_of_child = *cuidp;
  			while (num_of_child > 0) {
  				cuid = *++cuidp;
  				if (cuid != 0) {
  					return (0);
  				}
  				num_of_child --;
  			}
  		}
  		i ++;
  	}
  
  	return (1);
  }
  
  static int
  cb_verify_ref(
  	void *p1,
  	void *p2
  )
  {
  	int ec = 0;
  
  	isns_obj_t *parent = (isns_obj_t *)p1;
  	lookup_ctrl_t *lcp = (lookup_ctrl_t *)p2;
  
  	const void ***child;
  
  	const void **vpp;
  	const void *vp;
  	uint32_t vnum;
  
  	const void **evpp;
  	const void *evp;
  	uint32_t evnum;
  
  	isns_type_t pt; /* parent object type */
  	isns_type_t ct; /* child object type */
  	isns_type_t rt; /* ref object type */
  	isns_type_t et; /* peer object type */
  
  	uint32_t *up;
  	uint32_t u;
  	uint32_t unum;
  
  	lookup_ctrl_t lc;
  	uint8_t flag[MAX_OBJ_TYPE + 1] = { 0 };
  
  	int i, j, k;
  
  	pt = parent->type;
  
  	child = (const void ***)lcp->data[1].ptr;
  
  	for (i = 0; i < NUM_OF_CHILD[pt]; i++) {
  		ct = TYPE_OF_CHILD[pt][i];
  		rt = TYPE_OF_REF[ct][0];
  		if (rt == 0) {
  			continue;
  		}
  
  		et = TYPE_OF_REF[ct][1];
  		vpp = child[i];
  		if (vpp != NULL) {
  			vnum = (uint32_t)*vpp;
  			up = get_child_t(parent, et);
  			if (up != NULL) {
  				unum = *up;
  			} else {
  				unum = 0;
  			}
  		} else {
  			vnum = 0;
  		}
  
  		j = vnum;
  		while (j > 0) {
  			vp = vpp[j];
  			if (vp != NULL) {
  				(void) setup_ref_lcp(&lc, vp, NULL);
  				k = unum;
  				while (k > 0) {
  					u = up[k];
  					if (u != 0) {
  						ec = ref_new2old(
  						    &lc, et, u, vp);
  						if (ec != 0) {
  							return (ec);
  						}
  					}
  					k --;
  				} /* End of while each unum */
  			}
  			j --;
  		} /* End of while each vnum */
  
  		if (flag[ct] != 0) {
  			continue;
  		}
  
  		evnum = 0;
  		j = 0;
  		while (j < NUM_OF_CHILD[pt]) {
  			if (TYPE_OF_CHILD[pt][j] == et) {
  				evpp = child[j];
  				if (evpp != NULL) {
  					evnum = (uint32_t)*evpp;
  				}
  				break;
  			}
  			j ++;
  		}
  
  		j = vnum;
  		while (j > 0) {
  			vp = vpp[j];
  			k = evnum;
  			while (k > 0) {
  				evp = evpp[k];
  				if (vp != NULL && evp != NULL) {
  					(void) setup_ref_lcp(&lc, vp, evp);
  					ec = ref_new2new(&lc, vp, evp);
  					if (ec != 0) {
  						return (ec);
  					}
  				}
  				k --;
  			}
  			j --;
  		} /* End of while each vnum */
  
  		flag[et] = 1;
  	} /* End of for each type of child */
  
  	return (ec);
  }
  
  static int
  cb_ref_new2old(
  	void *p1,
  	void *p2
  )
  {
  	isns_obj_t *obj = (isns_obj_t *)p1;
  	lookup_ctrl_t *lcp = (lookup_ctrl_t *)p2;
  
  	isns_type_t et;
  	uint32_t uu;
  
  	uint32_t ref;
  
  	int match;
  
  	et = lcp->id[2];
  	uu = lcp->data[2].ui;
  
  	ref = get_ref_t(obj, et);
  
  	if (ref == uu) {
  		match = 1;
  	} else {
  		match = 0;
  	}
  
  	return (match);
  }
  
  static int
  cb_new_ref(
  	void *p1,
  	void *p2
  )
  {
  	int ec = 0;
  
  	lookup_ctrl_t *lcp = (lookup_ctrl_t *)p2;
  	isns_obj_t *a = (isns_obj_t *)p1;
  	isns_obj_t *b = (isns_obj_t *)lcp->data[2].ptr;
  
  	ec = new_ref(a, b);
  
  	return (ec);
  }
  
  static int
  ref_new2old(
  	lookup_ctrl_t *lcp,
  	isns_type_t et,
  	uint32_t uu,
  	const isns_obj_t *vp
  )
  {
  	int ec = 0;
  
  	int match;
  	uint32_t uid;
  
  	lookup_ctrl_t lc;
  
  	lcp->id[2] = et;
  	lcp->data[2].ui = uu;
  
  	uid = 0;
  	do {
  		lcp->curr_uid = uid;
  		match = cache_lookup(lcp, &uid, cb_ref_new2old);
  	} while (match == 0 && uid != 0);
  
  	if (match == 0) {
  		/* no such ref, create a default one */
  		SET_UID_LCP(&lc, et, uu);
  
  		lc.data[2].ptr = (uchar_t *)vp;
  
  		ec = cache_lookup(&lc, NULL, cb_new_ref);
  	}
  
  	return (ec);
  }
  
  static int
  ref_new2new(
  	lookup_ctrl_t *lcp,
  	const isns_obj_t *p1,
  	const isns_obj_t *p2
  )
  {
  	int ec = 0;
  
  	if (is_obj_there(lcp) != 0) {
  		return (0);
  	}
  
  	ec = new_ref(p1, p2);
  
  	return (ec);
  }
  
  static int
  new_ref(
  	const isns_obj_t *p1,
  	const isns_obj_t *p2
  )
  {
  	int ec = 0;
  
  	isns_obj_t *obj;
  
  	obj = make_ref[p1->type](p1, p2);
  	if (obj != NULL) {
  		ec = register_object(obj, NULL, NULL);
  	} else {
  		ec = ISNS_RSP_INTERNAL_ERROR;
  	}
  
  	return (ec);
  }
  
  /*
   * ****************************************************************************
   *
   * do_dereg:
   *	Physically remove an object along with the children objects,
   *	the reference object and the parent object recursively.
   *	Apporiate SCN is triggered.
   *
   * lcp	- the lookup control for the object being removed.
   * parent_flag	- 1: the object being removed is the parent object;
   *		  0: otherwise.
   * child_flag	- 1: the object being removed is a child object;
   *		  0: otherwise.
   * pending	- 1: do not remove the ESI entry immediately;
   *		  0: remove the ESI entry without any delay.
   * return - error code.
   *
   * ****************************************************************************
   */
  static int
  do_dereg(
  	lookup_ctrl_t *lcp,
  	int parent_flag,
  	int child_flag,
  	int pending
  )
  {
  	int ec = 0;
  
  	isns_obj_t *obj;
  	uint32_t *cuidp, num;
  	isns_type_t type;
  	uint32_t uid;
  	int i;
  
  	/* remove the object from object container */
  	obj = cache_remove(lcp, 0);
  
  	if (obj == NULL) {
  		return (0);
  	}
  
  	/* trigger a scn */
  	if (scn_q != NULL) {
  		(void) make_scn(ISNS_OBJECT_REMOVED, obj);
  	}
  
  	/* dereg children */
  	i = 0;
  	while (ec == 0 && !parent_flag &&
  	    i < NUM_OF_CHILD[obj->type]) {
  		type = TYPE_OF_CHILD[obj->type][i];
  		cuidp = get_child_n(obj, i);
  		if (cuidp != NULL) {
  			num = *cuidp;
  		} else {
  			num = 0;
  		}
  		while (ec == 0 && num > 0) {
  			uid = cuidp[num];
  			if (uid != 0) {
  				SET_UID_LCP(lcp, type, uid);
  				ec = do_dereg(lcp,
  				    parent_flag,
  				    1,
  				    pending);
  			}
  			num --;
  		}
  		i ++;
  	}
  
  	/* clear the ref bit on the ref'd object */
  	if (ec == 0 && TYPE_OF_REF[obj->type][0] > 0) {
  		uid = 0;
  		do {
  			(void) setup_ref_lcp(lcp, obj, NULL);
  			lcp->curr_uid = uid;
  			lcp->data[2].ui = obj->type;
  			if (cache_lookup(lcp, &uid, cb_clear_ref) != 0) {
  				UPDATE_LCP_UID(lcp, uid);
  				ec = do_dereg(lcp,
  				    parent_flag,
  				    child_flag,
  				    pending);
  			}
  		} while (uid != 0);
  	}
  
  	/* remove it from the parent */
  	if (ec == 0 && !child_flag &&
  	    TYPE_OF_PARENT[obj->type] > 0 &&
  	    (uid = setup_parent_lcp(lcp, obj)) != 0) {
  		if (cache_lookup(lcp, NULL, cb_remove_child) != 0) {
  			UPDATE_LCP_UID(lcp, uid);
  			ec = do_dereg(lcp,
  			    1,
  			    child_flag,
  			    0);
  		}
  	}
  
  	if (ec == 0 && !child_flag) {
  		/* remove it from persistent data store */
  		if (sys_q) {
  			ec = write_data(DATA_DELETE, obj);
  		}
  		/* remove esi event entry */
  		if (ec == 0) {
  			(void) esi_remove_obj(obj, pending);
  		}
  
  		/* save the parent uid for caller */
  		if (TYPE_OF_PARENT[obj->type] != 0) {
  			lcp->curr_uid = get_parent_uid(obj);
  		} else {
  			/* it's the parent itself */
  			lcp->curr_uid = get_obj_uid(obj);
  		}
  	}
  
  	/* remove this portal from scn registry */
  	if (ec == 0 &&
  	    obj->type == OBJ_PORTAL) {
  		(void) remove_scn_portal(get_obj_uid(obj));
  	}
  
  	/* free the object */
  	(void) free_object(obj);
  
  	return (ec);
  }
  
  /*
   * ****************************************************************************
   *
   * dereg_assoc:
   *	Remove one association object from object container.
   *
   * lcp	- the lookup control for the object being removed.
   * return - error code.
   *
   * ****************************************************************************
   */
  int
  dereg_assoc(
  	lookup_ctrl_t *lcp
  )
  {
  	isns_obj_t *obj;
  
  	obj = cache_remove(lcp, 1);
  
  	/* free the object */
  	if (obj != NULL) {
  		free_object(obj);
  	}
  
  	return (0);
  }
  
  /*
   * ****************************************************************************
   *
   * dereg_object:
   *	Remove one object from object container.
   *
   * lcp	- the lookup control for the object being removed.
   * return - error code.
   *
   * ****************************************************************************
   */
  int
  dereg_object(
  	lookup_ctrl_t *lcp,
  	int pending
  )
  {
  	return (do_dereg(lcp, 0, 0, pending));
  }
  
  /*
   * ****************************************************************************
   *
   * data_sync:
   *	Synchronize the cache with persistent data store.
   *	Flush the cache data to data store if the input ec is zero,
   *	retreat the changes in cache and ignore data store update
   *	if there is an error.
   *
   * ec	- error code.
   * return - error code.
   *
   * ****************************************************************************
   */
  int
  data_sync(
  	int ec
  )
  {
  	/* cache is updated successfully, commit the data to data store */
  	if (IS_CACHE_UPDATED()) {
  		if (ec == 0) {
  			ec = write_data(DATA_COMMIT, NULL);
  		}
  		if (ec == 0) {
  			/* successful, trigger the SCN */
  			(void) queue_msg_set(scn_q, SCN_TRIGGER, (void *)NULL);
  		} else {
  			shutdown_server();
  		}
  	} else {
  		/* ignore all SCNs which have been generated */
  		(void) queue_msg_set(scn_q, SCN_IGNORE, (void *)NULL);
  
  		(void) write_data(DATA_RETREAT, NULL);
  	}
  
  	return (ec);
  }
  
  static pthread_mutex_t name_mtx[3] = {
  	PTHREAD_MUTEX_INITIALIZER,
  	PTHREAD_MUTEX_INITIALIZER,
  	PTHREAD_MUTEX_INITIALIZER
  };
  static const char *name_pattern[3] = {
  	"ENTITY_ID_%d",
  	"DD_%d",
  	"DD-Set_%d"
  };
  static uint32_t name_count[3] = {
  	0,
  	0,
  	0
  };
  
  /*
   * ****************************************************************************
   *
   * make_unique_name:
   *	make a default unique name for a newly registered network entity,
   *	discovery domain or discovery domain set object.
   *
   * len	- pointer of the length of the new name for returning.
   * tag	- which attribute of the new name is for.
   * return - the name being made.
   *
   * ****************************************************************************
   */
  static char *
  make_unique_name(
  	int *len,
  	uint32_t tag
  )
  {
  	int i;
  	int count;
  	char name[32] = { 0 };
  
  	char *p;
  
  	lookup_ctrl_t lc;
  
  	lc.curr_uid = 0;
  
  	switch (tag) {
  	case ISNS_EID_ATTR_ID:
  		i = 0;
  		lc.type = OBJ_ENTITY;
  		lc.id[0] = ATTR_INDEX_ENTITY(ISNS_EID_ATTR_ID);
  		break;
  	case ISNS_DD_NAME_ATTR_ID:
  		i = 1;
  		lc.type = OBJ_DD;
  		lc.id[0] = ATTR_INDEX_DD(ISNS_DD_NAME_ATTR_ID);
  		break;
  	case ISNS_DD_SET_NAME_ATTR_ID:
  		i = 2;
  		lc.type = OBJ_DDS;
  		lc.id[0] = ATTR_INDEX_DDS(ISNS_DD_SET_NAME_ATTR_ID);
  		break;
  	default:
  		ASSERT(0);
  		break;
  	}
  
  	lc.op[0] = OP_STRING;
  	lc.op[1] = 0;
  	do {
  		(void) pthread_mutex_lock(&name_mtx[i]);
  		count = ++ name_count[i];
  		(void) pthread_mutex_unlock(&name_mtx[i]);
  		/* no more space, failure */
  		if (count == 0) {
  			return (NULL);
  		}
  		(void) sprintf(name, name_pattern[i], count);
  		lc.data[0].ptr = (uchar_t *)name;
  	} while (is_obj_there(&lc) != 0);
  
  	/* 4-bytes aligned length */
  	*len = strlen(name);
  	*len = *len + (4 - *len % 4);
  	p = (char *)malloc(*len);
  	if (p != NULL) {
  		(void) strcpy(p, name);
  	}
  	return (p);
  }
  
  #ifdef DEBUG
  void
  obj_dump(
  	void *p
  )
  {
  	print_object(NULL, (isns_obj_t *)p);
  }
  #endif
  