scn.c revision fcf3ce441efd61da9bb2884968af01cb7c1452cc
/*
* 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);
}
}