root/usr/src/cmd/isns/isnsd/scn.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 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "isns_server.h"
#include "isns_msgq.h"
#include "isns_cache.h"
#include "isns_cfg.h"
#include "isns_obj.h"
#include "isns_dseng.h"
#include "isns_log.h"
#include "isns_scn.h"
#include "isns_pdu.h"

/*
 * global variables.
 */

/*
 * local variables.
 */
static scn_registry_t *scn_registry = NULL;
static int scn_dispatched = 0;

/*
 * external variables.
 */
extern uint8_t mgmt_scn;
extern msg_queue_t *sys_q;
extern msg_queue_t *scn_q;
extern const int UID_ATTR_INDEX[MAX_OBJ_TYPE_FOR_SIZE];

#ifdef DEBUG
extern void dump_pdu1(isns_pdu_t *);
#endif

static int sf_gen(scn_raw_t *);
static int sf_error(scn_raw_t *);

static scn_raw_t *make_raw_entity(isns_obj_t *);
static scn_raw_t *make_raw_iscsi(isns_obj_t *);
static scn_raw_t *make_raw_portal(isns_obj_t *);
static scn_raw_t *make_raw_assoc_iscsi(isns_obj_t *);
static scn_raw_t *make_raw_assoc_dd(isns_obj_t *);
static scn_raw_t *(*const make_raw[MAX_OBJ_TYPE_FOR_SIZE])(isns_obj_t *) = {
        NULL,
        &make_raw_entity,
        &make_raw_iscsi,
        &make_raw_portal,
        NULL,                   /* OBJ_PG */
        NULL,                   /* OBJ_DD */
        NULL,                   /* OBJ_DDS */
        NULL,                   /* MAX_OBJ_TYPE */
        NULL,                   /* OBJ_DUMMY1 */
        NULL,                   /* OBJ_DUMMY2 */
        NULL,                   /* OBJ_DUMMY3 */
        NULL,                   /* OBJ_DUMMY4 */
        &make_raw_assoc_iscsi,
        &make_raw_assoc_dd
};

static scn_text_t *scn_gen_entity(scn_raw_t *);
static scn_text_t *scn_gen_iscsi(scn_raw_t *);
static scn_text_t *scn_gen_portal(scn_raw_t *);
static scn_text_t *scn_gen_assoc_dd(scn_raw_t *);
static scn_text_t *(*const scn_gen[MAX_OBJ_TYPE_FOR_SIZE])(scn_raw_t *) = {
        NULL,
        &scn_gen_entity,
        &scn_gen_iscsi,
        &scn_gen_portal,
        NULL,                   /* OBJ_PG */
        NULL,                   /* OBJ_DD */
        NULL,                   /* OBJ_DDS */
        NULL,                   /* MAX_OBJ_TYPE */
        NULL,                   /* OBJ_DUMMY1 */
        NULL,                   /* OBJ_DUMMY2 */
        NULL,                   /* OBJ_DUMMY3 */
        NULL,                   /* OBJ_DUMMY4 */
        &scn_gen_iscsi,
        &scn_gen_assoc_dd
};

#define SCN_TEST(E, BITMAP, UID1, UID2, NT) \
        (((E) & (BITMAP)) && \
        (!((BITMAP) & (ISNS_INIT_SELF_INFO_ONLY | \
                        ISNS_TARGET_SELF_INFO_ONLY)) || \
                ((UID1) == (UID2)) || \
                (((BITMAP) & ISNS_INIT_SELF_INFO_ONLY) && \
                        ((NT) & ISNS_INITIATOR_NODE_TYPE)) || \
                (((BITMAP) & ISNS_TARGET_SELF_INFO_ONLY) && \
                        ((NT) & ISNS_TARGET_NODE_TYPE))))

/*
 * local functions.
 */

/*
 * ****************************************************************************
 *
 * free_portal_1:
 *      Free one SCN portal or decrease the reference count if the portal
 *      is referenced by other SCN entry(s).
 *
 * p    - the portal.
 *
 * ****************************************************************************
 */
static void
free_portal_1(
        scn_portal_t *p
)
{
        if (p->ref <= 1) {
                if (p->sz == sizeof (in6_addr_t)) {
                        free(p->ip.in6);
                }
                free(p);
        } else {
                p->ref --;
        }
}

/*
 * ****************************************************************************
 *
 * free_portal:
 *      Free the unused portals, which are extracted for new SCN entry,
 *      after the new SCN entry is added.
 *
 * p    - the portal.
 *
 * ****************************************************************************
 */
static void
free_portal(
        scn_portal_t *p
)
{
        scn_portal_t *n;

        while (p != NULL) {
                n = p->next;
                free_portal_1(p);
                p = n;
        }
}

/*
 * ****************************************************************************
 *
 * free_portal_list:
 *      Free the list of portals while a SCN entry is being destroyed.
 *
 * l    - the portal list.
 *
 * ****************************************************************************
 */
static void
free_portal_list(
        scn_list_t *l
)
{
        scn_list_t *n;
        scn_portal_t *p;

        while (l != NULL) {
                n = l->next;
                p = l->data.portal;
                free_portal_1(p);
                free(l);
                l = n;
        }
}

/*
 * ****************************************************************************
 *
 * free_scn_text:
 *      Free one SCN or decrease the ref count after the SCN is emitted.
 *
 * text - the SCN.
 *
 * ****************************************************************************
 */
static void
free_scn_text(
        scn_text_t *text
)
{
        if (text->ref <= 1) {
                free(text->iscsi);
                free(text);
        } else {
                text->ref --;
        }
}

/*
 * ****************************************************************************
 *
 * free_scn_list:
 *      Free the the list of SCN.
 *
 * scn  - the list.
 *
 * ****************************************************************************
 */
static void
free_scn_list(
        scn_t *scn
)
{
        scn_t *next_scn;
        scn_list_t *list;
        scn_list_t *next_list;

        while (scn != NULL) {
                next_scn = scn->next;
                list = scn->data.list;
                while (list != NULL) {
                        next_list = list->next;
                        free_scn_text(list->data.text);
                        free(list);
                        list = next_list;
                }
                free(scn);
                scn = next_scn;
        }
}

/*
 * ****************************************************************************
 *
 * free_scn:
 *      Free all of SCNs which are dispatched to every entry.
 *
 * ****************************************************************************
 */
static void
free_scn(
)
{
        scn_registry_t *p;

        p = scn_registry;

        while (p != NULL) {
                free_scn_list(p->scn);
                p->scn = NULL;
                p = p->next;
        }
}

/*
 * ****************************************************************************
 *
 * free_entry:
 *      Free one SCN entry.
 *
 * e    - the SCN entry.
 *
 * ****************************************************************************
 */
static void
free_entry(
        scn_registry_t *e
)
{
        free_scn_list(e->scn);
        free_portal_list(e->portal.l);
        free(e->name);
        free(e);
}

/*
 * ****************************************************************************
 *
 * free_raw:
 *      Free the raw data after the SCN is generated from it.
 *
 * raw  - the raw SCN data.
 *
 * ****************************************************************************
 */
static void
free_raw(
        scn_raw_t *raw
)
{
        if (raw->ref == 0) {
                free(raw->iscsi);
        }
        if (raw->ip != NULL) {
                free(raw->ip);
        }
        free(raw);
}

/*
 * ****************************************************************************
 *
 * scn_add_portal:
 *      Add portals to the portal list of a SCN entry.
 *
 * e    - the SCN entry.
 * p    - the portals.
 * return - 0: successful, otherwise failed.
 *
 * ****************************************************************************
 */
static int
scn_add_portal(
        scn_registry_t *e,
        scn_portal_t *p
)
{
        scn_portal_t *x;
        scn_list_t *l, *m;

        scn_list_t **lp;

        int found_it;

        lp = &e->portal.l;
        while (p != NULL) {
                m = (scn_list_t *)malloc(sizeof (scn_list_t));
                if (m == NULL) {
                        return (1);
                }
                found_it = 0;
                e = scn_registry;
                while (e && !found_it) {
                        l = e->portal.l;
                        while (l && !found_it) {
                                x = l->data.portal;
                                if (x->uid == p->uid) {
                                        found_it = 1;
                                }
                                l = l->next;
                        }
                        e = e->next;
                }

                if (!found_it) {
                        x = p;
                }
                m->data.portal = x;
                x->ref ++;
                m->next = *lp;
                *lp = m;

                p = p->next;
        }

        return (0);
}

/*
 * ****************************************************************************
 *
 * scn_remove_portal:
 *      Remove a portal from the portal list of every SCN entry.
 *
 * uid  - the portal object uid.
 * return - always successful (0).
 *
 * ****************************************************************************
 */
static int
scn_remove_portal(
        uint32_t uid
)
{
        scn_registry_t **ep, *e;

        scn_portal_t *x;
        scn_list_t **lp, *l;

        ep = &scn_registry;
        e = *ep;

        while (e != NULL) {
                lp = &e->portal.l;
                l = *lp;
                while (l != NULL) {
                        x = l->data.portal;
                        if (x->uid == uid) {
                                /* remove it */
                                *lp = l->next;
                                free_portal_1(x);
                                free(l);
                        } else {
                                lp = &l->next;
                        }
                        l = *lp;
                }

                if (e->portal.l == NULL) {
                        /* no portal for this entry, destroy it */
                        *ep = e->next;
                        free_entry(e);
                } else {
                        ep = &e->next;
                }
                e = *ep;
        }

        return (0);
}

/*
 * ****************************************************************************
 *
 * scn_list_add:
 *      Add one SCN entry to the SCN entry list.
 *
 * e    - the SCN entry.
 * return - always successful (0).
 *
 * ****************************************************************************
 */
static int
scn_list_add(
        scn_registry_t *e
)
{
        scn_registry_t **pp;
        scn_portal_t *p;

        p = e->portal.p;
        e->portal.l = NULL;

        pp = &scn_registry;
        while (*pp) {
                if ((*pp)->uid == e->uid) {
                        /* replace the bitmap */
                        (*pp)->bitmap = e->bitmap;
                        free_portal(p);
                        free_entry(e);
                        return (0);
                } else if ((*pp)->uid < e->uid) {
                        break;
                }
                pp = &(*pp)->next;
        }

        (void) scn_add_portal(e, p);

        if (e->portal.l != NULL || sys_q == NULL) {
                /* insert it to the list */
                e->next = *pp;
                *pp = e;
        } else {
                /* no portal, ignore it */
                free_entry(e);
        }

        /* free the unused portal(s) */
        free_portal(p);

        return (0);
}

/*
 * ****************************************************************************
 *
 * scn_list_remove:
 *      Remove one SCN entry from the SCN entry list.
 *
 * uid  - the SCN entry unique ID.
 * return - always successful (0).
 *
 * ****************************************************************************
 */
static int
scn_list_remove(
        uint32_t uid
)
{
        scn_registry_t **ep, *e;

        ep = &scn_registry;
        e = *ep;
        while (e) {
                if (e->uid == uid) {
                        /* destroy it */
                        *ep = e->next;
                        free_entry(e);
                        break;
                } else if (e->uid < uid) {
                        break;
                }
                ep = &e->next;
                e = *ep;
        }

        return (0);
}

/*
 * ****************************************************************************
 *
 * cb_get_scn_port:
 *      The callback function which returns the SCN port of a portal object.
 *
 * p1   - the portal object.
 * p2   - the lookup control data.
 * return - the SCN port number.
 *
 * ****************************************************************************
 */
static int
cb_get_scn_port(
        void *p1,
        /*ARGSUSED*/
        void *p2
)
{
        isns_obj_t *obj = (isns_obj_t *)p1;

        isns_attr_t *attr = &obj->attrs[
            ATTR_INDEX_PORTAL(ISNS_SCN_PORT_ATTR_ID)];

        int port = 0;

        if (attr->tag != 0 && attr->value.ui != 0) {
                port = (int)attr->value.ui;
        }

        return (port);
}

/*
 * ****************************************************************************
 *
 * new_scn_portal:
 *      Make a new SCN portal.
 *
 * ref  - the ref count.
 * uid  - the portal object UID.
 * ip   - the ip address.
 * port - the port number.
 * return - the SCN portal.
 *
 * ****************************************************************************
 */
static scn_portal_t *
new_scn_portal(
        uint32_t ref,
        uint32_t uid,
        in6_addr_t *ip,
        uint32_t port
)
{
        scn_portal_t *p;

        p = (scn_portal_t *)malloc(sizeof (scn_portal_t));
        if (p != NULL) {
                p->uid = uid;
                /* convert the ipv6 to ipv4 */
                if (((int *)ip)[0] == 0x00 &&
                    ((int *)ip)[1] == 0x00 &&
                    ((uchar_t *)ip)[8] == 0x00 &&
                    ((uchar_t *)ip)[9] == 0x00 &&
                    ((uchar_t *)ip)[10] == 0xFF &&
                    ((uchar_t *)ip)[11] == 0xFF) {
                        p->sz = sizeof (in_addr_t);
                        p->ip.in = ((uint32_t *)ip)[3];
                        free(ip);
                } else {
                        p->sz = sizeof (in6_addr_t);
                        p->ip.in6 = ip;
                }
                p->port = port;
                p->ref = ref;
                p->so = 0;
                p->next = NULL;
        }

        return (p);
}

/*
 * ****************************************************************************
 *
 * extract scn_portal:
 *      Extract the SCN portal(s) for a storage node.
 *
 * name - the storage node name.
 * return - the SCN portal list.
 *
 * ****************************************************************************
 */
static scn_portal_t *
extract_scn_portal(
        uchar_t *name
)
{
        scn_portal_t *list = NULL;
        scn_portal_t *p;

        lookup_ctrl_t lc_pg, lc_p;
        uint32_t pg_uid, uid;

        in6_addr_t *ip;
        uint32_t port;

        lc_pg.type = OBJ_PG;
        lc_pg.curr_uid = 0;
        lc_pg.id[0] = ATTR_INDEX_PG(ISNS_PG_ISCSI_NAME_ATTR_ID);
        lc_pg.op[0] = OP_STRING;
        lc_pg.data[0].ptr = name;
        lc_pg.op[1] = 0;

        lc_pg.id[1] = ISNS_PG_PORTAL_IP_ADDR_ATTR_ID;
        lc_pg.id[2] = ISNS_PG_PORTAL_PORT_ATTR_ID;

        lc_p.type = OBJ_PORTAL;
        lc_p.curr_uid = 0;
        lc_p.id[0] = ATTR_INDEX_PORTAL(ISNS_PORTAL_IP_ADDR_ATTR_ID);
        lc_p.op[0] = OP_MEMORY_IP6;
        lc_p.id[1] = ATTR_INDEX_PORTAL(ISNS_PORTAL_PORT_ATTR_ID);
        lc_p.op[1] = OP_INTEGER;
        lc_p.op[2] = 0;

        while (cache_lookup(&lc_pg, &pg_uid, cb_clone_attrs) == 0 &&
            pg_uid != 0) {
                ip = lc_pg.data[1].ip;
                port = lc_pg.data[2].ui;
                if (ip != NULL) {
                        lc_p.data[0].ip = ip;
                        lc_p.data[1].ui = port;
                        port = cache_lookup(&lc_p, &uid, cb_get_scn_port);
                        if (port != 0 && uid != 0) {
                                /* ref starts from 1 */
                                p = new_scn_portal(1, uid, ip, port);
                                if (p != NULL) {
                                        p->next = list;
                                        list = p;
                                } else {
                                        free(ip);
                                        free(p);
                                }
                        } else {
                                /* portal not registered or no scn port */
                                free(ip);
                        }
                }
                lc_pg.curr_uid = pg_uid;
        }

        return (list);
}

/*
 * ****************************************************************************
 *
 * cb_update_scn_bitmap:
 *      The callback function which updates the SCN Bitmap attribute of
 *      a storage node object.
 *
 * p1   - the storage node object.
 * p2   - the lookup control data.
 * return - error code.
 *
 * ****************************************************************************
 */
static int
cb_update_scn_bitmap(
        void *p1,
        void *p2
)
{
        int ec = 0;

        isns_obj_t *obj = (isns_obj_t *)p1;
        lookup_ctrl_t *lcp = (lookup_ctrl_t *)p2;

        int id = ATTR_INDEX_ISCSI(ISNS_ISCSI_SCN_BITMAP_ATTR_ID);
        isns_attr_t *attr = &obj->attrs[id];

        uint32_t bitmap = lcp->data[2].ui;

        if (bitmap != 0) {
                attr->tag = ISNS_ISCSI_SCN_BITMAP_ATTR_ID;
                attr->len = 4;
        } else if (attr->tag == 0) {
                return (ec);
        } else {
                attr->tag = 0;
                attr->len = 0;
        }
        attr->value.ui = bitmap;

        if (sys_q != NULL) {
                ec = write_data(DATA_UPDATE, obj);
        }

        return (ec);
}

/*
 * ****************************************************************************
 *
 * cb_get_node_type:
 *      The callback function which returns the node type attribute of
 *      a storage node object.
 *
 * p1   - the storage node object.
 * p2   - the lookup control data.
 * return - error code.
 *
 * ****************************************************************************
 */
static int
cb_get_node_type(
        void *p1,
        /* LINTED E_FUNC_ARG_UNUSED */
        void *p2
)
{
        isns_obj_t *obj = (isns_obj_t *)p1;
        isns_attr_t *attr = &obj->attrs[
            ATTR_INDEX_ISCSI(ISNS_ISCSI_NODE_TYPE_ATTR_ID)];
        int nt = (int)attr->value.ui;

        return (nt);
}

/*
 * ****************************************************************************
 *
 * cb_get_node_type:
 *      The callback function which returns the storage node object UID
 *      from a portal group object.
 *
 * p1   - the pg object.
 * p2   - the lookup control data.
 * return - the storage node object UID.
 *
 * ****************************************************************************
 */
static int
cb_pg_node(
        void *p1,
        /* LINTED E_FUNC_ARG_UNUSED */
        void *p2
)
{
        uint32_t ref;

        ref = get_ref_t(p1, OBJ_ISCSI);

        return ((int)ref);
}

/*
 * ****************************************************************************
 *
 * make_raw_entity:
 *      Make raw SCN data with a Network Entity object.
 *
 * obj  - the network entity object.
 * return - the raw SCN data.
 *
 * ****************************************************************************
 */
static scn_raw_t *
make_raw_entity(
        /*ARGSUSED*/
        isns_obj_t *obj
)
{
        scn_raw_t *raw;

        raw = (scn_raw_t *)malloc(sizeof (scn_raw_t));
        if (raw != NULL) {
                raw->type = obj->type;
                raw->uid = get_obj_uid(obj);
                raw->iscsi = NULL;
                raw->ref = 0;
                raw->ilen = 0;
                raw->nt = 0;
                raw->ip = NULL;
                raw->dd_id = 0;
                raw->dds_id = 0;
        } else {
                isnslog(LOG_DEBUG, "make_raw_entity", "malloc failed.");
        }

        return (raw);
}

/*
 * ****************************************************************************
 *
 * make_raw_iscsi:
 *      Make raw SCN data with a Storage Node object.
 *
 * obj  - the storage node object.
 * return - the raw SCN data.
 *
 * ****************************************************************************
 */
static scn_raw_t *
make_raw_iscsi(
        isns_obj_t *obj
)
{
        uint32_t uid;
        uint32_t nt;
        uchar_t *iscsi;
        uint32_t ilen;

        isns_attr_t *attr;

        scn_raw_t *raw;

        uid = get_obj_uid(obj);
        attr = &obj->attrs[ATTR_INDEX_ISCSI(ISNS_ISCSI_NODE_TYPE_ATTR_ID)];
        nt = attr->value.ui;
        attr = &obj->attrs[ATTR_INDEX_ISCSI(ISNS_ISCSI_NAME_ATTR_ID)];

        raw = (scn_raw_t *)malloc(sizeof (scn_raw_t));
        ilen = attr->len;
        iscsi = (uchar_t *)malloc(ilen);
        if (raw != NULL && iscsi != NULL) {
                /* copy the iscsi storage node name */
                (void) strcpy((char *)iscsi, (char *)attr->value.ptr);

                raw->type = obj->type;
                raw->uid = uid;
                raw->iscsi = iscsi;
                raw->ref = 0;
                raw->ilen = ilen;
                raw->nt = nt;
                raw->ip = NULL;
                raw->dd_id = 0;
                raw->dds_id = 0;
        } else {
                free(raw);
                free(iscsi);
                raw = NULL;
                isnslog(LOG_DEBUG, "make_raw_iscsi", "malloc failed.");
        }

        return (raw);
}

/*
 * ****************************************************************************
 *
 * make_raw_portal:
 *      Make raw SCN data with a Portal object.
 *
 * obj  - the portal object.
 * return - the raw SCN data.
 *
 * ****************************************************************************
 */
static scn_raw_t *
make_raw_portal(
        isns_obj_t *obj
)
{
        isns_attr_t *attr;
        in6_addr_t *ip;
        uint32_t port;

        scn_raw_t *raw;

        raw = (scn_raw_t *)malloc(sizeof (scn_raw_t));
        ip = (in6_addr_t *)malloc(sizeof (in6_addr_t));
        if (raw != NULL && ip != NULL) {
                attr = &obj->attrs[
                    ATTR_INDEX_PORTAL(ISNS_PORTAL_IP_ADDR_ATTR_ID)];
                (void) memcpy(ip, attr->value.ip, sizeof (in6_addr_t));
                attr = &obj->attrs[
                    ATTR_INDEX_PORTAL(ISNS_PORTAL_PORT_ATTR_ID)];
                port = attr->value.ui;

                raw->type = obj->type;
                raw->uid = 0;
                raw->iscsi = NULL;
                raw->ref = 0;
                raw->ilen = 0;
                raw->nt = 0;
                raw->ip = ip;
                raw->port = port;
                raw->dd_id = 0;
                raw->dds_id = 0;
        } else {
                free(ip);
                free(raw);
                raw = NULL;
                isnslog(LOG_DEBUG, "make_raw_portal", "malloc failed.");
        }

        return (raw);
}

/*
 * ****************************************************************************
 *
 * make_raw_assoc_iscsi:
 *      Make raw SCN data with a Discovery Domain member association.
 *
 * obj  - the member association object.
 * return - the raw SCN data.
 *
 * ****************************************************************************
 */
static scn_raw_t *
make_raw_assoc_iscsi(
        isns_obj_t *obj
)
{
        uint32_t uid;
        uint32_t dd_id;
        uint32_t nt;

        lookup_ctrl_t lc;
        isns_attr_t *attr;

        scn_raw_t *raw;
        uchar_t *iscsi;
        uint32_t ilen;

        uid = get_obj_uid(obj);
        dd_id = get_parent_uid(obj);

        SET_UID_LCP(&lc, OBJ_ISCSI, uid);

        nt = cache_lookup(&lc, NULL, cb_get_node_type);

        attr = &obj->attrs[ATTR_INDEX_ASSOC_ISCSI(ISNS_DD_ISCSI_NAME_ATTR_ID)];

        raw = (scn_raw_t *)malloc(sizeof (scn_raw_t));
        ilen = attr->len;
        iscsi = (uchar_t *)malloc(ilen);
        if (raw != NULL && iscsi != NULL) {
                /* copy the iscsi storage node name */
                (void) strcpy((char *)iscsi, (char *)attr->value.ptr);

                raw->type = obj->type;
                raw->uid = uid;
                raw->iscsi = iscsi;
                raw->ref = 0;
                raw->ilen = ilen;
                raw->nt = nt;
                raw->ip = NULL;
                raw->dd_id = dd_id;
                raw->dds_id = 0;
        } else {
                free(raw);
                free(iscsi);
                raw = NULL;
                isnslog(LOG_DEBUG, "make_raw_assoc_iscsi", "malloc failed.");
        }

        return (raw);
}

/*
 * ****************************************************************************
 *
 * make_raw_assoc_dd:
 *      Make raw SCN data with a Discovery Domain Set member association.
 *
 * obj  - the member association object.
 * return - the raw SCN data.
 *
 * ****************************************************************************
 */
static scn_raw_t *
make_raw_assoc_dd(
        isns_obj_t *obj
)
{
        scn_raw_t *raw;

        raw = (scn_raw_t *)malloc(sizeof (scn_raw_t));
        if (raw != NULL) {
                raw->type = obj->type;
                raw->uid = 0;
                raw->iscsi = NULL;
                raw->ref = 0;
                raw->ilen = 0;
                raw->nt = 0;
                raw->ip = NULL;
                raw->dd_id = get_obj_uid(obj);
                raw->dds_id = get_parent_uid(obj);
        } else {
                isnslog(LOG_DEBUG, "make_raw_assoc_dd", "malloc failed.");
        }

        return (raw);
}

/*
 * ****************************************************************************
 *
 * scn_gen_entity:
 *      Generate SCN with the raw SCN data from a Network Entity object.
 *
 * raw  - the raw SCN data.
 * return - the SCN.
 *
 * ****************************************************************************
 */
static scn_text_t *
scn_gen_entity(
        /* LINTED E_FUNC_ARG_UNUSED */
        scn_raw_t *raw
)
{
        return (NULL);
}

/*
 * ****************************************************************************
 *
 * scn_gen_iscsi:
 *      Generate SCN with the raw SCN data from a Storage Node object.
 *
 * raw  - the raw SCN data.
 * return - the SCN.
 *
 * ****************************************************************************
 */
static scn_text_t *
scn_gen_iscsi(
        scn_raw_t *raw
)
{
        scn_text_t *text;

        text = (scn_text_t *)malloc(sizeof (scn_text_t));
        if (text != NULL) {
                text->flag = 0;
                text->ref = 1; /* start with 1 */
                text->uid = raw->uid;
                text->iscsi = raw->iscsi;
                raw->ref ++;
                text->ilen = raw->ilen;
                text->nt = raw->nt;
                text->dd_id = raw->dd_id;
                text->dds_id = raw->dds_id;
                text->next = NULL;
        } else {
                isnslog(LOG_DEBUG, "scn_gen_iscsi", "malloc failed.");
        }
        return (text);
}

/*
 * ****************************************************************************
 *
 * scn_gen_portal:
 *      Generate SCN with the raw SCN data from a Portal object.
 *
 * raw  - the raw SCN data.
 * return - the SCN.
 *
 * ****************************************************************************
 */
static scn_text_t *
scn_gen_portal(
        scn_raw_t *raw
)
{
        in6_addr_t *ip;
        uint32_t port;

        uint32_t pg_uid, uid;
        lookup_ctrl_t pg_lc, lc;

        uint32_t nt;
        uchar_t *name;
        int ilen;

        scn_text_t *text, *l = NULL;

        ip = raw->ip;
        port = raw->port;

        pg_lc.curr_uid = 0;
        pg_lc.type = OBJ_PG;
        pg_lc.id[0] = ATTR_INDEX_PG(ISNS_PG_PORTAL_IP_ADDR_ATTR_ID);
        pg_lc.op[0] = OP_MEMORY_IP6;
        pg_lc.data[0].ip = ip;
        pg_lc.id[1] = ATTR_INDEX_PG(ISNS_PG_PORTAL_PORT_ATTR_ID);
        pg_lc.op[1] = OP_INTEGER;
        pg_lc.data[1].ui = port;
        pg_lc.op[2] = 0;

        SET_UID_LCP(&lc, OBJ_ISCSI, 0);

        lc.id[1] = ISNS_ISCSI_NAME_ATTR_ID;
        lc.id[2] = ISNS_ISCSI_NODE_TYPE_ATTR_ID;
        lc.data[1].ptr = NULL;

        /* get a pg which is associated to the portal */
        uid = cache_lookup(&pg_lc, &pg_uid, cb_pg_node);
        while (pg_uid != 0) {
                if (uid != 0) {
                        lc.data[0].ui = uid;
                        (void) cache_lookup(&lc, NULL, cb_clone_attrs);
                        name = lc.data[1].ptr;
                        if (name != NULL) {
                                nt = lc.data[2].ui;
                                text = (scn_text_t *)malloc(
                                    sizeof (scn_text_t));
                                if (text != NULL) {
                                        text->flag = 0;
                                        text->ref = 1; /* start with 1 */
                                        text->uid = uid;
                                        text->iscsi = name;
                                        ilen = strlen((char *)name);
                                        ilen += 4 - (ilen % 4);
                                        text->ilen = ilen;
                                        text->nt = nt;
                                        text->dd_id = 0;
                                        text->dds_id = 0;
                                        text->next = l;
                                        l = text;
                                } else {
                                        free(name);
                                        isnslog(LOG_DEBUG, "scn_gen_portal",
                                            "malloc failed.");
                                }
                                lc.data[1].ptr = NULL;
                        } else {
                                isnslog(LOG_WARNING, "scn_gen_portal",
                                    "cannot get node name.");
                        }
                }

                /* get the next pg */
                pg_lc.curr_uid = pg_uid;
                uid = cache_lookup(&pg_lc, &pg_uid, cb_pg_node);
        }

        /* update the iscsi storage node object */
        raw->event = ISNS_OBJECT_UPDATED;

        return (l);
}

/*
 * ****************************************************************************
 *
 * scn_gen_assoc_dd:
 *      Generate SCN with the raw SCN data from a DD membership object.
 *
 * raw  - the raw SCN data.
 * return - the SCN.
 *
 * ****************************************************************************
 */
static scn_text_t *
scn_gen_assoc_dd(
        /* LINTED E_FUNC_ARG_UNUSED */
        scn_raw_t *raw
)
{
        return (NULL);
}

/*
 * ****************************************************************************
 *
 * make_scn:
 *      Make a SCN with an event and an object.
 *
 * event - the event.
 * obj   - the object.
 * return - always successful (0).
 *
 * ****************************************************************************
 */
int
make_scn(
        uint32_t event,
        isns_obj_t *obj
)
{
        scn_raw_t *raw = NULL;

        scn_raw_t *(*f)(isns_obj_t *) = make_raw[obj->type];

        if (f != NULL) {
                /* make raw scn data */
                raw = f(obj);
        }
        if (raw != NULL) {
                /* trigger an scn event */
                raw->event = event;
                (void) queue_msg_set(scn_q, SCN_SET, (void *)raw);
        }

        return (0);
}

/*
 * data structure of the SCN state transition table.
 */
typedef struct scn_tbl {
        int state;
        uint32_t event;
        isns_type_t type;
        int (*sf)(scn_raw_t *);
        int next_state;
} scn_tbl_t;

/*
 * the SCN state transition table.
 */
static const scn_tbl_t stbl[] = {
        { -1, 0, OBJ_PG, NULL, 0 },
        { -1, 0, OBJ_DD, NULL, 0 },
        { -1, 0, OBJ_DDS, NULL, 0 },

        { 0, ISNS_OBJECT_ADDED, OBJ_ENTITY, NULL, 1 },
        { 1, ISNS_OBJECT_ADDED, OBJ_ISCSI, sf_gen, 1 },
        { 1, ISNS_OBJECT_ADDED, 0, NULL, 1 },

        { 0, ISNS_OBJECT_UPDATED, OBJ_ENTITY, sf_gen, 2 },
        { 2, ISNS_OBJECT_UPDATED, 0, NULL, 2 },
        { 2, ISNS_OBJECT_ADDED, OBJ_ISCSI, sf_gen, 2 },
        { 2, ISNS_OBJECT_ADDED, 0, NULL, 2 },

        { 0, ISNS_OBJECT_REMOVED, OBJ_ENTITY, NULL, 3 },
        { 0, ISNS_OBJECT_REMOVED, 0, sf_gen, 4 },
        { 3, ISNS_OBJECT_REMOVED, OBJ_ISCSI, sf_gen, 3 },
        { 3, ISNS_OBJECT_REMOVED, 0, NULL, 3 },
        { 4, ISNS_OBJECT_REMOVED, 0, sf_gen, 4 },

        { 0, ISNS_MEMBER_ADDED, OBJ_ASSOC_ISCSI, sf_gen, 5 },
        { 5, ISNS_MEMBER_ADDED, OBJ_ASSOC_ISCSI, sf_gen, 5 },

        { 0, ISNS_MEMBER_ADDED, OBJ_ASSOC_DD, sf_gen, 6 },
        { 6, ISNS_MEMBER_ADDED, OBJ_ASSOC_DD, sf_gen, 6 },

        { 0, ISNS_MEMBER_REMOVED, OBJ_ASSOC_ISCSI, sf_gen, 7 },
        { 7, ISNS_MEMBER_REMOVED, OBJ_ASSOC_ISCSI, sf_gen, 7 },

        { 0, ISNS_MEMBER_REMOVED, OBJ_ASSOC_DD, sf_gen, 8 },
        { 8, ISNS_MEMBER_REMOVED, OBJ_ASSOC_DD, sf_gen, 8 },

        { -1, 0, 0, sf_error, -1 }
};

/*
 * ****************************************************************************
 *
 * scn_disp1:
 *      Dispatch one SCN to one SCN entry.
 *
 * event - the event.
 * p     - the SCN entry.
 * t     - the SCN.
 * return - always successful (0).
 *
 * ****************************************************************************
 */
static int
scn_disp1(
        uint32_t event,
        scn_registry_t *p,
        scn_text_t *t
)
{
        scn_t *s, *r = NULL;
        scn_list_t *l, **lp;

        s = p->scn;

        while (s != NULL) {
                if (s->event == event) {
                        l = s->data.list;
                        do {
                                if (l->data.text->uid == t->uid) {
                                        /* duplicated */
                                        return (0);
                                }
                                lp = &l->next;
                                l = *lp;
                        } while (l != NULL);
                        break;
                }
                r = s;
                s = s->next;
        }

        l = (scn_list_t *)malloc(sizeof (scn_list_t));
        if (l != NULL) {
                if (s == NULL) {
                        s = (scn_t *)malloc(sizeof (scn_t));
                        if (s != NULL) {
                                s->event = event;
                                s->next = NULL;
                                if (r != NULL) {
                                        r->next = s;
                                } else {
                                        p->scn = s;
                                }
                                lp = &s->data.list;
                        } else {
                                free(l);
                                isnslog(LOG_DEBUG, "scn_disp1",
                                    "malloc scn failed.\n");
                                return (0);
                        }
                }

                t->ref ++;
                l->data.text = t;
                l->next = NULL;
                *lp = l;
        } else {
                isnslog(LOG_DEBUG, "scn_disp1",
                    "malloc list failed.\n");
        }

        return (0);
}

/*
 * ****************************************************************************
 *
 * scn_disp1:
 *      Dispatch one SCN to every SCN entry and update the dispatch status.
 *
 * event - the event.
 * text  - the SCN.
 * return - always successful (0).
 *
 * ****************************************************************************
 */
static int
scn_disp(
        uint32_t event,
        scn_text_t *text
)
{
        scn_registry_t *registry, *p;
        uint32_t dd_id = 0;

        scn_text_t *t;

        uint32_t e;

        registry = scn_registry;

        t = text;
        while (t != NULL) {
                e = event;
                if (t->flag == 0) {
                        if (e & ISNS_MEMBER_ADDED) {
                                e |= ISNS_OBJECT_ADDED;
                        } else if (e & ISNS_MEMBER_REMOVED) {
                                e |= ISNS_OBJECT_REMOVED;
                        }
                }
                p = registry;
                while (p != NULL) {
                        if (SCN_TEST(e, p->bitmap, p->uid, t->uid, t->nt)) {
                                if (p->bitmap & ISNS_MGMT_REG) {
                                        /* management scn are not bound */
                                        /* by discovery domain service. */
                                        dd_id = 1;
                                } else {
                                        dd_id = 0;
                                        /* lock the cache for reading */
                                        (void) cache_lock_read();
                                        /* verify common dd */
                                        do {
                                                dd_id = get_common_dd(
                                                    p->uid,
                                                    t->uid,
                                                    dd_id);
                                        } while (dd_id > 0 &&
                                            is_dd_active(dd_id) == 0);
                                        /* unlock the cache */
                                        (void) cache_unlock_nosync();
                                }
                                if (dd_id != 0) {
                                        (void) scn_disp1(e, p, t);
                                }
                        }
                        p = p->next;
                }
                t = t->next;
        }

        while (text != NULL) {
                t = text->next;
                /* clean up the scn text(s) which nobody cares about. */
                free_scn_text(text);
                text = t;
        }

        if (dd_id != 0) {
                /* scn(s) are dispatched. */
                scn_dispatched = 1;
        }

        return (0);
}

/*
 * ****************************************************************************
 *
 * sf_gen:
 *      State transition function which generates and dispatches SCN(s).
 *
 * raw  - the raw SCN data.
 * return - always successful (0).
 *
 * ****************************************************************************
 */
static int
sf_gen(
        scn_raw_t *raw
)
{
        uint32_t event;

        scn_text_t *(*gen)(scn_raw_t *);
        scn_text_t *text = NULL;

        gen = scn_gen[raw->type];
        if (gen != NULL) {
                text = gen(raw);
        }

        event = raw->event;
        if (text != NULL) {
                (void) scn_disp(event, text);
        }

        return (0);
}

/*
 * ****************************************************************************
 *
 * sf_error:
 *      State transition function for an error state. It free any SCN(s)
 *      which have been generated and dispatched previously.
 *
 * raw  - the raw SCN data.
 * return - always successful (0).
 *
 * ****************************************************************************
 */
static int
sf_error(
        /* LINTED E_FUNC_ARG_UNUSED */
        scn_raw_t *raw
)
{
        free_scn();

        return (0);
}

/*
 * ****************************************************************************
 *
 * scn_transition:
 *      Performs the state transition when a SCN event occurs.
 *
 * state - the previous state.
 * raw   - the raw SCN data.
 * return - the next state.
 *
 * ****************************************************************************
 */
static int
scn_transition(
        int state,
        scn_raw_t *raw
)
{
        uint32_t event = raw->event;
        isns_type_t type = raw->type;

        int new_state = state;

        const scn_tbl_t *tbl;

        tbl = &stbl[0];
        for (;;) {
                if ((tbl->state == -1 || tbl->state == state) &&
                    (tbl->event == 0 || tbl->event == event) &&
                    (tbl->type == 0 || tbl->type == type)) {
                        if (tbl->next_state != 0) {
                                new_state = tbl->next_state;
                        }
                        if (tbl->sf != NULL) {
                                tbl->sf(raw);
                        }
                        break;
                }
                tbl ++;
        }

        if (new_state == -1) {
                isnslog(LOG_DEBUG, "scn_transition",
                    "prev state: %d new event: 0x%x new object: %d.\n",
                    state, event, type);
                new_state = 0;
        }

        state = new_state;

        return (state);
}

/*
 * ****************************************************************************
 *
 * connect_to:
 *      Create socket connection with peer network portal.
 *
 * sz   - the size of the ip addr.
 * in   - the ipv4 address.
 * in6  - the ipv6 address.
 * port2- the port info.
 * return - the socket descriptor.
 *
 * ****************************************************************************
 */
int
connect_to(
        int sz,
        in_addr_t in,
        /* LINTED E_FUNC_ARG_UNUSED */
        in6_addr_t *in6,
        uint32_t port2
)
{
        int so = -1;

        union {
                struct sockaddr sin;
                struct sockaddr_in in;
                struct sockaddr_in6 in6;
        } ca = { 0 };

        int tcp;
        uint16_t port;

        tcp = (port2 & 0x10000) == 0 ? 1 : 0;
        port = (uint16_t)(port2 & 0xFFFF);
        if (sz == sizeof (in_addr_t)) {
                if (tcp != 0) {
                        so = socket(AF_INET, SOCK_STREAM, 0);
                        if (so != -1) {
                                ca.in.sin_family = AF_INET;
                                ca.in.sin_port = htons(port);
                                ca.in.sin_addr.s_addr = in;
                                if (connect(so, &ca.sin, sizeof (ca.in)) !=
                                    0) {
                                        isnslog(LOG_DEBUG, "connect_to",
                                            "connect() failed %%m.");
                                        (void) close(so);
                                        so = -1;
                                }
                        } else {
                                isnslog(LOG_DEBUG, "connect_to",
                                    "socket() failed %%m.");
                        }
                } else {
                        /* FIXME: UDP support */
                        isnslog(LOG_DEBUG, "connect_to", "No UDP support.");
                }
        } else {
                /* FIXME: IPv6 support */
                isnslog(LOG_DEBUG, "connect_to", "No IPv6 support.");
        }

        return (so);
}

/*
 * ****************************************************************************
 *
 * emit_scn:
 *      Emit the SCN to any portal of the peer storage node.
 *
 * list - the list of portal.
 * pdu  - the SCN packet.
 * pl   - the SCN packet payload length.
 * return - always successful (0).
 *
 * ****************************************************************************
 */
static int
emit_scn(
        scn_list_t *list,
        isns_pdu_t *pdu,
        size_t pl
)
{
        int so = 0;
        scn_list_t *l;
        scn_portal_t *p;

        isns_pdu_t *rsp = NULL;
        size_t rsp_sz;

        pdu->version = htons((uint16_t)ISNSP_VERSION);
        pdu->func_id = htons((uint16_t)ISNS_SCN);
        pdu->xid = htons(get_server_xid());

        l = list;
        while (l != NULL) {
                p = l->data.portal;
                so = connect_to(p->sz, p->ip.in, p->ip.in6, p->port);
                if (so != -1) {
                        if (isns_send_pdu(so, pdu, pl) == 0) {
                                /* This may help Solaris iSCSI Initiator */
                                /* not to panic frequently. */
                                (void) isns_rcv_pdu(so, &rsp, &rsp_sz,
                                    ISNS_RCV_SHORT_TIMEOUT);
                        } else {
                                isnslog(LOG_DEBUG, "emit_scn",
                                    "sending packet failed.");
                        }
                        (void) close(so);
                        /* p->so = so; */
                        break;
                }
                l = l->next;
        }

        if (rsp != NULL) {
#ifdef DEBUG
                dump_pdu1(rsp);
#endif
                free(rsp);
        }

        return (0);
}

/*
 * ****************************************************************************
 *
 * scn_trigger1:
 *      Trigger one SCN for one SCN entry.
 *
 * t    - the time that SCN is being triggered.
 * p    - the SCN entry.
 * return - always successful (0).
 *
 * ****************************************************************************
 */
static int
scn_trigger1(
        time_t t,
        scn_registry_t *p
)
{
        int ec;

        isns_pdu_t *pdu = NULL;
        size_t sz;
        size_t pl;

        scn_t *s;
        scn_list_t *l;
        scn_text_t *x;

        union {
                uint32_t i32;
                uint64_t i64;
        } u;

#ifdef DEBUG
        char buff[1024] = { 0 };
        char *logbuff = buff;
#endif

        ec = pdu_reset_scn(&pdu, &pl, &sz);
        if (pdu == NULL) {
                goto scn_done;
        }

        /* add destination attribute */
        ec = pdu_add_tlv(&pdu, &pl, &sz,
            ISNS_ISCSI_NAME_ATTR_ID,
            p->nlen,
            (void *)p->name, 0);
        if (ec != 0) {
                goto scn_done;
        }

#ifdef DEBUG
        sprintf(logbuff, "==>%s ", p->name);
        logbuff += strlen(logbuff);
#endif

        /* add timestamp */
        u.i64 = BE_64((uint64_t)t);
        ec = pdu_add_tlv(&pdu, &pl, &sz,
            ISNS_TIMESTAMP_ATTR_ID,
            8,
            (void *)&u.i64, 1);

        s = p->scn;
        while (s != NULL && ec == 0) {
                u.i32 = htonl(s->event);
                ec = pdu_add_tlv(&pdu, &pl, &sz,
                    ISNS_ISCSI_SCN_BITMAP_ATTR_ID,
                    4,
                    (void *)&u.i32, 1);
#ifdef DEBUG
                sprintf(logbuff, "EVENT [%d] ", s->event);
                logbuff += strlen(logbuff);
#endif
                l = s->data.list;
                while (l != NULL && ec == 0) {
                        x = l->data.text;
                        if (x->flag == 0) {
                                ec = pdu_add_tlv(&pdu, &pl, &sz,
                                    ISNS_ISCSI_NAME_ATTR_ID,
                                    x->ilen, (void *)x->iscsi, 0);
#ifdef DEBUG
                                sprintf(logbuff, "FROM [%s] ", x->iscsi);
                                logbuff += strlen(logbuff);
#endif
                                if (ec == 0 &&
                                    (p->bitmap &
                                    (ISNS_MEMBER_ADDED |
                                    ISNS_MEMBER_REMOVED))) {
                                        /* management SCN */
                                        u.i32 = htonl(x->dd_id);
                                        ec = pdu_add_tlv(&pdu, &pl, &sz,
                                            ISNS_DD_ID_ATTR_ID,
                                            4, (void *)&u.i32, 1);
#ifdef DEBUG
                                        sprintf(logbuff, "IN DD [%d] ",
                                            x->dd_id);
                                        logbuff += strlen(logbuff);
#endif
                                }
                        } else {
                                /* add(remove) dd to(from) dd-set */
                                u.i32 = htonl(x->dd_id);
                                ec = pdu_add_tlv(&pdu, &pl, &sz,
                                    ISNS_DD_ID_ATTR_ID,
                                    4, (void *)&u.i32, 1);
                                u.i32 = htonl(x->dds_id);
                                if (ec == 0) {
                                        ec = pdu_add_tlv(&pdu, &pl, &sz,
                                            ISNS_DD_ID_ATTR_ID,
                                            4, (void *)&u.i32, 1);
                                }
#ifdef DEBUG
                                sprintf(logbuff, "FROM [%d] ", x->dd_id);
                                logbuff += strlen(logbuff);
                                sprintf(logbuff, "IN [%d] ", x->dds_id);
                                logbuff += strlen(logbuff);
#endif
                        }
                        l = l->next;
                }
                s = s->next;
        }

scn_done:
        if (ec == 0) {
#ifdef DEBUG
                isnslog(LOG_DEBUG, "scn_trigger1", buff);
#endif
                ec = emit_scn(p->portal.l, pdu, pl);
        } else {
                isnslog(LOG_DEBUG, "scn_trigger1", " failed.\n");
        }

        free(pdu);

        return (0);
}

/*
 * ****************************************************************************
 *
 * scn_trigger:
 *      Trigger one SCN for every SCN entry.
 *
 * return - always successful (0).
 *
 * ****************************************************************************
 */
static int
scn_trigger(
)
{
        time_t t;
        scn_registry_t *p;

        t = time(NULL);

        p = scn_registry;
        while (p != NULL) {
                if (p->scn != NULL) {
                        (void) scn_trigger1(t, p);
                }
                p = p->next;
        }

        return (0);
}

/*
 * global functions.
 */

/*
 * ****************************************************************************
 *
 * scn_list_load:
 *      Load one SCN entry and add it to the SCN entry list.
 *
 * uid  - the Storage Node object UID.
 * node - the Storage Node name.
 * nlen - the length of the name.
 * bitmap - the SCN bitmap.
 * return - error code.
 *
 * ****************************************************************************
 */
int
scn_list_load(
        uint32_t uid,
        uchar_t *node,
        uint32_t nlen,
        uint32_t bitmap
)
{
        int ec = 0;

        scn_registry_t *list;
        uchar_t *name;

        list = (scn_registry_t *)malloc(sizeof (scn_registry_t));
        name = (uchar_t *)malloc(nlen);

        if (list != NULL && name != NULL) {
                list->uid = uid;
                (void) strcpy((char *)name, (char *)node);
                list->name = name;
                list->nlen = nlen;
                list->bitmap = bitmap;
                list->portal.l = NULL;
                list->scn = NULL;
                list->next = NULL;
                ASSERT(scn_q == NULL);
                (void) scn_list_add(list);
        } else {
                free(list);
                free(name);
                ec = ISNS_RSP_INTERNAL_ERROR;
        }

        return (ec);
}

/*
 * ****************************************************************************
 *
 * verify_scn_portal:
 *      Extract and verify portals for every SCN entry(s) after they are
 *      loaded from data store, for those which do not have a SCN portal,
 *      remove it from the SCN entry list.
 *
 * return - 1: error occurs, otherwise 0.
 *
 * ****************************************************************************
 */
int
verify_scn_portal(
)
{
        scn_registry_t **pp, *e;
        scn_portal_t *p;

        pp = &scn_registry;
        while (*pp != NULL) {
                e = *pp;
                p = extract_scn_portal(e->name);
                if (p != NULL) {
                        if (scn_add_portal(e, p) != 0) {
                                return (1);
                        }
                }
                if (e->portal.l != NULL) {
                        pp = &e->next;
                } else {
                        /* remove this entry */
                        *pp = e->next;
                        free_entry(e);
                }
                /* free the unused portal(s) */
                free_portal(p);
        }

        return (0);
}

/*
 * ****************************************************************************
 *
 * add_scn_entry:
 *      Add a SCN entry.
 *
 * node - the Storage Node name.
 * nlen - the length of the name.
 * bitmap - the SCN bitmap.
 * return - error code.
 *
 * ****************************************************************************
 */
int
add_scn_entry(
        uchar_t *node,
        uint32_t nlen,
        uint32_t bitmap
)
{
        int ec = 0;

        uint32_t mgmt;
        scn_portal_t *p;

        lookup_ctrl_t lc;
        uint32_t uid;
        scn_registry_t *e;
        uchar_t *name;

        mgmt = bitmap & (
            ISNS_MGMT_REG |
            ISNS_MEMBER_REMOVED |
            ISNS_MEMBER_ADDED);

        if ((mgmt > 0 &&
            (mgmt_scn == 0 ||
            mgmt < ISNS_MGMT_REG ||
            is_control_node(node) == 0)) ||
            (p = extract_scn_portal(node)) == NULL) {
                return (ISNS_RSP_SCN_REGIS_REJECTED);
        }

        e = (scn_registry_t *)malloc(sizeof (scn_registry_t));
        name = (uchar_t *)malloc(nlen);
        if (e != NULL && name != NULL) {
                lc.type = OBJ_ISCSI;
                lc.curr_uid = 0;
                lc.id[0] = ATTR_INDEX_ISCSI(ISNS_ISCSI_NAME_ATTR_ID);
                lc.data[0].ptr = node;
                lc.op[0] = OP_STRING;
                lc.op[1] = 0;
                lc.data[2].ui = bitmap;
                ec = cache_lookup(&lc, &uid, cb_update_scn_bitmap);
                if (uid == 0) {
                        ec = ISNS_RSP_SCN_REGIS_REJECTED;
                }
                if (ec == 0) {
                        e->uid = uid;
                        (void) strcpy((char *)name, (char *)node);
                        e->name = name;
                        e->nlen = nlen;
                        e->bitmap = bitmap;
                        e->portal.p = p;
                        e->scn = NULL;
                        e->next = NULL;
                        (void) queue_msg_set(scn_q, SCN_ADD, (void *)e);
                }
        } else {
                ec = ISNS_RSP_INTERNAL_ERROR;
        }

        if (ec != 0) {
                free(e);
                free(name);
                free_portal(p);
        }

        return (ec);
}

/*
 * ****************************************************************************
 *
 * remove_scn_entry:
 *      Remove a SCN entry.
 *
 * node - the Storage Node name.
 * return - error code.
 *
 * ****************************************************************************
 */
int
remove_scn_entry(
        uchar_t *node
)
{
        int ec = 0;

        lookup_ctrl_t lc;
        uint32_t uid;

        lc.type = OBJ_ISCSI;
        lc.curr_uid = 0;
        lc.id[0] = ATTR_INDEX_ISCSI(ISNS_ISCSI_NAME_ATTR_ID);
        lc.data[0].ptr = node;
        lc.op[0] = OP_STRING;
        lc.op[1] = 0;
        lc.data[2].ui = 0;
        ec = cache_lookup(&lc, &uid, cb_update_scn_bitmap);
        if (ec == 0 && uid != 0) {
                (void) queue_msg_set(scn_q, SCN_REMOVE, (void *)uid);
        }

        return (ec);
}

/*
 * ****************************************************************************
 *
 * remove_scn_portal:
 *      Remove a portal from every SCN entry.
 *
 * uid  - the Portal object UID.
 * return - alrays successful (0).
 *
 * ****************************************************************************
 */
int
remove_scn_portal(
        uint32_t uid
)
{
        (void) queue_msg_set(scn_q, SCN_REMOVE_P, (void *)uid);

        return (0);
}

/*
 * ****************************************************************************
 *
 * scn_proc:
 *      The entry point of the SCN thread. It listens on the SCN message
 *      queue and process every SCN related stuff.
 *
 * arg  - nothing.
 * return - NULL.
 *
 * ****************************************************************************
 */
void *
scn_proc(
        /* LINTED E_FUNC_ARG_UNUSED */
        void *arg
)
{
        int state = 0;

        scn_raw_t *raw;
        msg_text_t *msg;

        for (;;) {
                msg = queue_msg_get(scn_q);
                switch (msg->id) {
                case SCN_ADD:
                        (void) scn_list_add((scn_registry_t *)msg->data);
                        break;
                case SCN_REMOVE:
                        (void) scn_list_remove((uint32_t)msg->data);
                        break;
                case SCN_REMOVE_P:
                        (void) scn_remove_portal((uint32_t)msg->data);
                        break;
                case SCN_SET:
                        raw = (scn_raw_t *)msg->data;
                        state = scn_transition(state, raw);
                        /* free the raw data */
                        free_raw(raw);
                        break;
                case SCN_TRIGGER:
                        if (scn_dispatched != 0) {
                                (void) scn_trigger();
                        }
                        /* FALLTHROUGH */
                case SCN_IGNORE:
                        /* clean the scn(s) */
                        free_scn();
                        /* reset the state */
                        state = 0;
                        /* reset the scn_dispatched flag */
                        scn_dispatched = 0;
                        break;
                case SCN_STOP:
                        queue_msg_free(msg);
                        return (NULL);
                default:
                        break;
                }
                queue_msg_free(msg);
        }
}