#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[];
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;
if ((timeval = time(NULL)) == -1) {
return (errno);
}
if ((tmbuf = localtime(&timeval)) == NULL) {
return (-1);
}
pid = getpid();
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);
}
return (0);
}
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;
}
}
static char *
scsi_find_command_name(int cmd)
{
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);
}
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);
}
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;
}
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];
command->uscsi_flags = USCSI_RQENABLE;
command->uscsi_flags |= flag;
errorMsg[0] = '\0';
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);
}
if (command->uscsi_timeout == 0) {
command->uscsi_timeout = 60;
}
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);
}
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 {
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);
}
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;
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);
}
(void) memset(buf_ptr, 0, buf_len);
(void) memset((char *)&ucmd, 0, sizeof (ucmd));
if ((page_code > MODEPAGE_ALLPAGES) || (pc > 3) ||
(buf_len > MAX_MODE_SENSE_LEN)) {
return (-1);
}
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);
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);
}
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 {
if (wwnConversion(hba_port_wwn) != wwnConversion(port_wwn)) {
(void) fprintf(stdout, MSGSTR(2097,
" 0x%-2x (Unknown Type)\n"),
(dtype_prop & DTYPE_MASK));
} else {
(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];
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: ");
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: ");
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))");
(void) fprintf(stdout, MSGSTR(2185, "\nINQUIRY:\n"));
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 (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 {
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++;
}
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;
}
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);
}
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);
}
}
static int
cleanup_dotdot_path(char *path)
{
char holder[MAXPATHLEN];
char *dotdot;
char *previous_slash;
dotdot = strstr(path, "/../");
if (dotdot == NULL) {
return (0);
}
if (dotdot == path) {
strcpy(holder, &path[3]);
strcpy(path, holder);
return (1);
}
*dotdot = '\0';
previous_slash = strrchr(path, '/');
if (previous_slash == NULL) {
return (0);
}
*(previous_slash+1) = '\0';
(void) strcat(path, dotdot+4);
return (1);
}
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;
if (osDevName == NULL) {
return (NULL);
}
strcpy(source, osDevName);
for (;;) {
if (source[0] != '/') {
tmp = getcwd(pwd, MAXPATHLEN);
if (tmp == NULL) {
return (NULL);
}
if (source[0] == '.' && source[1] == '/') {
strcpy(scratch, source+2);
} else {
strcpy(scratch, source);
}
strcpy(source, pwd);
(void) strcat(source, "/");
(void) strcat(source, scratch);
}
while (cleanup_dotdot_path(source))
;
if (flag == NOT_IGNORE_DANGLING_LINK) {
if (stat(source, &stbuf) == -1) {
if (!is_lstat_failed &&
strstr(source, "/devices")) {
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) {
if (lstat(source, &stbuf) == -1) {
return (NULL);
} else {
is_lstat_failed = B_FALSE;
}
} else {
return (NULL);
}
} else {
if (lstat(source, &stbuf) == -1) {
return (NULL);
}
}
} else if (flag == STANDARD_DEVNAME_HANDLING) {
if (stat(source, &stbuf) == -1) {
return (NULL);
}
if (lstat(source, &stbuf) == -1) {
return (NULL);
}
} else {
return (NULL);
}
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[cnt] = '\0';
if (scratch[0] != '/') {
tmp = strrchr(source, '/');
if (tmp == NULL) {
O_DPRINTF("Internal error... corrupt path.\n");
return (NULL);
}
*(tmp+1) = '\0';
(void) strcat(source, scratch);
} else {
strcpy(source, scratch);
}
}
}
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;
physical_path += DEV_PREFIX_STRLEN-1;
delimiter = strrchr(physical_path, ':');
if (delimiter != NULL) {
*delimiter = '\0';
}
(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_s);
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;
ioc->ret_elem = (uint_t *)&(current_path_count);
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);
}
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) {
pathlist_retry_count++;
initial_path_count = current_path_count;
} else {
break;
}
}
close(fd);
if (initial_path_count < current_path_count) {
*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;
if ((fd = open(path, O_NDELAY | O_RDWR)) == -1)
return (-1);
size = 20;
if ((*pg_buf = (uchar_t *)calloc(1, size)) == NULL) {
(void) close(fd);
return (L_MALLOC_FAILED);
}
if (status = scsi_mode_sense_cmd(fd, *pg_buf, size,
0, MODEPAGE_ALLPAGES)) {
(void) close(fd);
(void) free(*pg_buf);
return (status);
}
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);
}
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);
}
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;
}
}