OpenSolaris_b135/cmd/isns/isnsd/dump.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.
 */

/* This file is getting large unexpectly, a lex & yacc */
/* implementation is expected. */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>

#include "isns_server.h"
#include "isns_htab.h"
#include "isns_msgq.h"
#include "isns_obj.h"
#include "isns_func.h"
#include "isns_dd.h"
#include "isns_cache.h"
#include "isns_pdu.h"

#ifdef DEBUG
/*
 * external variables
 */
extern const int NUM_OF_CHILD[MAX_OBJ_TYPE];
extern const int TYPE_OF_CHILD[MAX_OBJ_TYPE][MAX_CHILD_TYPE];
extern const int UID_ATTR_INDEX[MAX_OBJ_TYPE_FOR_SIZE];
extern const int NUM_OF_REF[MAX_OBJ_TYPE_FOR_SIZE];

extern lookup_ctrl_t *setup_ddid_lcp(lookup_ctrl_t *, uint32_t);
extern lookup_ctrl_t *setup_ddsid_lcp(lookup_ctrl_t *, uint32_t);

/*
 * global variables
 */
int verbose_mc = 0;
int verbose_tc = 0;
int verbose_lock = 0;
int verbose_net = 0;
int verbose_parser = 0;

/*
 * local variables
 */
static void print_entity(char *, isns_obj_t *);
static void print_iscsi(char *, isns_obj_t *);
static void print_portal(char *, isns_obj_t *);
static void print_pg(char *, isns_obj_t *);
static void print_dd(char *, isns_obj_t *);
static void print_dds(char *, isns_obj_t *);
static void (*const print_func[MAX_OBJ_TYPE])(char *, isns_obj_t *) = {
	NULL,
	&print_entity,
	&print_iscsi,
	&print_portal,
	&print_pg,
	&print_dd,
	&print_dds
};
static int run_cmd(char *);

typedef struct {
	uint16_t func_id;
	char *fname;
} isnsp_fnames_t;
isnsp_fnames_t fnames[] = {
{ ISNS_DEV_ATTR_REG, "DevAttrReg" },
{ ISNS_DEV_ATTR_QRY, "DevAttrQry" },
{ ISNS_DEV_GET_NEXT, "DevGetNext" },
{ ISNS_DEV_DEREG, "DevDereg" },
{ ISNS_SCN_REG, "SCNReg" },
{ ISNS_SCN_DEREG, "SCNDereg" },
{ ISNS_DD_REG, "DDReg" },
{ ISNS_DD_DEREG, "DDDereg" },
{ ISNS_DDS_REG, "DDSReg" },
{ ISNS_DDS_DEREG, "DDSDereg" },
{ ISNS_SCN, "SCN" },
{ ISNS_ESI, "ESI" },
{ ISNS_HEARTBEAT, "Heartbeat" },
{ ISNS_DEV_ATTR_REG_RSP, "DevAttrRegRsp" },
{ ISNS_DEV_ATTR_QRY_RSP, "DevAttrQryRsp" },
{ ISNS_DEV_GET_NEXT_RSP, "DevGetNextRsp" },
{ ISNS_DEV_DEREG_RSP, "DevDeregRsp" },
{ ISNS_SCN_REG_RSP, "SCNRegRsp" },
{ ISNS_SCN_DEREG_RSP, "SCNDeregRsp" },
{ ISNS_SCN_RSP, "SCNRsp" },
{ ISNS_ESI_RSP, "ESIRsp" },
{ 0xFFFF, "Unknown" } };

static char *
get_func_name(
	uint16_t id
)
{
	int i = 0;
	isnsp_fnames_t *fp = &fnames[i ++];
	while (fp->func_id != 0xFFFF) {
		if (fp->func_id == id) {
			return (fp->fname);
		}
		fp = &fnames[i ++];
	}

	return ("UNKNOWN");
}

static char *
get_tlv_tag_name(
	uint32_t tag
)
{
	switch (tag) {
		case ISNS_DELIMITER_ATTR_ID:
			return ("Delimiter");
		case ISNS_EID_ATTR_ID:
			return ("Entity Identifier");
		case ISNS_ENTITY_PROTOCOL_ATTR_ID:
			return ("Entity Protocol");
		case ISNS_ENTITY_REG_PERIOD_ATTR_ID:
			return ("Registration Period");
		case ISNS_TIMESTAMP_ATTR_ID:
			return ("Timestamp");
		case ISNS_PORTAL_IP_ADDR_ATTR_ID:
			return ("Portal IP Address");
		case ISNS_PORTAL_PORT_ATTR_ID:
			return ("Portal TCP/UDP Port");
		case ISNS_PORTAL_NAME_ATTR_ID:
			return ("Portal Symbolic Name");
		case ISNS_ESI_INTERVAL_ATTR_ID:
			return ("ESI Interval");
		case ISNS_ESI_PORT_ATTR_ID:
			return ("ESI Port");
		case ISNS_SCN_PORT_ATTR_ID:
			return ("SCN Port");
		case ISNS_PORTAL_SEC_BMP_ATTR_ID:
			return ("Portal Security Bitmap");
		case ISNS_ISCSI_NAME_ATTR_ID:
			return ("iSCSI Name");
		case ISNS_ISCSI_NODE_TYPE_ATTR_ID:
			return ("iSCSI Node Type");
		case ISNS_ISCSI_ALIAS_ATTR_ID:
			return ("iSCSI Alias");
		case ISNS_ISCSI_AUTH_METHOD_ATTR_ID:
			return ("iSCSI Auth Method");
		case ISNS_ISCSI_SCN_BITMAP_ATTR_ID:
			return ("iSCSI SCN Bitmap");
		case ISNS_PG_ISCSI_NAME_ATTR_ID:
			return ("PG iSCSI Name");
		case ISNS_PG_PORTAL_IP_ADDR_ATTR_ID:
			return ("PG Portal IP Addr");
		case ISNS_PG_PORTAL_PORT_ATTR_ID:
			return ("PG Portal TCP/UDP Port");
		case ISNS_PG_TAG_ATTR_ID:
			return ("PG Tag (PGT)");
		case ISNS_PG_INDEX_ATTR_ID:
			return ("PG Index");
		case ISNS_DD_NAME_ATTR_ID:
			return ("DD Name");
		case ISNS_DD_ID_ATTR_ID:
			return ("DD Index");
		case ISNS_DD_ISCSI_INDEX_ATTR_ID:
			return ("DD ISCSI Node Index");
		case ISNS_DD_ISCSI_NAME_ATTR_ID:
			return ("DD ISCSI Node Name");
		case ISNS_DD_SET_NAME_ATTR_ID:
			return ("DDS Name");
		case ISNS_DD_SET_ID_ATTR_ID:
			return ("DDS Index");
		case ISNS_DD_SET_STATUS_ATTR_ID:
			return ("DDS Status");
		default:
			return ("Unknown");
	}
}

static void
dump_pdu(
	isns_pdu_t *pdu,
	int flag
)
{
	short ver, id, len, flags, xid, seq;

	uint8_t *payload = pdu->payload;
	isns_resp_t *resp;

	/* convert the data */
	if (flag) {
		ver = ntohs(pdu->version);
		id = ntohs(pdu->func_id);
		len = ntohs(pdu->payload_len);
		flags = ntohs(pdu->flags) & 0xFFFF;
		xid = ntohs(pdu->xid);
		seq = ntohs(pdu->seq);
	} else {
		ver = pdu->version;
		id = pdu->func_id;
		len = pdu->payload_len;
		flags = pdu->flags & 0xFFFF;
		xid = pdu->xid;
		seq = pdu->seq;
	}

	/* print the pdu header */
	printf("iSNSP Version: %d\n", ver);
	printf("Function ID: %s\n", get_func_name(id));
	printf("PDU Length: %d\n", len);
	printf("Flags: %x\n", flags);
	printf("    %d... .... .... .... : ISNS_FLAG_CLIENT\n",
	    ((flags & ISNS_FLAG_CLIENT) == 0) ? 0 : 1);
	printf("    .%d.. .... .... .... : ISNS_FLAG_SERVER\n",
	    ((flags & ISNS_FLAG_SERVER) == 0) ? 0 : 1);
	printf("    ..%d. .... .... .... : ISNS_FLAG_AUTH_BLK_PRESENTED\n",
	    ((flags & ISNS_FLAG_AUTH_BLK_PRESENTED) == 0) ? 0 : 1);
	printf("    ...%d .... .... .... : ISNS_FLAG_REPLACE_REG\n",
	    ((flags & ISNS_FLAG_REPLACE_REG) == 0) ? 0 : 1);
	printf("    .... %d... .... .... : ISNS_FLAG_LAST_PDU\n",
	    ((flags & ISNS_FLAG_LAST_PDU) == 0) ? 0 : 1);
	printf("    .... .%d.. .... .... : ISNS_FLAG_FIRST_PDU\n",
	    ((flags & ISNS_FLAG_FIRST_PDU) == 0) ? 0 : 1);
	printf("Transaction ID: %d\n", xid);
	printf("Sequence ID: %d\n", seq);

	printf("Payload: ...\n");
	if (id & ISNS_RSP_MASK) {
		resp = (isns_resp_t *)payload;
		printf("    ErrorCode: %d\n", ntohl(resp->status));
		len -= 4;
		payload += 4;
	}

	/* print the payload */
	while (len > 0) {
		isns_tlv_t *tlvp;
		int t, l;
		uint8_t *v;
		char *s;
		int i;
		in6_addr_t *ip;
		char pbuff[256] = { 0 };

		tlvp = (isns_tlv_t *)payload;

		/* convert the data */
		t = ntohl(tlvp->attr_id);
		l = ntohl(tlvp->attr_len);
		v = &(tlvp->attr_value[0]);

		/* print payload */
		if (l > 0) {
			printf("%s: ", get_tlv_tag_name(t));
			switch (t) {
				case ISNS_EID_ATTR_ID:
				case ISNS_ISCSI_NAME_ATTR_ID:
				case ISNS_ISCSI_ALIAS_ATTR_ID:
				case ISNS_ISCSI_AUTH_METHOD_ATTR_ID:
				case ISNS_PG_ISCSI_NAME_ATTR_ID:
				case ISNS_DD_NAME_ATTR_ID:
				case ISNS_DD_SET_NAME_ATTR_ID:
					s = (char *)v;
					printf("%s\n", s);
					break;
				case ISNS_ENTITY_PROTOCOL_ATTR_ID:
					i = ntohl(*(uint32_t *)v);
					printf("%s (%d)\n",
					    ((i == 1) ? "No Protocol" :
					    ((i == 2) ? "iSCSI" :
					    ((i == 3) ? "iFCP" :
					    "Others"))),
					    i);
					break;
				case ISNS_PORTAL_IP_ADDR_ATTR_ID:
				case ISNS_PG_PORTAL_IP_ADDR_ATTR_ID:
					ip = (in6_addr_t *)v;
					inet_ntop(AF_INET6, (void *)ip,
					    pbuff, sizeof (pbuff));
					printf("%s\n", pbuff);
					break;
				case ISNS_PORTAL_PORT_ATTR_ID:
				case ISNS_ESI_PORT_ATTR_ID:
				case ISNS_SCN_PORT_ATTR_ID:
					i = ntohl(*(uint32_t *)v);
					printf("%d\n", (i & 0x0000FFFF));
					printf("    .... .... %d... .... : "
					    "0=TCP\n",
					    ((i & 0x10000) == 0) ? 0 : 1);
					break;
				case ISNS_ISCSI_NODE_TYPE_ATTR_ID:
					i = ntohl(*(uint32_t *)v);
					printf("0x%x\t", i);
					if (i & ISNS_CONTROL_NODE_TYPE) {
						printf("Control ");
					}
					if (i & ISNS_INITIATOR_NODE_TYPE) {
						printf("Initiator ");
					}
					if (i & ISNS_TARGET_NODE_TYPE) {
						printf("Target ");
					}
					printf("\n");
					break;
				case ISNS_PG_TAG_ATTR_ID:
				default:
					i = ntohl(*(uint32_t *)v);
					printf("%d\n", i);
					break;
			}
			printf("    Attribute Tag: %s (%d)\n",
			    get_tlv_tag_name(t), t);
			printf("    Attribute Length: %d\n", l);
		} else {
			printf("%s: (%d)\n", get_tlv_tag_name(t), t);
		}

		len -= (sizeof (uint32_t) * 2 + l);
		payload += (sizeof (uint32_t) * 2 + l);
	}
}

void
dump_pdu1(
	isns_pdu_t *pdu
)
{
	if (verbose_net) {
		printf("### PDU RECEIVED ###\n");
		dump_pdu(pdu, 0);
	}
}

void
dump_pdu2(
	isns_pdu_t *pdu
)
{
	if (verbose_net) {
		printf("### PDU SENT ###\n");
		dump_pdu(pdu, 1);
	}
}

void
dump_db(
)
{
#if 0
	isns_list_t *list, *lista, *listb;
	isns_dds_t *dds;
	isns_dd_t *dd;
	isns_iscsi2_t *iscsi2;

	printf("### DUMP DATABASE ###\n");
	/* dump dds(s) */
	list = dds_list;
	while (list != NULL) {
		dds = list->obj.dds;
		printf("[DDS:%d]%s(%s)\n", dds->id, dds->name,
		    dds->status ? "enabled" : "disabled");
		lista = dds->dd_list;
		/* dd(s) that belong to this dds */
		while (lista != NULL) {
			dd = lista->obj.dd;
			printf("\t[DD:%d]%s\n", dd->id, dd->name);
			lista = lista->next;
		}
		list = list->next;
	}
	/* dump dd(s) */
	list = dd_list;
	while (list != NULL) {
		dd = list->obj.dd;
		printf("[DD:%d]%s\n", dd->id, dd->name);
		/* dds(s) this dd belongs to */
		lista = dd->dds_list;
		while (lista != NULL) {
			dds = lista->obj.dds;
			printf("\t[DDS:%d]%s\n", dds->id, dds->name);
			lista = lista->next;
		}
		/* node(s) that this dd have */
		listb = dd->iscsi_list;
		while (listb != NULL) {
			iscsi2 = listb->obj.iscsi2;
			printf("\t[ISCSI:%d]%s\n", iscsi2->id, iscsi2->name);
			listb = listb->next;
		}
		list = list->next;
	}
	/* dump node(s) */
	list = iscsi_list;
	while (list != NULL) {
		iscsi2 = list->obj.iscsi2;
		printf("[ISCSI:%d]%s\n", iscsi2->id, iscsi2->name);
		lista = iscsi2->dd_list;
		/* dd(s) that this node belongs to */
		while (lista != NULL) {
			dd = lista->obj.dd;
			printf("\t[DD:%d]%s\n", dd->id, dd->name);
			lista = lista->next;
		}
		list = list->next;
	}
#endif
}

static void
test_cli_help(
)
{
	printf("list          - list all of storage node.\n");
	printf("list dd  [id] - list all of dd or one with member.\n");
	printf("list dds [id] - list all of dd-set or one with member.\n");

	printf("\n");
	printf("new dd  <name>  - create a dd with name.\n");
	printf("new dds <name>  - create a dd-set with name.\n");
	printf("new ddn  <id> <name>  - create a dd with id and name.\n");
	printf("new ddsn <id> <name>  - create a dd-set with id and name.\n");
	printf("del dd   <id>   - delete a dd.\n");
	printf("del dds  <id>   - delete a dd-set.\n");

	printf("\n");
	printf("add dd   <dd_id>  <node_name> - add a node to dd.\n");
	printf("add ddn  <dd_id>  <node_id>   - add a node to dd.\n");
	printf("add ddsn <dds_id> <dd_id>     - add a dd to dd-set.\n");
	printf("remove dd   <dd_id> <node_name> - remove a node from dd.\n");
	printf("remove ddn  <dd_id> <node_id>   - remove a node from dd.\n");
	printf("remove ddsn <dds_id> <dd_id>    - remove a dd from dd-set.\n");

	printf("\n");
	printf("enable  <dds_id> - enable a dd-set.\n");
	printf("disable <dds_id> - disable a dd-set.\n");

	printf("\n");
	printf("file <f> - loading command from a file.\n");
	printf("pause    - suspend batch until enter key is pressed.\n");

	printf("help   - print this help.\n");
	printf("quit   - stop iSNS server and quit.\n");
}

static enum {
	CMD_LIST, CMD_LISTNE, CMD_LISTP, CMD_LISTPG,
	CMD_LISTDD, CMD_LISTDDS, CMD_LISTDDN, CMD_LISTDDSN,
	CMD_NEWDD, CMD_NEWDDS, CMD_NEWDDN, CMD_NEWDDSN,
	CMD_DELDD, CMD_DELDDS,
	CMD_ENABLE, CMD_DISABLE,
	CMD_ADDDD, CMD_ADDDDN, CMD_ADDDDSN,
	CMD_REMDD, CMD_REMDDN, CMD_REMDDSN,
	CMD_VIEW,
	CMD_FILE, CMD_PAUSE,
	CMD_HELP,
	CMD_VERBOSE_MEMORY, CMD_VERBOSE_NET,
	CMD_VERBOSE_PARSER, CMD_VERBOSE_TIME,
	CMD_VERBOSE_LOCK,
	CMD_QUIT,
	CMD_NONE, CMD_INVALID
};

static int
getcmd(
	int *argc, int *argv, char *cmd
)
{
	int j = 0;
	char tmp[256] = { 0 };
	*argc = 0;
	while (*cmd == ' ') cmd ++;

	if (*cmd == 0) {
		return (CMD_NONE);
	} else if (*cmd == '?') {
		return (CMD_HELP);
	}

	/* list, list dd, list dds, list dd 0 */
	if (strncmp(cmd, "list ", 5) == 0) {
		cmd += 5;
		while (*cmd == ' ') cmd ++;
		if (*cmd == 0) {
			return (CMD_LIST);
		} else if (*cmd == 'p') {
			cmd ++;
			while (*cmd == ' ') cmd ++;
			if (*cmd == 0) {
				return (CMD_LISTP);
			}
		} else if (*cmd == 'g') {
			cmd ++;
			while (*cmd == ' ') cmd ++;
			if (*cmd == 0) {
				return (CMD_LISTPG);
			}
		} else if (*cmd == 'e') {
			cmd ++;
			while (*cmd == ' ') cmd ++;
			if (*cmd == 0) {
				return (CMD_LISTNE);
			}
		} else if (strncmp(cmd, "dds ", 4) == 0) {
			cmd += 4;
			while (*cmd == ' ') cmd ++;
			if (*cmd == 0) {
				return (CMD_LISTDDS);
			}
			j = 0;
			while (*cmd >= '0' && *cmd <= '9') {
				tmp[j++] = *cmd ++;
			}
			tmp[j] = 0;
			while (*cmd == ' ') cmd ++;
			if (*cmd == 0 && j > 0) {
				argv[(*argc)++] = atoi(tmp);
				return (CMD_LISTDDSN);
			}
		} else if (strncmp(cmd, "dd ", 3) == 0) {
			cmd += 3;
			while (*cmd == ' ') cmd ++;
			if (*cmd == 0) {
				return (CMD_LISTDD);
			}
			j = 0;
			while (*cmd >= '0' && *cmd <= '9') {
				tmp[j++] = *cmd ++;
			}
			tmp[j] = 0;
			while (*cmd == ' ') cmd ++;
			if (*cmd == 0 && j > 0) {
				argv[(*argc)++] = atoi(tmp);
				return (CMD_LISTDDN);
			}
		}
		return (CMD_INVALID);
	}

	/* view 0 */
	if (strncmp(cmd, "view ", 5) == 0) {
		cmd += 5;
		while (*cmd == ' ') cmd ++;
		j = 0;
		while (*cmd >= '0' && *cmd <= '9') {
			tmp[j++] = *cmd ++;
		}
		tmp[j] = 0;
		while (*cmd == ' ') cmd ++;
		if (*cmd == 0 && j > 0) {
			argv[(*argc)++] = atoi(tmp);
			return (CMD_VIEW);
		}
		return (CMD_INVALID);
	}

	/* add dd name */
	/* add ddn/ddsn id id */
	if (strncmp(cmd, "add ", 4) == 0) {
		int addcmd = CMD_INVALID;
		cmd += 4;
		while (*cmd == ' ') cmd ++;
		if (strncmp(cmd, "dd ", 3) == 0) {
			cmd += 3;
			addcmd = CMD_ADDDD;
		} else if (strncmp(cmd, "ddn ", 4) == 0) {
			cmd += 4;
			addcmd = CMD_ADDDDN;
		} else if (strncmp(cmd, "ddsn ", 5) == 0) {
			cmd += 5;
			addcmd = CMD_ADDDDSN;
		} else {
			return (CMD_INVALID);
		}
		while (*cmd == ' ') cmd ++;
		j = 0;
		while (*cmd >= '0' && *cmd <= '9') {
			tmp[j++] = *cmd ++;
		}
		tmp[j] = 0;
		if (j > 0) {
			argv[(*argc)++] = atoi(tmp);
		} else {
			return (CMD_INVALID);
		}
		while (*cmd == ' ') cmd ++;
		if (*cmd != 0) {
			switch (addcmd) {
			case CMD_ADDDDN:
			case CMD_ADDDDSN:
				j = 0;
				while (*cmd >= '0' && *cmd <= '9') {
					tmp[j++] = *cmd ++;
				}
				tmp[j] = 0;
				while (*cmd == ' ') cmd ++;
				if (*cmd == 0 && j > 0) {
					argv[(*argc)++] = atoi(tmp);
				} else {
					return (CMD_INVALID);
				}
				break;
			case CMD_ADDDD:
				j = strlen(cmd);
				while (j > 0) {
					/* get rid of trail blank space */
					if (cmd[j - 1] == ' ') {
						cmd[--j] = 0;
					} else {
						break;
					}
				}
				if (j > 0) {
					cmd[j] = 0;
					argv[(*argc)++] = (int)cmd;
				} else {
					return (CMD_INVALID);
				}
				break;
			}
			return (addcmd);
		}
		return (CMD_INVALID);
	}

	/* remove dd name */
	/* remove ddn/ddsn id id */
	if (strncmp(cmd, "remove ", 7) == 0) {
		int rmcmd = CMD_INVALID;
		cmd += 7;
		while (*cmd == ' ') cmd ++;
		if (strncmp(cmd, "dd ", 3) == 0) {
			cmd += 3;
			while (*cmd == ' ') cmd ++;
			rmcmd = CMD_REMDD;
		} else if (strncmp(cmd, "ddn ", 4) == 0) {
			cmd += 4;
			while (*cmd == ' ') cmd ++;
			rmcmd = CMD_REMDDN;
		} else if (strncmp(cmd, "ddsn ", 5) == 0) {
			cmd += 5;
			while (*cmd == ' ') cmd ++;
			rmcmd = CMD_REMDDSN;
		} else {
			return (CMD_INVALID);
		}
		j = 0;
		while (*cmd >= '0' && *cmd <= '9') {
			tmp[j++] = *cmd ++;
		}
		tmp[j] = 0;
		if (j > 0) {
			argv[(*argc)++] = atoi(tmp);
		} else {
			return (CMD_INVALID);
		}
		while (*cmd == ' ') cmd ++;
		if (*cmd != 0) {
			switch (rmcmd) {
			case CMD_REMDDN:
			case CMD_REMDDSN:
				j = 0;
				while (*cmd >= '0' && *cmd <= '9') {
					tmp[j++] = *cmd ++;
				}
				tmp[j] = 0;
				while (*cmd == ' ') cmd ++;
				if (*cmd == 0 && j > 0) {
					argv[(*argc)++] = atoi(tmp);
				} else {
					return (CMD_INVALID);
				}
				break;
			case CMD_REMDD:
				j = strlen(cmd);
				while (j > 0) {
					/* get rid of trail blank space */
					if (cmd[j - 1] == ' ') {
						cmd[--j] = 0;
					} else {
						break;
					}
				}
				if (j > 0) {
					cmd[j] = 0;
					argv[(*argc)++] = (int)cmd;
				} else {
					return (CMD_INVALID);
				}
				break;
			}
			return (rmcmd);
		}
		return (CMD_INVALID);
	}

	/* new dd, new dds */
	if (strncmp(cmd, "new ", 4) == 0) {
		int newcmd = CMD_INVALID;
		cmd += 4;
		while (*cmd == ' ') cmd ++;
		if (strncmp(cmd, "dd ", 3) == 0) {
			cmd += 3;
			newcmd = CMD_NEWDD;
		} else if (strncmp(cmd, "dds ", 4) == 0) {
			cmd += 4;
			newcmd = CMD_NEWDDS;
		} else if (strncmp(cmd, "ddn ", 4) == 0) {
			cmd += 4;
			newcmd = CMD_NEWDDN;
		} else if (strncmp(cmd, "ddsn ", 5) == 0) {
			cmd += 5;
			newcmd = CMD_NEWDDSN;
		}
		if (newcmd != CMD_INVALID) {
			while (*cmd == ' ') cmd ++;
			if (*cmd == 0) {
				return (newcmd);
			}
			switch (newcmd) {
			case CMD_NEWDDN:
			case CMD_NEWDDSN:
				j = 0;
				while (*cmd >= '0' && *cmd <= '9') {
					tmp[j++] = *cmd ++;
				}
				tmp[j] = 0;
				if (*cmd == ' ' && j > 0) {
					argv[(*argc)++] = atoi(tmp);
				} else {
					return (CMD_INVALID);
				}
			case CMD_NEWDD:
			case CMD_NEWDDS:
				while (*cmd == ' ') cmd ++;
				if (*cmd != 0) {
					j = strlen(cmd);
				} else {
					j = 0;
				}
				while (j > 0) {
					/* get rid of trail blank space */
					if (cmd[j - 1] == ' ') {
						cmd[--j] = 0;
					} else {
						break;
					}
				}
				if (j > 0) {
					cmd[j] = 0;
					argv[(*argc)++] = (int)cmd;
				}
			}
			return (newcmd);
		}
		return (CMD_INVALID);
	}

	/* del dd, del dds, disable 0 */
	if (strncmp(cmd, "del ", 4) == 0) {
		int delcmd = CMD_INVALID;
		cmd += 4;
		while (*cmd == ' ') cmd ++;
		if (strncmp(cmd, "dds ", 4) == 0) {
			cmd += 4;
			delcmd = CMD_DELDDS;
		} else if (strncmp(cmd, "dd ", 3) == 0) {
			cmd += 3;
			delcmd = CMD_DELDD;
		}
		if (delcmd != CMD_INVALID) {
			while (*cmd == ' ') cmd ++;
			j = 0;
			while (*cmd >= '0' && *cmd <= '9') {
				tmp[j++] = *cmd ++;
			}
			tmp[j] = 0;
			while (*cmd == ' ') cmd ++;
			if (*cmd == 0 && j > 0) {
				argv[(*argc)++] = atoi(tmp);
				return (delcmd);
			}
		}
		return (CMD_INVALID);
	}

	/* enable 0 */
	if (strncmp(cmd, "enable ", 7) == 0) {
		cmd += 7;
		while (*cmd == ' ') cmd ++;
		j = 0;
		while (*cmd >= '0' && *cmd <= '9') {
			tmp[j++] = *cmd ++;
		}
		tmp[j] = 0;
		while (*cmd == ' ') cmd ++;
		if (*cmd == 0 && j > 0) {
			argv[(*argc)++] = atoi(tmp);
			return (CMD_ENABLE);
		}
		return (CMD_INVALID);
	}

	/* disable 0 */
	if (strncmp(cmd, "disable ", 8) == 0) {
		cmd += 8;
		while (*cmd == ' ') cmd ++;
		j = 0;
		while (*cmd >= '0' && *cmd <= '9') {
			tmp[j++] = *cmd ++;
		}
		tmp[j] = 0;
		while (*cmd == ' ') cmd ++;
		if (*cmd == 0 && j > 0) {
			argv[(*argc)++] = atoi(tmp);
			return (CMD_DISABLE);
		}
		return (CMD_INVALID);
	}

	/* file */
	if (strncmp(cmd, "file ", 5) == 0) {
		cmd += 5;
		while (*cmd == ' ') cmd ++;
		if (*cmd != 0) {
			j = strlen(cmd);
		} else {
			j = 0;
		}
		while (j > 0) {
			/* get rid of trail blank space */
			if (cmd[j - 1] == ' ') {
				cmd[--j] = 0;
			} else {
				break;
			}
		}
		if (j > 0) {
			cmd[j] = 0;
			argv[(*argc)++] = (int)cmd;
			return (CMD_FILE);
		}
		return (CMD_INVALID);
	}

	/* pause */
	if (strncmp(cmd, "pause ", 6) == 0) {
		cmd += 6;
		while (*cmd == ' ') cmd ++;
		if (*cmd == 0) {
			return (CMD_PAUSE);
		}
		return (CMD_INVALID);
	}

	/* help */
	if (strncmp(cmd, "help ", 5) == 0) {
		cmd += 5;
		while (*cmd == ' ') cmd ++;
		if (*cmd == 0) {
			return (CMD_HELP);
		}
		return (CMD_INVALID);
	}

	/* verbose */
	if (strncmp(cmd, "verbose ", 8) == 0) {
		cmd += 8;
		while (*cmd == ' ') cmd ++;
		if (*cmd == 0) {
			return (CMD_VERBOSE_PARSER);
		} else if (*cmd == 'm') {
			cmd ++;
			while (*cmd == ' ') cmd ++;
			if (*cmd == 0) {
				return (CMD_VERBOSE_MEMORY);
			}
		} else if (*cmd == 'n') {
			cmd ++;
			while (*cmd == ' ') cmd ++;
			if (*cmd == 0) {
				return (CMD_VERBOSE_NET);
			}
		} else if (*cmd == 'p') {
			cmd ++;
			while (*cmd == ' ') cmd ++;
			if (*cmd == 0) {
				return (CMD_VERBOSE_PARSER);
			}
		} else if (*cmd == 't') {
			cmd ++;
			while (*cmd == ' ') cmd ++;
			if (*cmd == 0) {
				return (CMD_VERBOSE_TIME);
			}
		} else if (*cmd == 'l') {
			cmd ++;
			while (*cmd == ' ') cmd ++;
			if (*cmd == 0) {
				return (CMD_VERBOSE_LOCK);
			}
		}
		return (CMD_INVALID);
	}

	/* quit */
	if (strncmp(cmd, "quit ", 5) == 0) {
		cmd += 5;
		while (*cmd == ' ') cmd ++;
		if (*cmd == 0) {
			return (CMD_QUIT);
		}
		return (CMD_INVALID);
	}

	return (CMD_INVALID);
}

static void
print_entity(
	char *ident,
	isns_obj_t *obj
)
{
	uint32_t uid;
	uchar_t *eid;
	uint32_t *cuid;
	int i, num;

	eid = obj->attrs[
	    ATTR_INDEX_ENTITY(ISNS_EID_ATTR_ID)].value.ptr;
	uid = get_obj_uid(obj);

	if (ident != NULL) {
		printf("%s%d\t%s\n", ident, uid, (const char *)eid);
	} else {
		printf("%d\t%s\n", uid, (const char *)eid);
	}

	i = 0;
	while (i < NUM_OF_CHILD[obj->type]) {
		cuid = get_child_n(obj, i);
		if (ident != NULL) {
			printf("%s\t%s%d:", "child", i);
		} else {
			printf("\t%s%d:", "child", i);
		}
		if (cuid != NULL) {
			num = *cuid ++;
		} else {
			num = 0;
		}
		while (num > 0) {
			printf("\t%d", *cuid ++);
			num --;
		}
		printf("\n");
		i ++;
	}
}

static void
print_iscsi(
	char *ident,
	isns_obj_t *obj
)
{
	uchar_t *name = obj->attrs[ATTR_INDEX_ISCSI(ISNS_ISCSI_NAME_ATTR_ID)]
	    .value.ptr;
	uchar_t *alias = obj->attrs[ATTR_INDEX_ISCSI(ISNS_ISCSI_ALIAS_ATTR_ID)]
	    .value.ptr;
	uint32_t type = obj->attrs[
	    ATTR_INDEX_ISCSI(ISNS_ISCSI_NODE_TYPE_ATTR_ID)].value.ui;
	uint32_t uid = get_obj_uid(obj);
	uint32_t puid = get_parent_uid(obj);

	if (!alias) {
		alias = (uchar_t *)"-";
	}

	if (ident != NULL) {
		printf("%s%d[%d]\t%s\n", ident,
		    uid, puid, (const char *)name);
		printf("%s\t%s", ident, alias);
	} else {
		printf("%d[%d]\t%s\n",
		    uid, puid, (const char *)name);
		printf("\t%s", alias);
	}
	if (IS_TYPE_TARGET(type)) {
		printf("\tTarget");
	}
	if (IS_TYPE_INITIATOR(type)) {
		printf("\tInitiator");
	}
	if (IS_TYPE_CONTROL(type)) {
		printf("\tControl");
	}
	if (IS_TYPE_UNKNOWN(type)) {
		printf("\t-");
	}
	printf("\n");
}

static void
print_portal(
	char *ident,
	isns_obj_t *obj
)
{
	char pbuff[256] = { 0 };
	in6_addr_t *ip = obj->attrs[
	    ATTR_INDEX_PORTAL(ISNS_PORTAL_IP_ADDR_ATTR_ID)].value.ip;
	uint32_t port = obj->attrs[
	    ATTR_INDEX_PORTAL(ISNS_PORTAL_PORT_ATTR_ID)].value.ui;
	uint32_t uid = get_obj_uid(obj);
	uint32_t puid = get_parent_uid(obj);

	inet_ntop(AF_INET6, (void *)ip, pbuff, sizeof (pbuff));
	if (ident != NULL) {
		printf("%s%d[%d]\t%s:%d", ident,
		    uid, puid, pbuff, PORT_NUMBER(port));
	} else {
		printf("%d[%d]\t%s:%d",
		    uid, puid, pbuff, PORT_NUMBER(port));
	}
	printf(" %s\n", IS_PORT_UDP(port) ? "UDP" : "TCP");
}

static void
print_pg(
	char *ident,
	isns_obj_t *obj
)
{
	uint32_t ref;
	int i;

	char pbuff[256] = { 0 };
	uchar_t *name = obj->attrs[ATTR_INDEX_PG(ISNS_PG_ISCSI_NAME_ATTR_ID)]
	    .value.ptr;
	in6_addr_t *ip = obj->attrs[
	    ATTR_INDEX_PG(ISNS_PG_PORTAL_IP_ADDR_ATTR_ID)].value.ip;
	uint32_t port = obj->attrs[
	    ATTR_INDEX_PG(ISNS_PG_PORTAL_PORT_ATTR_ID)].value.ui;
	uint32_t tag = obj->attrs[
	    ATTR_INDEX_PG(ISNS_PG_TAG_ATTR_ID)].value.ui;
	uint32_t uid = get_obj_uid(obj);
	uint32_t puid = get_parent_uid(obj);

	inet_ntop(AF_INET6, (void *)ip, pbuff, sizeof (pbuff));
	if (ident != NULL) {
		printf("%s%d[%d]\t[%d] %s\n", ident,
		    uid, puid, tag, (const char *)name);
		printf("%s\t%s:%d", ident, pbuff, PORT_NUMBER(port));
	} else {
		printf("%d[%d]\t[%d] %s\n",
		    uid, puid, tag, (const char *)name);
		printf("\t%s:%d", pbuff, PORT_NUMBER(port));
	}
	printf(" %s\n", IS_PORT_UDP(port) ? "UDP" : "TCP");

	if (NUM_OF_REF[obj->type] > 0) {
		if (ident != NULL) {
			printf("%s\t%s:", "ref");
		} else {
			printf("\t%s:", "ref");
		}
	}
	i = 0;
	while (i < NUM_OF_REF[obj->type]) {
		ref = get_ref_n(obj, i);
		printf("\t%d", ref);
		i ++;
	}
	if (i > 0) {
		printf("\n");
	}
}

static void
print_dd(
	char *ident,
	isns_obj_t *obj
)
{
	uchar_t *name = obj->attrs[ATTR_INDEX_DD(ISNS_DD_NAME_ATTR_ID)]
	    .value.ptr;
	uint32_t uid = obj->attrs[UID_ATTR_INDEX[OBJ_DD]].value.ui;

	if (ident != NULL) {
		printf("%s%d\t%s\n", ident, uid, (const char *)name);
	} else {
		printf("%d\t%s\n", uid, (const char *)name);
	}
}

static void
print_dds(
	char *ident,
	isns_obj_t *obj
)
{
	uchar_t *name = obj->attrs[ATTR_INDEX_DDS(
	    ISNS_DD_SET_NAME_ATTR_ID)].value.ptr;
	uint32_t uid = obj->attrs[UID_ATTR_INDEX[OBJ_DDS]].value.ui;
	uint32_t enabled = obj->attrs[ATTR_INDEX_DDS(
	    ISNS_DD_SET_STATUS_ATTR_ID)].value.ui;

	if (ident != NULL) {
		printf("%s%d\t%s\t\t(%s)\n", ident, uid,
		    (const char *)name, enabled ? "enabled" : "disabled");
	} else {
		printf("%d\t%s\t\t(%s)\n", uid,
		    (const char *)name, enabled ? "enabled" : "disabled");
	}
}

void
print_object(
	char *ident,
	isns_obj_t *obj
)
{
	print_func[obj->type](ident, obj);
}

/*ARGSUSED*/
static int
cb_print_obj_n(
	void *p1,
	void *p2
)
{
	isns_obj_t *obj = (isns_obj_t *)p1;
	print_func[obj->type](NULL, obj);

	return (0);
}

static void
list_pg(
)
{
	cache_dump_htab(OBJ_PG);
}

static void
list_portal(
)
{
	cache_dump_htab(OBJ_PORTAL);
}

static void
list_node(
)
{
	cache_dump_htab(OBJ_ISCSI);
}

static void
list_entity(
)
{
	cache_dump_htab(OBJ_ENTITY);
}

static void
list_dd(
)
{
	cache_dump_htab(OBJ_DD);
}

static void
list_ddn(
	uint32_t uid
)
{
	lookup_ctrl_t lc;

	bmp_t *p;
	uint32_t n;

	if (uid != 0) {
		setup_ddid_lcp(&lc, uid);
		cache_lookup(&lc, &uid, cb_print_obj_n);
	}

	if (uid != 0) {
		printf("--------------------------------\n");
		get_dd_matrix(uid, &p, &n);
		SET_UID_LCP(&lc, OBJ_ISCSI, 0);
		FOR_EACH_MEMBER(p, n, uid, {
			lc.data[0].ui = uid;
			cache_lookup(&lc, NULL, cb_print_obj_n);
		});
		free(p);
	} else {
		printf("no such dd.\n");
	}
}

static void
list_ddsn(
	uint32_t uid
)
{
	lookup_ctrl_t lc;

	bmp_t *p;
	uint32_t n;

	if (uid != 0) {
		setup_ddsid_lcp(&lc, uid);
		cache_lookup(&lc, &uid, cb_print_obj_n);
	}

	if (uid != 0) {
		printf("--------------------------------\n");
		get_dds_matrix(uid, &p, &n);
		SET_UID_LCP(&lc, OBJ_DD, 0);
		FOR_EACH_MEMBER(p, n, uid, {
			lc.data[0].ui = uid;
			cache_lookup(&lc, NULL, cb_print_obj_n);
		});
		free(p);
	} else {
		printf("no such dd-set.\n");
	}
}

static void
list_dds(
)
{
	cache_dump_htab(OBJ_DDS);
}

static void
new_dd_dds(
	int cmd_id,
	int argc,
	int *argv
)
{
	uint32_t buff[256];
	isns_pdu_t *pdu = (isns_pdu_t *)buff;
	uint8_t *payload = &pdu->payload[0];
	uint16_t payload_len = 0;
	isns_tlv_t *tlv;

	int len = 0;
	uint32_t uid = 0;
	char *name;

	conn_arg_t conn;

	pdu->version = ISNSP_VERSION;

	/* source attribute */
	tlv = (isns_tlv_t *)payload;
	tlv->attr_id = htonl(ISNS_ISCSI_NAME_ATTR_ID);
	tlv->attr_len = htonl(32);
	strcpy((char *)tlv->attr_value, "i am a control node.");
	payload += 8 + 32;
	payload_len += 8 + 32;

	/* key attributes */

	/* delimiter */
	tlv = (isns_tlv_t *)payload;
	tlv->attr_id = htonl(ISNS_DELIMITER_ATTR_ID);
	tlv->attr_len = htonl(0);
	payload += 8 + 0;
	payload_len += 8 + 0;

	/* operating attributes */
	switch (cmd_id) {
	case CMD_NEWDD:
		pdu->func_id = ISNS_DD_REG;
		if (argc == 1) {
			name = (char *)argv[0];
			len = strlen(name) + 1;
			len += 4 - (len % 4);
		}
		tlv = (isns_tlv_t *)payload;
		tlv->attr_id = htonl(ISNS_DD_NAME_ATTR_ID);
		tlv->attr_len = htonl(len);
		if (len > 0) {
			strcpy((char *)tlv->attr_value, name);
		}
		payload_len += 8 + len;
		break;
	case CMD_NEWDDS:
		pdu->func_id = ISNS_DDS_REG;
		if (argc == 1) {
			name = (char *)argv[0];
			len = strlen(name) + 1;
			len += 4 - (len % 4);
		}
		tlv = (isns_tlv_t *)payload;
		tlv->attr_id = htonl(ISNS_DD_SET_NAME_ATTR_ID);
		tlv->attr_len = htonl(len);
		if (len > 0) {
			strcpy((char *)tlv->attr_value, name);
		}
		payload_len += 8 + len;
		break;
	case CMD_NEWDDN:
		pdu->func_id = ISNS_DD_REG;
		switch (argc) {
		case 2:
			name = (char *)argv[1];
			len = strlen(name) + 1;
			len += 4 - (len % 4);
		case 1:
			uid = argv[0];
		}
		tlv = (isns_tlv_t *)payload;
		tlv->attr_id = htonl(ISNS_DD_NAME_ATTR_ID);
		tlv->attr_len = htonl(len);
		if (len > 0) {
			strcpy((char *)tlv->attr_value, name);
		}
		payload += 8 + len;
		payload_len += 8 + len;
		if (uid > 0) {
			tlv = (isns_tlv_t *)payload;
			tlv->attr_id = htonl(ISNS_DD_ID_ATTR_ID);
			tlv->attr_len = htonl(4);
			*(uint32_t *)tlv->attr_value = htonl(uid);
			payload_len += 8 + 4;
		}
		break;
	case CMD_NEWDDSN:
		pdu->func_id = ISNS_DDS_REG;
		switch (argc) {
		case 2:
			name = (char *)argv[1];
			len = strlen(name) + 1;
			len += 4 - (len % 4);
		case 1:
			uid = argv[0];
		}
		tlv = (isns_tlv_t *)payload;
		tlv->attr_id = htonl(ISNS_DD_SET_NAME_ATTR_ID);
		tlv->attr_len = htonl(len);
		if (len > 0) {
			strcpy((char *)tlv->attr_value, name);
		}
		payload_len += 8 + len;
		payload += 8 + len;
		if (uid > 0) {
			tlv = (isns_tlv_t *)payload;
			tlv->attr_id = htonl(ISNS_DD_SET_ID_ATTR_ID);
			tlv->attr_len = htonl(4);
			*(uint32_t *)tlv->attr_value = htonl(uid);
			payload_len += 8 + 4;
		}
		break;
	default:
		break;
	}

	pdu->payload_len = payload_len;

	dump_pdu1(pdu);

	conn.in_packet.pdu = pdu;
	conn.out_packet.pdu = NULL;
	conn.out_packet.sz = 0;

	if (packet_split_verify(&conn) == 0) {
		cache_lock(conn.lock);
		conn.handler(&conn);
		conn.ec = cache_unlock(conn.lock, conn.ec);
	}

	if (conn.out_packet.pdu != NULL) {
		pdu_update_code(conn.out_packet.pdu,
		    &conn.out_packet.pl, conn.ec);
		dump_pdu2(conn.out_packet.pdu);
		free(conn.out_packet.pdu);
	} else if (conn.ec != 0) {
		printf("operation failed[%d].\n", conn.ec);
	}
}

static void
del_dd_dds(
	int cmd_id,
	int uid
)
{
	uint32_t buff[256];
	isns_pdu_t *pdu = (isns_pdu_t *)buff;
	uint8_t *payload = &pdu->payload[0];
	uint16_t payload_len = 0;
	isns_tlv_t *tlv;

	uint32_t tag;

	conn_arg_t conn;

	if (uid == 0) {
		return;
	}

	pdu->version = ISNSP_VERSION;

	if (cmd_id == CMD_DELDD) {
		tag = ISNS_DD_ID_ATTR_ID;
		pdu->func_id = ISNS_DD_DEREG;
	} else {
		tag = ISNS_DD_SET_ID_ATTR_ID;
		pdu->func_id = ISNS_DDS_DEREG;
	}

	/* source attribute */
	tlv = (isns_tlv_t *)payload;
	tlv->attr_id = htonl(ISNS_ISCSI_NAME_ATTR_ID);
	tlv->attr_len = htonl(32);
	strcpy((char *)tlv->attr_value, "i am a control node.");
	payload_len += 8 + 32;
	payload += 8 + 32;

	/* key attributes */
	tlv = (isns_tlv_t *)payload;
	tlv->attr_id = htonl(tag);
	tlv->attr_len = htonl(4);
	*(uint32_t *)tlv->attr_value = htonl(uid);
	payload_len += 8 + 4;
	payload += 8 + 4;

	/* delimiter */
	tlv = (isns_tlv_t *)payload;
	tlv->attr_id = htonl(ISNS_DELIMITER_ATTR_ID);
	tlv->attr_len = htonl(0);
	payload_len += 8 + 0;
	payload += 8 + 0;

	/* operating attributes */

	pdu->payload_len = payload_len;

	dump_pdu1(pdu);

	conn.in_packet.pdu = pdu;
	conn.out_packet.pdu = NULL;
	conn.out_packet.sz = 0;

	if (packet_split_verify(&conn) == 0) {
		cache_lock(conn.lock);
		conn.handler(&conn);
		conn.ec = cache_unlock(conn.lock, conn.ec);
	}

	if (conn.out_packet.pdu != NULL) {
		pdu_update_code(conn.out_packet.pdu,
		    &conn.out_packet.pl, conn.ec);
		dump_pdu2(conn.out_packet.pdu);
		free(conn.out_packet.pdu);
	} else if (conn.ec != 0) {
		printf("operation failed[%d].\n", conn.ec);
	}
}

static void
update_dds(
	int cmd_id,
	int uid
)
{
	uint32_t buff[256];
	isns_pdu_t *pdu = (isns_pdu_t *)buff;
	uint8_t *payload = &pdu->payload[0];
	uint16_t payload_len = 0;
	isns_tlv_t *tlv;

	conn_arg_t conn;

	if (uid == 0) {
		return;
	}

	pdu->version = ISNSP_VERSION;

	pdu->func_id = ISNS_DDS_REG;

	/* source attribute */
	tlv = (isns_tlv_t *)payload;
	tlv->attr_id = htonl(ISNS_ISCSI_NAME_ATTR_ID);
	tlv->attr_len = htonl(32);
	strcpy((char *)tlv->attr_value, "i am a control node.");
	payload_len += 8 + 32;
	payload += 8 + 32;

	/* key attributes */
	tlv = (isns_tlv_t *)payload;
	tlv->attr_id = htonl(ISNS_DD_SET_ID_ATTR_ID);
	tlv->attr_len = htonl(4);
	*(uint32_t *)tlv->attr_value = htonl(uid);
	payload_len += 8 + 4;
	payload += 8 + 4;

	/* delimiter */
	tlv = (isns_tlv_t *)payload;
	tlv->attr_id = htonl(ISNS_DELIMITER_ATTR_ID);
	tlv->attr_len = htonl(0);
	payload_len += 8 + 0;
	payload += 8 + 0;

	/* operating attributes */
	tlv = (isns_tlv_t *)payload;
	tlv->attr_id = htonl(ISNS_DD_SET_STATUS_ATTR_ID);
	tlv->attr_len = htonl(4);
	if (cmd_id == CMD_ENABLE) {
		*(uint32_t *)tlv->attr_value = htonl(1);
	} else {
		*(uint32_t *)tlv->attr_value = htonl(0);
	}
	payload_len += 8 + 4;

	pdu->payload_len = payload_len;

	dump_pdu1(pdu);

	conn.in_packet.pdu = pdu;
	conn.out_packet.pdu = NULL;
	conn.out_packet.sz = 0;

	if (packet_split_verify(&conn) == 0) {
		cache_lock(conn.lock);
		conn.handler(&conn);
		conn.ec = cache_unlock(conn.lock, conn.ec);
	}

	if (conn.out_packet.pdu != NULL) {
		pdu_update_code(conn.out_packet.pdu,
		    &conn.out_packet.pl, conn.ec);
		dump_pdu2(conn.out_packet.pdu);
		free(conn.out_packet.pdu);
	} else if (conn.ec != 0) {
		printf("operation failed[%d].\n", conn.ec);
	}
}

static void
update_member(
	int cmd_id,
	int *argv
)
{
	uint32_t buff[256];
	isns_pdu_t *pdu = (isns_pdu_t *)buff;
	uint8_t *payload = &pdu->payload[0];
	uint16_t payload_len = 0;
	isns_tlv_t *tlv;
	uint32_t key_tag, op_tag, op_len;

	uint32_t uid = argv[0];
	uint32_t m_id;
	char *m_name;

	conn_arg_t conn;

	if (uid == 0) {
		printf("operation failed.\n");
		return;
	}

	pdu->version = ISNSP_VERSION;

	switch (cmd_id) {
	case CMD_ADDDD:
	case CMD_ADDDDN:
		pdu->func_id = ISNS_DD_REG;
		break;
	case CMD_REMDD:
	case CMD_REMDDN:
		pdu->func_id = ISNS_DD_DEREG;
		break;
	case CMD_ADDDDSN:
		pdu->func_id = ISNS_DDS_REG;
		break;
	case CMD_REMDDSN:
		pdu->func_id = ISNS_DDS_DEREG;
		break;
	}
	switch (cmd_id) {
	case CMD_ADDDD:
	case CMD_REMDD:
		key_tag = ISNS_DD_ID_ATTR_ID;
		op_tag = ISNS_DD_ISCSI_NAME_ATTR_ID;
		m_name = (char *)argv[1];
		op_len = strlen(m_name);
		op_len += 4 - (op_len % 4);
		break;
	case CMD_ADDDDN:
	case CMD_REMDDN:
		key_tag = ISNS_DD_ID_ATTR_ID;
		op_tag = ISNS_DD_ISCSI_INDEX_ATTR_ID;
		m_id = argv[1];
		op_len = 4;
		break;
	case CMD_ADDDDSN:
	case CMD_REMDDSN:
		key_tag = ISNS_DD_SET_ID_ATTR_ID;
		op_tag = ISNS_DD_ID_ATTR_ID;
		m_id = argv[1];
		op_len = 4;
		break;
	}

	/* source attribute */
	tlv = (isns_tlv_t *)payload;
	tlv->attr_id = htonl(ISNS_ISCSI_NAME_ATTR_ID);
	tlv->attr_len = htonl(32);
	strcpy((char *)tlv->attr_value, "i am a control node.");
	payload_len += 8 + 32;
	payload += 8 + 32;

	/* key attributes */
	tlv = (isns_tlv_t *)payload;
	tlv->attr_id = htonl(key_tag);
	tlv->attr_len = htonl(4);
	*(uint32_t *)tlv->attr_value = htonl(uid);
	payload_len += 8 + 4;
	payload += 8 + 4;

	/* delimiter */
	tlv = (isns_tlv_t *)payload;
	tlv->attr_id = htonl(ISNS_DELIMITER_ATTR_ID);
	tlv->attr_len = htonl(0);
	payload_len += 8 + 0;
	payload += 8 + 0;

	/* operating attributes */
	tlv = (isns_tlv_t *)payload;
	tlv->attr_id = htonl(op_tag);
	tlv->attr_len = htonl(op_len);
	switch (cmd_id) {
	case CMD_ADDDD:
	case CMD_REMDD:
		strcpy((char *)tlv->attr_value, m_name);
		break;
	case CMD_ADDDDN:
	case CMD_ADDDDSN:
	case CMD_REMDDN:
	case CMD_REMDDSN:
		*(uint32_t *)tlv->attr_value = htonl(m_id);
		break;
	}
	payload_len += 8 + op_len;

	pdu->payload_len = payload_len;

	dump_pdu1(pdu);

	conn.in_packet.pdu = pdu;
	conn.out_packet.pdu = NULL;
	conn.out_packet.sz = 0;

	if (packet_split_verify(&conn) == 0) {
		cache_lock(conn.lock);
		conn.handler(&conn);
		conn.ec = cache_unlock(conn.lock, conn.ec);
	}

	if (conn.out_packet.pdu != NULL) {
		pdu_update_code(conn.out_packet.pdu,
		    &conn.out_packet.pl, conn.ec);
		dump_pdu2(conn.out_packet.pdu);
		free(conn.out_packet.pdu);
	} else if (conn.ec != 0) {
		printf("operation failed[%d].\n", conn.ec);
	}
}

static void
cmd_file(
	char *file
)
{
	char i = 0, ch, cmd[256];
	FILE *f = fopen(file, "r");
	if (f != NULL) {
		while ((ch = fgetc(f)) != 0 && ch != EOF) {
			if (ch == '\t') {
				cmd[i++] = ' ';
			} else if (ch != '\n') {
				cmd[i++] = ch;
			} else {
				cmd[i ++] = ' ';
				cmd[i] = 0;
				i = 0;
				printf("%s\n", cmd);
				if (run_cmd(cmd) != 0) {
					break;
				}
			}
		}
		fclose(f);
	} else {
		printf("Cannot open file %s.\n", file);
	}
}

static int
run_cmd(
	char *cmd
)
{
	int argc, argv[32];
	int cmd_id;
	cmd_id = getcmd(&argc, argv, cmd);
	switch (cmd_id) {
		case CMD_LIST:
			list_node();
			break;
		case CMD_LISTNE:
			list_entity();
			break;
		case CMD_LISTP:
			list_portal();
			break;
		case CMD_LISTPG:
			list_pg();
			break;
		case CMD_LISTDD:
			list_dd();
			break;
		case CMD_LISTDDS:
			list_dds();
			break;
		case CMD_LISTDDN:
			list_ddn(argv[0]);
			break;
		case CMD_LISTDDSN:
			list_ddsn(argv[0]);
			break;
		case CMD_NEWDD:
		case CMD_NEWDDS:
		case CMD_NEWDDN:
		case CMD_NEWDDSN:
			new_dd_dds(cmd_id, argc, argv);
			break;
		case CMD_DELDD:
		case CMD_DELDDS:
			del_dd_dds(cmd_id, argv[0]);
			break;
		case CMD_ENABLE:
		case CMD_DISABLE:
			update_dds(cmd_id, argv[0]);
			break;
		case CMD_ADDDD:
		case CMD_ADDDDN:
		case CMD_ADDDDSN:
		case CMD_REMDD:
		case CMD_REMDDN:
		case CMD_REMDDSN:
			update_member(cmd_id, argv);
			break;
		case CMD_PAUSE:
			printf("Press enter to continue...");
			getchar();
			break;
		case CMD_FILE:
			cmd_file((char *)argv[0]);
			break;
		case CMD_HELP:
			test_cli_help();
			break;
		case CMD_VERBOSE_MEMORY:
			verbose_mc = !verbose_mc;
			break;
		case CMD_VERBOSE_NET:
			verbose_net = !verbose_net;
			break;
		case CMD_VERBOSE_TIME:
			verbose_tc = !verbose_tc;
			break;
		case CMD_VERBOSE_LOCK:
			verbose_lock = !verbose_lock;
			break;
		case CMD_VERBOSE_PARSER:
			verbose_parser = !verbose_parser;
			break;
		case CMD_QUIT:
			/* clean up cli */
			/* notify sys control */
			shutdown_server();
			return (1);
		case CMD_NONE:
			break;
		default:
			printf("invalid command\n");
			break;
	}
	if (cmd_id != CMD_NONE) {
		printf("\n>");
	} else {
		printf(">");
	}
	return (0);
}

/*ARGSUSED*/
void *cli_test(void *arg) {
	char i = 0, ch, cmd[256];

	printf("iSNS Server test CLI.\n");
	printf("Copyright 2007 Sun Microsystems, Inc.\n");

	printf("\n>");
	while ((ch = getchar()) != 0 && ch != EOF) {
		if (ch == '\t') {
			cmd[i++] = ' ';
		} else if (ch != '\n') {
			cmd[i++] = ch;
		} else {
			cmd[i ++] = ' ';
			cmd[i] = 0;
			i = 0;
			if (run_cmd(cmd) != 0) {
				break;
			}
		}
	}

	return (NULL);
}
#endif