OpenSolaris_b135/cmd/isns/isnsd/scn.c

Compare this file to the similar file:
Show the results in this format:

/*
 * 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();
			}
		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);
	}
}