root/usr/src/cmd/luxadm/lux_util.c
/*
 * 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 (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
 */



#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 != 0) {
                (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 != 0) {
                        (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, " (%s)", 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 != 0) {
                        (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 != 0) {
                                (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 != 0) {
                        (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)) != 0) {
                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)) != 0) {
                        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 = '\0';
        }

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