OpenSolaris_b135/cmd/luxadm/lux_util.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	<stdio.h>
#include	<unistd.h>
#include	<stdlib.h>
#include	<sys/param.h>
#include	<sys/types.h>
#include	<fcntl.h>
#include	<sys/stat.h>
#include	<string.h>
#include	<strings.h>
#include	<ctype.h>
#include	<errno.h>
#include	<assert.h>
#include	<sys/scsi/impl/uscsi.h>
#include	<sys/scsi/generic/commands.h>
#include	<sys/scsi/impl/commands.h>
#include	<sys/scsi/generic/sense.h>
#include	<sys/scsi/generic/mode.h>
#include	<sys/scsi/generic/status.h>
#include	<sys/scsi/generic/inquiry.h>
#include	<sys/scsi/adapters/scsi_vhci.h>
#include	<sys/byteorder.h>
#include	"common.h"
#include	"errorcodes.h"

#define	MAX_MODE_SENSE_LEN		0xffff
#define	MAXLEN		1000

#define	RETRY_PATHLIST	1
#define	BYTES_PER_LINE	16
#define	SCMD_UNKNOWN	0xff

#define	SCSI_VHCI	"/devices/scsi_vhci/"
#define	SLASH		"/"
#define	DEV_PREFIX	"/devices/"
#define	DEV_PREFIX_STRLEN	strlen(DEV_PREFIX)
#define	DEVICES_DIR	"/devices"

extern	char	*dtype[]; /* from adm.c */
extern	int	rand_r(unsigned int *);

static int cleanup_dotdot_path(char *path);
static int wait_random_time(void);
static char *scsi_find_command_name(int cmd);
static void scsi_printerr(struct uscsi_cmd *ucmd,
	    struct scsi_extended_sense *rq, int rqlen,
	    char msg_string[], char *err_string);
static void string_dump(char *hdr, uchar_t *src, int nbytes, int format,
	    char msg_string[]);
static int issue_uscsi_cmd(int file, struct uscsi_cmd *command, int flag);


static int
wait_random_time(void)
{
	time_t		timeval;
	struct tm	*tmbuf = NULL;
	struct timeval	tval;
	unsigned int	seed;
	int		random;
	pid_t		pid;

	/*
	 * Get the system time and use "system seconds"
	 * as 'seed' to generate a random number. Then,
	 * wait between 1/10 - 1/2 seconds before retry.
	 * Get the current process id and ex-or it with
	 * the seed so that the random number is always
	 * different even in case of multiple processes
	 * generate a random number at the same time.
	 */
	if ((timeval = time(NULL)) == -1) {
		return (errno);
	}
	if ((tmbuf = localtime(&timeval)) == NULL) {
		return (-1); /* L_LOCALTIME_ERROR */
	}

	pid = getpid();

	/* get a random number. */
	seed = (unsigned int) tmbuf->tm_sec;
	seed ^= pid;
	random = rand_r(&seed);


	random = ((random % 500) + 100) * MILLISEC;
	tval.tv_sec = random / MICROSEC;
	tval.tv_usec = random % MICROSEC;

	if (select(0, NULL, NULL, NULL, &tval) == -1) {
		return (-1); /* L_SELECT_ERROR */
	}
	return (0);
}

/*
 *		Special string dump for error message
 */
static	void
string_dump(char *hdr, uchar_t *src, int nbytes, int format, char msg_string[])
{
	int i;
	int n;
	char	*p;
	char	s[256];

	assert(format == HEX_ONLY || format == HEX_ASCII);

	(void) strcpy(s, hdr);
	for (p = s; *p; p++) {
		*p = ' ';
	}

	p = hdr;
	while (nbytes > 0) {
		(void) sprintf(&msg_string[strlen(msg_string)], "%s", p);
		p = s;
		n = MIN(nbytes, BYTES_PER_LINE);
		for (i = 0; i < n; i++) {
			(void) sprintf(&msg_string[strlen(msg_string)],
			    "%02x ", src[i] & 0xff);
		}
		if (format == HEX_ASCII) {
			for (i = BYTES_PER_LINE-n; i > 0; i--) {
				(void) sprintf(&msg_string[strlen(msg_string)],
				    "   ");
			}
			(void) sprintf(&msg_string[strlen(msg_string)],
			    "    ");
			for (i = 0; i < n; i++) {
				(void) sprintf(&msg_string[strlen(msg_string)],
				    "%c", isprint(src[i]) ? src[i] : '.');
			}
		}
		(void) sprintf(&msg_string[strlen(msg_string)], "\n");
		nbytes -= n;
		src += n;
	}
}
/*
 * Return a pointer to a string telling us the name of the command.
 */
static char *
scsi_find_command_name(int cmd)
{
	/*
	 * Names of commands.  Must have SCMD_UNKNOWN at end of list.
	 */
	struct scsi_command_name {
		int command;
		char	*name;
	} scsi_command_names[29];

	register struct scsi_command_name *c;

	scsi_command_names[0].command = SCMD_TEST_UNIT_READY;
	scsi_command_names[0].name = MSGSTR(61, "Test Unit Ready");

	scsi_command_names[1].command = SCMD_FORMAT;
	scsi_command_names[1].name = MSGSTR(110, "Format");

	scsi_command_names[2].command = SCMD_REASSIGN_BLOCK;
	scsi_command_names[2].name = MSGSTR(77, "Reassign Block");

	scsi_command_names[3].command = SCMD_READ;
	scsi_command_names[3].name = MSGSTR(27, "Read");

	scsi_command_names[4].command = SCMD_WRITE;
	scsi_command_names[4].name = MSGSTR(54, "Write");

	scsi_command_names[5].command = SCMD_READ_G1;
	scsi_command_names[5].name = MSGSTR(79, "Read(10 Byte)");

	scsi_command_names[6].command = SCMD_WRITE_G1;
	scsi_command_names[6].name = MSGSTR(51, "Write(10 Byte)");

	scsi_command_names[7].command = SCMD_MODE_SELECT;
	scsi_command_names[7].name = MSGSTR(97, "Mode Select");

	scsi_command_names[8].command = SCMD_MODE_SENSE;
	scsi_command_names[8].name = MSGSTR(95, "Mode Sense");

	scsi_command_names[9].command = SCMD_REASSIGN_BLOCK;
	scsi_command_names[9].name = MSGSTR(77, "Reassign Block");

	scsi_command_names[10].command = SCMD_REQUEST_SENSE;
	scsi_command_names[10].name = MSGSTR(74, "Request Sense");

	scsi_command_names[11].command = SCMD_READ_DEFECT_LIST;
	scsi_command_names[11].name = MSGSTR(80, "Read Defect List");

	scsi_command_names[12].command = SCMD_INQUIRY;
	scsi_command_names[12].name = MSGSTR(102, "Inquiry");

	scsi_command_names[13].command = SCMD_WRITE_BUFFER;
	scsi_command_names[13].name = MSGSTR(53, "Write Buffer");

	scsi_command_names[14].command = SCMD_READ_BUFFER;
	scsi_command_names[14].name = MSGSTR(82, "Read Buffer");

	scsi_command_names[15].command = SCMD_START_STOP;
	scsi_command_names[15].name = MSGSTR(67, "Start/Stop");

	scsi_command_names[16].command = SCMD_RESERVE;
	scsi_command_names[16].name = MSGSTR(72, "Reserve");

	scsi_command_names[17].command = SCMD_RELEASE;
	scsi_command_names[17].name = MSGSTR(75, "Release");

	scsi_command_names[18].command = SCMD_MODE_SENSE_G1;
	scsi_command_names[18].name = MSGSTR(94, "Mode Sense(10 Byte)");

	scsi_command_names[19].command = SCMD_MODE_SELECT_G1;
	scsi_command_names[19].name = MSGSTR(96, "Mode Select(10 Byte)");

	scsi_command_names[20].command = SCMD_READ_CAPACITY;
	scsi_command_names[20].name = MSGSTR(81, "Read Capacity");

	scsi_command_names[21].command = SCMD_SYNC_CACHE;
	scsi_command_names[21].name = MSGSTR(64, "Synchronize Cache");

	scsi_command_names[22].command = SCMD_READ_DEFECT_LIST;
	scsi_command_names[22].name = MSGSTR(80, "Read Defect List");

	scsi_command_names[23].command = SCMD_GDIAG;
	scsi_command_names[23].name = MSGSTR(108, "Get Diagnostic");

	scsi_command_names[24].command = SCMD_SDIAG;
	scsi_command_names[24].name = MSGSTR(69, "Set Diagnostic");

	scsi_command_names[25].command = SCMD_PERS_RESERV_IN;
	scsi_command_names[25].name = MSGSTR(10500, "Persistent Reserve In");

	scsi_command_names[26].command = SCMD_PERS_RESERV_OUT;
	scsi_command_names[26].name = MSGSTR(10501, "Persistent Reserve out");

	scsi_command_names[27].command = SCMD_LOG_SENSE;
	scsi_command_names[27].name = MSGSTR(10502, "Log Sense");

	scsi_command_names[28].command = SCMD_UNKNOWN;
	scsi_command_names[28].name = MSGSTR(25, "Unknown");


	for (c = scsi_command_names; c->command != SCMD_UNKNOWN; c++)
		if (c->command == cmd)
			break;
	return (c->name);
}


/*
 *	Function to create error message containing
 *	scsi request sense information
 */

static void
scsi_printerr(struct uscsi_cmd *ucmd, struct scsi_extended_sense *rq,
		int rqlen, char msg_string[], char *err_string)
{
	int		blkno;

	switch (rq->es_key) {
	case KEY_NO_SENSE:
		(void) sprintf(msg_string, MSGSTR(91, "No sense error"));
		break;
	case KEY_RECOVERABLE_ERROR:
		(void) sprintf(msg_string, MSGSTR(76, "Recoverable error"));
		break;
	case KEY_NOT_READY:
		(void) sprintf(msg_string,
		    MSGSTR(10503,
		    "Device Not ready. Error: Random Retry Failed: %s\n."),
		    err_string);
		break;
	case KEY_MEDIUM_ERROR:
		(void) sprintf(msg_string, MSGSTR(99, "Medium error"));
		break;
	case KEY_HARDWARE_ERROR:
		(void) sprintf(msg_string, MSGSTR(106, "Hardware error"));
		break;
	case KEY_ILLEGAL_REQUEST:
		(void) sprintf(msg_string, MSGSTR(103, "Illegal request"));
		break;
	case KEY_UNIT_ATTENTION:
		(void) sprintf(msg_string,
		    MSGSTR(10504,
		    "Unit attention."
		    "Error: Random Retry Failed.\n"));
		break;
	case KEY_WRITE_PROTECT:
		(void) sprintf(msg_string, MSGSTR(52, "Write protect error"));
		break;
	case KEY_BLANK_CHECK:
		(void) sprintf(msg_string, MSGSTR(131, "Blank check error"));
		break;
	case KEY_VENDOR_UNIQUE:
		(void) sprintf(msg_string, MSGSTR(58, "Vendor unique error"));
		break;
	case KEY_COPY_ABORTED:
		(void) sprintf(msg_string, MSGSTR(123, "Copy aborted error"));
		break;
	case KEY_ABORTED_COMMAND:
		(void) sprintf(msg_string,
		    MSGSTR(10505,
		    "Aborted command. Error: Random Retry Failed.\n"));
		break;
	case KEY_EQUAL:
		(void) sprintf(msg_string, MSGSTR(117, "Equal error"));
		break;
	case KEY_VOLUME_OVERFLOW:
		(void) sprintf(msg_string, MSGSTR(57, "Volume overflow"));
		break;
	case KEY_MISCOMPARE:
		(void) sprintf(msg_string, MSGSTR(98, "Miscompare error"));
		break;
	case KEY_RESERVED:
		(void) sprintf(msg_string, MSGSTR(10506,
		    "Reserved value found"));
		break;
	default:
		(void) sprintf(msg_string, MSGSTR(59, "Unknown error"));
		break;
	}

	(void) sprintf(&msg_string[strlen(msg_string)],
	    MSGSTR(10507, " during: %s"),
	    scsi_find_command_name(ucmd->uscsi_cdb[0]));

	if (rq->es_valid) {
		blkno = (rq->es_info_1 << 24) | (rq->es_info_2 << 16) |
		    (rq->es_info_3 << 8) | rq->es_info_4;
		(void) sprintf(&msg_string[strlen(msg_string)],
		    MSGSTR(49, ": block %d (0x%x)"), blkno, blkno);
	}

	(void) sprintf(&msg_string[strlen(msg_string)], "\n");

	if (rq->es_add_len >= 6) {
		(void) sprintf(&msg_string[strlen(msg_string)],
		    MSGSTR(132, "  Additional sense: 0x%x   "
		    "ASC Qualifier: 0x%x\n"),
		    rq->es_add_code, rq->es_qual_code);
		/*
		 * rq->es_add_info[ADD_SENSE_CODE],
		 * rq->es_add_info[ADD_SENSE_QUAL_CODE]);
		 */
	}
	if (rq->es_key == KEY_ILLEGAL_REQUEST) {
		string_dump(MSGSTR(47, " cmd:   "), (uchar_t *)ucmd,
		    sizeof (struct uscsi_cmd), HEX_ONLY, msg_string);
		string_dump(MSGSTR(48, " cdb:   "),
		    (uchar_t *)ucmd->uscsi_cdb,
		    ucmd->uscsi_cdblen, HEX_ONLY, msg_string);
	}
	string_dump(MSGSTR(43, " sense:  "),
	    (uchar_t *)rq, 8 + rq->es_add_len, HEX_ONLY, msg_string);
	rqlen = rqlen;	/* not used */
}


/*
 * Execute a command and determine the result.
 */
static int
issue_uscsi_cmd(int file, struct uscsi_cmd *command, int flag)
{
	struct scsi_extended_sense	*rqbuf;
	int				status, i, retry_cnt = 0, err;
	char				errorMsg[MAXLEN];

	/*
	 * Set function flags for driver.
	 *
	 * Set Automatic request sense enable
	 *
	 */
	command->uscsi_flags = USCSI_RQENABLE;
	command->uscsi_flags |= flag;

	/* intialize error message array */
	errorMsg[0] = '\0';

	/* print command for debug */
	if (getenv("_LUX_S_DEBUG") != NULL) {
		if ((command->uscsi_cdb == NULL) ||
		    (flag & USCSI_RESET) ||
		    (flag & USCSI_RESET_ALL)) {
			if (flag & USCSI_RESET) {
				(void) printf("  Issuing a SCSI Reset.\n");
			}
			if (flag & USCSI_RESET_ALL) {
				(void) printf("  Issuing a SCSI Reset All.\n");
			}

		} else {
			(void) printf("  Issuing the following "
			    "SCSI command: %s\n",
			    scsi_find_command_name(command->uscsi_cdb[0]));
			(void) printf("	fd=0x%x cdb=", file);
			for (i = 0; i < (int)command->uscsi_cdblen; i++) {
				(void) printf("%x ", *(command->uscsi_cdb + i));
			}
			(void) printf("\n\tlen=0x%x bufaddr=0x%x buflen=0x%x"
			    " flags=0x%x\n",
			    command->uscsi_cdblen,
			    command->uscsi_bufaddr,
			    command->uscsi_buflen, command->uscsi_flags);

			if ((command->uscsi_buflen > 0) &&
			    ((flag & USCSI_READ) == 0)) {
				(void) dump_hex_data("  Buffer data: ",
				    (uchar_t *)command->uscsi_bufaddr,
				    MIN(command->uscsi_buflen, 512), HEX_ASCII);
			}
		}
		(void) fflush(stdout);
	}


	/*
	 * Default command timeout in case command left it 0
	 */
	if (command->uscsi_timeout == 0) {
		command->uscsi_timeout = 60;
	}
	/*	Issue command - finally */

retry:
	status = ioctl(file, USCSICMD, command);
	if (status == 0 && command->uscsi_status == 0) {
		if (getenv("_LUX_S_DEBUG") != NULL) {
			if ((command->uscsi_buflen > 0) &&
			    (flag & USCSI_READ)) {
				(void) dump_hex_data("\tData read:",
				    (uchar_t *)command->uscsi_bufaddr,
				    MIN(command->uscsi_buflen, 512), HEX_ASCII);
			}
		}
		return (status);
	}
	if ((status != 0) && (command->uscsi_status == 0)) {
		if ((getenv("_LUX_S_DEBUG") != NULL) ||
		    (getenv("_LUX_ER_DEBUG") != NULL)) {
			(void) printf("Unexpected USCSICMD ioctl error: %s\n",
			    strerror(errno));
		}
		return (status);
	}

	/*
	 * Just a SCSI error, create error message
	 * Retry once for Unit Attention,
	 * Not Ready, and Aborted Command
	 */
	if ((command->uscsi_rqbuf != NULL) &&
	    (((char)command->uscsi_rqlen - (char)command->uscsi_rqresid) > 0)) {

		rqbuf = (struct scsi_extended_sense *)command->uscsi_rqbuf;

		switch (rqbuf->es_key) {
		case KEY_NOT_READY:
			if (retry_cnt++ < 1) {
				ER_DPRINTF("Note: Device Not Ready."
				    " Retrying...\n");

				if ((err = wait_random_time()) == 0) {
					goto retry;
				} else {
					return (err);
				}
			}
			break;

		case KEY_UNIT_ATTENTION:
			if (retry_cnt++ < 1) {
				ER_DPRINTF("  cmd():"
				" UNIT_ATTENTION: Retrying...\n");

				goto retry;
			}
			break;

		case KEY_ABORTED_COMMAND:
			if (retry_cnt++ < 1) {
				ER_DPRINTF("Note: Command is aborted."
				" Retrying...\n");

				goto retry;
			}
			break;
		}
		if ((getenv("_LUX_S_DEBUG") != NULL) ||
		    (getenv("_LUX_ER_DEBUG") != NULL)) {
			scsi_printerr(command,
			    (struct scsi_extended_sense *)command->uscsi_rqbuf,
			    (command->uscsi_rqlen - command->uscsi_rqresid),
			    errorMsg, strerror(errno));
		}

	} else {

		/*
		 * Retry 5 times in case of BUSY, and only
		 * once for Reservation-conflict, Command
		 * Termination and Queue Full. Wait for
		 * random amount of time (between 1/10 - 1/2 secs.)
		 * between each retry. This random wait is to avoid
		 * the multiple threads being executed at the same time
		 * and also the constraint in Photon IB, where the
		 * command queue has a depth of one command.
		 */
		switch ((uchar_t)command->uscsi_status & STATUS_MASK) {
		case STATUS_BUSY:
			if (retry_cnt++ < 5) {
				if ((err = wait_random_time()) == 0) {
					R_DPRINTF("  cmd(): No. of retries %d."
					    " STATUS_BUSY: Retrying...\n",
					    retry_cnt);
					goto retry;

				} else {
					return (err);
				}
			}
			break;

		case STATUS_RESERVATION_CONFLICT:
			if (retry_cnt++ < 1) {
				if ((err = wait_random_time()) == 0) {
					R_DPRINTF("  cmd():"
					" RESERVATION_CONFLICT:"
					" Retrying...\n");
					goto retry;

				} else {
					return (err);
				}
			}
			break;

		case STATUS_TERMINATED:
			if (retry_cnt++ < 1) {
				R_DPRINTF("Note: Command Terminated."
				    " Retrying...\n");

				if ((err = wait_random_time()) == 0) {
					goto retry;
				} else {
					return (err);
				}
			}
			break;

		case STATUS_QFULL:
			if (retry_cnt++ < 1) {
				R_DPRINTF("Note: Command Queue is full."
				" Retrying...\n");

				if ((err = wait_random_time()) == 0) {
					goto retry;
				} else {
					return (err);
				}
			}
			break;
		}

	}
	if (((getenv("_LUX_S_DEBUG") != NULL) ||
	    (getenv("_LUX_ER_DEBUG") != NULL)) &&
	    (errorMsg[0] != '\0')) {
		(void) fprintf(stdout, "  %s\n", errorMsg);
	}
	return (L_SCSI_ERROR | command->uscsi_status);
}

/*
 *		MODE SENSE USCSI command
 *
 *
 *		pc = page control field
 *		page_code = Pages to return
 */
int
scsi_mode_sense_cmd(int fd,
	uchar_t *buf_ptr,
	int buf_len,
	uchar_t pc,
	uchar_t page_code)
{
	struct uscsi_cmd	ucmd;
	/* 10 byte Mode Select cmd */
	union scsi_cdb	cdb =  {SCMD_MODE_SENSE_G1, 0, 0, 0, 0, 0, 0, 0, 0, 0};
	struct	scsi_extended_sense	sense;
	int		status;
	static	int	uscsi_count;

	if ((fd < 0) || (buf_ptr == NULL) || (buf_len < 0)) {
		return (-1); /* L_INVALID_ARG */
	}

	(void) memset(buf_ptr, 0, buf_len);
	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
	/* Just for me  - a sanity check */
	if ((page_code > MODEPAGE_ALLPAGES) || (pc > 3) ||
	    (buf_len > MAX_MODE_SENSE_LEN)) {
		return (-1); /* L_ILLEGAL_MODE_SENSE_PAGE */
	}
	cdb.g1_addr3 = (pc << 6) + page_code;
	cdb.g1_count1 = buf_len>>8;
	cdb.g1_count0 = buf_len & 0xff;
	ucmd.uscsi_cdb = (caddr_t)&cdb;
	ucmd.uscsi_cdblen = CDB_GROUP1;
	ucmd.uscsi_bufaddr = (caddr_t)buf_ptr;
	ucmd.uscsi_buflen = buf_len;
	ucmd.uscsi_rqbuf = (caddr_t)&sense;
	ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
	ucmd.uscsi_timeout = 120;

	status = issue_uscsi_cmd(fd, &ucmd, USCSI_READ);
	/* Bytes actually transfered */
	if (status == 0) {
		uscsi_count = buf_len - ucmd.uscsi_resid;
		S_DPRINTF("  Number of bytes read on "
		"Mode Sense 0x%x\n", uscsi_count);
		if (getenv("_LUX_D_DEBUG") != NULL) {
			(void) dump_hex_data("  Mode Sense data: ", buf_ptr,
			    uscsi_count, HEX_ASCII);
		}
	}
	return (status);
}

int
scsi_release(char *path)
{
	struct uscsi_cmd	ucmd;
	union scsi_cdb		cdb = {SCMD_RELEASE, 0, 0, 0, 0, 0};
	struct	scsi_extended_sense	sense;
	int	fd, status;

	P_DPRINTF("  scsi_release: Release: Path %s\n", path);
	if ((fd = open(path, O_NDELAY | O_RDONLY)) == -1)
		return (1);

	(void) memset((char *)&ucmd, 0, sizeof (ucmd));

	ucmd.uscsi_cdb = (caddr_t)&cdb;
	ucmd.uscsi_cdblen = CDB_GROUP0;
	ucmd.uscsi_bufaddr = NULL;
	ucmd.uscsi_buflen = 0;
	ucmd.uscsi_rqbuf = (caddr_t)&sense;
	ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
	ucmd.uscsi_timeout = 60;
	status = (issue_uscsi_cmd(fd, &ucmd, 0));

	(void) close(fd);
	return (status);
}

int
scsi_reserve(char *path)
{
	struct uscsi_cmd	ucmd;
	union scsi_cdb	cdb = {SCMD_RESERVE, 0, 0, 0, 0, 0};
	struct	scsi_extended_sense	sense;
	int	fd, status;

	P_DPRINTF("  scsi_reserve: Reserve: Path %s\n", path);
	if ((fd = open(path, O_NDELAY | O_RDONLY)) == -1)
		return (1);

	(void) memset((char *)&ucmd, 0, sizeof (ucmd));

	ucmd.uscsi_cdb = (caddr_t)&cdb;
	ucmd.uscsi_cdblen = CDB_GROUP0;
	ucmd.uscsi_bufaddr = NULL;
	ucmd.uscsi_buflen = 0;
	ucmd.uscsi_rqbuf = (caddr_t)&sense;
	ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
	ucmd.uscsi_timeout = 60;
	status = (issue_uscsi_cmd(fd, &ucmd, 0));

	(void) close(fd);
	return (status);
}

/*
 * Print out fabric dev dtype
 */
void
print_fabric_dtype_prop(uchar_t *hba_port_wwn, uchar_t *port_wwn,
	uchar_t dtype_prop)
{
	if ((dtype_prop & DTYPE_MASK) < 0x10) {
		(void) fprintf(stdout, " 0x%-2x (%s)\n",
		    (dtype_prop & DTYPE_MASK),
		    dtype[(dtype_prop & DTYPE_MASK)]);
	} else if ((dtype_prop & DTYPE_MASK) < 0x1f) {
		(void) fprintf(stdout,
		    MSGSTR(2096, " 0x%-2x (Reserved)\n"),
		    (dtype_prop & DTYPE_MASK));
	} else {
		/* Check to see if this is the HBA */
		if (wwnConversion(hba_port_wwn) != wwnConversion(port_wwn)) {
			(void) fprintf(stdout, MSGSTR(2097,
			    " 0x%-2x (Unknown Type)\n"),
			    (dtype_prop & DTYPE_MASK));
		} else {
			/* MATCH */
			(void) fprintf(stdout, MSGSTR(2241,
			    " 0x%-2x (Unknown Type,Host Bus Adapter)\n"),
			    (dtype_prop & DTYPE_MASK));
		}
	}
}


void
print_inq_data(char *arg_path, char *path, L_inquiry inq, uchar_t *serial,
    size_t serial_len)
{
	char	**p;
	uchar_t	*v_parm;
	int	scsi_3, length;
	char	byte_number[MAXNAMELEN];
	static	char *scsi_inquiry_labels_2[21];
	static	char *scsi_inquiry_labels_3[22];
#define	MAX_ANSI_VERSION	6
	static	char	*ansi_version[MAX_ANSI_VERSION];
	/*
	 * Intialize scsi_inquiry_labels_2 with i18n strings
	 */
	scsi_inquiry_labels_2[0] = MSGSTR(138, "Vendor:                     ");
	scsi_inquiry_labels_2[1] = MSGSTR(149, "Product:                    ");
	scsi_inquiry_labels_2[2] = MSGSTR(139, "Revision:                   ");
	scsi_inquiry_labels_2[3] = MSGSTR(143, "Firmware Revision           ");
	scsi_inquiry_labels_2[4] = MSGSTR(144, "Serial Number               ");
	scsi_inquiry_labels_2[5] = MSGSTR(140, "Device type:                ");
	scsi_inquiry_labels_2[6] = MSGSTR(145, "Removable media:            ");
	scsi_inquiry_labels_2[7] = MSGSTR(146, "ISO version:                ");
	scsi_inquiry_labels_2[8] = MSGSTR(147, "ECMA version:               ");
	scsi_inquiry_labels_2[9] = MSGSTR(148, "ANSI version:               ");
	scsi_inquiry_labels_2[10] =
	    MSGSTR(2168, "Async event notification:   ");
	scsi_inquiry_labels_2[11] =
	    MSGSTR(2169, "Terminate i/o process msg:  ");
	scsi_inquiry_labels_2[12] = MSGSTR(150, "Response data format:       ");
	scsi_inquiry_labels_2[13] = MSGSTR(151, "Additional length:          ");
	scsi_inquiry_labels_2[14] = MSGSTR(152, "Relative addressing:        ");
	scsi_inquiry_labels_2[15] =
	    MSGSTR(2170, "32 bit transfers:           ");
	scsi_inquiry_labels_2[16] =
	    MSGSTR(2171, "16 bit transfers:           ");
	scsi_inquiry_labels_2[17] =
	    MSGSTR(2172, "Synchronous transfers:      ");
	scsi_inquiry_labels_2[18] = MSGSTR(153, "Linked commands:            ");
	scsi_inquiry_labels_2[19] = MSGSTR(154, "Command queueing:           ");
	scsi_inquiry_labels_2[20] =
	    MSGSTR(2173, "Soft reset option:          ");

	/*
	 * Intialize scsi_inquiry_labels_3 with i18n strings
	 */
	scsi_inquiry_labels_3[0] = MSGSTR(138, "Vendor:                     ");
	scsi_inquiry_labels_3[1] = MSGSTR(149, "Product:                    ");
	scsi_inquiry_labels_3[2] = MSGSTR(139, "Revision:                   ");
	scsi_inquiry_labels_3[3] = MSGSTR(143, "Firmware Revision           ");
	scsi_inquiry_labels_3[4] = MSGSTR(144, "Serial Number               ");
	scsi_inquiry_labels_3[5] = MSGSTR(140, "Device type:                ");
	scsi_inquiry_labels_3[6] = MSGSTR(145, "Removable media:            ");
	scsi_inquiry_labels_3[7] = MSGSTR(2174, "Medium Changer Element:     ");
	scsi_inquiry_labels_3[8] = MSGSTR(146, "ISO version:                ");
	scsi_inquiry_labels_3[9] = MSGSTR(147, "ECMA version:               ");
	scsi_inquiry_labels_3[10] = MSGSTR(148, "ANSI version:               ");
	scsi_inquiry_labels_3[11] =
	    MSGSTR(2175, "Async event reporting:      ");
	scsi_inquiry_labels_3[12] =
	    MSGSTR(2176, "Terminate task:             ");
	scsi_inquiry_labels_3[13] =
	    MSGSTR(2177, "Normal ACA Supported:       ");
	scsi_inquiry_labels_3[14] = MSGSTR(150, "Response data format:       ");
	scsi_inquiry_labels_3[15] = MSGSTR(151, "Additional length:          ");
	scsi_inquiry_labels_3[16] =
	    MSGSTR(2178, "Cmd received on port:       ");
	scsi_inquiry_labels_3[17] =
	    MSGSTR(2179, "SIP Bits:                   ");
	scsi_inquiry_labels_3[18] = MSGSTR(152, "Relative addressing:        ");
	scsi_inquiry_labels_3[19] = MSGSTR(153, "Linked commands:            ");
	scsi_inquiry_labels_3[20] =
	    MSGSTR(2180, "Transfer Disable:           ");
	scsi_inquiry_labels_3[21] = MSGSTR(154, "Command queueing:           ");

	/*
	 * Intialize scsi_inquiry_labels_3 with i18n strings
	 */
	ansi_version[0] = MSGSTR(2181,
	    " (Device might or might not comply to an ANSI version)");
	ansi_version[1] = MSGSTR(2182,
	    " (This code is reserved for historical uses)");
	ansi_version[2] = MSGSTR(2183,
	    " (Device complies to ANSI X3.131-1994 (SCSI-2))");
	ansi_version[3] = MSGSTR(2184,
	    " (Device complies to ANSI INCITS 301-1997 (SPC))");
	ansi_version[4] = MSGSTR(2226,
	    " (Device complies to ANSI INCITS 351-2001 (SPC-2))");
	ansi_version[5] = MSGSTR(2227,
	    " (Device complies to ANSI INCITS 408-2005 (SPC-3))");

	/* print inquiry information */

	(void) fprintf(stdout, MSGSTR(2185, "\nINQUIRY:\n"));
		/*
		 * arg_path is the path sent to luxadm by the user.  if arg_path
		 * is a /devices path, then we do not need to print out physical
		 * path info
		 */
	if (strcmp(arg_path, path) != 0 &&
	    strstr(arg_path, "/devices/") == NULL) {
		(void) fprintf(stdout, "  ");
		(void) fprintf(stdout,
		    MSGSTR(5, "Physical Path:"));
		(void) fprintf(stdout, "\n  %s\n", path);
	}
	if (inq.inq_ansi < 3) {
		p = scsi_inquiry_labels_2;
		scsi_3 = 0;
	} else {
		p = scsi_inquiry_labels_3;
		scsi_3 = 1;
	}
	if (inq.inq_len < 11) {
		p += 1;
	} else {
		/* */
		(void) fprintf(stdout, "%s", *p++);
		print_chars(inq.inq_vid, sizeof (inq.inq_vid), 0);
		(void) fprintf(stdout, "\n");
	}
	if (inq.inq_len < 27) {
		p += 1;
	} else {
		(void) fprintf(stdout, "%s", *p++);
		print_chars(inq.inq_pid, sizeof (inq.inq_pid), 0);
		(void) fprintf(stdout, "\n");
	}
	if (inq.inq_len < 31) {
		p += 1;
	} else {
		(void) fprintf(stdout, "%s", *p++);
		print_chars(inq.inq_revision, sizeof (inq.inq_revision), 0);
		(void) fprintf(stdout, "\n");
	}
	if (inq.inq_len < 39) {
		p += 2;
	} else {
		/*
		 * If Pluto then print
		 * firmware rev & serial #.
		 */
		if (strstr((char *)inq.inq_pid, "SSA") != 0) {
			(void) fprintf(stdout, "%s", *p++);
			print_chars(inq.inq_firmware_rev,
			    sizeof (inq.inq_firmware_rev), 0);
			(void) fprintf(stdout, "\n");
			(void) fprintf(stdout, "%s", *p++);
			print_chars(serial, serial_len, 0);
			(void) fprintf(stdout, "\n");
		} else if ((inq.inq_dtype & DTYPE_MASK) != DTYPE_ESI) {
			p++;
			(void) fprintf(stdout, "%s", *p++);
			print_chars(serial, serial_len, 0);
			(void) fprintf(stdout, "\n");
		} else {
			/* if we miss both the above if's */
			p += 2;
		}
	}

	(void) fprintf(stdout, "%s0x%x (", *p++, (inq.inq_dtype & DTYPE_MASK));
	if ((inq.inq_dtype & DTYPE_MASK) < 0x10) {
		(void) fprintf(stdout, "%s", dtype[inq.inq_dtype & DTYPE_MASK]);
	} else if ((inq.inq_dtype & DTYPE_MASK) < 0x1f) {
		(void) fprintf(stdout, MSGSTR(71, "Reserved"));
	} else {
		(void) fprintf(stdout, MSGSTR(2186, "Unknown device"));
	}
	(void) fprintf(stdout, ")\n");

	(void) fprintf(stdout, "%s", *p++);
	if (inq.inq_rmb != NULL) {
		(void) fprintf(stdout, MSGSTR(40, "yes"));
	} else {
		(void) fprintf(stdout, MSGSTR(45, "no"));
	}
	(void) fprintf(stdout, "\n");

	if (scsi_3) {
		(void) fprintf(stdout, "%s", *p++);
		if (inq.inq_mchngr != NULL) {
			(void) fprintf(stdout, MSGSTR(40, "yes"));
		} else {
			(void) fprintf(stdout, MSGSTR(45, "no"));
		}
		(void) fprintf(stdout, "\n");
	}
	(void) fprintf(stdout, "%s%d\n", *p++, inq.inq_iso);
	(void) fprintf(stdout, "%s%d\n", *p++, inq.inq_ecma);

	(void) fprintf(stdout, "%s%d", *p++, inq.inq_ansi);
	if (inq.inq_ansi < MAX_ANSI_VERSION) {
		(void) fprintf(stdout, "%s", ansi_version[inq.inq_ansi]);
	} else
		(void) fprintf(stdout, MSGSTR(71, " (Reserved)"));

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

	if (inq.inq_aenc) {
		(void) fprintf(stdout, "%s", *p++);
		(void) fprintf(stdout, MSGSTR(40, "yes"));
		(void) fprintf(stdout, "\n");
	} else {
		p++;
	}
	if (scsi_3) {
		(void) fprintf(stdout, "%s", *p++);
		if (inq.inq_normaca != NULL) {
			(void) fprintf(stdout, MSGSTR(40, "yes"));
		} else {
			(void) fprintf(stdout, MSGSTR(45, "no"));
		}
		(void) fprintf(stdout, "\n");
	}
	if (inq.inq_trmiop) {
		(void) fprintf(stdout, "%s", *p++);
		(void) fprintf(stdout, MSGSTR(40, "yes"));
		(void) fprintf(stdout, "\n");
	} else {
		p++;
	}
	(void) fprintf(stdout, "%s%d\n", *p++, inq.inq_rdf);
	(void) fprintf(stdout, "%s0x%x\n", *p++, inq.inq_len);
	if (scsi_3) {
		if (inq.inq_dual_p) {
			if (inq.inq_port != NULL) {
				(void) fprintf(stdout, MSGSTR(2187,
				    "%sa\n"), *p++);
			} else {
				(void) fprintf(stdout, MSGSTR(2188,
				    "%sb\n"), *p++);
			}
		} else {
			p++;
		}
	}
	if (scsi_3) {
		if (inq.inq_SIP_1 || inq.ui.inq_3.inq_SIP_2 ||
		    inq.ui.inq_3.inq_SIP_3) {
			(void) fprintf(stdout, "%s%d, %d, %d\n", *p,
			    inq.inq_SIP_1, inq.ui.inq_3.inq_SIP_2,
			    inq.ui.inq_3.inq_SIP_3);
		}
		p++;

	}

	if (inq.ui.inq_2.inq_2_reladdr) {
		(void) fprintf(stdout, "%s", *p);
		(void) fprintf(stdout, MSGSTR(40, "yes"));
		(void) fprintf(stdout, "\n");
	}
	p++;

	if (!scsi_3) {
		if (inq.ui.inq_2.inq_wbus32) {
			(void) fprintf(stdout, "%s", *p);
			(void) fprintf(stdout, MSGSTR(40, "yes"));
			(void) fprintf(stdout, "\n");
		}
		p++;

		if (inq.ui.inq_2.inq_wbus16) {
			(void) fprintf(stdout, "%s", *p);
			(void) fprintf(stdout, MSGSTR(40, "yes"));
			(void) fprintf(stdout, "\n");
		}
		p++;

		if (inq.ui.inq_2.inq_sync) {
			(void) fprintf(stdout, "%s", *p);
			(void) fprintf(stdout, MSGSTR(40, "yes"));
			(void) fprintf(stdout, "\n");
		}
		p++;

	}
	if (inq.ui.inq_2.inq_linked) {
		(void) fprintf(stdout, "%s", *p);
		(void) fprintf(stdout, MSGSTR(40, "yes"));
		(void) fprintf(stdout, "\n");
	}
	p++;

	if (scsi_3) {
		(void) fprintf(stdout, "%s", *p++);
		if (inq.ui.inq_3.inq_trandis != NULL) {
			(void) fprintf(stdout, MSGSTR(40, "yes"));
		} else {
			(void) fprintf(stdout, MSGSTR(45, "no"));
		}
		(void) fprintf(stdout, "\n");
	}

	if (inq.ui.inq_2.inq_cmdque) {
		(void) fprintf(stdout, "%s", *p);
		(void) fprintf(stdout, MSGSTR(40, "yes"));
		(void) fprintf(stdout, "\n");
	}
	p++;

	if (!scsi_3) {
		if (inq.ui.inq_2.inq_sftre) {
			(void) fprintf(stdout, "%s", *p);
			(void) fprintf(stdout, MSGSTR(40, "yes"));
			(void) fprintf(stdout, "\n");
		}
		p++;

	}

	/*
	 * Now print the vendor-specific data.
	 */
	v_parm = inq.inq_ven_specific_1;
	if (inq.inq_len >= 32) {
		length = inq.inq_len - 31;
		if (strstr((char *)inq.inq_pid, "SSA") != 0) {
			(void) fprintf(stdout, MSGSTR(2189,
			    "Number of Ports, Targets:   %d,%d\n"),
			    inq.inq_ssa_ports, inq.inq_ssa_tgts);
			v_parm += 20;
			length -= 20;
		} else if ((strstr((char *)inq.inq_pid, "SUN") != 0) ||
		    (strncmp((char *)inq.inq_vid, "SUN     ",
		    sizeof (inq.inq_vid)) == 0)) {
			v_parm += 16;
			length -= 16;
		}
		/*
		 * Do hex Dump of rest of the data.
		 */
		if (length > 0) {
			(void) fprintf(stdout,
			    MSGSTR(2190,
			"              VENDOR-SPECIFIC PARAMETERS\n"));
			(void) fprintf(stdout,
			    MSGSTR(2191,
			    "Byte#                  Hex Value            "
			    "                 ASCII\n"));
			(void) sprintf(byte_number,
			    "%d    ", inq.inq_len - length + 5);
			dump_hex_data(byte_number, v_parm,
			    MIN(length, inq.inq_res3 - v_parm), HEX_ASCII);
		}
		/*
		 * Skip reserved bytes 56-95.
		 */
		length -= (inq.inq_box_name - v_parm);
		if (length > 0) {
			(void) sprintf(byte_number, "%d    ",
			    inq.inq_len - length + 5);
			dump_hex_data(byte_number, inq.inq_box_name,
			    MIN(length, sizeof (inq.inq_box_name) +
			    sizeof (inq.inq_avu)), HEX_ASCII);
		}
	}
	if (getenv("_LUX_D_DEBUG") != NULL) {
		dump_hex_data("\nComplete Inquiry: ",
		    (uchar_t *)&inq,
		    MIN(inq.inq_len + 5, sizeof (inq)), HEX_ASCII);
	}
}

/*
 * Internal routine to clean up ../'s in paths.
 * returns 0 if no "../" are left.
 *
 * Wouldn't it be nice if there was a standard system library
 * routine to do this...?
 */
static int
cleanup_dotdot_path(char *path)
{
	char holder[MAXPATHLEN];
	char *dotdot;
	char *previous_slash;

	/* Find the first "/../" in the string */
	dotdot = strstr(path, "/../");
	if (dotdot == NULL) {
		return (0);
	}


	/*
	 * If the [0] character is '/' and "../" immediatly
	 * follows it, then we can strip the ../
	 *
	 *	/../../foo/bar == /foo/bar
	 *
	 */
	if (dotdot == path) {
		strcpy(holder, &path[3]); /* strip "/.." */
		strcpy(path, holder);
		return (1);
	}

	/*
	 * Now look for the LAST "/" before the "/../"
	 * as this is the parent dir we can get rid of.
	 * We do this by temporarily truncating the string
	 * at the '/' just before "../" using the dotdot pointer.
	 */
	*dotdot = '\0';
	previous_slash = strrchr(path, '/');
	if (previous_slash == NULL) {
		/*
		 * hmm, somethings wrong.  path looks something
		 * like "foo/../bar/" so we can't really deal with it.
		 */
		return (0);
	}
	/*
	 * Now truncate the path just after the previous '/'
	 * and slam everything after the "../" back on
	 */
	*(previous_slash+1) = '\0';
	(void) strcat(path, dotdot+4);
	return (1); /* We may have more "../"s */
}

/*
 * Follow symbolic links from the logical device name to
 * the /devfs physical device name.  To be complete, we
 * handle the case of multiple links.  This function
 * either returns NULL (no links, or some other error),
 * or the physical device name, alloc'ed on the heap.
 *
 * NOTE: If the path is relative, it will be forced into
 * an absolute path by pre-pending the pwd to it.
 */
char *
get_slash_devices_from_osDevName(char *osDevName, int flag)
{
	struct stat	stbuf;
	char		source[MAXPATHLEN];
	char		scratch[MAXPATHLEN];
	char		pwd[MAXPATHLEN];
	char		*tmp, *phys_path;
	int		cnt;
	boolean_t	is_lstat_failed = B_TRUE;

	/* return NULL if path is NULL */
	if (osDevName == NULL) {
		return (NULL);
	}

	strcpy(source, osDevName);
	for (;;) {

		/*
		 * First make sure the path is absolute.  If not, make it.
		 * If it's already an absolute path, we have no need
		 * to determine the cwd, so the program should still
		 * function within security-by-obscurity directories.
		 */
		if (source[0] != '/') {
			tmp = getcwd(pwd, MAXPATHLEN);
			if (tmp == NULL) {
				return (NULL);
			}
			/*
			 * Handle special case of "./foo/bar"
			 */
			if (source[0] == '.' && source[1] == '/') {
				strcpy(scratch, source+2);
			} else { /* no "./" so just take everything */
				strcpy(scratch, source);
			}
			strcpy(source, pwd);
			(void) strcat(source, "/");
			(void) strcat(source, scratch);
		}

		/*
		 * Clean up any "../"s that are in the path
		 */
		while (cleanup_dotdot_path(source))
			;

		/*
		 * source is now an absolute path to the link we're
		 * concerned with
		 */
		if (flag == NOT_IGNORE_DANGLING_LINK) {
			/*
			 * In order not to ingore dangling links, check
			 * the lstat. If lstat succeeds, return the path
			 * from readlink.
			 * Note: osDevName input with /devices path from
			 * a dangling /dev link doesn't pass lstat so
			 * NULL is returned.
			 */
			if (stat(source, &stbuf) == -1) {
				if (!is_lstat_failed &&
				    strstr(source, "/devices")) {
					/*
					 * lstat succeeded previously and source
					 * contains "/devices" then it is
					 * dangling node.
					 */
					phys_path = (char *)calloc(1,
					    strlen(source) + 1);
					if (phys_path != NULL) {
						(void) strncpy(phys_path,
						    source, strlen(source) + 1);
					}
					return (phys_path);
				} else if (is_lstat_failed) {
					/* check lstat result. */
					if (lstat(source, &stbuf) == -1) {
						return (NULL);
					} else {
						/* and continue */
						is_lstat_failed = B_FALSE;
					}
				} else {
					/*
					 * With algorithm that resolves a link
					 * and then issues readlink(), should
					 * not be reached here.
					 */
					return (NULL);
				}
			} else {
				if (lstat(source, &stbuf) == -1) {
					/*
					 * when stat succeeds it is not
					 * a dangling node so it is not
					 * a special case.
					 */
					return (NULL);
				}
			}
		} else if (flag == STANDARD_DEVNAME_HANDLING) {
			/*
			 * See if there's a real file out there.  If not,
			 * we have a dangling link and we ignore it.
			 */
			if (stat(source, &stbuf) == -1) {
				return (NULL);
			}
			if (lstat(source, &stbuf) == -1) {
				return (NULL);
			}
		} else {
			/* invalid flag */
			return (NULL);
		}

		/*
		 * If the file is not a link, we're done one
		 * way or the other.  If there were links,
		 * return the full pathname of the resulting
		 * file.
		 *
		 * Note:  All of our temp's are on the stack,
		 * so we have to copy the final result to the heap.
		 */
		if (!S_ISLNK(stbuf.st_mode)) {
			phys_path = (char *)calloc(1, strlen(source) + 1);
			if (phys_path != NULL) {
				(void) strncpy(phys_path, source,
				    strlen(source) + 1);
			}
			return (phys_path);
		}
		cnt = readlink(source, scratch, sizeof (scratch));
		if (cnt < 0) {
			return (NULL);
		}
		/*
		 * scratch is on the heap, and for some reason readlink
		 * doesn't always terminate things properly so we have
		 * to make certain we're properly terminated
		 */
		scratch[cnt] = '\0';

		/*
		 * Now check to see if the link is relative.  If so,
		 * then we have to append it to the directory
		 * which the source was in. (This is non trivial)
		 */
		if (scratch[0] != '/') {
			tmp = strrchr(source, '/');
			if (tmp == NULL) { /* Whoa!  Something's hosed! */
				O_DPRINTF("Internal error... corrupt path.\n");
				return (NULL);
			}
			/* Now strip off just the directory path */
			*(tmp+1) = '\0'; /* Keeping the last '/' */
			/* and append the new link */
			(void) strcat(source, scratch);
			/*
			 * Note:  At this point, source should have "../"s
			 * but we'll clean it up in the next pass through
			 * the loop.
			 */
		} else {
			/* It's an absolute link so no worries */
			strcpy(source, scratch);
		}
	}
	/* Never reach here */
}

/*
 * Input - Space for client_path, phci_path and paddr fields of ioc structure
 * need to be allocated by the caller of this routine.
 */
int
get_scsi_vhci_pathinfo(char *dev_path, sv_iocdata_t *ioc, int *path_count)
{
	char	*physical_path, *physical_path_s;
	int	retval;
	int	fd;
	int	initial_path_count;
	int	current_path_count;
	int 	i;
	char	*delimiter;
	int	malloc_error = 0;
	int 	prop_buf_size;
	int	pathlist_retry_count = 0;

	if (strncmp(dev_path, SCSI_VHCI, strlen(SCSI_VHCI)) != NULL) {
		if ((physical_path = get_slash_devices_from_osDevName(
		    dev_path, STANDARD_DEVNAME_HANDLING)) == NULL) {
			return (L_INVALID_PATH);
		}
		if (strncmp(physical_path, SCSI_VHCI,
		    strlen(SCSI_VHCI)) != NULL) {
			free(physical_path);
			return (L_INVALID_PATH);
		}
	} else {
		if ((physical_path = calloc(1, MAXPATHLEN)) == NULL) {
			return (L_MALLOC_FAILED);
		}
		(void) strcpy(physical_path, dev_path);
	}
	physical_path_s = physical_path;

	/* move beyond "/devices" prefix */
	physical_path += DEV_PREFIX_STRLEN-1;
	/* remove  :c,raw suffix */
	delimiter = strrchr(physical_path, ':');
	/* if we didn't find the ':' fine, else truncate */
	if (delimiter != NULL) {
		*delimiter = NULL;
	}

	/*
	 * We'll call ioctl SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO
	 * at least twice.  The first time will get the path count
	 * and the size of the ioctl propoerty buffer.  The second
	 * time will get the path_info for each path.
	 *
	 * It's possible that additional paths are added while this
	 * code is running.  If the path count increases between the
	 * 2 ioctl's above, then we'll retry (and assume all is well).
	 */
	(void) strcpy(ioc->client, physical_path);
	ioc->buf_elem = 1;
	ioc->ret_elem = (uint_t *)&(initial_path_count);
	ioc->ret_buf = NULL;

	/* free physical path */
	free(physical_path_s);

	/* 0 buf_size asks driver to return actual size needed */
	/* open the ioctl file descriptor */
	if ((fd = open("/devices/scsi_vhci:devctl", O_RDWR)) < 0) {
		return (L_OPEN_PATH_FAIL);
	}

	retval = ioctl(fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, ioc);
	if (retval != 0) {
		close(fd);
		return (L_SCSI_VHCI_ERROR);
	}
	prop_buf_size = SV_PROP_MAX_BUF_SIZE;


	while (pathlist_retry_count <= RETRY_PATHLIST) {
		ioc->buf_elem = initial_path_count;
		/* Make driver put actual # paths in variable */
		ioc->ret_elem = (uint_t *)&(current_path_count);

		/*
		 * Allocate space for array of path_info structures.
		 * Allocate enough space for # paths from get_pathcount
		 */
		ioc->ret_buf = (sv_path_info_t *)
		    calloc(initial_path_count, sizeof (sv_path_info_t));
		if (ioc->ret_buf == NULL) {
			close(fd);
			return (L_MALLOC_FAILED);
		}

		/*
		 * Allocate space for path properties returned by driver
		 */
		malloc_error = 0;
		for (i = 0; i < initial_path_count; i++) {
			ioc->ret_buf[i].ret_prop.buf_size = prop_buf_size;
			if ((ioc->ret_buf[i].ret_prop.buf =
			    (caddr_t)malloc(prop_buf_size)) == NULL) {
				malloc_error = 1;
				break;
			}
			if ((ioc->ret_buf[i].ret_prop.ret_buf_size =
			    (uint_t *)malloc(sizeof (uint_t))) == NULL) {
				malloc_error = 1;
				break;
			}
		}
		if (malloc_error == 1) {
			for (i = 0; i < initial_path_count; i++) {
				free(ioc->ret_buf[i].ret_prop.buf);
				free(ioc->ret_buf[i].ret_prop.ret_buf_size);
			}
			free(ioc->ret_buf);
			close(fd);
			return (L_MALLOC_FAILED);
		}

		retval = ioctl(fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, ioc);
		if (retval != 0) {
			for (i = 0; i < initial_path_count; i++) {
				free(ioc->ret_buf[i].ret_prop.buf);
				free(ioc->ret_buf[i].ret_prop.ret_buf_size);
			}
			free(ioc->ret_buf);
			close(fd);
			return (L_SCSI_VHCI_ERROR);
		}
		if (initial_path_count < current_path_count) {
			/* then a new path was added */
			pathlist_retry_count++;
			initial_path_count = current_path_count;
		} else {
			break;
		}
	}
	/* we are done with ioctl's, lose the fd */
	close(fd);

	/*
	 * Compare the length num elements from the ioctl response
	 *   and the caller's request - use smaller value.
	 *
	 * pathlist_p->path_count now has count returned from ioctl.
	 * ioc.buf_elem has the value the caller provided.
	 */
	if (initial_path_count < current_path_count) {
		/* More paths exist than we allocated space for */
		*path_count = initial_path_count;
	} else {
		*path_count = current_path_count;
	}

	return (0);
}

int
get_mode_page(char *path, uchar_t **pg_buf)
{
	struct mode_header_g1	*mode_header_ptr;
	int		status, size, fd;

	/* open controller */
	if ((fd = open(path, O_NDELAY | O_RDWR)) == -1)
		return (-1); /* L_OPEN_PATH_FAIL */

	/*
	 * Read the first part of the page to get the page size
	 */
	size = 20;
	if ((*pg_buf = (uchar_t *)calloc(1, size)) == NULL) {
		(void) close(fd);
		return (L_MALLOC_FAILED);
	}
	/* read page */
	if (status = scsi_mode_sense_cmd(fd, *pg_buf, size,
	    0, MODEPAGE_ALLPAGES)) {
		(void) close(fd);
		(void) free(*pg_buf);
		return (status);
	}
	/* Now get the size for all pages */
	mode_header_ptr = (struct mode_header_g1 *)(void *)*pg_buf;
	size = ntohs(mode_header_ptr->length) +
	    sizeof (mode_header_ptr->length);
	(void) free(*pg_buf);
	if ((*pg_buf = (uchar_t *)calloc(1, size)) == NULL) {
		(void) close(fd);
		return (L_MALLOC_FAILED);
	}
	/* read all pages */
	if (status = scsi_mode_sense_cmd(fd, *pg_buf, size,
	    0, MODEPAGE_ALLPAGES)) {
		(void) close(fd);
		(void) free(*pg_buf);
		return (status);
	}
	(void) close(fd);
	return (0);
}

/*
 * Dump a structure in hexadecimal.
 */
void
dump_hex_data(char *hdr, uchar_t *src, int nbytes, int format)
{
	int i;
	int n;
	char	*p;
	char	s[256];

	assert(format == HEX_ONLY || format == HEX_ASCII);

	(void) strcpy(s, hdr);
	for (p = s; *p; p++) {
		*p = ' ';
	}

	p = hdr;
	while (nbytes > 0) {
		(void) fprintf(stdout, "%s", p);
		p = s;
		n = MIN(nbytes, BYTES_PER_LINE);
		for (i = 0; i < n; i++) {
			(void) fprintf(stdout, "%02x ", src[i] & 0xff);
		}
		if (format == HEX_ASCII) {
			for (i = BYTES_PER_LINE-n; i > 0; i--) {
				(void) fprintf(stdout, "   ");
			}
			(void) fprintf(stdout, "    ");
			for (i = 0; i < n; i++) {
				(void) fprintf(stdout, "%c",
				    isprint(src[i]) ? src[i] : '.');
			}
		}
		(void) fprintf(stdout, "\n");
		nbytes -= n;
		src += n;
	}
}