root/usr/src/cmd/isns/isnsd/obj.c
/*
 * 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