OpenSolaris_b135/cmd/iscsiadm/iscsiadm_main.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 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <stdlib.h>
#include <stdio.h>
#include <wchar.h>
#include <widec.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <libintl.h>
#include <limits.h>
#include <string.h>
#include <strings.h>
#include <syslog.h>
#include <errno.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <wctype.h>
#include <assert.h>

#include <ima.h>
#include <libsun_ima.h>
#include <sys/iscsi_protocol.h>
#include <sys/scsi/adapters/iscsi_if.h>

#include "cmdparse.h"
#include "sun_ima.h"
#include "iscsiadm.h"

#define	VERSION_STRING_MAX_LEN	10
#define	MAX_LONG_CHAR_LEN 19

#define	MAX_AUTH_METHODS 5
/*
 * Version number:
 *  MAJOR - This should only change when there is an incompatible change made
 *  to the interfaces or the output.
 *
 *  MINOR - This should change whenever there is a new command or new feature
 *  with no incompatible change.
 */
#define	VERSION_STRING_MAJOR	    "1"
#define	VERSION_STRING_MINOR	    "0"

#define	OPTIONSTRING1	"yes|no"
#define	OPTIONSTRING2	"initiator node name"
#define	OPTIONSTRING3	"initiator node alias"
#define	OPTIONSTRING4	"enable|disable"
#define	OPTIONSTRING5	"key=value,..."
#define	OPTIONSTRING6	"none|CRC32"
#define	OPTIONSTRING7	"CHAP name"
#define	OPTIONSTRING8	"<# sessions>|<IP Address>[,<IP Address>]*"
#define	OPTIONSTRING9	"tunable-prop=value"
#define	OPTIONVAL1	"0 to 3600"
#define	OPTIONVAL2	"512 to 2**24 - 1"
#define	OPTIONVAL3	"1 to 65535"
#define	OPTIONVAL4	"<IP address>[:port]"

#define	MAX_ISCSI_NAME_LEN	    223
#define	MAX_ADDRESS_LEN		    255
#define	MIN_CHAP_SECRET_LEN	    12
#define	MAX_CHAP_SECRET_LEN	    16
#define	DEFAULT_ISCSI_PORT	    3260
#define	ISNS_DEFAULT_SERVER_PORT    3205
#define	DEFAULT_RADIUS_PORT	    1812
#define	MAX_CHAP_NAME_LEN	    512
#define	ISCSI_DEFAULT_RX_TIMEOUT_VALUE		"60"
#define	ISCSI_DEFAULT_CONN_DEFAULT_LOGIN_MAX	"180"
#define	ISCSI_DEFAULT_LOGIN_POLLING_DELAY	"60"

/* For listNode */
#define	INF_ERROR		1
#define	INVALID_NODE_NAME	2

#define	IMABOOLPRINT(prop, option)	 \
	if ((option) == PRINT_CONFIGURED_PARAMS) { \
		(void) fprintf(stdout, "%s/%s\n", \
		(prop).defaultValue == IMA_TRUE ? gettext("yes") : \
			gettext("no"), \
		(prop).currentValueValid == IMA_TRUE ? \
			((prop).currentValue == IMA_TRUE ? \
			gettext("yes"): gettext("no")) : "-"); \
	} else if ((option) == PRINT_NEGOTIATED_PARAMS) { \
		(void) fprintf(stdout, "%s\n", \
		(prop).currentValueValid == IMA_TRUE ? \
		(((prop).currentValue == IMA_TRUE) ? gettext("yes") : \
		gettext("no")) : "-"); \
	}

#define	IMAMINMAXPRINT(prop, option) \
	if ((option) == PRINT_CONFIGURED_PARAMS) { \
		(void) fprintf(stdout, "%d/", (prop).defaultValue); \
		if ((prop).currentValueValid == IMA_TRUE) { \
			(void) fprintf(stdout, "%d\n", (prop).currentValue); \
		} else if ((prop).currentValueValid == IMA_FALSE) { \
			(void) fprintf(stdout, "%s\n", "-"); \
		} \
	} else if ((option) == PRINT_NEGOTIATED_PARAMS) { \
		if ((prop).currentValueValid == IMA_TRUE) { \
			(void) fprintf(stdout, "%d\n", (prop).currentValue); \
		} else if ((prop).currentValueValid == IMA_FALSE) { \
			(void) fprintf(stdout, "%s\n", "-"); \
		} \
	}

/* forward declarations */
#define	PARSE_ADDR_OK				0
#define	PARSE_ADDR_MISSING_CLOSING_BRACKET	1
#define	PARSE_ADDR_PORT_OUT_OF_RANGE		2
#define	PARSE_TARGET_OK				0
#define	PARSE_TARGET_INVALID_TPGT		1
#define	PARSE_TARGET_INVALID_ADDR		2

#define	PRINT_CONFIGURED_PARAMS			1
#define	PRINT_NEGOTIATED_PARAMS			2

typedef enum iSCSINameCheckStatus {
	iSCSINameCheckOK,
	iSCSINameLenZero,
	iSCSINameLenExceededMax,
	iSCSINameUnknownType,
	iSCSINameInvalidCharacter,
	iSCSINameIqnFormatError,
	iSCSINameEUIFormatError,
	iSCSINameIqnDateFormatError,
	iSCSINameIqnSubdomainFormatError,
	iSCSINameIqnInvalidYearError,
	iSCSINameIqnInvalidMonthError,
	iSCSINameIqnFQDNError
} iSCSINameCheckStatusType;

/* Utility functions */
iSCSINameCheckStatusType iSCSINameStringProfileCheck(wchar_t *name);
boolean_t isNaturalNumber(char *numberStr, uint32_t upperBound);
static int parseAddress(char *address_port_str, uint16_t defaultPort,
    char *address_str, size_t address_str_len,
    uint16_t *port, boolean_t *isIpv6);
int parseTarget(char *targetStr,
    wchar_t *targetNameStr,
    size_t targetNameStrLen,
    boolean_t *targetAddressSpecified,
    wchar_t *targetAddressStr,
    size_t targetAddressStrLen,
    uint16_t *port,
    boolean_t *tpgtSpecified,
    uint16_t *tpgt,
    boolean_t *isIpv6);
static int chkConnLoginMaxPollingLoginDelay(IMA_OID oid,
    int key, int uintValue);

/* subcommand functions */
static int addFunc(int, char **, int, cmdOptions_t *, void *, int *);
static int listFunc(int, char **, int, cmdOptions_t *, void *, int *);
static int modifyFunc(int, char **, int, cmdOptions_t *, void *, int *);
static int removeFunc(int, char **, int, cmdOptions_t *, void *, int *);

/* helper functions */
static char *getExecBasename(char *);
static int getNodeProps(IMA_NODE_PROPERTIES *);
static int getSecret(char *, int *, int, int);
static int getTargetAddress(int, char *, IMA_TARGET_ADDRESS *);
static int printLoginParameters(char *, IMA_OID, int);
static void printDiscoveryMethod(char *, IMA_UINT32);
static void printTargetLuns(IMA_OID_LIST *);
static void printSendTargets(SUN_IMA_DISC_ADDRESS_KEY_PROPERTIES *);
static void printDigestAlgorithm(SUN_IMA_DIGEST_ALGORITHM_VALUE *, int);
static int setLoginParameter(IMA_OID, int, char *);
static int setLoginParameters(IMA_OID, char *);
static int setTunableParameters(IMA_OID, char *);
static void printLibError(IMA_STATUS);
/* LINTED E_STATIC_UNUSED */
static int sunPluginChk(IMA_OID, boolean_t *);
static int sunInitiatorFind(IMA_OID *);
static int getAuthMethodValue(char *, IMA_AUTHMETHOD *);
static int getLoginParam(char *);
static int getTunableParam(char *);
static void iSCSINameCheckStatusDisplay(iSCSINameCheckStatusType status);
static int modifyIndividualTargetParam(cmdOptions_t *optionList,
    IMA_OID targetOid, int *);
static void listCHAPName(IMA_OID oid);
static int printConfiguredSessions(IMA_OID);
static int printTunableParameters(IMA_OID oid);

/* object functions per subcommand */
static int addAddress(int, int, char *[], int *);
static int addStaticConfig(int, char *[], int *);
static int listDiscovery(int *);
static int listDiscoveryAddress(int, char *[], cmdOptions_t *, int *);
static int listISNSServerAddress(int, char *[], cmdOptions_t *, int *);
static int listNode(int *);
static int listStaticConfig(int, char *[], int *);
static int listTarget(int, char *[], cmdOptions_t *, int *);
static int listTargetParam(int, char *[], cmdOptions_t *, int *);
static int modifyDiscovery(cmdOptions_t *, int *);
static int modifyNodeAuthMethod(IMA_OID, char *, int *);
static int modifyNodeAuthParam(IMA_OID oid, int, char *, int *);
static int modifyNodeRadiusConfig(IMA_OID, char *, int *);
static int modifyNodeRadiusAccess(IMA_OID, char *, int *);
static int modifyNodeRadiusSharedSecret(IMA_OID, int *);
static int modifyNode(cmdOptions_t *, int *);
static int modifyTargetAuthMethod(IMA_OID, char *, int *);
static int modifyTargetAuthParam(IMA_OID oid, int param, char *chapName, int *);
static int modifyTargetParam(cmdOptions_t *, char *, int *);
static int removeAddress(int, int, char *[], int *);
static int removeStaticConfig(int, char *[], int *);
static int removeTargetParam(int, char *[], int *);
static int modifyTargetBidirAuthFlag(IMA_OID, char *, int *);
static int modifyConfiguredSessions(IMA_OID targetOid, char *optarg);

/* LINTED E_STATIC_UNUSED */
static IMA_STATUS getISCSINodeParameter(int paramType,
    IMA_OID *oid,
    void *pProps,
    uint32_t paramIndex);
/* LINTED E_STATIC_UNUSED */
static IMA_STATUS setISCSINodeParameter(int paramType,
    IMA_OID *oid,
    void *pProps,
    uint32_t paramIndex);
/* LINTED E_STATIC_UNUSED */
static IMA_STATUS getDigest(IMA_OID oid, int ioctlCmd,
    SUN_IMA_DIGEST_ALGORITHM_VALUE *algorithm);

IMA_STATUS getNegotiatedDigest(int digestType,
	SUN_IMA_DIGEST_ALGORITHM_VALUE *algorithm,
	SUN_IMA_CONN_PROPERTIES *connProps);

/* globals */
static char *cmdName;

/*
 * Available option letters:
 *
 * bcefgijklmnoquwxyz
 *
 * DEFGHIJKLMOQUVWXYZ
 */

/*
 * Add new options here
 */
optionTbl_t longOptions[] = {
	{"static", required_arg, 's', OPTIONSTRING4},
	{"sendtargets", required_arg, 't', OPTIONSTRING4},
	{"iSNS", required_arg, 'i', OPTIONSTRING4},
	{"headerdigest", required_arg, 'h', OPTIONSTRING6},
	{"datadigest", required_arg, 'd', OPTIONSTRING6},
	{"login-param", required_arg, 'p', OPTIONSTRING5},
	{"authentication", required_arg, 'a', "CHAP|none"},
	{"bi-directional-authentication", required_arg, 'B', OPTIONSTRING4},
	{"CHAP-secret", no_arg, 'C', NULL},
	{"CHAP-name", required_arg, 'H', OPTIONSTRING7},
	{"node-name", required_arg, 'N', OPTIONSTRING2},
	{"node-alias", required_arg, 'A', OPTIONSTRING3},
	{"radius-server", required_arg, 'r', OPTIONVAL4},
	{"radius-access", required_arg, 'R', OPTIONSTRING4},
	{"radius-shared-secret", no_arg, 'P', NULL},
	{"verbose", no_arg, 'v', NULL},
	{"scsi-target", no_arg, 'S', NULL},
	{"configured-sessions", required_arg, 'c', OPTIONSTRING8},
	{"tunable-param", required_arg, 'T', OPTIONSTRING9},
	{NULL, 0, 0, 0}
};

parameterTbl_t loginParams[] = {
	{"dataseqinorder", DATA_SEQ_IN_ORDER},
	{"defaulttime2retain", DEFAULT_TIME_2_RETAIN},
	{"defaulttime2wait", DEFAULT_TIME_2_WAIT},
	{"firstburstlength", FIRST_BURST_LENGTH},
	{"immediatedata", IMMEDIATE_DATA},
	{"initialr2t", INITIAL_R2T},
	{"maxburstlength", MAX_BURST_LENGTH},
	{"datapduinorder", DATA_PDU_IN_ORDER},
	{"maxoutstandingr2t", MAX_OUTSTANDING_R2T},
	{"maxrecvdataseglen", MAX_RECV_DATA_SEG_LEN},
	{"maxconnections", MAX_CONNECTIONS},
	{"errorrecoverylevel", ERROR_RECOVERY_LEVEL},
	{NULL, 0}
};

parameterTbl_t tunableParams[] = {
	{"recv-login-rsp-timeout", RECV_LOGIN_RSP_TIMEOUT},
	{"conn-login-max", CONN_LOGIN_MAX},
	{"polling-login-delay", POLLING_LOGIN_DELAY},
	{NULL, 0}
};

/*
 * Add new subcommands here
 */
subcommand_t subcommands[] = {
	{"add", ADD, addFunc},
	{"list", LIST, listFunc},
	{"modify", MODIFY, modifyFunc},
	{"remove", REMOVE, removeFunc},
	{NULL, 0, NULL}
};

/*
 * Add objects here
 */
object_t objects[] = {
	{"discovery", DISCOVERY},
	{"discovery-address", DISCOVERY_ADDRESS},
	{"isns-server", ISNS_SERVER_ADDRESS},
	{"initiator-node", NODE},
	{"static-config", STATIC_CONFIG},
	{"target", TARGET},
	{"target-param", TARGET_PARAM},
	{NULL, 0}
};

/*
 * Rules for subcommands and objects
 */
objectRules_t objectRules[] = {
	{TARGET, 0, LIST, 0, ADD|REMOVE|MODIFY, LIST,
	"target-name"},
	{TARGET_PARAM, MODIFY|REMOVE, LIST, 0, ADD, MODIFY,
	"target-name"},
	{DISCOVERY, 0, 0, LIST|MODIFY, ADD|REMOVE, 0, NULL},
	{NODE, 0, 0, MODIFY|LIST, ADD|REMOVE, 0, NULL},
	{STATIC_CONFIG, ADD|REMOVE, LIST, 0, MODIFY, ADD|REMOVE|LIST,
	"target-name,target-address[:port-number][,tpgt]"},
	{DISCOVERY_ADDRESS, ADD|REMOVE, LIST, 0, MODIFY,
	ADD|REMOVE|LIST, "IP-address[:port-number]"},
	{ISNS_SERVER_ADDRESS, ADD|REMOVE, LIST, 0, MODIFY,
	ADD|REMOVE|LIST, "IP-address[:port-number]"},
	{0, 0, 0, 0, 0, NULL}
};

/*
 * list of objects, subcommands, valid short options, required flag and
 * exclusive option string
 *
 * If it's not here, there are no options for that object.
 */
optionRules_t optionRules[] = {
	{DISCOVERY, MODIFY, "sti", B_TRUE, NULL},
	{DISCOVERY_ADDRESS, LIST, "v", B_FALSE, NULL},
	{ISNS_SERVER_ADDRESS, LIST, "v", B_FALSE, NULL},
	{TARGET, LIST, "vS", B_FALSE, NULL},
	{NODE, MODIFY, "NAhdCaRrPHcT", B_TRUE, "CP"},
	{TARGET_PARAM, MODIFY, "ahdBCpcHT", B_TRUE, "C"},
	{TARGET_PARAM, LIST, "v", B_FALSE, NULL},
	{0, 0, 0, 0, 0}
};


static boolean_t
targetNamesEqual(wchar_t *name1, wchar_t *name2)
{
	int i;
	wchar_t wchar1, wchar2;

	if (name1 == NULL || name2 == NULL) {
		return (B_FALSE);
	}

	if (wcslen(name1) != wcslen(name2)) {
		return (B_FALSE);
	}

	/*
	 * Convert names to lower case and compare
	 */
	for (i = 0; i < wcslen(name1); i++) {
		wchar1 = towctrans((wint_t)name1[i], wctrans("tolower"));
		wchar2 = towctrans((wint_t)name2[i], wctrans("tolower"));

		if (wchar1 != wchar2) {
			return (B_FALSE);
		}
	}

	return (B_TRUE);
}

static boolean_t
ipAddressesEqual(IMA_TARGET_ADDRESS addr1, IMA_TARGET_ADDRESS addr2)
{
#define	IPV4_ADDR_BYTES 4
#define	IPV6_ADDR_BYTES 16

	int compSize;

	if (addr1.hostnameIpAddress.id.ipAddress.ipv4Address !=
	    addr2.hostnameIpAddress.id.ipAddress.ipv4Address) {
		return (B_FALSE);
	}

	compSize = IPV6_ADDR_BYTES;
	if (addr1.hostnameIpAddress.id.ipAddress.ipv4Address) {
		compSize = IPV4_ADDR_BYTES;
	}

	if (bcmp(addr1.hostnameIpAddress.id.ipAddress.ipAddress,
	    addr2.hostnameIpAddress.id.ipAddress.ipAddress, compSize) == 0) {
		return (B_TRUE);
	}

	return (B_FALSE);
}

static int
getLoginParam(char *arg)
{
	parameterTbl_t *paramp;
	int len;

	for (paramp = loginParams; paramp->name; paramp++) {
		len = strlen(arg);
		if (len == strlen(paramp->name) &&
		    strncasecmp(arg, paramp->name, len) == 0) {
			return (paramp->val);
		}
	}
	return (-1);
}

static int
getTunableParam(char *arg)
{
	parameterTbl_t *paramp;
	int len;

	for (paramp = tunableParams; paramp->name != NULL; paramp++) {
		len = strlen(arg);
		if (len == strlen(paramp->name) &&
		    strncasecmp(arg, paramp->name, len) == 0) {
			return (paramp->val);
		}
	}
	return (-1);
}

static void
printLibError(IMA_STATUS status)
{
	char *errorString;
	switch (status) {
	case IMA_ERROR_NOT_SUPPORTED:
		errorString =
		gettext("Operation currently not supported");
		break;
	case IMA_ERROR_INSUFFICIENT_MEMORY:
		errorString = gettext("Insufficient memory");
		break;
	case IMA_ERROR_UNEXPECTED_OS_ERROR:
		errorString = gettext("unexpected OS error");
		break;
	case IMA_ERROR_UNKNOWN_ERROR:
		errorString = gettext("Unknown error");
		break;
	case IMA_ERROR_LU_IN_USE:
		errorString = gettext("Logical unit in use");
		break;
	case IMA_ERROR_INVALID_PARAMETER:
		errorString = gettext("Invalid parameter specified");
		break;
	case IMA_ERROR_INVALID_OBJECT_TYPE:
		errorString =
		gettext("Internal library error: Invalid oid type specified");
		break;
	case IMA_ERROR_INCORRECT_OBJECT_TYPE:
		errorString =
		gettext("Internal library error: Incorrect oid type specified");
		break;
	case IMA_ERROR_OBJECT_NOT_FOUND:
		errorString = gettext("Internal library error: Oid not found");
		break;
	case IMA_ERROR_NAME_TOO_LONG:
		errorString = gettext("Name too long");
		break;
	default:
		errorString = gettext("Unknown error");
	}
	(void) fprintf(stderr, "%s: %s\n", cmdName, errorString);
}

/*
 * input:
 *  execFullName - exec name of program (argv[0])
 *
 * Returns:
 *  command name portion of execFullName
 */
static char *
getExecBasename(char *execFullname)
{
	char *lastSlash, *execBasename;

	/* guard against '/' at end of command invocation */
	for (;;) {
		lastSlash = strrchr(execFullname, '/');
		if (lastSlash == NULL) {
			execBasename = execFullname;
			break;
		} else {
			execBasename = lastSlash + 1;
			if (*execBasename == '\0') {
				*lastSlash = '\0';
				continue;
			}
			break;
		}
	}
	return (execBasename);
}


/*
 * input:
 *  nodeProps - pointer to caller allocated IMA_NODE_PROPERTIES
 *
 * returns:
 *  zero on success
 *  non-zero otherwise
 */
static int
getNodeProps(IMA_NODE_PROPERTIES *nodeProps)
{
	IMA_OID sharedNodeOid;

	IMA_STATUS status = IMA_GetSharedNodeOid(&sharedNodeOid);
	if (!(IMA_SUCCESS(status))) {
		printLibError(status);
		return (INF_ERROR);
	}

	status = IMA_GetNodeProperties(sharedNodeOid, nodeProps);
	if (!IMA_SUCCESS(status)) {
		printLibError(status);
		return (INF_ERROR);
	}

	return (0);
}

/*
 * sunInitiatorFind
 * Purpose:
 *  Finds the Sun iSCSI initiator (LHBA). This CLI currently supports only
 *  one initiator.
 *
 * output:
 *  oid of initiator
 *
 * Returns:
 *  zero on success with initiator found
 *  > 0 on success with no initiator found
 *  < 0 on failure
 */
static int
sunInitiatorFind(IMA_OID *oid)
{
	IMA_OID_LIST *lhbaList = NULL;

	IMA_STATUS status = IMA_GetLhbaOidList(&lhbaList);
	if (!IMA_SUCCESS(status)) {
		printLibError(status);
		return (-1);
	}

	if ((lhbaList == NULL) || (lhbaList->oidCount == 0)) {
		printLibError(IMA_ERROR_OBJECT_NOT_FOUND);
		if (lhbaList != NULL)
			(void) IMA_FreeMemory(lhbaList);
		return (-1);
	}

	*oid = lhbaList->oids[0];
	(void) IMA_FreeMemory(lhbaList);

	return (0);
}

/*
 * input:
 *  wcInput - wide character string containing discovery address
 * output:
 *  address - IMA_TARGET_ADDRESS structure containing valid
 *	discovery address
 * returns:
 *  zero on success
 *  non-zero on failure
 */

static int
getTargetAddress(int addrType, char *ipStr, IMA_TARGET_ADDRESS *address)
{
	char cCol = ':';
	char cBracketL = '['; /* Open Bracket '[' */
	char cBracketR = ']'; /* Close Bracket ']' */
	char *colPos;
	char *startPos;
	unsigned long inputPort;
	int addressType = AF_INET;
	char *tmpStrPtr, tmpStr[SUN_IMA_IP_ADDRESS_PORT_LEN];
	int rval;

	/* Check if this is a ipv6 address */
	if (ipStr[0] == cBracketL) {
		addressType = AF_INET6;
		startPos = strchr(ipStr, cBracketR);
		if (!startPos) {
			(void) fprintf(stderr, "%s: %s: ']' %s\n",
			    cmdName, ipStr, gettext("missing"));
			return (1);
		}
		(void) strlcpy(tmpStr, ipStr+1, startPos-ipStr);
		address->hostnameIpAddress.id.ipAddress.ipv4Address = IMA_FALSE;
		tmpStrPtr = tmpStr;
	} else {
		/* set start position to beginning of input object */
		addressType = AF_INET;
		startPos = ipStr;
		address->hostnameIpAddress.id.ipAddress.ipv4Address = IMA_TRUE;
		tmpStrPtr = ipStr;
	}
	/* wcschr for ':'. If not there, use default port */
	colPos = strchr(startPos, cCol);

	if (!colPos) {
		if (addrType == DISCOVERY_ADDRESS) {
			inputPort = DEFAULT_ISCSI_PORT;
		} else if (addrType == ISNS_SERVER_ADDRESS) {
			inputPort = ISNS_DEFAULT_SERVER_PORT;
		} else {
			*colPos = NULL;
		}
	} else {
		*colPos = NULL;
	}

	rval = inet_pton(addressType, tmpStrPtr,
	    address->hostnameIpAddress.id.ipAddress.ipAddress);
	/* inet_pton returns 1 on success */
	if (rval != 1) {
		(void) fprintf(stderr, "%s: %s: %s\n", cmdName, ipStr,
		    gettext("invalid IP address"));
		return (1);
	}


	if (colPos) {
		char *errchr;

		colPos++;
		if (*colPos == NULL) {
			(void) fprintf(stderr, "%s: %s: %s\n",
			    cmdName, ipStr,
			    gettext("port number missing"));
			return (1);
		}

		/*
		 * convert port string to unsigned value
		 * Note:  Don't remove errno = 0 as you may get false failures.
		 */
		errno = 0;
		inputPort = strtol(colPos, &errchr, 10);
		if (errno != 0 || inputPort == 0 && errchr != NULL) {
			(void) fprintf(stderr, "%s: %s:%s %s\n",
			    cmdName, ipStr, colPos,
			    gettext("port number invalid"));
			return (1);
		}
		/* make sure it's in the range */
		if (inputPort > USHRT_MAX) {
			(void) fprintf(stderr, "%s: %s: %s\n",
			    cmdName, ipStr,
			    gettext("port number out of range"));
			return (1);
		}
	}
	address->portNumber  = inputPort;

	return (0);
}

/*
 * Print results of send targets command
 */
static void
printSendTargets(SUN_IMA_DISC_ADDRESS_KEY_PROPERTIES *pList)
{
	char outBuf[INET6_ADDRSTRLEN];
	int inetSize;
	int af;
	int i;

	for (i = 0; i < pList->keyCount; i++) {
		if (pList->keys[i].address.ipAddress.ipv4Address == IMA_TRUE) {
			af = AF_INET;
			inetSize = INET_ADDRSTRLEN;
		} else {
			af = AF_INET6;
			inetSize = INET6_ADDRSTRLEN;
		}
		(void) fprintf(stdout, gettext("\tTarget name: %ws\n"),
		    pList->keys[i].name);
		(void) fprintf(stdout, "\t\t%s: %15s:%d", "Target address",
		    inet_ntop(af, &(pList->keys[i].address.ipAddress.ipAddress),
		    outBuf, inetSize), pList->keys[i].address.portNumber);
		(void) fprintf(stdout, ", %d", pList->keys[i].tpgt);
		(void) fprintf(stdout, "\n");
	}
}


/*
 * Print all login parameters
 */
static int
printLoginParameters(char *prefix, IMA_OID oid, int printOption)
{
	IMA_STATUS status;
	IMA_BOOL_VALUE propBool;
	IMA_MIN_MAX_VALUE propMinMax;
	char longString[MAX_LONG_CHAR_LEN + 1];
	SUN_IMA_CONN_PROPERTIES	*connProps = NULL;
	IMA_OID_LIST *pConnList;

	(void) memset(longString, 0, sizeof (longString));

	switch (printOption) {
		case PRINT_CONFIGURED_PARAMS:
			(void) fprintf(stdout, "%s%s:\n",
			    prefix,
			    gettext("Login Parameters (Default/Configured)"));
			break;
		case PRINT_NEGOTIATED_PARAMS:
			(void) fprintf(stdout, "%s%s:\n",
			    prefix,
			    gettext("Login Parameters (Negotiated)"));
			status = SUN_IMA_GetConnOidList(
			    &oid,
			    &pConnList);

			if (!IMA_SUCCESS(status)) {
				printLibError(status);
				return (1);
			}

			status = SUN_IMA_GetConnProperties(&pConnList->oids[0],
			    &connProps);
			propBool.currentValueValid = connProps->valuesValid;
			propMinMax.currentValueValid = connProps->valuesValid;
			break;
		default:
			return (1);
	}

	if (printOption == PRINT_NEGOTIATED_PARAMS) {
		propBool.currentValue = connProps->dataSequenceInOrder;
	} else {
		status = IMA_GetDataSequenceInOrderProperties(oid, &propBool);
	}
	if (!IMA_SUCCESS(status)) {
		printLibError(status);
		(void) IMA_FreeMemory(connProps);
		return (1);
	}
	(void) fprintf(stdout, "%s\t%s: ", prefix,
	    gettext("Data Sequence In Order"));
	IMABOOLPRINT(propBool, printOption);


	if (printOption == PRINT_NEGOTIATED_PARAMS) {
		propBool.currentValue = connProps->dataPduInOrder;
	} else {
		status = IMA_GetDataPduInOrderProperties(oid, &propBool);
	}
	if (!IMA_SUCCESS(status)) {
		printLibError(status);
		(void) IMA_FreeMemory(connProps);
		return (1);
	}
	(void) fprintf(stdout, "%s\t%s: ", prefix,
	    gettext("Data PDU In Order"));
	IMABOOLPRINT(propBool, printOption);


	if (printOption == PRINT_NEGOTIATED_PARAMS) {
		propMinMax.currentValue = connProps->defaultTime2Retain;
	} else {
		status = IMA_GetDefaultTime2RetainProperties(oid, &propMinMax);
	}
	if (!IMA_SUCCESS(status)) {
		printLibError(status);
		(void) IMA_FreeMemory(connProps);
		return (1);
	}
	(void) fprintf(stdout, "%s\t%s: ", prefix,
	    gettext("Default Time To Retain"));
	IMAMINMAXPRINT(propMinMax, printOption);


	if (printOption == PRINT_NEGOTIATED_PARAMS) {
		propMinMax.currentValue = connProps->defaultTime2Wait;
	} else {
		status = IMA_GetDefaultTime2WaitProperties(oid, &propMinMax);
	}
	if (!IMA_SUCCESS(status)) {
		printLibError(status);
		(void) IMA_FreeMemory(connProps);
		return (1);
	}
	(void) fprintf(stdout, "%s\t%s: ", prefix,
	    gettext("Default Time To Wait"));
	IMAMINMAXPRINT(propMinMax, printOption);


	if (printOption == PRINT_NEGOTIATED_PARAMS) {
		propMinMax.currentValue = connProps->errorRecoveryLevel;
	} else {
		status = IMA_GetErrorRecoveryLevelProperties(oid, &propMinMax);
	}
	if (!IMA_SUCCESS(status)) {
		printLibError(status);
		(void) IMA_FreeMemory(connProps);
		return (1);
	}
	(void) fprintf(stdout, "%s\t%s: ", prefix,
	    gettext("Error Recovery Level"));
	IMAMINMAXPRINT(propMinMax, printOption);


	if (printOption == PRINT_NEGOTIATED_PARAMS) {
		propMinMax.currentValue = connProps->firstBurstLength;
	} else {
		status = IMA_GetFirstBurstLengthProperties(oid,
		    &propMinMax);
	}
	if (!IMA_SUCCESS(status)) {
		printLibError(status);
		(void) IMA_FreeMemory(connProps);
		return (1);
	}
	(void) fprintf(stdout, "%s\t%s: ",
	    prefix, gettext("First Burst Length"));
	IMAMINMAXPRINT(propMinMax, printOption);


	if (printOption == PRINT_NEGOTIATED_PARAMS) {
		propBool.currentValue = connProps->immediateData;
	} else {
		status = IMA_GetImmediateDataProperties(oid, &propBool);
	}
	if (!IMA_SUCCESS(status)) {
		printLibError(status);
		(void) IMA_FreeMemory(connProps);
		return (1);
	}
	(void) fprintf(stdout, "%s\t%s: ", prefix, gettext("Immediate Data"));
	IMABOOLPRINT(propBool, printOption);


	if (printOption == PRINT_NEGOTIATED_PARAMS) {
		propBool.currentValue = connProps->initialR2T;
	} else {
		status = IMA_GetInitialR2TProperties(oid, &propBool);
	}
	if (!IMA_SUCCESS(status)) {
		printLibError(status);
		(void) IMA_FreeMemory(connProps);
		return (1);
	}
	(void) fprintf(stdout, "%s\t%s: ", prefix,
	    gettext("Initial Ready To Transfer (R2T)"));
	IMABOOLPRINT(propBool, printOption);


	if (printOption == PRINT_NEGOTIATED_PARAMS) {
		propMinMax.currentValue = connProps->maxBurstLength;
	} else {
		status = IMA_GetMaxBurstLengthProperties(oid, &propMinMax);
	}
	if (!IMA_SUCCESS(status)) {
		printLibError(status);
		(void) IMA_FreeMemory(connProps);
		return (1);
	}
	(void) fprintf(stdout, "%s\t%s: ", prefix, gettext("Max Burst Length"));
	IMAMINMAXPRINT(propMinMax, printOption);


	if (printOption == PRINT_NEGOTIATED_PARAMS) {
		propMinMax.currentValue = connProps->maxOutstandingR2T;
	} else {
		status = IMA_GetMaxOutstandingR2TProperties(oid, &propMinMax);
	}
	if (!IMA_SUCCESS(status)) {
		printLibError(status);
		(void) IMA_FreeMemory(connProps);
		return (1);
	}
	(void) fprintf(stdout, "%s\t%s: ", prefix,
	    gettext("Max Outstanding R2T"));
	IMAMINMAXPRINT(propMinMax, printOption);


	if (printOption == PRINT_NEGOTIATED_PARAMS) {
		propMinMax.currentValue = connProps->maxRecvDataSegmentLength;
	} else {
		status = IMA_GetMaxRecvDataSegmentLengthProperties(oid,
		    &propMinMax);
	}
	if (!IMA_SUCCESS(status)) {
		printLibError(status);
		(void) IMA_FreeMemory(connProps);
		return (1);
	}
	(void) fprintf(stdout, "%s\t%s: ", prefix,
	    gettext("Max Receive Data Segment Length"));
	IMAMINMAXPRINT(propMinMax, printOption);


	if (printOption == PRINT_NEGOTIATED_PARAMS) {
		propMinMax.currentValue = connProps->maxConnections;
	} else {
		status = IMA_GetMaxConnectionsProperties(oid, &propMinMax);
	}
	if (!IMA_SUCCESS(status)) {
		printLibError(status);
		(void) IMA_FreeMemory(connProps);
		return (1);
	}
	(void) fprintf(stdout, "%s\t%s: ", prefix, gettext("Max Connections"));
	IMAMINMAXPRINT(propMinMax, printOption);

	(void) IMA_FreeMemory(connProps);
	return (0);
}

/*
 * Print discovery information.
 */
static void
printDiscoveryMethod(char *prefix, IMA_UINT32 discoveryMethodFlags)
{
	(void) fprintf(stdout, "%s%s: ", prefix, gettext("Discovery Method"));
	if (discoveryMethodFlags == IMA_TARGET_DISCOVERY_METHOD_UNKNOWN) {
		(void) fprintf(stdout, "%s\n", gettext("NA"));
	} else {
		if (!((discoveryMethodFlags &
		    IMA_TARGET_DISCOVERY_METHOD_STATIC) ^
		    IMA_TARGET_DISCOVERY_METHOD_STATIC)) {
			(void) fprintf(stdout, "%s ", gettext("Static"));
		}
		if (!((discoveryMethodFlags &
		    IMA_TARGET_DISCOVERY_METHOD_SENDTARGETS) ^
		    IMA_TARGET_DISCOVERY_METHOD_SENDTARGETS)) {
			(void) fprintf(stdout, "%s ", gettext("SendTargets"));
		}
		if (!((discoveryMethodFlags &
		    IMA_TARGET_DISCOVERY_METHOD_ISNS) ^
		    IMA_TARGET_DISCOVERY_METHOD_ISNS)) {
			(void) fprintf(stdout, "%s ", gettext("iSNS"));
		}
		(void) fprintf(stdout, "\n");
	}
}

/*
 * printConnectionList - Prints the conection list provided
 */
static void
printConnectionList(char *prefix, IMA_OID_LIST *pConnList)
{
	IMA_STATUS		imaStatus;
	int			i;
	SUN_IMA_CONN_PROPERTIES	*connProps;
	union {
		char	ipv4[INET_ADDRSTRLEN+1];
		char	ipv6[INET6_ADDRSTRLEN+1];
	} tmp;

	for (i = 0; i < pConnList->oidCount; i++) {
		imaStatus = SUN_IMA_GetConnProperties(&pConnList->oids[i],
		    &connProps);

		if (imaStatus != IMA_STATUS_SUCCESS) {
			continue;
		}

		(void) fprintf(stdout, "%sCID: %d\n", prefix,
		    connProps->connectionID);

		(void) memset(&tmp, 0, sizeof (tmp));
		if (connProps->local.ipAddress.ipv4Address == IMA_TRUE) {
			if (inet_ntop(AF_INET,
			    &connProps->local.ipAddress.ipAddress[0],
			    &tmp.ipv4[0],
			    INET_ADDRSTRLEN)) {
				(void) fprintf(stdout,
				    "%s  %s: %s:%u\n",
				    prefix,
				    gettext("IP address (Local)"),
				    &tmp.ipv4[0],
				    ntohs(connProps->local.portNumber));
			}
		} else {
			if (inet_ntop(AF_INET6,
			    &connProps->local.ipAddress.ipAddress[0],
			    &tmp.ipv6[0],
			    INET6_ADDRSTRLEN)) {
				(void) fprintf(stdout,
				    "%s  %s: [%s]:%u\n",
				    prefix,
				    gettext("IP address (Local)"),
				    &tmp.ipv6[0],
				    ntohs(connProps->local.portNumber));
			}
		}
		if (connProps->peer.ipAddress.ipv4Address == IMA_TRUE) {
			if (inet_ntop(AF_INET,
			    &connProps->peer.ipAddress.ipAddress[0],
			    &tmp.ipv4[0],
			    INET_ADDRSTRLEN)) {
				(void) fprintf(stdout,
				    "%s  %s: %s:%u\n",
				    prefix,
				    gettext("IP address (Peer)"),
				    &tmp.ipv4[0],
				    ntohs(connProps->peer.portNumber));
			}
		} else {
			if (inet_ntop(AF_INET6,
			    &connProps->peer.ipAddress.ipAddress[0],
			    &tmp.ipv6[0],
			    INET6_ADDRSTRLEN)) {
				(void) fprintf(stdout,
				    "%s  %s: [%s]:%u\n",
				    prefix,
				    gettext("IP address (Peer)"),
				    &tmp.ipv6[0],
				    ntohs(connProps->peer.portNumber));
			}
		}

		(void) IMA_FreeMemory(connProps);
	}
}

/*
 * Set login parameters on a target or initiator
 */
static int
setLoginParameter(IMA_OID oid, int optval, char *optarg)
{
	IMA_STATUS status = IMA_STATUS_SUCCESS;
	IMA_UINT uintValue;
	IMA_BOOL boolValue;
	SUN_IMA_DIGEST_ALGORITHM digestAlgList[1];
	IMA_MIN_MAX_VALUE propMinMax;
	char *endptr;

	/*
	 * for clarity, there are two switch statements
	 * The first loads the variable and the second
	 * calls the appropriate API
	 */
	switch (optval) {
		case DATA_SEQ_IN_ORDER:
		case IMMEDIATE_DATA:
		case INITIAL_R2T:
		case DATA_PDU_IN_ORDER:
			/* implement 'default'? */
			if (strcasecmp(optarg, "yes") == 0) {
				boolValue = IMA_TRUE;
			} else if (strcasecmp(optarg, "no") == 0) {
				boolValue = IMA_FALSE;
			} else {
				(void) fprintf(stderr, "%s: %s - %s\n",
				    cmdName,
				    gettext("invalid option argument"),
				    optarg);
				return (1);
			}
			break;
		case DEFAULT_TIME_2_RETAIN:
		case DEFAULT_TIME_2_WAIT:
			errno = 0;
			uintValue = strtoul(optarg, &endptr, 0);
			if (*endptr != '\0' || errno != 0) {
				(void) fprintf(stderr, "%s: %s - %s\n",
				    cmdName,
				    gettext("invalid option argument"),
				    optarg);
				return (1);
			}
			if (uintValue > 3600) {
				(void) fprintf(stderr, "%s: %s\n",
				    cmdName,
gettext("value must be between 0 and 3600"));
				return (1);
			}
			break;
		case FIRST_BURST_LENGTH:
		case MAX_BURST_LENGTH:
		case MAX_RECV_DATA_SEG_LEN:
			errno = 0;
			/* implement 'default'? */
			uintValue = strtoul(optarg, &endptr, 0);
			if (*endptr != '\0' || errno != 0) {
				(void) fprintf(stderr, "%s: %s - %s\n",
				    cmdName,
				    gettext("invalid option argument"),
				    optarg);
				return (1);
			}
			if (uintValue < 512 || uintValue > 16777215) {
				(void) fprintf(stderr, "%s: %s\n",
				    cmdName,
gettext("value must be between 512 and 16777215"));
				return (1);
			}
			break;
		case MAX_OUTSTANDING_R2T:
			errno = 0;
			uintValue = strtoul(optarg, &endptr, 0);
			if (*endptr != '\0' || errno != 0) {
				(void) fprintf(stderr, "%s: %s - %s\n",
				    cmdName,
				    gettext("invalid option argument"),
				    optarg);
				return (1);
			}
			if (uintValue < 1 || uintValue > 65535) {
				(void) fprintf(stderr, "%s: %s\n",
				    cmdName,
gettext("value must be between 1 and 65535"));
				return (1);
			}
			break;
		case HEADER_DIGEST:
		case DATA_DIGEST:
			if (strcasecmp(optarg, "none") == 0) {
				digestAlgList[0] = SUN_IMA_DIGEST_NONE;
			} else if (strcasecmp(optarg, "CRC32") == 0) {
				digestAlgList[0] = SUN_IMA_DIGEST_CRC32;
			} else {
				(void) fprintf(stderr, "%s: %s - %s\n",
				    cmdName,
				    gettext("invalid option argument"),
				    optarg);
				return (1);
			}
			break;
		case MAX_CONNECTIONS:
			errno = 0;
			uintValue = strtoul(optarg, &endptr, 0);
			if (*endptr != '\0' || errno != 0) {
				(void) fprintf(stderr, "%s: %s - %s\n",
				    cmdName,
				    gettext("invalid option argument"),
				    optarg);
				return (1);
			}
			if (uintValue < 1 || uintValue > 256) {
				(void) fprintf(stderr, "%s: %s\n",
				    cmdName,
gettext("value must be between 1 and 256"));
				return (1);
			}
			break;
		case ERROR_RECOVERY_LEVEL:
			errno = 0;
			uintValue = strtoul(optarg, &endptr, 0);
			if (*endptr != '\0' || errno != 0) {
				(void) fprintf(stderr, "%s: %s - %s\n",
				    cmdName,
				    gettext("invalid option argument"),
				    optarg);
				return (1);
			}
			if (uintValue > 2) {
				(void) fprintf(stderr, "%s: %s\n",
				    cmdName,
gettext("value must be between 0 and 2"));
				return (1);
			}
			break;
		default:
			(void) fprintf(stderr, "%s: %c: %s\n",
			    cmdName, optval, gettext("unknown option"));
			return (1);
	}

	switch (optval) {
		case DATA_PDU_IN_ORDER:
			status = IMA_SetDataPduInOrder(oid, boolValue);
			break;
		case DATA_SEQ_IN_ORDER:
			status = IMA_SetDataSequenceInOrder(oid, boolValue);
			break;
		case DEFAULT_TIME_2_RETAIN:
			status = IMA_SetDefaultTime2Retain(oid, uintValue);
			break;
		case DEFAULT_TIME_2_WAIT:
			status = IMA_SetDefaultTime2Wait(oid, uintValue);
			break;
		case FIRST_BURST_LENGTH:
			status = IMA_SetFirstBurstLength(oid, uintValue);

			/*
			 * If this call fails check to see if it's because
			 * the requested value is > than maxBurstLength
			 */
			if (!IMA_SUCCESS(status)) {
				status = IMA_GetMaxBurstLengthProperties(oid,
				    &propMinMax);
				if (!IMA_SUCCESS(status)) {
					printLibError(status);
					return (1);
				}
				if (uintValue > propMinMax.currentValue) {
					(void) fprintf(stderr,
					    "%s: %s\n", cmdName,
					    gettext("firstBurstLength must " \
					    "be less than or equal to than " \
					    "maxBurstLength"));
				}
				return (1);
			}

			break;
		case IMMEDIATE_DATA:
			status = IMA_SetImmediateData(oid, boolValue);
			break;
		case INITIAL_R2T:
			status = IMA_SetInitialR2T(oid, boolValue);
			break;
		case MAX_BURST_LENGTH:
			status = IMA_SetMaxBurstLength(oid, uintValue);
			/*
			 * If this call fails check to see if it's because
			 * the requested value is < than firstBurstLength
			 */
			if (!IMA_SUCCESS(status)) {
				status = IMA_GetFirstBurstLengthProperties(oid,
				    &propMinMax);
				if (!IMA_SUCCESS(status)) {
					printLibError(status);
					return (1);
				}
				if (uintValue < propMinMax.currentValue) {
					(void) fprintf(stderr, "%s: %s\n",
					    cmdName,
					    gettext("maxBurstLength must be " \
					    "greater than or equal to " \
					    "firstBurstLength"));
				}
				return (1);
			}
			break;

		case MAX_OUTSTANDING_R2T:
			status = IMA_SetMaxOutstandingR2T(oid, uintValue);
			break;
		case MAX_RECV_DATA_SEG_LEN:
			status = IMA_SetMaxRecvDataSegmentLength(oid,
			    uintValue);
			break;
		case HEADER_DIGEST:
			status = SUN_IMA_SetHeaderDigest(oid, 1,
			    &digestAlgList[0]);
			break;
		case DATA_DIGEST:
			status = SUN_IMA_SetDataDigest(oid, 1,
			    &digestAlgList[0]);
			break;
		case MAX_CONNECTIONS:
			status = IMA_SetMaxConnections(oid, uintValue);
			break;
		case ERROR_RECOVERY_LEVEL:
			status = IMA_SetErrorRecoveryLevel(oid, uintValue);
			break;
	}
	if (!IMA_SUCCESS(status)) {
		printLibError(status);
		return (1);
	}
	return (0);
}

static void
printDigestAlgorithm(SUN_IMA_DIGEST_ALGORITHM_VALUE *digestAlgorithms,
    int printOption)
{
	int i;

	if (printOption == PRINT_CONFIGURED_PARAMS) {
		for (i = 0; i < digestAlgorithms->defaultAlgorithmCount; i++) {
			if (i > 0) {
				(void) fprintf(stdout, "|");
			}
			switch (digestAlgorithms->defaultAlgorithms[i]) {
				case SUN_IMA_DIGEST_NONE:
					(void) fprintf(stdout,
					    gettext("NONE"));
					break;
				case SUN_IMA_DIGEST_CRC32:
					(void) fprintf(stdout,
					    gettext("CRC32"));
					break;
				default:
					(void) fprintf(stdout,
					    gettext("Unknown"));
					break;
			}
		}
		(void) fprintf(stdout, "/");
		if (digestAlgorithms->currentValid == IMA_TRUE) {
			for (i = 0;
			    i < digestAlgorithms->currentAlgorithmCount; i++) {
				if (i > 0) {
					(void) fprintf(stdout, "|");
				}
				switch (digestAlgorithms->
				    currentAlgorithms[i]) {
					case SUN_IMA_DIGEST_NONE:
						(void) fprintf(stdout,
						    gettext("NONE"));
						break;
					case SUN_IMA_DIGEST_CRC32:
						(void) fprintf(stdout,
						    gettext("CRC32"));
						break;
					default:
						(void) fprintf(stdout,
						    gettext("Unknown"));
						break;
				}
			}
		} else {
			(void) fprintf(stdout, "-");
		}
		(void) fprintf(stdout, "\n");
	} else if (printOption == PRINT_NEGOTIATED_PARAMS) {

		if (digestAlgorithms->negotiatedValid == IMA_TRUE) {
			for (i = 0;
			    i < digestAlgorithms->negotiatedAlgorithmCount;
			    i++) {
				if (i > 0) {
					(void) fprintf(stdout, "|");
				}
				switch (digestAlgorithms->
				    negotiatedAlgorithms[i]) {
					case SUN_IMA_DIGEST_NONE:
						(void) fprintf(stdout,
						    gettext("NONE"));
						break;
					case SUN_IMA_DIGEST_CRC32:
						(void) fprintf(stdout,
						    gettext("CRC32"));
						break;
					default:
						(void) fprintf(stdout,
						    gettext("Unknown"));
						break;
				}
			}
		} else {
			(void) fprintf(stdout, "-");
		}
		(void) fprintf(stdout, "\n");
	}
}

static int
setLoginParameters(IMA_OID oid, char *optarg)
{
	char keyp[MAXOPTARGLEN];
	char valp[MAXOPTARGLEN];
	int key;
	char *nameValueString, *indexp, *delim = NULL;

	if ((nameValueString = strdup(optarg)) == NULL) {
		if (errno == ENOMEM) {
			(void) fprintf(stderr, "%s: %s\n",
			    cmdName, strerror(errno));
		} else {
			(void) fprintf(stderr, "%s: %s\n", cmdName,
			    gettext("unknown error"));
		}
		return (1);
	}

	indexp = nameValueString;

	/*
	 * Retrieve all login params from option argument
	 * Syntax <key=value,...>
	 */
	while (indexp) {
		if (delim = strchr(indexp, ',')) {
			delim[0] = '\0';
		}
		(void) memset(keyp, 0, sizeof (keyp));
		(void) memset(valp, 0, sizeof (valp));
		if (sscanf(indexp, gettext("%[^=]=%s"), keyp, valp) != 2) {
			(void) fprintf(stderr, "%s: %s: %s\n", cmdName,
			    gettext("Unknown param"), indexp);
			if (nameValueString) {
				free(nameValueString);
				nameValueString = NULL;
			}
			return (1);
		}
		if ((key = getLoginParam(keyp)) == -1) {
			(void) fprintf(stderr, "%s: %s: %s\n", cmdName,
			    gettext("Unknown key"), keyp);
			if (nameValueString) {
				free(nameValueString);
				nameValueString = NULL;
			}
			return (1);
		}
		if (setLoginParameter(oid, key, valp) != 0) {
			if (nameValueString) {
				free(nameValueString);
				nameValueString = NULL;
			}
			return (1);
		}
		if (delim) {
			indexp = delim + 1;
		} else {
			indexp = NULL;
		}
	}

	if (nameValueString) {
		free(nameValueString);
		nameValueString = NULL;
	}
	return (0);
}

/*
 * Print logical unit information for a specific target
 */
static void
printTargetLuns(IMA_OID_LIST * lunList)
{
	int	j;
	IMA_STATUS status;
	SUN_IMA_LU_PROPERTIES	lunProps;

	for (j = 0; j < lunList->oidCount; j++) {
		status = SUN_IMA_GetLuProperties(lunList->oids[j],
		    &lunProps);
		if (!IMA_SUCCESS(status)) {
			printLibError(status);
			return;
		}

		if (lunProps.imaProps.osDeviceNameValid == IMA_TRUE) {
			(void) fprintf(stdout, "\tLUN: %lld\n",
			    lunProps.imaProps.targetLun);
			(void) fprintf(stdout, "\t     Vendor:  %s\n",
			    lunProps.vendorId);
			(void) fprintf(stdout, "\t     Product: %s\n",
			    lunProps.productId);
			(void) fprintf(stdout,
			    gettext("\t     OS Device Name: %ws\n"),
			    lunProps.imaProps.osDeviceName);
		}
	}
}

/*
 * Retrieve CHAP secret from input
 */
static int
getSecret(char *secret, int *secretLen, int minSecretLen, int maxSecretLen)
{
	char *chapSecret;

	/* get password */
	chapSecret = getpassphrase(gettext("Enter secret:"));

	if (chapSecret == NULL) {
		(void) fprintf(stderr, "%s: %s\n", cmdName,
		    gettext("Unable to get secret"));
		*secret = NULL;
		return (1);
	}

	if (strlen(chapSecret) > maxSecretLen) {
		(void) fprintf(stderr, "%s: %s %d\n", cmdName,
		    gettext("secret too long, maximum length is"),
		    maxSecretLen);
		*secret = NULL;
		return (1);
	}

	if (strlen(chapSecret) < minSecretLen) {
		(void) fprintf(stderr, "%s: %s %d\n", cmdName,
		    gettext("secret too short, minimum length is"),
		    minSecretLen);
		*secret = NULL;
		return (1);
	}

	(void) strcpy(secret, chapSecret);

	chapSecret = getpassphrase(gettext("Re-enter secret:"));

	if (chapSecret == NULL) {
		(void) fprintf(stderr, "%s: %s\n", cmdName,
		    gettext("Unable to get secret"));
		*secret = NULL;
		return (1);
	}

	if (strcmp(secret, chapSecret) != 0) {
		(void) fprintf(stderr, "%s: %s\n", cmdName,
		    gettext("secrets do not match, secret not changed"));
		*secret = NULL;
		return (1);
	}
	*secretLen = strlen(chapSecret);
	return (0);
}

/*
 * Lists the discovery attributes
 */
static int
listDiscovery(int *funcRet)
{
	IMA_OID	initiatorOid;
	IMA_DISCOVERY_PROPERTIES discProps;
	int ret;
	IMA_STATUS status;

	assert(funcRet != NULL);


	/* Find Sun initiator */
	ret = sunInitiatorFind(&initiatorOid);
	if (ret > 0) {
		(void) fprintf(stderr, "%s: %s\n",
		    cmdName, gettext("no initiator found"));
	}

	if (ret != 0) {
		return (ret);
	}

	/* Get discovery attributes from IMA */
	status = IMA_GetDiscoveryProperties(initiatorOid, &discProps);
	if (!IMA_SUCCESS(status)) {
		printLibError(status);
		*funcRet = 1;
		return (ret);
	}


	(void) fprintf(stdout, "%s:\n", "Discovery");
	(void) fprintf(stdout, "\tStatic: %s\n",
	    discProps.staticDiscoveryEnabled == IMA_TRUE ? \
	    gettext("enabled") : gettext("disabled"));
	(void) fprintf(stdout, "\tSend Targets: %s\n",
	    discProps.sendTargetsDiscoveryEnabled == IMA_TRUE ? \
	    gettext("enabled") : gettext("disabled"));
	(void) fprintf(stdout, "\tiSNS: %s\n",
	    discProps.iSnsDiscoveryEnabled == IMA_TRUE ? \
	    gettext("enabled") : gettext("disabled"));

	return (0);
}

/*
 * Print all initiator node attributes
 */
static int
listNode(int *funcRet)
{
	IMA_OID	initiatorOid;
	IMA_NODE_PROPERTIES nodeProps;
	IMA_STATUS status;
	int ret;
	IMA_UINT maxEntries = MAX_AUTH_METHODS;
	IMA_AUTHMETHOD	methodList[MAX_AUTH_METHODS];
	SUN_IMA_RADIUS_CONFIG radiusConfig;
	SUN_IMA_DIGEST_ALGORITHM_VALUE digestAlgorithms;
	IMA_BOOL radiusAccess;

	int i;

	assert(funcRet != NULL);

	ret = getNodeProps(&nodeProps);
	if (ret != 0) {
		return (ret);
	}

	if (nodeProps.nameValid == IMA_FALSE) {
		return (INVALID_NODE_NAME);
	}

	/* Find Sun initiator */
	ret = sunInitiatorFind(&initiatorOid);
	if (ret > 0) {
		(void) fprintf(stderr, "%s: %s\n",
		    cmdName, gettext("no initiator found"));
	}

	if (ret != 0) {
		return (ret);
	}
	/* Begin output */
	(void) fprintf(stdout, gettext("%s: %ws\n"),
	    gettext("Initiator node name"),
	    nodeProps.name);
	(void) fprintf(stdout, gettext("Initiator node alias: "));
	if (nodeProps.aliasValid == IMA_TRUE) {
		(void) fprintf(stdout, gettext("%ws\n"), nodeProps.alias);
	} else {
		(void) fprintf(stdout, "%s\n", "-");
	}
	(void) fprintf(stdout, "\t%s:\n",
	    gettext("Login Parameters (Default/Configured)"));

	/* Get Digest configuration */
	status = SUN_IMA_GetHeaderDigest(initiatorOid, &digestAlgorithms);
	if (IMA_SUCCESS(status)) {
		(void) fprintf(stdout, "\t\t%s: ", gettext("Header Digest"));
		printDigestAlgorithm(&digestAlgorithms,
		    PRINT_CONFIGURED_PARAMS);
	} else {
		printLibError(status);
		*funcRet = 1;
		return (ret);
	}

	status = SUN_IMA_GetDataDigest(initiatorOid, &digestAlgorithms);
	if (IMA_SUCCESS(status)) {
		(void) fprintf(stdout, "\t\t%s: ", gettext("Data Digest"));
		printDigestAlgorithm(&digestAlgorithms,
		    PRINT_CONFIGURED_PARAMS);
	} else {
		printLibError(status);
		*funcRet = 1;
		return (ret);
	}

	/* Get authentication type for this lhba */
	status = IMA_GetInUseInitiatorAuthMethods(initiatorOid, &maxEntries,
	    &methodList[0]);
	(void) fprintf(stdout, "\t%s: ", gettext("Authentication Type"));
	if (!IMA_SUCCESS(status)) {
		/* No authentication method set - default is NONE */
		(void) fprintf(stdout, gettext("NONE"));
	} else {
		for (i = 0; i < maxEntries; i++) {
			if (i > 0) {
				(void) fprintf(stdout, "|");
			}
			switch (methodList[i]) {
				case IMA_AUTHMETHOD_NONE:
					(void) fprintf(stdout, gettext("NONE"));
					break;
				case IMA_AUTHMETHOD_CHAP:
					(void) fprintf(stdout, gettext("CHAP"));
					listCHAPName(initiatorOid);
					break;
				default:
					(void) fprintf(stdout,
					    gettext("unknown type"));
					break;
			}
		}
	}
	(void) fprintf(stdout, "\n");


	/* Get RADIUS configuration */
	status = SUN_IMA_GetInitiatorRadiusConfig(initiatorOid, &radiusConfig);
	(void) fprintf(stdout, "\t%s: ", gettext("RADIUS Server"));
	if (IMA_SUCCESS(status)) {
		if (strlen(radiusConfig.hostnameIpAddress) > 0) {
			(void) fprintf(stdout, "%s:%d",
			    radiusConfig.hostnameIpAddress,
			    radiusConfig.port);
		} else {
			(void) fprintf(stdout, "%s", gettext("NONE"));
		}
	} else {
		(void) fprintf(stdout, "%s", gettext("NONE"));
	}
	(void) fprintf(stdout, "\n");

	status = SUN_IMA_GetInitiatorRadiusAccess(initiatorOid,
	    &radiusAccess);
	(void) fprintf(stdout, "\t%s: ", gettext("RADIUS Access"));
	if (IMA_SUCCESS(status)) {
		if (radiusAccess == IMA_TRUE) {
			(void) fprintf(stdout, "%s", gettext("enabled"));
		} else {
			(void) fprintf(stdout, "%s", gettext("disabled"));
		}
	} else if (status == IMA_ERROR_OBJECT_NOT_FOUND) {
		(void) fprintf(stdout, "%s", gettext("disabled"));
	} else {
		(void) fprintf(stdout, "%s", gettext("unknown"));
	}
	(void) fprintf(stdout, "\n");

	/* print tunable parameters information. */
	ret = printTunableParameters(initiatorOid);

	/* print configured session information. */
	ret = printConfiguredSessions(initiatorOid);

	return (ret);
}

/*
 * Print discovery addresses
 */
static int
listDiscoveryAddress(int objectLen, char *objects[], cmdOptions_t *options,
    int *funcRet)
{
	IMA_OID	initiatorOid;
	SUN_IMA_DISC_ADDR_PROP_LIST *discoveryAddressPropertiesList;
	IMA_DISCOVERY_ADDRESS_PROPERTIES discAddrProps;
	IMA_TARGET_ADDRESS address;
	SUN_IMA_DISC_ADDRESS_KEY_PROPERTIES *pList;
	IMA_STATUS status;
	wchar_t wcInputObject[MAX_ADDRESS_LEN + 1];
	int ret;
	boolean_t object = B_FALSE;
	int outerLoop;
	boolean_t found;
	boolean_t verbose = B_FALSE;
	int i, j;
	cmdOptions_t *optionList = options;
	char sAddr[SUN_IMA_IP_ADDRESS_PORT_LEN];

	assert(funcRet != NULL);

	/* Find Sun initiator */
	ret = sunInitiatorFind(&initiatorOid);
	if (ret > 0) {
		(void) fprintf(stderr, "%s: %s\n",
		    cmdName, gettext("no initiator found"));
	}

	if (ret != 0) {
		return (ret);
	}

	for (; optionList->optval; optionList++) {
		switch (optionList->optval) {
			case 'v':
				verbose = B_TRUE;
				break;
			default:
				(void) fprintf(stderr, "%s: %c: %s\n",
				    cmdName, optionList->optval,
				    gettext("unknown option"));
				return (1);
		}
	}

	/*
	 * If there are multiple objects, execute outer 'for' loop that
	 * many times for each target detail, otherwise, execute it only
	 * once with summaries only
	 */
	if (objectLen > 0) {
		object = B_TRUE;
		outerLoop = objectLen;
	} else {
		object = B_FALSE;
		outerLoop = 1;
	}

	status = SUN_IMA_GetDiscoveryAddressPropertiesList(
	    &discoveryAddressPropertiesList);
	if (!IMA_SUCCESS(status)) {
		printLibError(status);
		*funcRet = 1;
		return (ret);
	}

	for (i = 0; i < outerLoop; i++) {
		if (object) {
			/* initialize */
			(void) memset(&wcInputObject[0], 0,
			    sizeof (wcInputObject));
			(void) memset(&address, 0, sizeof (address));
			if (mbstowcs(wcInputObject, objects[i],
			    (MAX_ADDRESS_LEN + 1)) == (size_t)-1) {
				(void) fprintf(stderr, "%s: %s\n",
				    cmdName,
				    gettext("conversion error"));
				ret = 1;
				continue;
			}

			/*
			 * if one or more objects were input,
			 * get the values
			 */
			if (getTargetAddress(DISCOVERY_ADDRESS,
			    objects[i], &address) != 0) {
				ret = 1;
				continue;
			}
		}
		for (found = B_FALSE, j = 0;
		    j < discoveryAddressPropertiesList->discAddrCount;
		    j++) {
			discAddrProps =
			    discoveryAddressPropertiesList->props[j];

			/*
			 * Compare the discovery address with the input if
			 * one was input
			 */
			if (object &&
			    ipAddressesEqual(discAddrProps.discoveryAddress,
			    address) && (discAddrProps.discoveryAddress.
			    portNumber == address.portNumber)) {
				found = B_TRUE;
			}

			if (!object || found) {
				/* Print summary - always */
				if (discAddrProps.discoveryAddress.
				    hostnameIpAddress.id.ipAddress.
				    ipv4Address) {
					(void) inet_ntop(AF_INET, discAddrProps.
					    discoveryAddress.hostnameIpAddress.
					    id.ipAddress.ipAddress, sAddr,
					    sizeof (sAddr));
					(void) fprintf(stdout,
					    "Discovery Address: %s:%u\n",
					    sAddr, discAddrProps.
					    discoveryAddress.portNumber);
				} else {
					(void) inet_ntop(AF_INET6,
					    discAddrProps.
					    discoveryAddress.hostnameIpAddress.
					    id.ipAddress.ipAddress, sAddr,
					    sizeof (sAddr));
					(void) fprintf(stdout,
					    "DiscoveryAddress: [%s]:%u\n",
					    sAddr, discAddrProps.
					    discoveryAddress.portNumber);
				}
			}

			if ((!object || found) && verbose) {
				IMA_NODE_PROPERTIES nodeProps;

				if (getNodeProps(&nodeProps) != 0) {
					break;
				}

				/*
				 * Issue sendTargets only when an addr is
				 * specified.
				 */
				status = SUN_IMA_SendTargets(nodeProps.name,
				    discAddrProps.discoveryAddress, &pList);
				if (!IMA_SUCCESS(status)) {
					(void) fprintf(stderr, "%s\n",
					    gettext("\tUnable to get "\
					    "targets."));
					*funcRet = 1;
					continue;
				}
				printSendTargets(pList);
			}

			if (found) {
				/* we found the discovery address - break */
				break;
			}
		}
		/*
		 * There was an object entered but we didn't
		 * find it.
		 */
		if (object && !found) {
			(void) fprintf(stdout, "%s: %s\n",
			    objects[i], gettext("not found"));
		}
	}
	return (ret);
}

/*
 * Print ISNS Server addresses
 */
static int
listISNSServerAddress(int objectLen, char *objects[], cmdOptions_t *options,
    int *funcRet)
{
	IMA_OID	initiatorOid;
	SUN_IMA_DISC_ADDR_PROP_LIST	    *discoveryAddressPropertiesList;
	IMA_DISCOVERY_ADDRESS_PROPERTIES discAddrProps;
	IMA_TARGET_ADDRESS address;
	SUN_IMA_DISC_ADDRESS_KEY_PROPERTIES *pList;
	IMA_STATUS status;
	wchar_t wcInputObject[MAX_ADDRESS_LEN + 1];
	int ret;
	boolean_t object = B_FALSE;
	int outerLoop;
	boolean_t found;
	boolean_t showTarget = B_FALSE;
	int i, j;
	cmdOptions_t *optionList = options;
	char sAddr[SUN_IMA_IP_ADDRESS_PORT_LEN];

	assert(funcRet != NULL);

	/* Find Sun initiator */
	ret = sunInitiatorFind(&initiatorOid);
	if (ret > 0) {
		(void) fprintf(stderr, "%s: %s\n",
		    cmdName, gettext("no initiator found"));
	}

	if (ret != 0) {
		return (ret);
	}

	for (; optionList->optval; optionList++) {
		switch (optionList->optval) {
			case 'v':
				showTarget = B_TRUE;
				break;
			default:
				(void) fprintf(stderr, "%s: %c: %s\n",
				    cmdName, optionList->optval,
				    gettext("unknown option"));
				return (1);
		}
	}

	/*
	 * If there are multiple objects, execute outer 'for' loop that
	 * many times for each target detail, otherwise, execute it only
	 * once with summaries only
	 */
	if (objectLen > 0) {
		object = B_TRUE;
		outerLoop = objectLen;
	} else {
		object = B_FALSE;
		outerLoop = 1;
	}

	status = SUN_IMA_GetISNSServerAddressPropertiesList(
	    &discoveryAddressPropertiesList);
	if (!IMA_SUCCESS(status)) {
		printLibError(status);
		*funcRet = 1;
		return (ret);
	}

	for (i = 0; i < outerLoop; i++) {
		if (object) {
			/* initialize */
			(void) memset(&wcInputObject[0], 0,
			    sizeof (wcInputObject));
			(void) memset(&address, 0, sizeof (address));
			if (mbstowcs(wcInputObject, objects[i],
			    (MAX_ADDRESS_LEN + 1)) == (size_t)-1) {
				(void) fprintf(stderr, "%s: %s\n",
				    cmdName,
				    gettext("conversion error"));
				ret = 1;
				continue;
			}

			/*
			 * if one or more objects were input,
			 * get the values
			 */
			if (getTargetAddress(ISNS_SERVER_ADDRESS,
			    objects[i], &address) != 0) {
				ret = 1;
				continue;
			}
		}
		for (found = B_FALSE, j = 0;
		    j < discoveryAddressPropertiesList->discAddrCount;
		    j++) {
			discAddrProps =
			    discoveryAddressPropertiesList->props[j];

			/*
			 * Compare the discovery address with the input if
			 * one was input
			 */
			if (object &&
			    ipAddressesEqual(discAddrProps.discoveryAddress,
			    address) &&
			    (discAddrProps.discoveryAddress.portNumber ==
			    address.portNumber)) {
				found = B_TRUE;
			}

			if (!object || found) {
				/* Print summary - always */
				if (discAddrProps.discoveryAddress.
				    hostnameIpAddress.id.ipAddress.
				    ipv4Address) {
					(void) inet_ntop(AF_INET, discAddrProps.
					    discoveryAddress.hostnameIpAddress.
					    id.ipAddress.ipAddress, sAddr,
					    sizeof (sAddr));
				} else {
					(void) inet_ntop(AF_INET6,
					    discAddrProps.
					    discoveryAddress.hostnameIpAddress.
					    id.ipAddress.ipAddress, sAddr,
					    sizeof (sAddr));
				}
				(void) fprintf(stdout,
				    "iSNS Server IP Address: %s:%u\n",
				    sAddr,
				    discAddrProps.discoveryAddress.portNumber);
			}

			if ((!object || found) && showTarget) {
				IMA_NODE_PROPERTIES nodeProps;

				if (getNodeProps(&nodeProps) != 0) {
					break;
				}

				/*
				 * Issue sendTargets only when an addr is
				 * specified.
				 */
				status = SUN_IMA_RetrieveISNSServerTargets(
				    discAddrProps.discoveryAddress,
				    &pList);
				if (!IMA_SUCCESS(status)) {
					/*
					 * Check if the discovery mode is
					 * disabled.
					 */
					if (status ==
					    IMA_ERROR_OBJECT_NOT_FOUND) {
						(void) fprintf(stderr, "%s\n",
						    gettext("\tiSNS "\
						    "discovery "\
						    "mode "\
						    "disabled. "\
						    "No targets "\
						    "to report."));

					} else {
						(void) fprintf(stderr, "%s\n",
						    gettext("\tUnable "\
						    "to get "\
						    "targets."));
					}
					continue;
				}
				printSendTargets(pList);
			}

			if (found) {
				/* we found the discovery address - break */
				break;
			}
		}
		/*
		 * There was an object entered but we didn't
		 * find it.
		 */
		if (object && !found) {
			(void) fprintf(stdout, "%s: %s\n",
			    objects[i], gettext("not found"));
		}
	}
	return (ret);
}

/*
 * Print static configuration targets
 */
static int
listStaticConfig(int operandLen, char *operand[], int *funcRet)
{
	IMA_STATUS status;
	IMA_OID	initiatorOid;
	IMA_OID_LIST *staticTargetList;
	SUN_IMA_STATIC_TARGET_PROPERTIES staticTargetProps;
	wchar_t staticTargetName[MAX_ISCSI_NAME_LEN + 1];
	wchar_t staticTargetAddress[SUN_IMA_IP_ADDRESS_PORT_LEN];
	wchar_t wcCol;
	char sAddr[SUN_IMA_IP_ADDRESS_PORT_LEN];
	int ret;
	boolean_t object = B_FALSE;
	int outerLoop;
	boolean_t found; /* B_TRUE if a target name is found */
	boolean_t matched; /* B_TRUE if a specific target is found */
	boolean_t targetAddressSpecified = B_FALSE;
	boolean_t tpgtSpecified = B_FALSE;
	boolean_t isIpv6;
	int i, j;
	IMA_UINT16 port = 0;
	IMA_UINT16 tpgt = 0;
	char tmpStr[SUN_IMA_IP_ADDRESS_PORT_LEN];
	wchar_t tmpTargetAddress[SUN_IMA_IP_ADDRESS_PORT_LEN];

	assert(funcRet != NULL);

	/* Find Sun initiator */
	ret = sunInitiatorFind(&initiatorOid);
	if (ret > 0) {
		(void) fprintf(stderr, "%s: %s\n",
		    cmdName, gettext("no initiator found"));
	}

	if (ret != 0) {
		return (ret);
	}

	/*
	 * If there are multiple objects, execute outer 'for' loop that
	 * many times for each static config detail, otherwise, execute it only
	 * once with summaries only
	 */
	if (operandLen > 0) {
		object = B_TRUE;
		outerLoop = operandLen;
	} else {
		object = B_FALSE;
		outerLoop = 1;
	}

	/* convert ':' to wide char for wchar string search */
	if (mbtowc(&wcCol, ":", sizeof (wcCol)) == -1) {
		(void) fprintf(stderr, "%s: %s\n",
		    cmdName, gettext("conversion error"));
		return (1);
	}

	status = IMA_GetStaticDiscoveryTargetOidList(initiatorOid,
	    &staticTargetList);
	if (!IMA_SUCCESS(status)) {
		printLibError(status);
		*funcRet = 1;
		return (ret);
	}

	for (i = 0; i < outerLoop; i++) {
		if (object) {
			if (parseTarget(operand[i],
			    &staticTargetName[0],
			    MAX_ISCSI_NAME_LEN + 1,
			    &targetAddressSpecified,
			    &staticTargetAddress[0],
			    SUN_IMA_IP_ADDRESS_PORT_LEN,
			    &port,
			    &tpgtSpecified,
			    &tpgt,
			    &isIpv6) != PARSE_TARGET_OK) {
				ret = 1;
				continue;
			}
		}

		for (found = B_FALSE, j = 0; j < staticTargetList->oidCount;
		    j++) {
			boolean_t isIpv6 = B_FALSE;
			IMA_UINT16 stpgt;
			IMA_BOOL defaultTpgt;

			matched = B_FALSE;
			(void) memset(&staticTargetProps, 0,
			    sizeof (staticTargetProps));

			status = SUN_IMA_GetStaticTargetProperties(
			    staticTargetList->oids[j], &staticTargetProps);
			if (!IMA_SUCCESS(status)) {
				printLibError(status);
				(void) IMA_FreeMemory(staticTargetList);
				*funcRet = 1;
				return (ret);
			}

			stpgt = staticTargetProps.staticTarget.targetAddress.
			    tpgt;

			defaultTpgt = staticTargetProps.staticTarget.
			    targetAddress.defaultTpgt;

			isIpv6 = !staticTargetProps.staticTarget.targetAddress.
			    imaStruct.hostnameIpAddress.id.ipAddress.
			    ipv4Address;

			/*
			 * Compare the static target name with the input if
			 * one was input
			 */

			if (object &&
			    (targetNamesEqual(
			    staticTargetProps.staticTarget.targetName,
			    staticTargetName) == B_TRUE)) {
				/* targetName found - found = B_TRUE */
				found = B_TRUE;
				if (targetAddressSpecified == B_FALSE) {
					matched = B_TRUE;
				} else {

				if (staticTargetProps.staticTarget.
				    targetAddress.imaStruct.
				    hostnameIpAddress.id.ipAddress.
				    ipv4Address == IMA_TRUE) {
					(void) inet_ntop(AF_INET,
					    staticTargetProps.
					    staticTarget.targetAddress.
					    imaStruct.hostnameIpAddress.id.
					    ipAddress.ipAddress, tmpStr,
					    sizeof (tmpStr));
				} else {
					(void) inet_ntop(AF_INET6,
					    staticTargetProps.
					    staticTarget.targetAddress.
					    imaStruct.hostnameIpAddress.id.
					    ipAddress.ipAddress, tmpStr,
					    sizeof (tmpStr));
				}

				if (mbstowcs(tmpTargetAddress, tmpStr,
				    SUN_IMA_IP_ADDRESS_PORT_LEN) ==
				    (size_t)-1) {
					(void) fprintf(stderr, "%s: %s\n",
					    cmdName,
					gettext("conversion error"));
					ret = 1;
					continue;
				}

				if (wcsncmp(tmpTargetAddress,
				    staticTargetAddress,
				    SUN_IMA_IP_ADDRESS_PORT_LEN)
				    == 0 &&
				    staticTargetProps.
				    staticTarget.targetAddress.
				    imaStruct.portNumber == port) {
					/*
					 * Since an object is
					 * specified, it should also
					 * have a tpgt specified. If
					 * not, that means the object
					 * specified is associated with
					 * the default tpgt. In
					 * either case, a tpgt
					 * comparison should be done
					 * before claiming that a
					 * match is found.
					 */
					if ((tpgt == stpgt &&
					    tpgtSpecified == B_TRUE &&
					    defaultTpgt == IMA_FALSE) ||
					    (tpgt == stpgt &&
					    tpgtSpecified == B_FALSE &&
					    defaultTpgt == IMA_TRUE)) {
						matched = B_TRUE;
					}
				}

				}
			}

			if (!object || matched) {
				/* print summary - always */
				(void) fprintf(stdout, gettext("%s: %ws,"),
				    "Static Configuration Target",
				    staticTargetProps.staticTarget.targetName);

				if (isIpv6 == B_FALSE) {
					(void) inet_ntop(AF_INET,
					    staticTargetProps.
					    staticTarget.targetAddress.
					    imaStruct.hostnameIpAddress.id.
					    ipAddress.ipAddress, sAddr,
					    sizeof (sAddr));
					(void) fprintf(stdout, "%s:%d",
					    sAddr,
					    staticTargetProps.staticTarget.
					    targetAddress.imaStruct.portNumber);
				} else {
					(void) inet_ntop(AF_INET6,
					    staticTargetProps.
					    staticTarget.targetAddress.
					    imaStruct.hostnameIpAddress.id.
					    ipAddress.ipAddress, sAddr,
					    sizeof (sAddr));
					(void) fprintf(stdout, "[%s]:%d",
					    sAddr,
					    staticTargetProps.staticTarget.
					    targetAddress.imaStruct.portNumber);
				}

				if (staticTargetProps.staticTarget.
				    targetAddress.
				    defaultTpgt == IMA_FALSE) {
					(void) fprintf(stdout, ",%d\n",
					    staticTargetProps.
					    staticTarget.targetAddress.tpgt);
				} else {
					(void) fprintf(stdout, "\n");
				}
			}

		}
		/*
		 * No details to display, but if there were:
		 *  if (object && found)...
		 *
		 */

		/*
		 * There was an object entered but we didn't
		 * find it.
		 */
		if (object && !found) {
			(void) fprintf(stdout, "%s: %s\n",
			    operand[i], gettext("not found"));
			ret = 1; /* DIY test fix */
		}
	}
	return (ret);
}

/*
 * Print targets
 */
/*ARGSUSED*/
static int
listTarget(int objectLen, char *objects[], cmdOptions_t *options, int *funcRet)
{
	IMA_OID	initiatorOid;
	IMA_OID_LIST *targetList;
	IMA_OID_LIST *lunList;
	SUN_IMA_TARGET_PROPERTIES targetProps;
	IMA_STATUS status;
	IMA_OID_LIST *pConnList;
	SUN_IMA_CONN_PROPERTIES *connProps;

	int ret;
	wchar_t targetName[MAX_ISCSI_NAME_LEN + 1];
	wchar_t targetAddress[SUN_IMA_IP_ADDRESS_PORT_LEN];
	int outerLoop;
	boolean_t found;
	boolean_t operandEntered = B_FALSE;
	boolean_t verbose = B_FALSE;
	boolean_t scsi_target = B_FALSE;
	boolean_t targetAddressSpecified = B_FALSE;
	boolean_t isIpv6 = B_FALSE;
	int i, j;
	cmdOptions_t *optionList = options;
	boolean_t tpgtSpecified = B_FALSE;
	IMA_UINT16 port = 0;
	uint16_t tpgt;

	assert(funcRet != NULL);

	/* Find Sun initiator */
	ret = sunInitiatorFind(&initiatorOid);
	if (ret > 0) {
		(void) fprintf(stderr, "%s: %s\n",
		    cmdName, gettext("no initiator found"));
	}

	if (ret != 0) {
		return (ret);
	}

	for (; optionList->optval; optionList++) {
		switch (optionList->optval) {
			case 'S':
				scsi_target = B_TRUE;
				break;
			case 'v':
				verbose = B_TRUE;
				break;
			default:
				(void) fprintf(stderr, "%s: %c: %s\n",
				    cmdName, optionList->optval,
				    gettext("unknown option"));
				return (1);
		}
	}

	/*
	 * If there are multiple objects, execute outer 'for' loop that
	 * many times for each target detail, otherwise, execute it only
	 * once with summaries only
	 */
	if (objectLen > 0) {
		operandEntered = B_TRUE;
		outerLoop = objectLen;
	} else {
		operandEntered = B_FALSE;
		outerLoop = 1;
	}

	status = SUN_IMA_GetSessionOidList(initiatorOid, &targetList);
	if (!IMA_SUCCESS(status)) {
		printLibError(status);
		*funcRet = 1;
		return (ret);
	}

	for (i = 0; i < outerLoop; i++) {

		tpgtSpecified = B_FALSE;
		if (operandEntered) {
			if (parseTarget(objects[i],
			    &targetName[0],
			    MAX_ISCSI_NAME_LEN + 1,
			    &targetAddressSpecified,
			    &targetAddress[0],
			    SUN_IMA_IP_ADDRESS_PORT_LEN,
			    &port,
			    &tpgtSpecified,
			    &tpgt,
			    &isIpv6) != PARSE_TARGET_OK) {
				ret = 1;
				continue;
			}
		}
		for (found = B_FALSE, j = 0; j < targetList->oidCount; j++) {
			status = SUN_IMA_GetTargetProperties(
			    targetList->oids[j],
			    &targetProps);
			if (!IMA_SUCCESS(status)) {
				printLibError(status);
				(void) IMA_FreeMemory(targetList);
				*funcRet = 1;
				return (ret);
			}

			/*
			 * Compare the target name with the input if
			 * one was input, if they match, print the target's info
			 *
			 * if no target name was input, continue printing this
			 * target
			 */
			if (operandEntered) {
				if (targetNamesEqual(targetProps.imaProps.name,
				    targetName) == B_TRUE) {
					if (tpgtSpecified == B_TRUE) {
						if (targetProps.
						    defaultTpgtConf ==
						    IMA_FALSE &&
						    targetProps.
						    tpgtConf == tpgt) {
							found = B_TRUE;
						} else {
							/*
							 * tpgt does not match,
							 * move on to next
							 * target
							 */
							continue;
						}
					} else {
						found = B_TRUE;
					}
				} else {
					/*
					 * target name does not match, move on
					 * to next target
					 */
					continue;
				}
			}

			/* print summary - always */
			(void) fprintf(stdout, gettext("%s: %ws\n"),
			    gettext("Target"), targetProps.imaProps.name);

			/* Alias */
			(void) fprintf(stdout, "\t%s: ", gettext("Alias"));
			if (wslen(targetProps.imaProps.alias) > (size_t)0) {
				(void) fprintf(stdout, gettext("%ws\n"),
				    targetProps.imaProps.alias);
			} else {
				(void) fprintf(stdout, "%s\n", "-");
			}

			if (targetProps.defaultTpgtNego != IMA_TRUE) {
				(void) fprintf(stdout, "%s%s: %d\n",
				    "\t", gettext("TPGT"),
				    targetProps.tpgtNego);
			} else if (targetProps.defaultTpgtConf != IMA_TRUE) {
				(void) fprintf(stdout, "%s%s: %d\n",
				    "\t", gettext("TPGT"),
				    targetProps.tpgtConf);
			}

			(void) fprintf(stdout,
			    "%s%s: %02x%02x%02x%02x%02x%02x\n",
			    "\t", gettext("ISID"),
			    targetProps.isid[0], targetProps.isid[1],
			    targetProps.isid[2], targetProps.isid[3],
			    targetProps.isid[4], targetProps.isid[5]);

			pConnList = NULL;
			status = SUN_IMA_GetConnOidList(
			    &targetList->oids[j],
			    &pConnList);

			if (!IMA_SUCCESS(status)) {
				printLibError(status);
				(void) IMA_FreeMemory(targetList);
				*funcRet = 1;
				return (ret);
			}

			(void) fprintf(stdout, "%s%s: %lu\n",
			    "\t",
			    gettext("Connections"),
			    pConnList->oidCount);

			if (verbose) {
				SUN_IMA_DIGEST_ALGORITHM_VALUE digestAlgorithms;

				printConnectionList("\t\t", pConnList);
				printDiscoveryMethod(
				    "\t\t  ",
				    targetProps.imaProps.discoveryMethodFlags);
				(void) printLoginParameters(
				    "\t\t  ",
				    targetList->oids[j],
				    PRINT_NEGOTIATED_PARAMS);

				/* Get Digest configuration */
				status = SUN_IMA_GetConnProperties(
				    &pConnList->oids[0], &connProps);

				(void) getNegotiatedDigest(
				    ISCSI_LOGIN_PARAM_HEADER_DIGEST,
				    &digestAlgorithms, connProps);

				if (IMA_SUCCESS(status)) {
					(void) fprintf(stdout, "\t\t  \t%s: ",
					    gettext("Header Digest"));
					printDigestAlgorithm(
					    &digestAlgorithms,
					    PRINT_NEGOTIATED_PARAMS);
				} else {
					(void) IMA_FreeMemory(pConnList);
					(void) IMA_FreeMemory(targetList);
					printLibError(status);
					*funcRet = 1;
					return (ret);
				}

				(void) getNegotiatedDigest(
				    ISCSI_LOGIN_PARAM_DATA_DIGEST,
				    &digestAlgorithms, connProps);

				if (IMA_SUCCESS(status)) {
					(void) fprintf(stdout, "\t\t  \t%s: ",
					    gettext("Data Digest"));
					printDigestAlgorithm(
					    &digestAlgorithms,
					    PRINT_NEGOTIATED_PARAMS);
				} else {
					(void) IMA_FreeMemory(pConnList);
					(void) IMA_FreeMemory(targetList);
					printLibError(status);
					*funcRet = 1;
					return (ret);
				}

				(void) fprintf(stdout, "\n");
			}

			if (scsi_target) {
				status = IMA_GetLuOidList(
				    targetList->oids[j],
				    &lunList);
				if (!IMA_SUCCESS(status)) {
					printLibError(status);
					(void) IMA_FreeMemory(targetList);
					*funcRet = 1;
					return (ret);
				}
				if (lunList->oidCount != 0) {
					printTargetLuns(lunList);
				}
				(void) fprintf(stdout, "\n");
				(void) IMA_FreeMemory(lunList);
			}
		}
		/*
		 * did we find the object
		 */

		if (operandEntered && !found) {
			(void) fprintf(stdout, "%s: %s\n",
			    objects[i], gettext("not found"));
		}
	}

	(void) IMA_FreeMemory(targetList);
	return (ret);
}


/*
 * Print configured session information
 */
static int
printConfiguredSessions(IMA_OID oid)
{
	IMA_STATUS		status;
	const char		*rtn;
	SUN_IMA_CONFIG_SESSIONS	*pConfigSessions;
	char			address[MAX_ADDRESS_LEN];
	int			out;

	/* Get configured session information */
	status = SUN_IMA_GetConfigSessions(oid, &pConfigSessions);

	if (IMA_SUCCESS(status)) {
		(void) fprintf(stdout, "\t%s: ",
		    gettext("Configured Sessions"));
		if (pConfigSessions->bound == IMA_FALSE) {
			/* default binding */
			(void) fprintf(stdout, "%lu\n", pConfigSessions->out);
		} else {
			/* hardcoded binding */
			for (out = 0;
			    out < pConfigSessions->out; out++) {
				if (pConfigSessions->bindings[out].
				    ipAddress.ipv4Address == IMA_TRUE) {
					rtn = inet_ntop(AF_INET,
					    pConfigSessions->bindings[out].
					    ipAddress.ipAddress, address,
					    MAX_ADDRESS_LEN);
				} else {
					rtn = inet_ntop(AF_INET6,
					    pConfigSessions->bindings[out].
					    ipAddress.ipAddress, address,
					    MAX_ADDRESS_LEN);
				}
				if (rtn != NULL) {
					(void) printf("%s ", address);
				}
			}
			(void) fprintf(stdout, "\n");
		}
	} else {
		free(pConfigSessions);
		printLibError(status);
		return (1);
	}

	free(pConfigSessions);
	return (0);
}

/*
 * Print target parameters
 */
static int
listTargetParam(int operandLen, char *operand[], cmdOptions_t *options,
    int *funcRet)
{
	IMA_STATUS status;
	IMA_OID	initiatorOid;
	IMA_OID_LIST *targetList;
	IMA_AUTHMETHOD	methodList[MAX_AUTH_METHODS];
	SUN_IMA_TARGET_PROPERTIES targetProps;
	IMA_UINT maxEntries = MAX_AUTH_METHODS;
	IMA_BOOL bidirAuth;
	int ret;
	wchar_t targetName[MAX_ISCSI_NAME_LEN + 1];
	wchar_t targetAddress[SUN_IMA_IP_ADDRESS_PORT_LEN];
	boolean_t operandEntered = B_FALSE;
	boolean_t targetAddressSpecified = B_FALSE;
	boolean_t printObject = B_FALSE;
	boolean_t tpgtSpecified = B_FALSE;
	boolean_t isIpv6 = B_FALSE;
	int outerLoop;
	boolean_t found;
	int i, j;
	SUN_IMA_DIGEST_ALGORITHM_VALUE digestAlgorithms;
	boolean_t verbose = B_FALSE;
	cmdOptions_t *optionList = options;
	IMA_UINT16 port = 0;
	IMA_UINT16 tpgt = 0;

	assert(funcRet != NULL);

	/* Find Sun initiator */
	ret = sunInitiatorFind(&initiatorOid);
	if (ret > 0) {
		(void) fprintf(stderr, "%s: %s\n",
		    cmdName, gettext("no initiator found"));
	}

	if (ret != 0) {
		return (ret);
	}

	for (; optionList->optval; optionList++) {
		switch (optionList->optval) {
			case 'v':
				verbose = B_TRUE;
				break;
			default:
				(void) fprintf(stderr, "%s: %c: %s\n",
				    cmdName, optionList->optval,
				    gettext("unknown option"));
				return (1);
		}
	}

	/*
	 * If there are multiple operands, execute outer 'for' loop that
	 * many times to find each target parameter operand entered, otherwise,
	 * execute it only once for all target parameters returned.
	 */
	if (operandLen > 0) {
		operandEntered = B_TRUE;
		outerLoop = operandLen;
	} else {
		operandEntered = B_FALSE;
		outerLoop = 1;
	}

	/*
	 * Ideally there should be an interface available for obtaining
	 * the list of target-param objects. Since the driver currently
	 * creates a target OID and the associated session structure when
	 * a target-param object is created, we can leverage the target
	 * OID list and use it to manage the target-param objects. When
	 * we stop creating a session for target-param object in the
	 * driver, we will switch to using a different interface to
	 * obtain target-param objects.
	 */
	status = IMA_GetTargetOidList(initiatorOid, &targetList);
	if (!IMA_SUCCESS(status)) {
		printLibError(status);
		*funcRet = 1;
		return (ret);
	}

	for (i = 0; i < outerLoop; i++) {
		if (operandEntered) {
			if (parseTarget(operand[i],
			    &targetName[0],
			    MAX_ISCSI_NAME_LEN + 1,
			    &targetAddressSpecified,
			    &targetAddress[0],
			    SUN_IMA_IP_ADDRESS_PORT_LEN,
			    &port,
			    &tpgtSpecified,
			    &tpgt,
			    &isIpv6) != PARSE_TARGET_OK) {
				ret = 1;
				continue;
			}
		}
		for (j = 0; j < targetList->oidCount; j++) {
			found = B_FALSE;
			printObject = B_FALSE;
			status = SUN_IMA_GetTargetProperties(
			    targetList->oids[j],
			    &targetProps);
			if (!IMA_SUCCESS(status)) {
				printLibError(status);
				(void) IMA_FreeMemory(targetList);
				*funcRet = 1;
				return (ret);
			}

			/*
			 * Compare the target name with the input if
			 * one was input
			 */
			if (operandEntered &&
			    (targetNamesEqual(targetProps.imaProps.name,
			    targetName) == B_TRUE)) {
				/*
				 * For now, regardless of whether a target
				 * address is specified, we return B_TRUE
				 * because IMA_TARGET_PROPERTIES does not
				 * have a field for specifying address.
				 */
				found = B_TRUE;
			}

			/*
			 * if no operand was entered OR
			 * an operand was entered and it was
			 * found, we want to print
			 */
			if (!operandEntered || found) {
				printObject = B_TRUE;
			}

			if (printObject) {
				(void) fprintf(stdout, gettext("%s: %ws\n"),
				    gettext("Target"),
				    targetProps.imaProps.name);

				(void) fprintf(stdout,
				    "\t%s: ", gettext("Alias"));
				if (wslen(targetProps.imaProps.alias) >
				    (size_t)0) {
					(void) fprintf(stdout,
					    gettext("%ws\n"),
					    targetProps.imaProps.alias);
				} else {
					(void) fprintf(stdout, "%s\n", "-");
				}
			}

			if (printObject && verbose) {
				/* Get bidirectional authentication flag */
				(void) fprintf(stdout, "\t%s: ",
				    gettext("Bi-directional Authentication"));
				status = SUN_IMA_GetTargetBidirAuthFlag(
				    targetList->oids[j],
				    &bidirAuth);
				if (IMA_SUCCESS(status)) {
					if (bidirAuth == IMA_TRUE) {
						(void) fprintf(stdout,
						    gettext("enabled"));
					} else {
						(void) fprintf(stdout,
						    gettext("disabled"));
					}
				} else {
					(void) fprintf(stdout,
					    gettext("disabled"));
				}
				(void) fprintf(stdout, "\n");

				/* Get authentication type for this target */
				status = SUN_IMA_GetTargetAuthMethods(
				    initiatorOid,
				    targetList->oids[j],
				    &maxEntries,
				    &methodList[0]);
				(void) fprintf(stdout, "\t%s: ",
				    gettext("Authentication Type"));
				if (!IMA_SUCCESS(status)) {
					/*
					 * No authentication method define
					 * NONE by default.
					 */
					(void) fprintf(stdout, gettext("NONE"));
				} else {
					for (i = 0; i < maxEntries; i++) {
						if (i > 0) {
							(void) fprintf(stdout,
							    "|");
						}
						switch (methodList[i]) {
						case IMA_AUTHMETHOD_NONE:
							(void) fprintf(stdout,
							    gettext("NONE"));
							break;

						case IMA_AUTHMETHOD_CHAP:
							(void) fprintf(stdout,
							    gettext("CHAP"));
							listCHAPName(
							    targetList->
							    oids[j]);
							break;

						default:
							(void) fprintf(stdout,
							    gettext(
							    "unknown "
							    "type"));
							break;
						}
					}
				}
				(void) fprintf(stdout, "\n");
				if (printLoginParameters("\t",
				    targetList->oids[j],
				    PRINT_CONFIGURED_PARAMS)
				    != 0) {
					(void) IMA_FreeMemory(targetList);
					*funcRet = 1;
					return (ret);
				}

				/* Get Digest configuration */
				status = SUN_IMA_GetHeaderDigest(
				    targetList->oids[j],
				    &digestAlgorithms);
				if (IMA_SUCCESS(status)) {
					(void) fprintf(stdout, "\t\t%s: ",
					    gettext("Header Digest"));
					printDigestAlgorithm(&digestAlgorithms,
					    PRINT_CONFIGURED_PARAMS);
				} else {
					printLibError(status);
					*funcRet = 1;
					return (ret);
				}

				status = SUN_IMA_GetDataDigest(
				    targetList->oids[j],
				    &digestAlgorithms);
				if (IMA_SUCCESS(status)) {
					(void) fprintf(stdout, "\t\t%s: ",
					    gettext("Data Digest"));
					printDigestAlgorithm(&digestAlgorithms,
					    PRINT_CONFIGURED_PARAMS);
				} else {
					printLibError(status);
					*funcRet = 1;
					return (ret);
				}

				/* print tunable parameters infomation */
				if (printTunableParameters(
				    targetList->oids[j]) != 0) {
					*funcRet = 1;
					return (ret);
				}

				/* print configured session information */
				if (printConfiguredSessions(
				    targetList->oids[j]) != 0) {
					*funcRet = 1;
					return (ret);
				}

				(void) fprintf(stdout, "\n");
			}

			if (found) {
				break;
			}
		}
		if (operandEntered && !found) {
			*funcRet = 1; /* DIY message fix */
			(void) fprintf(stdout, "%s: %s\n",
			    operand[i], gettext("not found"));
		}
	}

	(void) IMA_FreeMemory(targetList);
	return (ret);
}

/*
 * Modify discovery attributes
 */
static int
modifyDiscovery(cmdOptions_t *options, int *funcRet)
{
	IMA_OID oid;
	IMA_STATUS status;
	IMA_BOOL setDiscovery;
	IMA_HOST_ID hostId;

	int ret;
	cmdOptions_t *optionList = options;

	assert(funcRet != NULL);

	/* Find Sun initiator */
	ret = sunInitiatorFind(&oid);
	if (ret > 0) {
		(void) fprintf(stderr, "%s: %s\n",
		    cmdName, gettext("no initiator found"));
	}

	if (ret != 0) {
		return (ret);
	}

	for (; optionList->optval; optionList++) {
		/* check optarg and set bool accordingly */
		if (strcasecmp(optionList->optarg, ISCSIADM_ARG_ENABLE) == 0) {
			setDiscovery = IMA_TRUE;
		} else if (strcasecmp(optionList->optarg, ISCSIADM_ARG_DISABLE)
		    == 0) {
			setDiscovery = IMA_FALSE;
		} else {
			(void) fprintf(stderr, "%s: %s\n",
			    cmdName, gettext("invalid option argument"));
			return (1);
		}

		switch (optionList->optval) {
			case 's':
				/* Set static discovery */
				status = IMA_SetStaticDiscovery(oid,
				    setDiscovery);
				if (!IMA_SUCCESS(status)) {
					printLibError(status);
					*funcRet = 1;
					return (ret);
				}
				break;
			case 't':
				/* Set send targets discovery */
				status = IMA_SetSendTargetsDiscovery(oid,
				    setDiscovery);
				if (!IMA_SUCCESS(status)) {
					printLibError(status);
					*funcRet = 1;
					return (ret);
				}
				break;
			case 'i':
				/* Set iSNS discovery */
				(void) memset(&hostId, 0, sizeof (hostId));
				status = IMA_SetIsnsDiscovery(oid, setDiscovery,
				    IMA_ISNS_DISCOVERY_METHOD_STATIC, &hostId);
				if (!IMA_SUCCESS(status)) {
					printLibError(status);
					*funcRet = 1;
					return (ret);
				}
				break;
			default:
				(void) fprintf(stderr, "%s: %c: %s\n",
				    cmdName, optionList->optval,
				    gettext("unknown option"));
				return (1);
		}
	}

	return (ret);
}

/*
 * Set the initiator node's authentication method
 */
static int
modifyNodeAuthParam(IMA_OID oid, int param, char *chapName, int *funcRet)
{
	IMA_INITIATOR_AUTHPARMS authParams;
	IMA_STATUS status;
	int ret;
	int secretLen = MAX_CHAP_SECRET_LEN;

	IMA_BYTE chapSecret[MAX_CHAP_SECRET_LEN + 1];

	assert(funcRet != NULL);

	/*
	 * Start with existing parameters and modify with the desired change
	 * before passing along.  We ignore any failures as they probably
	 * are caused by non-existence of auth params for the given node.
	 */
	status = IMA_GetInitiatorAuthParms(oid, IMA_AUTHMETHOD_CHAP,
	    &authParams);

	switch (param) {
	case AUTH_NAME:
		if (chapName == NULL) {
			(void) fprintf(stderr, "CHAP name cannot be NULL.\n");
			return (1);
		}
		if (strlen(chapName) == 0) {
			(void) fprintf(stderr, "CHAP name cannot be empty.\n");
			return (1);
		}
		(void) memset(&authParams.chapParms.name, 0,
		    sizeof (authParams.chapParms.name));
		(void) memcpy(&authParams.chapParms.name,
		    &chapName[0], strlen(chapName));
		authParams.chapParms.nameLength = strlen(chapName);
		break;

	case AUTH_PASSWORD :
		ret = getSecret((char *)&chapSecret[0], &secretLen,
		    MIN_CHAP_SECRET_LEN, MAX_CHAP_SECRET_LEN);

		if (ret != 0) {
			return (ret);
		}

		(void) memset(&authParams.chapParms.challengeSecret, 0,
		    sizeof (authParams.chapParms.challengeSecret));
		(void) memcpy(&authParams.chapParms.challengeSecret,
		    &chapSecret[0], secretLen);
		authParams.chapParms.challengeSecretLength = secretLen;
		break;

	default:
		(void) fprintf(stderr, "Invalid auth parameter %d\n", param);
		return (1);
	}

	status = IMA_SetInitiatorAuthParms(oid, IMA_AUTHMETHOD_CHAP,
	    &authParams);
	if (!IMA_SUCCESS(status)) {
		printLibError(status);
		*funcRet = 1;
	}
	return (ret);
}

/*
 * Set the target's authentication method
 */
static int
modifyTargetAuthParam(IMA_OID oid, int param, char *chapName, int *funcRet)
{
	IMA_INITIATOR_AUTHPARMS authParams;
	IMA_STATUS status;
	int ret;
	int secretLen = MAX_CHAP_SECRET_LEN;

	IMA_BYTE chapSecret[MAX_CHAP_SECRET_LEN + 1];

	assert(funcRet != NULL);

	/*
	 * Start with existing parameters and modify with the desired change
	 * before passing along.  We ignore any get failures as they probably
	 * are caused by non-existence of auth params for the given target.
	 */
	status = SUN_IMA_GetTargetAuthParms(oid, IMA_AUTHMETHOD_CHAP,
	    &authParams);

	switch (param) {
	case AUTH_NAME:
		if (chapName == NULL) {
			(void) fprintf(stderr, "CHAP name cannot be NULL.\n");
			return (1);
		}
		if (strlen(chapName) == 0) {
			(void) fprintf(stderr, "CHAP name cannot be empty.\n");
			return (1);
		}
		(void) memset(&authParams.chapParms.name, 0,
		    sizeof (authParams.chapParms.name));
		(void) memcpy(&authParams.chapParms.name,
		    &chapName[0], strlen(chapName));
		authParams.chapParms.nameLength = strlen(chapName);
		break;

	case AUTH_PASSWORD :
		ret = getSecret((char *)&chapSecret[0], &secretLen,
		    1, MAX_CHAP_SECRET_LEN);

		if (ret != 0) {
			return (ret);
		}

		(void) memset(&authParams.chapParms.challengeSecret, 0,
		    sizeof (authParams.chapParms.challengeSecret));
		(void) memcpy(&authParams.chapParms.challengeSecret,
		    &chapSecret[0], secretLen);
		authParams.chapParms.challengeSecretLength = secretLen;
		break;

	default:
		(void) fprintf(stderr, "Invalid auth parameter %d\n", param);
		return (1);
	}

	status = SUN_IMA_SetTargetAuthParams(oid, IMA_AUTHMETHOD_CHAP,
	    &authParams);
	if (!IMA_SUCCESS(status)) {
		printLibError(status);
		*funcRet = 1;
	}
	return (0);
}

static int
modifyTargetBidirAuthFlag(IMA_OID targetOid, char *optarg, int *funcRet)
{
	IMA_BOOL boolValue;
	IMA_STATUS status;

	assert(funcRet != NULL);

	if (strcasecmp(optarg, ISCSIADM_ARG_ENABLE) == 0) {
		boolValue = IMA_TRUE;
	} else if (strcasecmp(optarg, ISCSIADM_ARG_DISABLE) == 0) {
		boolValue = IMA_FALSE;
	} else {
		(void) fprintf(stderr, "%s: %s %s\n",
		    cmdName, gettext("invalid option argument"), optarg);
		return (1);
	}

	status = SUN_IMA_SetTargetBidirAuthFlag(targetOid, &boolValue);
	if (!IMA_SUCCESS(status)) {
		printLibError(status);
		*funcRet = 1;
	}
	return (0);
}

static int
modifyConfiguredSessions(IMA_OID targetOid, char *optarg)
{
	SUN_IMA_CONFIG_SESSIONS *pConfigSessions;
	IMA_STATUS		status;
	int			sessions;
	int			size;
	char			tmp[1024];
	boolean_t		isIpv6 = B_FALSE;
	uint16_t		port;
	char			address[MAX_ADDRESS_LEN];
	char			*commaPos;
	char			*addressPos;
	int			rtn;

	/*
	 * Strip the first int value from the string.  If we sprintf
	 * this back to a string and it matches the original string
	 * then this command is using default binding.  If not a
	 * match we have hard coded binding or a usage error.
	 */
	sessions = atoi(optarg);
	(void) sprintf(tmp, "%d", sessions);
	if (strcmp(optarg, tmp) == 0) {
		/* default binding */

		/* allocate the required pConfigSessions */
		size = sizeof (SUN_IMA_CONFIG_SESSIONS);
		pConfigSessions = (SUN_IMA_CONFIG_SESSIONS *)calloc(1, size);
		if (pConfigSessions == NULL) {
			return (1);
		}

		/* setup pConfigSessions */
		pConfigSessions->bound	= IMA_FALSE;
		pConfigSessions->in	= sessions;
		pConfigSessions->out	= 0;
	} else {
		/* hardcoded binding */

		/*
		 * First we need to determine how many bindings
		 * are available.  This can be done by scanning
		 * for the number of ',' + 1.
		 */
		sessions = 1;
		commaPos = strchr(optarg, ',');
		while (commaPos != NULL) {
			sessions++;
			commaPos = strchr(++commaPos, ',');
		}

		/* allocate the required pConfigSessions */
		size = sizeof (SUN_IMA_CONFIG_SESSIONS) + ((sessions - 1) *
		    sizeof (IMA_ADDRESS_KEY));
		pConfigSessions = (SUN_IMA_CONFIG_SESSIONS *)calloc(1, size);
		if (pConfigSessions == NULL) {
			return (1);
		}

		/* setup pConfigSessions */
		pConfigSessions->bound	= IMA_TRUE;
		pConfigSessions->in	= sessions;
		pConfigSessions->out	= 0;

		/* Now fill in the binding information.  */
		sessions = 0;
		addressPos = optarg;
		/*
		 * Walk thru possible address strings
		 * stop once all strings are processed.
		 */
		while (addressPos != NULL) {
			/*
			 * Check if there is another address after this
			 * one. If so terminate the current address and
			 * keep a pointer to the next one.
			 */
			commaPos = strchr(addressPos, ',');
			if (commaPos != NULL) {
				*commaPos++ = 0x00;
			}

			/*
			 * Parse current address.  If invalid abort
			 * processing of addresses and free memory.
			 */
			if (parseAddress(addressPos, 0, address,
			    MAX_ADDRESS_LEN, &port, &isIpv6) != PARSE_ADDR_OK) {
				free(pConfigSessions);
				printLibError(IMA_ERROR_INVALID_PARAMETER);
				return (1);
			}

			/* Convert address into binary form */
			if (isIpv6 == B_FALSE) {
				pConfigSessions->bindings[sessions].
				    ipAddress.ipv4Address = IMA_TRUE;
				rtn = inet_pton(AF_INET, address,
				    pConfigSessions->bindings[sessions].
				    ipAddress.ipAddress);
			} else {
				pConfigSessions->bindings[sessions].ipAddress.
				    ipv4Address =
				    IMA_FALSE;
				rtn = inet_pton(AF_INET6, address,
				    pConfigSessions->bindings[sessions].
				    ipAddress.ipAddress);
			}
			if (rtn == 0) {
				/* inet_pton found address invalid */
				free(pConfigSessions);
				printLibError(IMA_ERROR_INVALID_PARAMETER);
				return (1);
			}

			/* update addressPos to next address */
			sessions++;
			addressPos = commaPos;
		}
	}

	/* issue SUN_IMA request */
	status = SUN_IMA_SetConfigSessions(targetOid,
	    pConfigSessions);
	if (!IMA_SUCCESS(status)) {
		printLibError(status);
		free(pConfigSessions);
		return (1);
	}

	free(pConfigSessions);
	return (0);
}

static int
getAuthMethodValue(char *method, IMA_AUTHMETHOD *value)
{
	if (strcasecmp(method, "chap") == 0) {
		*value = IMA_AUTHMETHOD_CHAP;
		return (0);
	}

	if (strcasecmp(method, "none") == 0) {
		*value =  IMA_AUTHMETHOD_NONE;
		return (0);
	}

	return (1);
}


/*
 * Set the authentication method
 * Currently only supports CHAP and NONE
 */
static int
modifyNodeAuthMethod(IMA_OID oid, char *optarg, int *funcRet)
{
	IMA_AUTHMETHOD methodList[MAX_AUTH_METHODS];
	IMA_UINT methodCount = 0;
	IMA_STATUS status;
	IMA_AUTHMETHOD value;
	char *method;
	char *commaPos;

	assert(funcRet != NULL);

	/*
	 * optarg will be a , delimited set of auth methods, in order
	 * of preference
	 * if any values here are incorrect, return without setting
	 * anything.
	 */
	method = optarg;

	commaPos = strchr(optarg, ',');

	while (commaPos && methodCount < MAX_AUTH_METHODS) {
		*commaPos = NULL;
		if (getAuthMethodValue(method, &value) != 0) {
			(void) fprintf(stderr, "%s: a: %s\n",
			    cmdName, gettext("invalid option argument"));
			return (1);
		}
		methodList[methodCount++] = value;
		commaPos++;
		method = commaPos;
		commaPos = strchr(method, ',');
	}
	/* Should not find more method specified - if found, error */
	if (commaPos) {
		(void) fprintf(stderr, "%s: -a: %s\n",
		    cmdName, gettext("invalid option argument"));
		return (1);
	}
	if (getAuthMethodValue(method, &value) != 0) {
		(void) fprintf(stderr, "%s: -a: %s\n",
		    cmdName, gettext("invalid option argument"));
		return (1);
	}
	methodList[methodCount++] = value;

	status = IMA_SetInitiatorAuthMethods(oid, methodCount, &methodList[0]);
	if (!IMA_SUCCESS(status)) {
		printLibError(status);
		*funcRet = 1;
	}
	return (0);
}

static int
modifyTargetAuthMethod(IMA_OID oid, char *optarg, int *funcRet)
{
	IMA_AUTHMETHOD methodList[MAX_AUTH_METHODS];
	IMA_UINT methodCount = 0;
	IMA_STATUS status;
	IMA_AUTHMETHOD value;
	char *method;
	char *commaPos;

	assert(funcRet != NULL);

	/*
	 * optarg will be a , delimited set of auth methods, in order
	 * of preference
	 * if any values here are incorrect, return without setting
	 * anything.
	 */
	method = optarg;

	commaPos = strchr(optarg, ',');

	while (commaPos && methodCount < MAX_AUTH_METHODS) {
		*commaPos = NULL;
		if (getAuthMethodValue(method, &value) != 0) {
			(void) fprintf(stderr, "%s: a: %s\n",
			    cmdName, gettext("invalid option argument"));
			return (1);
		}
		methodList[methodCount++] = value;
		commaPos++;
		method = commaPos;
		commaPos = strchr(method, ',');
	}
	/* Should not find more method specified - if found, error */
	if (commaPos) {
		(void) fprintf(stderr, "%s: -a: %s\n",
		    cmdName, gettext("invalid option argument"));
		return (1);
	}
	if (getAuthMethodValue(method, &value) != 0) {
		(void) fprintf(stderr, "%s: -a: %s\n",
		    cmdName, gettext("invalid option argument"));
		return (1);
	}
	methodList[methodCount++] = value;

	status = SUN_IMA_SetTargetAuthMethods(oid, &methodCount,
	    &methodList[0]);
	if (!IMA_SUCCESS(status)) {
		printLibError(status);
		*funcRet = 1;
	}
	return (0);
}

/*
 * Modify the RADIUS configuration of the initiator node.
 *
 * Return 0 on success.
 */
static int
modifyNodeRadiusConfig(IMA_OID oid, char *optarg, int *funcRet)
{
	SUN_IMA_RADIUS_CONFIG config;
	IMA_STATUS status;
	boolean_t isIpv6 = B_FALSE;
	uint16_t port;

	assert(funcRet != NULL);

	(void) memset(&config, 0, sizeof (SUN_IMA_RADIUS_CONFIG));
	if (parseAddress(optarg, DEFAULT_RADIUS_PORT,
	    &config.hostnameIpAddress[0], SUN_IMA_IP_ADDRESS_PORT_LEN,
	    &port, &isIpv6) !=
	    PARSE_ADDR_OK) {
		return (1);
	}
	config.port = (IMA_UINT16)port;
	config.isIpv6 = (isIpv6 == B_TRUE) ? IMA_TRUE : IMA_FALSE;
	/* Not setting shared secret here. */
	config.sharedSecretValid = IMA_FALSE;

	status = SUN_IMA_SetInitiatorRadiusConfig(oid, &config);
	if (!IMA_SUCCESS(status)) {
		printLibError(status);
		*funcRet = 1;
	}

	return (0);
}

/*
 * Modify the RADIUS access flag of the initiator node.
 *
 * Return 0 on success.
 */
static int
modifyNodeRadiusAccess(IMA_OID oid, char *optarg, int *funcRet)
{
	IMA_BOOL radiusAccess;
	IMA_OID initiatorOid;
	IMA_STATUS status;
	SUN_IMA_RADIUS_CONFIG radiusConfig;
	int ret;

	assert(funcRet != NULL);

	/* Check if Radius Config is there */
	ret = sunInitiatorFind(&initiatorOid);
	if (ret != 0) {
		(void) fprintf(stderr, "%s: %s\n",
		    cmdName, gettext("no initiator found"));
		return (1);
	}
	(void) memset(&radiusConfig, 0, sizeof (SUN_IMA_RADIUS_CONFIG));
	status = SUN_IMA_GetInitiatorRadiusConfig(initiatorOid, &radiusConfig);
	if (!IMA_SUCCESS(status)) {
		(void) fprintf(stderr, "%s: %s\n",
		    cmdName, gettext("RADIUS server not configured yet"));
		*funcRet = 1;
		return (ret);
	}

	/* Check if Radius Shared is set */
	if (radiusConfig.sharedSecretValid == IMA_FALSE) {
		(void) fprintf(stderr, "%s: %s\n", cmdName,
		    gettext("RADIUS server secret not configured yet"));
		return (1);
	}

	if (strcasecmp(optarg, ISCSIADM_ARG_ENABLE) == 0) {
		radiusAccess = IMA_TRUE;
	} else if (strcasecmp(optarg, ISCSIADM_ARG_DISABLE) == 0) {
		radiusAccess = IMA_FALSE;
	} else {
		(void) fprintf(stderr, "%s: %s %s\n",
		    cmdName,
		    gettext("invalid option argument"),
		    optarg);
		return (1);
	}
	status = SUN_IMA_SetInitiatorRadiusAccess(oid, radiusAccess);
	if (!IMA_SUCCESS(status)) {
		printLibError(status);
		*funcRet = 1;
	}

	return (ret);
}

/*
 * Modify the RADIUS shared secret.
 *
 * Returns:
 *  zero on success.
 *  > 0 on failure.
 */
static int
modifyNodeRadiusSharedSecret(IMA_OID oid, int *funcRet)
{
	IMA_BYTE radiusSharedSecret[SUN_IMA_MAX_RADIUS_SECRET_LEN + 1];
	IMA_OID initiatorOid;
	IMA_STATUS status;
	SUN_IMA_RADIUS_CONFIG radiusConfig;
	int ret;
	int secretLen = SUN_IMA_MAX_RADIUS_SECRET_LEN;

	assert(funcRet != NULL);

	ret = getSecret((char *)&radiusSharedSecret[0], &secretLen,
	    0, SUN_IMA_MAX_RADIUS_SECRET_LEN);
	if (ret != 0) {
		return (1);
	}

	ret = sunInitiatorFind(&initiatorOid);
	if (ret > 0) {
		(void) fprintf(stderr, "%s: %s\n",
		    cmdName, gettext("no initiator found"));
	}
	if (ret != 0) {
		return (1);
	}
	/* First obtain existing RADIUS configuration (if any) */
	(void) memset(&radiusConfig, 0, sizeof (SUN_IMA_RADIUS_CONFIG));
	status = SUN_IMA_GetInitiatorRadiusConfig(initiatorOid, &radiusConfig);
	if (!IMA_SUCCESS(status)) {
		(void) fprintf(stderr, "%s: %s\n",
		    cmdName, gettext("RADIUS server not configured yet"));
		return (1);
	}

	/* Modify the shared secret only */
	radiusConfig.sharedSecretLength = secretLen;
	(void) memcpy(&radiusConfig.sharedSecret,
	    &radiusSharedSecret[0], secretLen);
	radiusConfig.sharedSecretValid = IMA_TRUE;
	status = SUN_IMA_SetInitiatorRadiusConfig(oid, &radiusConfig);
	if (!IMA_SUCCESS(status)) {
		printLibError(status);
		*funcRet = 1;
	}

	return (0);
}

/*
 * Set initiator node attributes.
 */
static int
modifyNode(cmdOptions_t *options, int *funcRet)
{
	IMA_NODE_NAME	nodeName;
	IMA_NODE_ALIAS	nodeAlias;
	IMA_OID		oid;
	IMA_STATUS	status;
	cmdOptions_t	*optionList = options;
	int		ret;
	iSCSINameCheckStatusType nameCheckStatus;
	IMA_OID sharedNodeOid;
	int 		i;
	int 		lowerCase;
	IMA_BOOL	iscsiBoot = IMA_FALSE;
	IMA_BOOL	mpxioEnabled = IMA_FALSE;
	char		*mb_name = NULL;
	int		prefixlen = 0;

	assert(funcRet != NULL);

	/* Get boot session's info */
	(void) SUN_IMA_GetBootIscsi(&iscsiBoot);
	if (iscsiBoot == IMA_TRUE) {
		status = SUN_IMA_GetBootMpxio(&mpxioEnabled);
		if (!IMA_SUCCESS(status)) {
			(void) fprintf(stderr, "%s: %s\n",
			    cmdName, gettext("unable to get MPxIO info"
			    " of root disk"));
			*funcRet = 1;
			return (1);
		}
	}

	/* Find Sun initiator */
	ret = sunInitiatorFind(&oid);
	if (ret != 0) {
		(void) fprintf(stderr, "%s: %s\n",
		    cmdName, gettext("no initiator found"));
		return (ret);
	}

	for (; optionList->optval; optionList++) {
		switch (optionList->optval) {
			case 'N':
				if (strlen(optionList->optarg) >=
				    MAX_ISCSI_NAME_LEN) {
					(void) fprintf(stderr, "%s: %s %d\n",
					    cmdName,
					    gettext("name too long, \
					    maximum length is:"),
					    MAX_ISCSI_NAME_LEN);
				}

				/* Take the first operand as node name. */
				(void) memset(&nodeName, 0,
				    sizeof (IMA_NODE_NAME));
				if (mbstowcs(nodeName, optionList->optarg,
				    IMA_NODE_NAME_LEN) == (size_t)-1) {
					(void) fprintf(stderr, "%s: %s\n",
					    cmdName,
					    gettext("conversion error"));
					return (1);
				}

				prefixlen = strlen(ISCSI_IQN_NAME_PREFIX);
				mb_name = (char *)calloc(1, prefixlen + 1);
				if (mb_name == NULL) {
					return (1);
				}

				if (wcstombs(mb_name, nodeName,
				    prefixlen) == (size_t)-1) {
					(void) fprintf(stderr, "%s: %s\n",
					    cmdName,
					    gettext("conversion error"));
					(void) IMA_FreeMemory(mb_name);
					return (1);
				}
				if (strncmp(mb_name, ISCSI_IQN_NAME_PREFIX,
				    prefixlen) == 0) {
					/*
					 * For iqn format, we should map
					 * the upper-case characters to
					 * their lower-case equivalents.
					 */
					for (i = 0; nodeName[i] != 0; i++) {
						lowerCase =
						    tolower(nodeName[i]);
						nodeName[i] = lowerCase;
					}
				}
				(void) IMA_FreeMemory(mb_name);

				/* Perform string profile checks */
				nameCheckStatus =
				    iSCSINameStringProfileCheck(nodeName);
				iSCSINameCheckStatusDisplay(nameCheckStatus);
				if (nameCheckStatus != iSCSINameCheckOK) {
					*funcRet = 1; /* DIY message fix */
					return (1);
				}

				/*
				 * IMA_GetSharedNodeOid(&sharedNodeOid);
				 * if (!IMA_SUCCESS(status)) {
				 *   printLibError(status);
				 *   return (INF_ERROR);
				 * }
				 */
				if (iscsiBoot == IMA_TRUE) {
					(void) fprintf(stderr, "%s: %s\n",
					    cmdName, gettext("iscsi boot, not"
					    " allowed to change"
					    " initiator's name"));
					return (1);
				}
				oid.objectType = IMA_OBJECT_TYPE_NODE;
				status = IMA_SetNodeName(oid, nodeName);
				if (!IMA_SUCCESS(status)) {
					printLibError(status);
					*funcRet = 1;
					return (ret);
				}
				break;

			case 'A':
				if (iscsiBoot == IMA_TRUE) {
					(void) fprintf(stderr, "%s: %s\n",
					    cmdName, gettext("iscsi boot, not"
					    " allowed to change"
					    " initiator's alias"));
					return (1);
				}
				/* Take the first operand as node alias. */
				if (strlen(optionList->optarg) >=
				    MAX_ISCSI_NAME_LEN) {
					(void) fprintf(stderr, "%s: %s %d\n",
					    cmdName,
					    gettext("alias too long, maximum  \
					    length is:"),
					    MAX_ISCSI_NAME_LEN);
				}

				(void) memset(&nodeAlias, 0,
				    sizeof (IMA_NODE_ALIAS));
				if (mbstowcs(nodeAlias, optionList->optarg,
				    IMA_NODE_ALIAS_LEN) == (size_t)-1) {
					(void) fprintf(stderr, "%s: %s\n",
					    cmdName,
					    gettext("conversion error"));
					return (1);
				}

				status = IMA_GetSharedNodeOid(&sharedNodeOid);
				if (!IMA_SUCCESS(status)) {
					printLibError(status);
					*funcRet = 1;
					return (ret);
				}

				status = IMA_SetNodeAlias(sharedNodeOid,
				    nodeAlias);
				if (!IMA_SUCCESS(status)) {
					printLibError(status);
					*funcRet = 1;
					return (ret);
				}
				break;

			case 'a':
				if (iscsiBoot == IMA_TRUE) {
					(void) fprintf(stderr, "%s: %s\n",
					    cmdName, gettext("iscsi boot, not"
					    " allowed to change authentication"
					    " method"));
					return (1);
				}
				if (modifyNodeAuthMethod(oid, options->optarg,
				    funcRet) != 0) {
					return (1);
				}
				break;

			case 'R':
				if (modifyNodeRadiusAccess(oid, options->optarg,
				    funcRet) != 0) {
					return (1);
				}
				break;

			case 'r':
				if (modifyNodeRadiusConfig(oid, options->optarg,
				    funcRet) != 0) {
					return (1);
				}
				break;

			case 'P':
				if (modifyNodeRadiusSharedSecret(oid, funcRet)
				    != 0) {
					return (1);
				}
				break;

			case 'C':
				if (iscsiBoot == IMA_TRUE) {
					(void) fprintf(stderr, "%s: %s\n",
					    cmdName, gettext("iscsi boot, not"
					    " allowed to change CHAP secret"));
					return (1);
				}
				if (modifyNodeAuthParam(oid, AUTH_PASSWORD,
				    NULL, funcRet) != 0) {
					return (1);
				}
				break;

			case 'c':
				if (iscsiBoot == IMA_TRUE) {
					if (mpxioEnabled == IMA_FALSE) {
						(void) fprintf(stderr,
						    "%s: %s\n", cmdName,
						    gettext("iscsi"
						    " boot and MPxIO"
						    " is disabled, not allowed"
						    " to change number of"
						    " sessions to be"
						    " configured"));
						return (1);
					}
				}
				if (modifyConfiguredSessions(oid,
				    optionList->optarg) != 0) {
					if (iscsiBoot == IMA_TRUE) {
						(void) fprintf(stderr,
						    "%s: %s\n", cmdName,
						    gettext("iscsi boot,"
						    " fail to set configured"
						    " session"));
					}
					return (1);
				}
				break;


			case 'H':
				if (iscsiBoot == IMA_TRUE) {
					(void) fprintf(stderr, "%s: %s\n",
					    cmdName, gettext("iscsi boot, not"
					    " allowed to change CHAP name"));
					return (1);
				}
				if (modifyNodeAuthParam(oid, AUTH_NAME,
				    optionList->optarg, funcRet) != 0) {
					return (1);
				}
				break;


			case 'd':
				if (iscsiBoot == IMA_TRUE) {
					if (mpxioEnabled == IMA_FALSE) {
						(void) fprintf(stderr,
						    "%s: %s\n", cmdName,
						    gettext("iscsi"
						    " boot and MPxIO"
						    " is disabled, not"
						    " allowed to"
						    " change initiator's"
						    " login params"));
						return (1);
					}
				}
				if (setLoginParameter(oid, DATA_DIGEST,
				    optionList->optarg) != 0) {
					return (1);
				}
				break;

			case 'h':
				if (iscsiBoot == IMA_TRUE) {
					if (mpxioEnabled == IMA_FALSE) {
						(void) fprintf(stderr,
						    "%s: %s\n", cmdName,
						    gettext("iscsi"
						    " boot and MPxIO"
						    " is disabled, not"
						    " allowed to"
						    " change initiator's"
						    " login params"));
						return (1);
					}
				}
				if (setLoginParameter(oid, HEADER_DIGEST,
				    optionList->optarg) != 0) {
					return (1);
				}
				break;

			case 'T':
				if (setTunableParameters(oid,
				    optionList->optarg) != 0) {
					return (1);
				}
				break;
			default:
				(void) fprintf(stderr, "%s: %c: %s\n",
				    cmdName, optionList->optval,
				    gettext("unknown option"));
				break;
		}
	}

	return (ret);
}

/*
 * Modify target parameters
 */
static int
modifyTargetParam(cmdOptions_t *options, char *targetName, int *funcRet)
{
	IMA_OID oid;
	IMA_OID targetOid;
	IMA_STATUS status;
	IMA_OID_LIST *targetList;
	SUN_IMA_TARGET_PROPERTIES targetProps;
	wchar_t wcInputObject[MAX_ISCSI_NAME_LEN + 1];
	wchar_t targetAddress[SUN_IMA_IP_ADDRESS_PORT_LEN];
	int ret;
	boolean_t found;
	boolean_t targetAddressSpecified = B_TRUE;
	boolean_t tpgtSpecified = B_FALSE;
	boolean_t isIpv6 = B_FALSE;
	int i;
	iSCSINameCheckStatusType nameCheckStatus;
	IMA_UINT16 port = 0;
	IMA_UINT16 tpgt = 0;

	IMA_NODE_NAME bootTargetName;
	IMA_INITIATOR_AUTHPARMS bootTargetCHAP;
	IMA_BOOL  iscsiBoot;
	IMA_BOOL  mpxioEnabled;

	cmdOptions_t *optionList = options;

	assert(funcRet != NULL);

	/* Find Sun initiator */
	ret = sunInitiatorFind(&oid);
	if (ret > 0) {
		(void) fprintf(stderr, "%s: %s\n",
		    cmdName, gettext("no initiator found"));
	}

	if (ret != 0) {
		return (ret);
	}

	if (parseTarget(targetName,
	    &wcInputObject[0],
	    MAX_ISCSI_NAME_LEN + 1,
	    &targetAddressSpecified,
	    &targetAddress[0],
	    SUN_IMA_IP_ADDRESS_PORT_LEN,
	    &port,
	    &tpgtSpecified,
	    &tpgt,
	    &isIpv6) != PARSE_TARGET_OK) {
		return (1);
	}

	/* Perform string profile checks */
	nameCheckStatus = iSCSINameStringProfileCheck(wcInputObject);
	iSCSINameCheckStatusDisplay(nameCheckStatus);
	if (nameCheckStatus != iSCSINameCheckOK) {
		return (1);
	}

	status = IMA_GetTargetOidList(oid, &targetList);
	if (!IMA_SUCCESS(status)) {
		printLibError(status);
		*funcRet = 1;
		return (0);
	}

	(void) SUN_IMA_GetBootIscsi(&iscsiBoot);
	if (iscsiBoot == IMA_TRUE) {
		status = SUN_IMA_GetBootMpxio(&mpxioEnabled);
		if (!IMA_SUCCESS(status)) {
			(void) fprintf(stderr, "%s: %s\n",
			    cmdName, gettext("unable to get MPxIO info"
			    " of root disk"));
			*funcRet = 1;
			return (ret);
		}
		status = SUN_IMA_GetBootTargetName(bootTargetName);
		if (!IMA_SUCCESS(status)) {
			(void) fprintf(stderr, "%s: %s\n",
			    cmdName, gettext("unable to get boot target's"
			    " name"));
			*funcRet = 1;
			return (ret);
		}
		status = SUN_IMA_GetBootTargetAuthParams(&bootTargetCHAP);
		if (!IMA_SUCCESS(status)) {
			(void) fprintf(stderr, "%s: %s\n",
			    cmdName, gettext("unable to get boot target's"
			    " auth param"));
			*funcRet = 1;
			return (ret);
		}
	}

	/* find target oid */
	for (found = B_FALSE, i = 0; i < targetList->oidCount; i++) {
		status = SUN_IMA_GetTargetProperties(targetList->oids[i],
		    &targetProps);
		if (!IMA_SUCCESS(status)) {
			printLibError(status);
			(void) IMA_FreeMemory(targetList);
			*funcRet = 1;
			return (ret);
		}

		/*
		 * Compare the target name with the input name
		 */
		if ((targetNamesEqual(wcInputObject, targetProps.imaProps.name)
		    == B_TRUE)) {
			/*
			 * For now, regardless of whether a target address
			 * is specified, we return B_TRUE because
			 * IMA_TARGET_PROPERTIES does not have a field for
			 * specifying address.
			 */
			found = B_TRUE;
			targetOid = targetList->oids[i];

			if ((targetNamesEqual(bootTargetName, wcInputObject)
			    == B_TRUE) && (iscsiBoot == IMA_TRUE)) {
				/*
				 * iscsi booting, need changed target param is
				 * booting target, for auth param, not allow
				 * to change, for others dependent on mpxio
				 */

				if ((optionList->optval == 'C') ||
				    (optionList->optval == 'H') ||
				    (optionList->optval == 'B') ||
				    (optionList->optval == 'a')) {
					/*
					 * -C CHAP secret set
					 * -H CHAP name set
					 * -a authentication
					 * -B bi-directional-authentication
					 */
					(void) fprintf(stderr, "%s: %s\n",
					    cmdName, gettext("iscsi boot,"
					    " not allowed to modify"
					    " authentication parameters"
					    "  of boot target"));
					return (1);
				}
				if (mpxioEnabled == IMA_FALSE) {
					(void) fprintf(stderr, "%s: %s\n",
					    cmdName, gettext("iscsi boot and"
					    " MPxIO is disabled, not allowed"
					    " to modify boot target's"
					    " parameters"));
					return (1);
				}

			}

			if (modifyIndividualTargetParam(optionList, targetOid,
			    funcRet) != 0) {
				return (ret);
			}

			/*
			 * Even after finding a matched target, keep going
			 * since there could be multiple target objects
			 * associated with one target name in the system
			 * because of different TPGTs.
			 */
		}
	}

	/* If the target OID cannot be found create one */
	if (!found) {
		status = SUN_IMA_CreateTargetOid(wcInputObject, &targetOid);
		if (!IMA_SUCCESS(status)) {
			printLibError(status);
			(void) IMA_FreeMemory(targetList);
			*funcRet = 1;
			return (ret);
		}
		if (modifyIndividualTargetParam(optionList, targetOid,
		    funcRet) != 0) {
				return (ret);
		}
	}

	(void) IMA_FreeMemory(targetList);
	return (ret);
}

/*
 * Add one or more addresses
 */
static int
addAddress(int addrType, int operandLen, char *operand[], int *funcRet)
{
	IMA_STATUS status;
	IMA_OID oid, addressOid;
	SUN_IMA_TARGET_ADDRESS address;
	wchar_t wcInputObject[MAX_ADDRESS_LEN + 1];
	int ret;
	int i;

	assert(funcRet != NULL);

	/* Find Sun initiator */
	ret = sunInitiatorFind(&oid);
	if (ret > 0) {
		(void) fprintf(stderr, "%s: %s\n",
		    cmdName, gettext("no initiator found"));
	}

	if (ret != 0) {
		return (ret);
	}

	/*
	 * Format of discovery address operand:
	 *
	 * <IP address|hostname>:<port>
	 */
	for (i = 0; i < operandLen; i++) {
		/* initialize */
		(void) memset(&wcInputObject[0], 0, sizeof (wcInputObject));
		(void) memset(&address, 0, sizeof (address));

		if (mbstowcs(wcInputObject, operand[i],
		    (MAX_ADDRESS_LEN + 1)) == (size_t)-1) {
			(void) fprintf(stderr, "%s: %s\n",
			    cmdName, gettext("conversion error"));
			ret = 1;
			continue;
		}
		if (getTargetAddress(addrType, operand[i], &address.imaStruct)
		    != 0) {
			ret = 1;
			continue;
		}
		if (addrType == DISCOVERY_ADDRESS) {
			status = IMA_AddDiscoveryAddress(oid,
			    address.imaStruct, &addressOid);
			if (!IMA_SUCCESS(status)) {
				printLibError(status);
				*funcRet = 1;
				return (ret);
			}
		} else if (addrType == ISNS_SERVER_ADDRESS) {
			status = SUN_IMA_AddISNSServerAddress(address);
			if (!IMA_SUCCESS(status)) {
				printLibError(status);
				*funcRet = 1;
				return (ret);
			}
		}
	}
	return (ret);
}

/*
 * Add one or more static configuration targets
 */
static int
addStaticConfig(int operandLen, char *operand[], int *funcRet)
{
	int i;
	boolean_t targetAddressSpecified = B_FALSE;
	boolean_t tpgtSpecified = B_FALSE;
	boolean_t isIpv6 = B_FALSE;
	int ret;
	int addrType;
	IMA_STATUS status;
	IMA_OID oid;
	SUN_IMA_STATIC_DISCOVERY_TARGET staticConfig;
	IMA_UINT16 port = 0;
	IMA_UINT16 tpgt = 0;
	wchar_t staticTargetName[MAX_ISCSI_NAME_LEN + 1];
	wchar_t staticTargetAddress[SUN_IMA_IP_ADDRESS_PORT_LEN];
	iSCSINameCheckStatusType nameCheckStatus;
	char sAddr[SUN_IMA_IP_ADDRESS_PORT_LEN];

	assert(funcRet != NULL);

	/* Find Sun initiator */
	ret = sunInitiatorFind(&oid);
	if (ret > 0) {
		(void) fprintf(stderr, "%s: %s\n",
		    cmdName, gettext("no initiator found"));
	}

	if (ret != 0) {
		return (ret);
	}

	/*
	 * Format of static config operand:
	 *  <target-name>,<IP address|hostname>[:port][,tpgt]
	 */
	for (i = 0; i < operandLen; i++) {
		if (parseTarget(operand[i],
		    &staticTargetName[0],
		    MAX_ISCSI_NAME_LEN + 1,
		    &targetAddressSpecified,
		    &staticTargetAddress[0],
		    SUN_IMA_IP_ADDRESS_PORT_LEN,
		    &port,
		    &tpgtSpecified,
		    &tpgt,
		    &isIpv6) != PARSE_TARGET_OK) {
			ret = 1;
			continue;
		}

		if (targetAddressSpecified != B_TRUE) {
			(void) fprintf(stderr, "%s: %s\n",
			    cmdName, gettext("missing target address"));
			*funcRet = 1; /* DIY message fix */
			return (1);
		}
		/* Perform string profile checks */
		nameCheckStatus = iSCSINameStringProfileCheck(staticTargetName);
		iSCSINameCheckStatusDisplay(nameCheckStatus);
		if (nameCheckStatus != iSCSINameCheckOK) {
			*funcRet = 1; /* DIY message fix */
			return (1);
		}
		(void) wcsncpy(staticConfig.targetName, staticTargetName,
		    MAX_ISCSI_NAME_LEN + 1);

		(void) wcstombs(sAddr, staticTargetAddress, sizeof (sAddr));

		if (isIpv6 == B_TRUE) {
			staticConfig.targetAddress.imaStruct.hostnameIpAddress.
			    id.ipAddress.ipv4Address = B_FALSE;
			addrType = AF_INET6;
		} else {
			staticConfig.targetAddress.imaStruct.hostnameIpAddress.
			    id.ipAddress.ipv4Address = B_TRUE;
			addrType = AF_INET;
		}

		if (inet_pton(addrType, sAddr, staticConfig.targetAddress.
		    imaStruct.hostnameIpAddress.id.ipAddress.ipAddress) != 1) {
			(void) fprintf(stderr, "%s: %s\n",
			    cmdName, gettext("static config conversion error"));
			ret = 1;
			continue;
		}

		staticConfig.targetAddress.imaStruct.portNumber = port;
		if (tpgtSpecified == B_TRUE) {
			staticConfig.targetAddress.defaultTpgt = B_FALSE;
			staticConfig.targetAddress.tpgt = tpgt;
		} else {
			staticConfig.targetAddress.defaultTpgt = B_TRUE;
			staticConfig.targetAddress.tpgt = 0;
		}

		status = SUN_IMA_AddStaticTarget(oid, staticConfig, &oid);
		if (!IMA_SUCCESS(status)) {
			printLibError(status);
			*funcRet = 1;
			return (1);
		}
	}

	if (ret != 0) {
		*funcRet = 1;
	}

	return (ret);
}

/*
 * Remove one or more addresses
 */
static int
removeAddress(int addrType, int operandLen, char *operand[], int *funcRet)
{
	IMA_STATUS status;
	IMA_OID initiatorOid;
	SUN_IMA_TARGET_ADDRESS address;
	wchar_t wcInputObject[MAX_ADDRESS_LEN + 1];
	int ret;
	int i;

	assert(funcRet != NULL);

	/* Find Sun initiator */
	ret = sunInitiatorFind(&initiatorOid);
	if (ret > 0) {
		(void) fprintf(stderr, "%s: %s\n",
		    cmdName, gettext("no initiator found"));
	}

	if (ret != 0) {
		return (ret);
	}

	for (i = 0; i < operandLen; i++) {
		/* initialize */
		(void) memset(&wcInputObject[0], 0, sizeof (wcInputObject));
		(void) memset(&address, 0, sizeof (address));

		if (mbstowcs(wcInputObject, operand[i],
		    MAX_ADDRESS_LEN + 1) == (size_t)-1) {
			(void) fprintf(stderr, "%s: %s\n",
			    cmdName, gettext("conversion error"));
			ret = 1;
			continue;
		}

		if (getTargetAddress(addrType, operand[i], &address.imaStruct)
		    != 0) {
			ret = 1;
			continue;
		}

		if (addrType == DISCOVERY_ADDRESS) {
			status = SUN_IMA_RemoveDiscoveryAddress(address);
			if (!IMA_SUCCESS(status)) {
				if (status == IMA_ERROR_OBJECT_NOT_FOUND) {
					(void) fprintf(stderr, "%s: %s\n",
					    operand[i], gettext("not found"));
				} else {
					printLibError(status);
				}
				*funcRet = 1;
			}
		} else {
			status = SUN_IMA_RemoveISNSServerAddress(address);
			if (!IMA_SUCCESS(status)) {
				printLibError(status);
				*funcRet = 1;
			}
		}
	}
	return (ret);
}

/*
 * Remove one or more static configuration targets
 */
static int
removeStaticConfig(int operandLen, char *operand[], int *funcRet)
{
	IMA_STATUS status;
	IMA_OID initiatorOid;
	IMA_OID_LIST *staticTargetList;
	SUN_IMA_STATIC_TARGET_PROPERTIES staticTargetProps;
	wchar_t staticTargetName[MAX_ISCSI_NAME_LEN + 1];
	wchar_t staticTargetAddress[SUN_IMA_IP_ADDRESS_PORT_LEN];
	int ret;
	boolean_t atLeastFoundOne;
	boolean_t matched;
	boolean_t targetAddressSpecified = B_TRUE;
	boolean_t tpgtSpecified = B_FALSE;
	boolean_t isIpv6 = B_FALSE;
	int i, j;
	IMA_UINT16 port = 0;
	IMA_UINT16 tpgt = 0;
	iSCSINameCheckStatusType nameCheckStatus;
	char tmpStr[SUN_IMA_IP_ADDRESS_PORT_LEN];
	wchar_t tmpTargetAddress[SUN_IMA_IP_ADDRESS_PORT_LEN];

	assert(funcRet != NULL);

	/* Find Sun initiator */
	ret = sunInitiatorFind(&initiatorOid);
	if (ret > 0) {
		(void) fprintf(stderr, "%s: %s\n",
		    cmdName, gettext("no initiator found"));
	}

	if (ret != 0) {
		return (ret);
	}

	status = IMA_GetStaticDiscoveryTargetOidList(initiatorOid,
	    &staticTargetList);
	if (!IMA_SUCCESS(status)) {
		printLibError(status);
		*funcRet = 1;
		return (ret);
	}

	for (i = 0; i < operandLen; i++) {
		if (parseTarget(operand[i],
		    &staticTargetName[0],
		    MAX_ISCSI_NAME_LEN + 1,
		    &targetAddressSpecified,
		    &staticTargetAddress[0],
		    SUN_IMA_IP_ADDRESS_PORT_LEN,
		    &port,
		    &tpgtSpecified,
		    &tpgt,
		    &isIpv6) != PARSE_TARGET_OK) {
			ret = 1;
			continue;
		}

		/* Perform string profile checks */
		nameCheckStatus = iSCSINameStringProfileCheck(staticTargetName);
		iSCSINameCheckStatusDisplay(nameCheckStatus);
		if (nameCheckStatus != iSCSINameCheckOK) {
			return (1);
		}

		for (atLeastFoundOne = B_FALSE, j = 0;
		    j < staticTargetList->oidCount;
		    j++) {
			IMA_UINT16 stpgt;

			matched = B_FALSE;
			status = SUN_IMA_GetStaticTargetProperties(
			    staticTargetList->oids[j], &staticTargetProps);
			if (!IMA_SUCCESS(status)) {
				if (status == IMA_ERROR_OBJECT_NOT_FOUND) {
					/*
					 * When removing multiple static-config
					 * entries we need to expect get
					 * failures. These failures occur when
					 * we are trying to get entry
					 * information we have just removed.
					 * Ignore the failure and continue.
					 */
					ret = 1;
					continue;
				} else {
					printLibError(status);
					(void) IMA_FreeMemory(staticTargetList);
					*funcRet = 1;
					return (ret);
				}
			}

			stpgt =
			    staticTargetProps.staticTarget.targetAddress.tpgt;

			/*
			 * Compare the static target name with the input if
			 * one was input
			 */
			if ((targetNamesEqual(
			    staticTargetProps.staticTarget.targetName,
			    staticTargetName) == B_TRUE)) {
				if (targetAddressSpecified == B_FALSE) {
					matched = B_TRUE;
				} else {

					if (staticTargetProps.staticTarget.
					    targetAddress.imaStruct.
					    hostnameIpAddress.
					    id.ipAddress.ipv4Address ==
					    IMA_TRUE) {
						(void) inet_ntop(AF_INET,
						    staticTargetProps.
						    staticTarget.targetAddress.
						    imaStruct.hostnameIpAddress.
						    id.ipAddress.ipAddress,
						    tmpStr,
						    sizeof (tmpStr));
					} else {
						(void) inet_ntop(AF_INET6,
						    staticTargetProps.
						    staticTarget.targetAddress.
						    imaStruct.hostnameIpAddress.
						    id.ipAddress.ipAddress,
						    tmpStr,
						    sizeof (tmpStr));
					}

					if (mbstowcs(tmpTargetAddress, tmpStr,
					    SUN_IMA_IP_ADDRESS_PORT_LEN) ==
					    (size_t)-1) {
						(void) fprintf(stderr,
						    "%s: %s\n",
						    cmdName, gettext(
						    "conversion error"));
						ret = 1;
						continue;
					}

					if ((wcsncmp(tmpTargetAddress,
					    staticTargetAddress,
					    SUN_IMA_IP_ADDRESS_PORT_LEN) ==
					    0) && (staticTargetProps.
					    staticTarget.targetAddress.
					    imaStruct.portNumber == port)) {
						if (tpgtSpecified == B_FALSE) {
							matched = B_TRUE;
						} else {
							if (tpgt == stpgt) {
								matched =
								    B_TRUE;
							}
						}
					}
				}

				if (matched) {
					status =
					    IMA_RemoveStaticDiscoveryTarget(
					    staticTargetList->oids[j]);
					if (!IMA_SUCCESS(status)) {
						printLibError(status);
						*funcRet = 1;
						return (ret);
					}
					atLeastFoundOne = B_TRUE;
				}
			}
		}
		if (!atLeastFoundOne) {
			(void) fprintf(stderr, gettext("%ws,%ws: %s\n"),
			    staticTargetName, staticTargetAddress,
			    gettext("not found"));
		}
	}
	return (ret);
}

/*
 * Remove one or more target params.
 */
static int
removeTargetParam(int operandLen, char *operand[], int *funcRet)
{
	char *commaPos;
	IMA_STATUS status;
	IMA_OID initiatorOid;
	IMA_OID_LIST *targetList;
	SUN_IMA_TARGET_PROPERTIES targetProps;
	wchar_t wcInputObject[MAX_ISCSI_NAME_LEN + 1];
	int ret;
	boolean_t found;
	int i, j;
	IMA_NODE_NAME bootTargetName;
	IMA_BOOL	iscsiBoot = IMA_FALSE;
	IMA_BOOL	mpxioEnabled = IMA_FALSE;

	/* Get boot session's info */
	(void) SUN_IMA_GetBootIscsi(&iscsiBoot);
	if (iscsiBoot == IMA_TRUE) {
		status = SUN_IMA_GetBootMpxio(&mpxioEnabled);
		if (!IMA_SUCCESS(status)) {
			(void) fprintf(stderr, "%s: %s\n",
			    cmdName, gettext("unable to get MPxIO info of"
			    " root disk"));
			*funcRet = 1;
			return (1);
		}
		status = SUN_IMA_GetBootTargetName(bootTargetName);
		if (!IMA_SUCCESS(status)) {
			(void) fprintf(stderr, "%s: %s\n",
			    cmdName, gettext("unable to get boot"
			    " target's name"));
			*funcRet = 1;
			return (1);
		}
	}

	assert(funcRet != NULL);

	/* Find Sun initiator */
	ret = sunInitiatorFind(&initiatorOid);
	if (ret > 0) {
		(void) fprintf(stderr, "%s: %s\n",
		    cmdName, gettext("no initiator found"));
	}

	if (ret != 0) {
		return (ret);
	}

	status = IMA_GetTargetOidList(initiatorOid, &targetList);
	if (!IMA_SUCCESS(status)) {
		printLibError(status);
		*funcRet = 1;
		return (ret);
	}

	for (i = 0; i < operandLen; i++) {
		/* initialize */
		commaPos = strchr(operand[i], ',');
		if (commaPos) {
			/* Ignore IP address. */
			*commaPos = NULL;
		}
		(void) memset(&wcInputObject[0], 0, sizeof (wcInputObject));
		if (mbstowcs(wcInputObject, operand[i],
		    MAX_ISCSI_NAME_LEN + 1) == (size_t)-1) {
			(void) fprintf(stderr, "%s: %s\n", cmdName,
			    gettext("conversion error"));
			ret = 1;
			continue;
		}

		for (found = B_FALSE, j = 0; j < targetList->oidCount;
		    j++) {
			status = SUN_IMA_GetTargetProperties(
			    targetList->oids[j], &targetProps);
			if (!IMA_SUCCESS(status)) {
				printLibError(status);
				(void) IMA_FreeMemory(targetList);
				*funcRet = 1;
				return (ret);
			}

			/*
			 * Compare the target name with the input if
			 * one was input
			 */
			if (targetNamesEqual(targetProps.imaProps.name,
			    wcInputObject) == B_TRUE) {
				found = B_TRUE;
				if ((targetNamesEqual(bootTargetName,
				    wcInputObject) == B_TRUE) &&
				    (iscsiBoot == IMA_TRUE)) {
					/*
					 * iscsi booting, need changed target
					 * param is booting target, booting
					 * session mpxio disabled, not
					 * allow to update
					 */
					if (mpxioEnabled == IMA_FALSE) {
						(void) fprintf(stderr,
						    "%s: %s\n", cmdName,
						    gettext("iscsi boot"
						    " with MPxIO disabled,"
						    " not allowed to remove"
						    " boot sess param"));
						ret = 1;
						continue;
					}

				}

				status = SUN_IMA_RemoveTargetParam(
				    targetList->oids[j]);
				if (!IMA_SUCCESS(status)) {
					printLibError(status);
					(void) IMA_FreeMemory(targetList);
					*funcRet = 1;
					return (ret);
				}
			}
		}
		if (!found) {
			/* Silently ignoring it? */
			(void) fprintf(stderr, gettext("%ws: %s\n"),
			    wcInputObject, gettext("not found"));
		}
	}

	(void) IMA_FreeMemory(targetList);
	return (ret);
}

/*ARGSUSED*/
static int
addFunc(int operandLen, char *operand[], int object, cmdOptions_t *options,
    void *addArgs, int *funcRet)
{
	int ret;

	assert(funcRet != NULL);

	switch (object) {
		case DISCOVERY_ADDRESS:
		case ISNS_SERVER_ADDRESS:
			ret = addAddress(object, operandLen, operand, funcRet);
			break;
		case STATIC_CONFIG:
			ret = addStaticConfig(operandLen, operand, funcRet);
			break;
		default:
			(void) fprintf(stderr, "%s: %s\n",
			    cmdName, gettext("unknown object"));
			ret = 1;
			break;
	}
	return (ret);
}

/*ARGSUSED*/
static int
listFunc(int operandLen, char *operand[], int object, cmdOptions_t *options,
    void *addArgs, int *funcRet)
{
	int ret;

	assert(funcRet != NULL);

	switch (object) {
	case DISCOVERY:
		ret = listDiscovery(funcRet);
		break;
	case DISCOVERY_ADDRESS:
		ret = listDiscoveryAddress(operandLen, operand, options,
		    funcRet);
		break;
	case ISNS_SERVER_ADDRESS:
		ret = listISNSServerAddress(operandLen, operand, options,
		    funcRet);
		break;
	case NODE:
		ret = listNode(funcRet);
		break;
	case STATIC_CONFIG:
		ret = listStaticConfig(operandLen, operand, funcRet);
		break;
	case TARGET:
		ret = listTarget(operandLen, operand, options, funcRet);
		break;
	case TARGET_PARAM:
		ret = listTargetParam(operandLen, operand, options, funcRet);
		break;
	default:
		(void) fprintf(stderr, "%s: %s\n",
		    cmdName, gettext("unknown object"));
		ret = 1;
		break;
	}
	return (ret);
}

/*ARGSUSED*/
static int
modifyFunc(int operandLen, char *operand[], int object, cmdOptions_t *options,
    void *addArgs, int *funcRet)
{
	int ret, i;

	assert(funcRet != NULL);

	switch (object) {
	case DISCOVERY:
		ret = modifyDiscovery(options, funcRet);
		break;
	case NODE:
		ret = modifyNode(options, funcRet);
		break;
	case TARGET_PARAM:
		i = 0;
		while (operand[i]) {
			ret = modifyTargetParam(options, operand[i], funcRet);

			if (ret) {
				(void) fprintf(stderr, "%s: %s: %s\n",
				    cmdName, gettext("modify failed"),
				    operand[i]);
				return (ret);
			}
			i++;
		}

		break;
	default:
		(void) fprintf(stderr, "%s: %s\n",
		    cmdName, gettext("unknown object"));
		ret = 1;
		break;
	}
	return (ret);
}

/*ARGSUSED*/
static int
removeFunc(int operandLen, char *operand[], int object, cmdOptions_t *options,
    void *addArgs, int *funcRet)
{
	int ret;

	switch (object) {
		case DISCOVERY_ADDRESS:
		case ISNS_SERVER_ADDRESS:
			ret = removeAddress(object, operandLen, operand,
			    funcRet);
			break;
		case STATIC_CONFIG:
			ret = removeStaticConfig(operandLen, operand, funcRet);
			break;
		case TARGET_PARAM:
			ret = removeTargetParam(operandLen, operand, funcRet);
			break;
		default:
			(void) fprintf(stderr, "%s: %s\n",
			    cmdName, gettext("unknown object"));
			ret = 1;
			break;
	}
	return (ret);
}

static void
iSCSINameCheckStatusDisplay(iSCSINameCheckStatusType status)
{
	switch (status) {
		case iSCSINameLenZero:
			(void) fprintf(stderr, "%s: %s\n",
			    cmdName, gettext("empty iSCSI name."));
			break;
		case iSCSINameLenExceededMax:
			(void) fprintf(stderr, "%s: %s\n", cmdName,
			    gettext("iSCSI name exceeded maximum length."));
			break;
		case iSCSINameUnknownType:
			(void) fprintf(stderr, "%s: %s\n", cmdName,
			    gettext("unknown iSCSI name type."));
			break;
		case iSCSINameInvalidCharacter:
			(void) fprintf(stderr, "%s: %s\n",
			    cmdName,
			    gettext("iSCSI name invalid character used"));
			break;
		case iSCSINameIqnFormatError:
			(void) fprintf(stderr, "%s: %s\n", cmdName,
			    gettext("iqn formatting error."));
			break;
		case iSCSINameIqnDateFormatError:
			(void) fprintf(stderr, "%s: %s\n",
			    cmdName, gettext("invalid iqn date." \
			    "  format is: YYYY-MM"));
			break;
		case iSCSINameIqnSubdomainFormatError:
			(void) fprintf(stderr, "%s: %s\n",
			    cmdName, gettext("missing subdomain after \":\""));
			break;
		case iSCSINameIqnInvalidYearError:
			(void) fprintf(stderr, "%s: %s\n",
			    cmdName, gettext("invalid year"));
			break;
		case iSCSINameIqnInvalidMonthError:
			(void) fprintf(stderr, "%s: %s\n",
			    cmdName, gettext("invalid month"));
			break;
		case iSCSINameIqnFQDNError:
			(void) fprintf(stderr, "%s: %s\n",
			    cmdName, gettext("missing reversed fully qualified"\
			    " domain name"));
			break;
		case iSCSINameEUIFormatError:
			(void) fprintf(stderr, "%s: %s\n", cmdName,
			    gettext("eui formatting error."));
			break;
	}
}

/*
 * A convenient function to modify the target parameters of an individual
 * target.
 *
 * Return 0 if successful
 * Return 1 if failed
 */
static int
modifyIndividualTargetParam(cmdOptions_t *optionList, IMA_OID targetOid,
    int *funcRet)
{
	assert(funcRet != NULL);

	for (; optionList->optval; optionList++) {
		switch (optionList->optval) {
			case 'a':
				if (modifyTargetAuthMethod(targetOid,
				    optionList->optarg, funcRet) != 0) {
					return (1);
				}
				break;
			case 'B':
				if (modifyTargetBidirAuthFlag(targetOid,
				    optionList->optarg, funcRet) != 0) {
					return (1);
				}
				break;
			case 'C':
				if (modifyTargetAuthParam(targetOid,
				    AUTH_PASSWORD, NULL, funcRet) != 0) {
					return (1);
				}
				break;
			case 'd':
				if (setLoginParameter(targetOid, DATA_DIGEST,
				    optionList->optarg) != 0) {
					return (1);
				}
				break;
			case 'h':
				if (setLoginParameter(targetOid, HEADER_DIGEST,
				    optionList->optarg) != 0) {
					return (1);
				}
				break;
			case 'p':
				/* Login parameter */
				if (setLoginParameters(targetOid,
				    optionList->optarg) != 0) {
					return (1);
				}
				break;
			case 'c':
				/* Modify configure sessions */
				if (modifyConfiguredSessions(targetOid,
				    optionList->optarg) != 0) {
					return (1);
				}
				break;
			case 'H':
				if (modifyTargetAuthParam(targetOid, AUTH_NAME,
				    optionList->optarg, funcRet) != 0) {
					return (1);
				}
				break;
			case 'T':
				if (setTunableParameters(targetOid,
				    optionList->optarg) != 0) {
					return (1);
				}
				break;
		}
	}

	return (0);
}

/*
 * This helper function could go into a utility module for general use.
 */
static int
parseAddress(char *address_port_str,
    uint16_t defaultPort,
    char *address_str,
    size_t address_str_len,
    uint16_t *port,
    boolean_t *isIpv6)
{
	char port_str[64];
	int tmp_port;
	char *errchr;

	if (address_port_str[0] == '[') {
		/* IPv6 address */
		char *close_bracket_pos;
		close_bracket_pos = strchr(address_port_str, ']');
		if (!close_bracket_pos) {
			syslog(LOG_USER|LOG_DEBUG,
			    "IP address format error: %s\n", address_str);
			return (PARSE_ADDR_MISSING_CLOSING_BRACKET);
		}

		*close_bracket_pos = NULL;
		(void) strlcpy(address_str, &address_port_str[1],
		    address_str_len);

		/* Extract the port number */
		close_bracket_pos++;
		if (*close_bracket_pos == ':') {
			close_bracket_pos++;
			if (*close_bracket_pos != NULL) {
				(void) strlcpy(port_str, close_bracket_pos, 64);
				tmp_port = strtol(port_str, &errchr, 10);
				if (tmp_port == 0 && errchr != NULL) {
					(void) fprintf(stderr, "%s: %s:%s %s\n",
					    cmdName, address_str,
					    close_bracket_pos,
					    gettext("port number invalid"));
					return (PARSE_ADDR_PORT_OUT_OF_RANGE);
				}
				if ((tmp_port > 0) && (tmp_port > USHRT_MAX) ||
				    (tmp_port < 0)) {
					/* Port number out of range */
					syslog(LOG_USER|LOG_DEBUG,
					    "Specified port out of range: %d",
					    tmp_port);
					return (PARSE_ADDR_PORT_OUT_OF_RANGE);
				} else {
					*port = (uint16_t)tmp_port;
				}
			} else {
				*port = defaultPort;
			}
		} else {
			*port = defaultPort;
		}

		*isIpv6 = B_TRUE;
	} else {
		/* IPv4 address */
		char *colon_pos;
		colon_pos = strchr(address_port_str, ':');
		if (!colon_pos) {
			/* No port number specified. */
			*port = defaultPort;
			(void) strlcpy(address_str, address_port_str,
			    address_str_len);
		} else {
			*colon_pos = (char)NULL;
			(void) strlcpy(address_str, address_port_str,
			    address_str_len);

			/* Extract the port number */
			colon_pos++;
			if (*colon_pos != NULL) {

				(void) strlcpy(port_str, colon_pos, 64);
				tmp_port = strtol(port_str, &errchr, 10);
				if (tmp_port == 0 && errchr != NULL) {
					(void) fprintf(stderr, "%s: %s:%s %s\n",
					    cmdName, address_str, colon_pos,
					    gettext("port number invalid"));
					return (PARSE_ADDR_PORT_OUT_OF_RANGE);
				}
				if ((tmp_port > 0) && (tmp_port > USHRT_MAX) ||
				    (tmp_port < 0)) {
					/* Port number out of range */
					syslog(LOG_USER|LOG_DEBUG,
					    "Specified port out of range: %d",
					    tmp_port);
					return (PARSE_ADDR_PORT_OUT_OF_RANGE);
				} else {
					*port = (uint16_t)tmp_port;
				}
			} else {
				*port = defaultPort;
			}
		}

		*isIpv6 = B_FALSE;
	}

	return (PARSE_ADDR_OK);
}

/*
 * This helper function could go into a utility module for general use.
 */
iSCSINameCheckStatusType
iSCSINameStringProfileCheck(wchar_t *name)
{
	char mb_name[MAX_ISCSI_NAME_LEN + 1];
	size_t name_len;
	char *tmp;

	(void) wcstombs(mb_name, name, MAX_ISCSI_NAME_LEN + 1);

	if ((name_len = strlen(mb_name)) == 0) {
		return (iSCSINameLenZero);
	} else if (name_len > MAX_ISCSI_NAME_LEN) {
		return (iSCSINameLenExceededMax);
	}

	/*
	 * check for invalid characters
	 * According to RFC 3722 iSCSI name must be either a letter,
	 * a digit or one of the following '-' '.' ':'
	 */
	for (tmp = mb_name; *tmp != NULL; tmp++) {
		if ((isalnum(*tmp) == 0) &&
		    (*tmp != '-') &&
		    (*tmp != '.') &&
		    (*tmp != ':')) {
			return (iSCSINameInvalidCharacter);
		}
	}

	if (strncmp(mb_name, ISCSI_IQN_NAME_PREFIX,
	    strlen(ISCSI_IQN_NAME_PREFIX)) == 0) {
		/*
		 * If name is of type iqn, check date string and naming
		 * authority.
		 */
		char *strp = NULL;

		/*
		 * Don't allow the string to end with a colon.  If there is a
		 * colon then there must be a subdomain provided.
		 */
		if (mb_name[strlen(mb_name) - 1] == ':') {
			return (iSCSINameIqnSubdomainFormatError);
		}

		/* Date string */
		strp = strtok(&mb_name[3], ".");
		if (strp) {
			char tmpYear[5], tmpMonth[3], *endPtr = NULL;
			int year, month;

			/* Date string should be in YYYY-MM format */
			if (strlen(strp) != strlen("YYYY-MM") ||
			    strp[4] != '-') {
				return (iSCSINameIqnDateFormatError);
			}

			/*
			 * Validate year.  Only validating that the
			 * year can be converted to a number.  No
			 * validation will be done on year's actual
			 * value.
			 */
			(void) strncpy(tmpYear, strp, 4);
			tmpYear[4] = '\0';

			errno = 0;
			year = strtol(tmpYear, &endPtr, 10);
			if (errno != 0 || *endPtr != '\0' ||
			    year < 0 || year > 9999) {
				return (iSCSINameIqnInvalidYearError);
			}

			/*
			 * Validate month is valid.
			 */
			(void) strncpy(tmpMonth, &strp[5], 2);
			tmpMonth[2] = '\0';
			errno = 0;
			month = strtol(tmpMonth, &endPtr, 10);

			if (errno != 0 || *endPtr != '\0' ||
			    month < 1 || month > 12) {
				return (iSCSINameIqnInvalidMonthError);
			}

			/*
			 * A reversed FQDN needs to be provided.  We
			 * will only check for a "." followed by more
			 * than two or more characters.  The list of domains is
			 * too large and changes too frequently to
			 * add validation for.
			 */
			strp = strtok(NULL, ".");
			if (!strp || strlen(strp) < 2) {
				return (iSCSINameIqnFQDNError);
			}

			/* Name authority string */
			strp = strtok(NULL, ":");
			if (strp) {
				return (iSCSINameCheckOK);
			} else {
				return (iSCSINameIqnFQDNError);
			}
		} else {
			return (iSCSINameIqnFormatError);
		}
	} else if (strncmp(mb_name, ISCSI_EUI_NAME_PREFIX,
	    strlen(ISCSI_EUI_NAME_PREFIX)) == 0) {
		/* If name is of type EUI, change its length */

		if (strlen(mb_name) != ISCSI_EUI_NAME_LEN) {
			return (iSCSINameEUIFormatError);
		}

		for (tmp = mb_name + strlen(ISCSI_EUI_NAME_PREFIX) + 1;
		    *tmp != '\0'; tmp++) {
			if (isxdigit(*tmp)) {
				continue;
			}
			return (iSCSINameEUIFormatError);
		}

		return (iSCSINameCheckOK);
	} else {
		return (iSCSINameUnknownType);
	}
}

/*
 * This helper function could go into a utility module for general use.
 *
 * Returns:
 * B_TRUE is the numberStr is an unsigned natural number and within the
 * specified bound.
 * B_FALSE otherwise.
 */
boolean_t
isNaturalNumber(char *numberStr, uint32_t upperBound)
{
	int i;
	int number_str_len;

	if ((number_str_len = strlen(numberStr)) == 0) {
		return (B_FALSE);
	}

	for (i = 0; i < number_str_len; i++) {
		if (numberStr[i] < 060 || numberStr[i] > 071) {
			return (B_FALSE);
		}
	}

	if (atoi(numberStr) > upperBound) {
		return (B_FALSE);
	}

	return (B_TRUE);
}

/*
 * This helper function could go into a utility module for general use.
 * It parses a target string in the format of:
 *
 * 	<target_name>,[<ip_address>[:port][,tpgt]]
 *
 * and creates wchar strings for target name and target address. It
 * also populates port and tpgt if found.
 *
 * Returns:
 * 	PARSE_TARGET_OK if parsing is successful.
 *	PARSE_TARGET_INVALID_TPGT if the specified tpgt is
 *	invalid.
 * 	PARSE_TARGET_INVALID_ADDR if the address specified is
 *	invalid.
 */
int
parseTarget(char *targetStr,
		wchar_t *targetNameStr,
		size_t targetNameStrLen,
		boolean_t *targetAddressSpecified,
		wchar_t *targetAddressStr,
		size_t targetAddressStrLen,
		uint16_t *port,
		boolean_t *tpgtSpecified,
		uint16_t *tpgt,
		boolean_t *isIpv6)
{
	char *commaPos;
	char *commaPos2;
	char targetAddress[SUN_IMA_IP_ADDRESS_PORT_LEN];
	int i;
	int lowerCase;

	(void) memset(targetNameStr, 0,
	    targetNameStrLen * sizeof (wchar_t));
	(void) memset(targetAddressStr, 0,
	    targetAddressStrLen * sizeof (wchar_t));

	commaPos = strchr(targetStr, ',');
	if (commaPos != NULL) {
		*commaPos = NULL;
		commaPos++;
		*targetAddressSpecified = B_TRUE;

		/*
		 * Checking of tpgt makes sense only when
		 * the target address/port are specified.
		 */
		commaPos2 = strchr(commaPos, ',');
		if (commaPos2 != NULL) {
			*commaPos2 = NULL;
			commaPos2++;
			if (isNaturalNumber(commaPos2, ISCSI_MAX_TPGT_VALUE) ==
			    B_TRUE) {
				*tpgt = atoi(commaPos2);
				*tpgtSpecified = B_TRUE;
			} else {
				(void) fprintf(stderr, "%s: %s\n", cmdName,
				    gettext("parse target invalid TPGT"));
				return (PARSE_TARGET_INVALID_TPGT);
			}
		}

		switch (parseAddress(commaPos, ISCSI_LISTEN_PORT,
		    &targetAddress[0], MAX_ADDRESS_LEN + 1, port, isIpv6)) {
		case PARSE_ADDR_PORT_OUT_OF_RANGE:
			return (PARSE_TARGET_INVALID_ADDR);
		case PARSE_ADDR_OK:
			break;
		default:
			(void) fprintf(stderr, "%s: %s\n",
			    cmdName, gettext("cannot parse target name"));
			return (PARSE_TARGET_INVALID_ADDR);
		}
		(void) mbstowcs(targetAddressStr, targetAddress,
		    targetAddressStrLen);
		for (i = 0; targetAddressStr[i] != 0; i++) {
			lowerCase = tolower(targetAddressStr[i]);
			targetAddressStr[i] = lowerCase;
		}
	} else {
		*targetAddressSpecified = B_FALSE;
		*tpgtSpecified = B_FALSE;
	}

	(void) mbstowcs(targetNameStr, targetStr, targetNameStrLen);
	for (i = 0; targetNameStr[i] != 0; i++) {
		lowerCase = tolower(targetNameStr[i]);
		targetNameStr[i] = lowerCase;
	}

	return (PARSE_TARGET_OK);
}

/*ARGSUSED*/
static void
listCHAPName(IMA_OID oid)
{
	IMA_INITIATOR_AUTHPARMS authParams;
	IMA_STATUS status;
	IMA_BYTE chapName [MAX_CHAP_NAME_LEN + 1];

	/* Get Chap Name depending upon oid object type */
	if (oid.objectType == IMA_OBJECT_TYPE_LHBA) {
		status = IMA_GetInitiatorAuthParms(oid,
		    IMA_AUTHMETHOD_CHAP, &authParams);
	} else {
		status = SUN_IMA_GetTargetAuthParms(oid,
		    IMA_AUTHMETHOD_CHAP, &authParams);
	}

	(void) fprintf(stdout, "\n\t\t%s: ", gettext("CHAP Name"));

	if (IMA_SUCCESS(status)) {
		/*
		 * Default chap name will be the node name.  The default will
		 * be set by the driver.
		 */
		if (authParams.chapParms.nameLength != 0) {
			(void) memset(chapName, 0, sizeof (chapName));
			(void) memcpy(chapName, authParams.chapParms.name,
			    authParams.chapParms.nameLength);
			(void) fprintf(stdout, "%s", chapName);

		} else {
			(void) fprintf(stdout, "%s", "-");
		}
	} else {
		(void) fprintf(stdout, "%s", "-");
	}
}

static boolean_t
checkServiceStatus(void)
{
	IMA_STATUS	status	=	IMA_ERROR_UNKNOWN_ERROR;
	IMA_BOOL	enabled =	0;

	status = SUN_IMA_GetSvcStatus(&enabled);

	if (status != IMA_STATUS_SUCCESS) {
		(void) fprintf(stdout, "%s\n%s\n",
		    gettext("Unable to query the service status of"
		    " iSCSI initiator."),
		    gettext("For more information, please refer to"
		    " iscsi(7D)."));
		return (B_FALSE);
	}

	if (enabled == 0) {
		(void) fprintf(stdout, "%s\n%s\n",
		    gettext("iSCSI Initiator Service is disabled,"
		    " try 'svcadm enable network/iscsi/initiator' to"
		    " enable the service."),
		    gettext("For more information, please refer to"
		    " iscsi(7D)."));
		return (B_FALSE);
	}

	return (B_TRUE);
}

/*
 * Prints out see manual page.
 * Called out through atexit(3C) so is always last thing displayed.
 */
void
seeMan(void)
{
	static int sent = 0;

	if (sent)
		return;

	(void) fprintf(stdout, "%s %s(1M)\n",
	    gettext("For more information, please see"), cmdName);

	sent = 1;
}


/*
 * main calls a parser that checks syntax of the input command against
 * various rules tables.
 *
 * The parser provides usage feedback based upon same tables by calling
 * two usage functions, usage and subUsage, handling command and subcommand
 * usage respectively.
 *
 * The parser handles all printing of usage syntactical errors
 *
 * When syntax is successfully validated, the parser calls the associated
 * function using the subcommands table functions.
 *
 * Syntax is as follows:
 *	command subcommand [options] resource-type [<object>]
 *
 * The return value from the function is placed in funcRet
 */
int
main(int argc, char *argv[])
{
	synTables_t synTables;
	char versionString[VERSION_STRING_MAX_LEN];
	int ret;
	int funcRet = 0;
	void *subcommandArgs = NULL;

	if (geteuid() != 0) {
		(void) fprintf(stderr, "%s\n", gettext("permission denied"));
		return (1);
	}

	if (checkServiceStatus() == B_FALSE) {
		return (1);
	}

	/* set global command name */
	cmdName = getExecBasename(argv[0]);

	(void) snprintf(versionString, sizeof (versionString), "%s.%s",
	    VERSION_STRING_MAJOR, VERSION_STRING_MINOR);
	synTables.versionString = versionString;
	synTables.longOptionTbl = &longOptions[0];
	synTables.subcommandTbl = &subcommands[0];
	synTables.objectTbl = &objects[0];
	synTables.objectRulesTbl = &objectRules[0];
	synTables.optionRulesTbl = &optionRules[0];

	/* call the CLI parser */
	ret = cmdParse(argc, argv, synTables, subcommandArgs, &funcRet);
	if (ret == -1) {
		perror(cmdName);
		ret = 1;
	}

	if (funcRet != 0) {
		(void) fprintf(stderr, "%s: %s\n",
		    cmdName, gettext("Unable to complete operation"));
		ret = 1;
	}
	return (ret);
}

static int
setTunableParameters(IMA_OID oid, char *optarg)
{
	char keyp[MAXOPTARGLEN];
	char valp[MAXOPTARGLEN];
	int key;
	IMA_STATUS status;
	IMA_UINT uintValue;
	ISCSI_TUNABLE_PARAM	tunableObj;
	char *nameValueString, *endptr;

	if ((nameValueString = strdup(optarg)) == NULL) {
		if (errno == ENOMEM) {
			(void) fprintf(stderr, "%s: %s\n",
			    cmdName, strerror(errno));
		} else {
			(void) fprintf(stderr, "%s: %s\n", cmdName,
			    gettext("unknown error"));
		}
		return (1);
	}

	(void) memset(keyp, 0, sizeof (keyp));
	(void) memset(valp, 0, sizeof (valp));
	if (sscanf(nameValueString, gettext("%[^=]=%s"), keyp, valp) != 2) {
		(void) fprintf(stderr, "%s: %s: %s\n", cmdName,
		    gettext("Unknown param"), nameValueString);
		if (nameValueString) {
			free(nameValueString);
			nameValueString = NULL;
		}
		return (1);
	}
	if ((key = getTunableParam(keyp)) == -1) {
		(void) fprintf(stderr, "%s: %s: %s\n", cmdName,
		    gettext("Unknown key"), keyp);
		if (nameValueString) {
			free(nameValueString);
			nameValueString = NULL;
		}
		return (1);
	}
	switch (key) {
	case RECV_LOGIN_RSP_TIMEOUT:
	case CONN_LOGIN_MAX:
	case POLLING_LOGIN_DELAY:
		errno = 0;
		uintValue = strtoul(valp, &endptr, 0);
		if (*endptr != '\0' || errno != 0) {
			(void) fprintf(stderr, "%s: %s - %s\n",
			    cmdName,
			    gettext("invalid option argument"),
			    optarg);
			if (nameValueString) {
				free(nameValueString);
				nameValueString = NULL;
			}
			return (1);
		}
		if (uintValue > 3600) {
			(void) fprintf(stderr, "%s: %s\n",
			    cmdName,
gettext("value must be between 0 and 3600"));
			if (nameValueString) {
				free(nameValueString);
				nameValueString = NULL;
			}
			return (1);
		}

		if (chkConnLoginMaxPollingLoginDelay(oid, key, uintValue) > 0) {
			if (nameValueString) {
				free(nameValueString);
				nameValueString = NULL;
			}
			return (1);
		}

		if (key == RECV_LOGIN_RSP_TIMEOUT) {
			tunableObj.tunable_objectType =
			    ISCSI_RX_TIMEOUT_VALUE;
		} else if (key == CONN_LOGIN_MAX) {
			tunableObj.tunable_objectType =
			    ISCSI_CONN_DEFAULT_LOGIN_MAX;
		} else if (key == POLLING_LOGIN_DELAY) {
			tunableObj.tunable_objectType =
			    ISCSI_LOGIN_POLLING_DELAY;
		}
		tunableObj.tunable_objectValue = valp;
		status = SUN_IMA_SetTunableProperties(oid, &tunableObj);
		break;
	default:
		(void) fprintf(stderr, "%s: %s: %s\n", cmdName,
		    gettext("Unsupported key"), keyp);
		if (nameValueString) {
			free(nameValueString);
			nameValueString = NULL;
		}
		return (1);
	}
	if (!IMA_SUCCESS(status)) {
		printLibError(status);
		if (nameValueString) {
			free(nameValueString);
			nameValueString = NULL;
		}
		return (1);
	}

	if (nameValueString) {
		free(nameValueString);
		nameValueString = NULL;
	}
	return (0);
}

/*
 * Print tunable parameters information
 */
static int
printTunableParameters(IMA_OID oid)
{
	ISCSI_TUNABLE_PARAM tunableObj;
	char value[MAXOPTARGLEN] = "\0";
	IMA_STATUS status;

	tunableObj.tunable_objectValue = value;
	(void) fprintf(stdout, "\t%s:\n",
	    gettext("Tunable Parameters (Default/Configured)"));
	tunableObj.tunable_objectType = ISCSI_RX_TIMEOUT_VALUE;
	status = SUN_IMA_GetTunableProperties(oid, &tunableObj);
	if (!IMA_SUCCESS(status)) {
		printLibError(status);
		return (1);
	}
	if (value[0] == '\0') {
		value[0] = '-';
		value[1] = '\0';
	}
	(void) fprintf(stdout, "\t\t%s: ",
	    gettext("Session Login Response Time"));
	(void) fprintf(stdout, "%s/%s\n", ISCSI_DEFAULT_RX_TIMEOUT_VALUE,
	    tunableObj.tunable_objectValue);

	value[0] = '\0';
	tunableObj.tunable_objectType = ISCSI_CONN_DEFAULT_LOGIN_MAX;
	status = SUN_IMA_GetTunableProperties(oid, &tunableObj);
	if (!IMA_SUCCESS(status)) {
		printLibError(status);
		return (1);
	}
	if (value[0] == '\0') {
		value[0] = '-';
		value[1] = '\0';
	}
	(void) fprintf(stdout, "\t\t%s: ",
	    gettext("Maximum Connection Retry Time"));
	(void) fprintf(stdout, "%s/%s\n", ISCSI_DEFAULT_CONN_DEFAULT_LOGIN_MAX,
	    tunableObj.tunable_objectValue);

	value[0] = '\0';
	tunableObj.tunable_objectType = ISCSI_LOGIN_POLLING_DELAY;
	status = SUN_IMA_GetTunableProperties(oid, &tunableObj);
	if (!IMA_SUCCESS(status)) {
		printLibError(status);
		return (1);
	}
	if (value[0] == '\0') {
		value[0] = '-';
		value[1] = '\0';
	}
	(void) fprintf(stdout, "\t\t%s: ",
	    gettext("Login Retry Time Interval"));
	(void) fprintf(stdout, "%s/%s\n", ISCSI_DEFAULT_LOGIN_POLLING_DELAY,
	    tunableObj.tunable_objectValue);
	return (0);
}

/*
 * This is helper function to check conn_login_max and polling_login_delay.
 */
static int
chkConnLoginMaxPollingLoginDelay(IMA_OID oid, int key, int uintValue)
{
	char valuep[MAXOPTARGLEN];
	IMA_STATUS	status;
	IMA_UINT	getValue;
	ISCSI_TUNABLE_PARAM	getObj;
	char *endptr;

	if (key == CONN_LOGIN_MAX) {
		getObj.tunable_objectType = ISCSI_LOGIN_POLLING_DELAY;
	} else {
		getObj.tunable_objectType = ISCSI_CONN_DEFAULT_LOGIN_MAX;
	}
	valuep[0] = '\0';
	getObj.tunable_objectValue = valuep;
	status = SUN_IMA_GetTunableProperties(oid, &getObj);
	if (!IMA_SUCCESS(status)) {
		printLibError(status);
		return (1);
	}
	if (valuep[0] == '\0') {
		if (key == CONN_LOGIN_MAX) {
			(void) strlcpy(valuep,
			    ISCSI_DEFAULT_LOGIN_POLLING_DELAY,
			    strlen(ISCSI_DEFAULT_LOGIN_POLLING_DELAY) +1);
		} else {
			(void) strlcpy(valuep,
			    ISCSI_DEFAULT_CONN_DEFAULT_LOGIN_MAX,
			    strlen(ISCSI_DEFAULT_CONN_DEFAULT_LOGIN_MAX) +1);
		}
	}

	errno = 0;
	getValue = strtoul(valuep, &endptr, 0);
	if (*endptr != '\0' || errno != 0) {
		(void) fprintf(stderr, "%s: %s - %s\n",
		    cmdName,
		    gettext("cannot convert tunable string"),
		    valuep);
		return (1);
	}
	if (key == CONN_LOGIN_MAX) {
		if (uintValue < getValue) {
			(void) fprintf(stderr, "%s: %s %ld\n",
			    cmdName, gettext("value must larger than"),
			    getValue);
			return (1);
		}
	} else {
		if (uintValue > getValue) {
			(void) fprintf(stderr, "%s: %s %ld\n",
			    cmdName, gettext("value must smaller than"),
			    getValue);
			return (1);
		}
	}
	return (0);
}