summaryrefslogtreecommitdiff
path: root/usr/src/cmd/luxadm/lux_util.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/luxadm/lux_util.c')
-rw-r--r--usr/src/cmd/luxadm/lux_util.c1594
1 files changed, 1594 insertions, 0 deletions
diff --git a/usr/src/cmd/luxadm/lux_util.c b/usr/src/cmd/luxadm/lux_util.c
new file mode 100644
index 0000000000..41e8ce7aa2
--- /dev/null
+++ b/usr/src/cmd/luxadm/lux_util.c
@@ -0,0 +1,1594 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+
+#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];
+static char *ansi_version[4];
+ /*
+ * 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 SCSI-3)");
+
+ /* print inquiry information */
+
+ (void) fprintf(stdout, MSGSTR(2185, "\nINQUIRY:\n"));
+ /*
+ * arg_path is the path sent to luxadm by the user. if arg_path
+ * is a /devices path, then we do not need to print out physical
+ * path info
+ */
+ if (strcmp(arg_path, path) != 0 &&
+ strstr(arg_path, "/devices/") == NULL) {
+ (void) fprintf(stdout, " ");
+ (void) fprintf(stdout,
+ MSGSTR(5, "Physical Path:"));
+ (void) fprintf(stdout, "\n %s\n", path);
+ }
+ if (inq.inq_ansi < 3) {
+ p = scsi_inquiry_labels_2;
+ scsi_3 = 0;
+ } else {
+ p = scsi_inquiry_labels_3;
+ scsi_3 = 1;
+ }
+ if (inq.inq_len < 11) {
+ p += 1;
+ } else {
+ /* */
+ (void) fprintf(stdout, "%s", *p++);
+ print_chars(inq.inq_vid, sizeof (inq.inq_vid), 0);
+ (void) fprintf(stdout, "\n");
+ }
+ if (inq.inq_len < 27) {
+ p += 1;
+ } else {
+ (void) fprintf(stdout, "%s", *p++);
+ print_chars(inq.inq_pid, sizeof (inq.inq_pid), 0);
+ (void) fprintf(stdout, "\n");
+ }
+ if (inq.inq_len < 31) {
+ p += 1;
+ } else {
+ (void) fprintf(stdout, "%s", *p++);
+ print_chars(inq.inq_revision, sizeof (inq.inq_revision), 0);
+ (void) fprintf(stdout, "\n");
+ }
+ if (inq.inq_len < 39) {
+ p += 2;
+ } else {
+ /*
+ * If Pluto then print
+ * firmware rev & serial #.
+ */
+ if (strstr((char *)inq.inq_pid, "SSA") != 0) {
+ (void) fprintf(stdout, "%s", *p++);
+ print_chars(inq.inq_firmware_rev,
+ sizeof (inq.inq_firmware_rev), 0);
+ (void) fprintf(stdout, "\n");
+ (void) fprintf(stdout, "%s", *p++);
+ print_chars(serial, serial_len, 0);
+ (void) fprintf(stdout, "\n");
+ } else if ((inq.inq_dtype & DTYPE_MASK) != DTYPE_ESI) {
+ p++;
+ (void) fprintf(stdout, "%s", *p++);
+ print_chars(serial, serial_len, 0);
+ (void) fprintf(stdout, "\n");
+ } else {
+ /* if we miss both the above if's */
+ p += 2;
+ }
+ }
+
+ (void) fprintf(stdout, "%s0x%x (",
+ *p++, (inq.inq_dtype & DTYPE_MASK));
+ if ((inq.inq_dtype & DTYPE_MASK) < 0x10) {
+ (void) fprintf(stdout, "%s", dtype[inq.inq_dtype & DTYPE_MASK]);
+ } else if ((inq.inq_dtype & DTYPE_MASK) < 0x1f) {
+ (void) fprintf(stdout, MSGSTR(71, "Reserved"));
+ } else {
+ (void) fprintf(stdout, MSGSTR(2186, "Unknown device"));
+ }
+ (void) fprintf(stdout, ")\n");
+
+ (void) fprintf(stdout, "%s", *p++);
+ if (inq.inq_rmb != NULL) {
+ (void) fprintf(stdout, MSGSTR(40, "yes"));
+ } else {
+ (void) fprintf(stdout, MSGSTR(45, "no"));
+ }
+ (void) fprintf(stdout, "\n");
+
+ if (scsi_3) {
+ (void) fprintf(stdout, "%s", *p++);
+ if (inq.inq_mchngr != NULL) {
+ (void) fprintf(stdout, MSGSTR(40, "yes"));
+ } else {
+ (void) fprintf(stdout, MSGSTR(45, "no"));
+ }
+ (void) fprintf(stdout, "\n");
+ }
+ (void) fprintf(stdout, "%s%d\n", *p++, inq.inq_iso);
+ (void) fprintf(stdout, "%s%d\n", *p++, inq.inq_ecma);
+
+ (void) fprintf(stdout, "%s%d", *p++, inq.inq_ansi);
+ if (inq.inq_ansi < 0x4) {
+ (void) fprintf(stdout, "%s", ansi_version[inq.inq_ansi]);
+ } else
+ (void) fprintf(stdout, MSGSTR(71, "Reserved"));
+ (void) fprintf(stdout, "\n");
+
+ if (inq.inq_aenc) {
+ (void) fprintf(stdout, "%s", *p++);
+ (void) fprintf(stdout, MSGSTR(40, "yes"));
+ (void) fprintf(stdout, "\n");
+ } else {
+ p++;
+ }
+ if (scsi_3) {
+ (void) fprintf(stdout, "%s", *p++);
+ if (inq.inq_normaca != NULL) {
+ (void) fprintf(stdout, MSGSTR(40, "yes"));
+ } else {
+ (void) fprintf(stdout, MSGSTR(45, "no"));
+ }
+ (void) fprintf(stdout, "\n");
+ }
+ if (inq.inq_trmiop) {
+ (void) fprintf(stdout, "%s", *p++);
+ (void) fprintf(stdout, MSGSTR(40, "yes"));
+ (void) fprintf(stdout, "\n");
+ } else {
+ p++;
+ }
+ (void) fprintf(stdout, "%s%d\n", *p++, inq.inq_rdf);
+ (void) fprintf(stdout, "%s0x%x\n", *p++, inq.inq_len);
+ if (scsi_3) {
+ if (inq.inq_dual_p) {
+ if (inq.inq_port != NULL) {
+ (void) fprintf(stdout, MSGSTR(2187,
+ "%sa\n"), *p++);
+ } else {
+ (void) fprintf(stdout, MSGSTR(2188,
+ "%sb\n"), *p++);
+ }
+ } else {
+ p++;
+ }
+ }
+ if (scsi_3) {
+ if (inq.inq_SIP_1 || inq.ui.inq_3.inq_SIP_2 ||
+ inq.ui.inq_3.inq_SIP_3) {
+ (void) fprintf(stdout, "%s%d, %d, %d\n", *p,
+ inq.inq_SIP_1, inq.ui.inq_3.inq_SIP_2,
+ inq.ui.inq_3.inq_SIP_3);
+ }
+ p++;
+
+ }
+
+ if (inq.ui.inq_2.inq_2_reladdr) {
+ (void) fprintf(stdout, "%s", *p);
+ (void) fprintf(stdout, MSGSTR(40, "yes"));
+ (void) fprintf(stdout, "\n");
+ }
+ p++;
+
+ if (!scsi_3) {
+
+ if (inq.ui.inq_2.inq_wbus32) {
+ (void) fprintf(stdout, "%s", *p);
+ (void) fprintf(stdout, MSGSTR(40, "yes"));
+ (void) fprintf(stdout, "\n");
+ }
+ p++;
+
+ if (inq.ui.inq_2.inq_wbus16) {
+ (void) fprintf(stdout, "%s", *p);
+ (void) fprintf(stdout, MSGSTR(40, "yes"));
+ (void) fprintf(stdout, "\n");
+ }
+ p++;
+
+ if (inq.ui.inq_2.inq_sync) {
+ (void) fprintf(stdout, "%s", *p);
+ (void) fprintf(stdout, MSGSTR(40, "yes"));
+ (void) fprintf(stdout, "\n");
+ }
+ p++;
+
+ }
+ if (inq.ui.inq_2.inq_linked) {
+ (void) fprintf(stdout, "%s", *p);
+ (void) fprintf(stdout, MSGSTR(40, "yes"));
+ (void) fprintf(stdout, "\n");
+ }
+ p++;
+
+ if (scsi_3) {
+ (void) fprintf(stdout, "%s", *p++);
+ if (inq.ui.inq_3.inq_trandis != NULL) {
+ (void) fprintf(stdout, MSGSTR(40, "yes"));
+ } else {
+ (void) fprintf(stdout, MSGSTR(45, "no"));
+ }
+ (void) fprintf(stdout, "\n");
+ }
+
+ if (inq.ui.inq_2.inq_cmdque) {
+ (void) fprintf(stdout, "%s", *p);
+ (void) fprintf(stdout, MSGSTR(40, "yes"));
+ (void) fprintf(stdout, "\n");
+ }
+ p++;
+
+ if (!scsi_3) {
+ if (inq.ui.inq_2.inq_sftre) {
+ (void) fprintf(stdout, "%s", *p);
+ (void) fprintf(stdout, MSGSTR(40, "yes"));
+ (void) fprintf(stdout, "\n");
+ }
+ p++;
+
+ }
+
+ /*
+ * Now print the vendor-specific data.
+ */
+ v_parm = inq.inq_ven_specific_1;
+ if (inq.inq_len >= 32) {
+ length = inq.inq_len - 31;
+ if (strstr((char *)inq.inq_pid, "SSA") != 0) {
+ (void) fprintf(stdout, MSGSTR(2189,
+ "Number of Ports, Targets: %d,%d\n"),
+ inq.inq_ssa_ports, inq.inq_ssa_tgts);
+ v_parm += 20;
+ length -= 20;
+ } else if ((strstr((char *)inq.inq_pid, "SUN") != 0) ||
+ (strncmp((char *)inq.inq_vid, "SUN ",
+ sizeof (inq.inq_vid)) == 0)) {
+ v_parm += 16;
+ length -= 16;
+ }
+ /*
+ * Do hex Dump of rest of the data.
+ */
+ if (length > 0) {
+ (void) fprintf(stdout,
+ MSGSTR(2190,
+ " VENDOR-SPECIFIC PARAMETERS\n"));
+ (void) fprintf(stdout,
+ MSGSTR(2191,
+ "Byte# Hex Value "
+ " ASCII\n"));
+ (void) sprintf(byte_number,
+ "%d ", inq.inq_len - length + 5);
+ dump_hex_data(byte_number, v_parm,
+ MIN(length, inq.inq_res3 - v_parm), HEX_ASCII);
+ }
+ /*
+ * Skip reserved bytes 56-95.
+ */
+ length -= (inq.inq_box_name - v_parm);
+ if (length > 0) {
+ (void) sprintf(byte_number, "%d ",
+ inq.inq_len - length + 5);
+ dump_hex_data(byte_number, inq.inq_box_name,
+ MIN(length, sizeof (inq.inq_box_name) +
+ sizeof (inq.inq_avu)), HEX_ASCII);
+ }
+ }
+ if (getenv("_LUX_D_DEBUG") != NULL) {
+ dump_hex_data("\nComplete Inquiry: ",
+ (uchar_t *)&inq,
+ MIN(inq.inq_len + 5, sizeof (inq)), HEX_ASCII);
+ }
+}
+
+/*
+ * Internal routine to clean up ../'s in paths.
+ * returns 0 if no "../" are left.
+ *
+ * Wouldn't it be nice if there was a standard system library
+ * routine to do this...?
+ */
+static int
+cleanup_dotdot_path(char *path)
+{
+ char holder[MAXPATHLEN];
+ char *dotdot;
+ char *previous_slash;
+
+ /* Find the first "/../" in the string */
+ dotdot = strstr(path, "/../");
+ if (dotdot == NULL) {
+ return (0);
+ }
+
+
+ /*
+ * If the [0] character is '/' and "../" immediatly
+ * follows it, then we can strip the ../
+ *
+ * /../../foo/bar == /foo/bar
+ *
+ */
+ if (dotdot == path) {
+ strcpy(holder, &path[3]); /* strip "/.." */
+ strcpy(path, holder);
+ return (1);
+ }
+
+ /*
+ * Now look for the LAST "/" before the "/../"
+ * as this is the parent dir we can get rid of.
+ * We do this by temporarily truncating the string
+ * at the '/' just before "../" using the dotdot pointer.
+ */
+ *dotdot = '\0';
+ previous_slash = strrchr(path, '/');
+ if (previous_slash == NULL) {
+ /*
+ * hmm, somethings wrong. path looks something
+ * like "foo/../bar/" so we can't really deal with it.
+ */
+ return (0);
+ }
+ /*
+ * Now truncate the path just after the previous '/'
+ * and slam everything after the "../" back on
+ */
+ *(previous_slash+1) = '\0';
+ (void) strcat(path, dotdot+4);
+ return (1); /* We may have more "../"s */
+}
+
+/*
+ * Follow symbolic links from the logical device name to
+ * the /devfs physical device name. To be complete, we
+ * handle the case of multiple links. This function
+ * either returns NULL (no links, or some other error),
+ * or the physical device name, alloc'ed on the heap.
+ *
+ * NOTE: If the path is relative, it will be forced into
+ * an absolute path by pre-pending the pwd to it.
+ */
+char *
+get_slash_devices_from_osDevName(char *osDevName, int flag)
+{
+ struct stat stbuf;
+ char source[MAXPATHLEN];
+ char scratch[MAXPATHLEN];
+ char pwd[MAXPATHLEN];
+ char *tmp, *phys_path;
+ int cnt;
+ boolean_t is_lstat_failed = B_TRUE;
+
+ /* return NULL if path is NULL */
+ if (osDevName == NULL) {
+ return (NULL);
+ }
+
+ strcpy(source, osDevName);
+ for (;;) {
+
+ /*
+ * First make sure the path is absolute. If not, make it.
+ * If it's already an absolute path, we have no need
+ * to determine the cwd, so the program should still
+ * function within security-by-obscurity directories.
+ */
+ if (source[0] != '/') {
+ tmp = getcwd(pwd, MAXPATHLEN);
+ if (tmp == NULL) {
+ return (NULL);
+ }
+ /*
+ * Handle special case of "./foo/bar"
+ */
+ if (source[0] == '.' && source[1] == '/') {
+ strcpy(scratch, source+2);
+ } else { /* no "./" so just take everything */
+ strcpy(scratch, source);
+ }
+ strcpy(source, pwd);
+ (void) strcat(source, "/");
+ (void) strcat(source, scratch);
+ }
+
+ /*
+ * Clean up any "../"s that are in the path
+ */
+ while (cleanup_dotdot_path(source));
+
+ /*
+ * source is now an absolute path to the link we're
+ * concerned with
+ */
+ if (flag == NOT_IGNORE_DANGLING_LINK) {
+ /*
+ * In order not to ingore dangling links, check
+ * the lstat. If lstat succeeds, return the path
+ * from readlink.
+ * Note: osDevName input with /devices path from
+ * a dangling /dev link doesn't pass lstat so
+ * NULL is returned.
+ */
+ if (stat(source, &stbuf) == -1) {
+ if (!is_lstat_failed &&
+ strstr(source, "/devices")) {
+ /*
+ * lstat succeeded previously and source
+ * contains "/devices" then it is dangling node.
+ */
+ phys_path = (char *)calloc(1, strlen(source) + 1);
+ if (phys_path != NULL) {
+ (void) strncpy(phys_path, source,
+ strlen(source) + 1);
+ }
+ return (phys_path);
+ } else if (is_lstat_failed) {
+ /* check lstat result. */
+ if (lstat(source, &stbuf) == -1) {
+ return (NULL);
+ } else {
+ is_lstat_failed = B_FALSE; /* and coninue */
+ }
+ } else {
+ /*
+ * With algorithm that resolves a link and
+ * then issues readlink(), should not be
+ * reached here.
+ */
+ return (NULL);
+ }
+ } else {
+ if (lstat(source, &stbuf) == -1) {
+ /*
+ * when stat succeeds it is not a dangling node
+ * so it is not a special case.
+ */
+ return (NULL);
+ }
+ }
+ } else if (flag == STANDARD_DEVNAME_HANDLING) {
+ /*
+ * See if there's a real file out there. If not,
+ * we have a dangling link and we ignore it.
+ */
+ if (stat(source, &stbuf) == -1) {
+ return (NULL);
+ }
+ if (lstat(source, &stbuf) == -1) {
+ return (NULL);
+ }
+ } else {
+ /* invalid flag */
+ return (NULL);
+ }
+
+ /*
+ * If the file is not a link, we're done one
+ * way or the other. If there were links,
+ * return the full pathname of the resulting
+ * file.
+ *
+ * Note: All of our temp's are on the stack,
+ * so we have to copy the final result to the heap.
+ */
+ if (!S_ISLNK(stbuf.st_mode)) {
+ phys_path = (char *)calloc(1, strlen(source) + 1);
+ if (phys_path != NULL) {
+ (void) strncpy(phys_path, source,
+ strlen(source) + 1);
+ }
+ return (phys_path);
+ }
+ cnt = readlink(source, scratch, sizeof (scratch));
+ if (cnt < 0) {
+ return (NULL);
+ }
+ /*
+ * scratch is on the heap, and for some reason readlink
+ * doesn't always terminate things properly so we have
+ * to make certain we're properly terminated
+ */
+ scratch[cnt] = '\0';
+
+ /*
+ * Now check to see if the link is relative. If so,
+ * then we have to append it to the directory
+ * which the source was in. (This is non trivial)
+ */
+ if (scratch[0] != '/') {
+ tmp = strrchr(source, '/');
+ if (tmp == NULL) { /* Whoa! Something's hosed! */
+ O_DPRINTF("Internal error... corrupt path.\n");
+ return (NULL);
+ }
+ /* Now strip off just the directory path */
+ *(tmp+1) = '\0'; /* Keeping the last '/' */
+ /* and append the new link */
+ (void) strcat(source, scratch);
+ /*
+ * Note: At this point, source should have "../"s
+ * but we'll clean it up in the next pass through
+ * the loop.
+ */
+ } else {
+ /* It's an absolute link so no worries */
+ strcpy(source, scratch);
+ }
+ }
+ /* Never reach here */
+}
+
+/*
+ * Input - Space for client_path, phci_path and paddr fields of ioc structure
+ * need to be allocated by the caller of this routine.
+ */
+int
+get_scsi_vhci_pathinfo(char *dev_path, sv_iocdata_t *ioc, int *path_count)
+{
+ char *physical_path, *physical_path_s;
+ int retval;
+ int fd;
+ int initial_path_count;
+ int current_path_count;
+ int i;
+ char *delimiter;
+ int malloc_error = 0;
+ int prop_buf_size;
+ int pathlist_retry_count = 0;
+
+ if (strncmp(dev_path, SCSI_VHCI,
+ strlen(SCSI_VHCI)) != NULL) {
+ if ((physical_path = get_slash_devices_from_osDevName(
+ dev_path, STANDARD_DEVNAME_HANDLING)) == NULL) {
+ return (L_INVALID_PATH);
+ }
+ if (strncmp(physical_path, SCSI_VHCI,
+ strlen(SCSI_VHCI)) != NULL) {
+ free(physical_path);
+ return (L_INVALID_PATH);
+ }
+ } else {
+ if ((physical_path = calloc(1, MAXPATHLEN)) == NULL) {
+ return (L_MALLOC_FAILED);
+ }
+ (void) strcpy(physical_path, dev_path);
+ }
+ physical_path_s = physical_path;
+
+ /* move beyond "/devices" prefix */
+ physical_path += DEV_PREFIX_STRLEN-1;
+ /* remove :c,raw suffix */
+ delimiter = strrchr(physical_path, ':');
+ /* if we didn't find the ':' fine, else truncate */
+ if (delimiter != NULL) {
+ *delimiter = NULL;
+ }
+
+ /*
+ * We'll call ioctl SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO
+ * at least twice. The first time will get the path count
+ * and the size of the ioctl propoerty buffer. The second
+ * time will get the path_info for each path.
+ *
+ * It's possible that additional paths are added while this
+ * code is running. If the path count increases between the
+ * 2 ioctl's above, then we'll retry (and assume all is well).
+ */
+ (void) strcpy(ioc->client, physical_path);
+ ioc->buf_elem = 1;
+ ioc->ret_elem = (uint_t *)&(initial_path_count);
+ ioc->ret_buf = NULL;
+
+ /* free physical path */
+ free(physical_path_s);
+
+ /* 0 buf_size asks driver to return actual size needed */
+ /* open the ioctl file descriptor */
+ if ((fd = open("/devices/scsi_vhci:devctl", O_RDWR)) < 0) {
+ return (L_OPEN_PATH_FAIL);
+ }
+
+ retval = ioctl(fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, ioc);
+ if (retval != 0) {
+ close(fd);
+ return (L_SCSI_VHCI_ERROR);
+ }
+ prop_buf_size = SV_PROP_MAX_BUF_SIZE;
+
+
+ while (pathlist_retry_count <= RETRY_PATHLIST) {
+ ioc->buf_elem = initial_path_count;
+ /* Make driver put actual # paths in variable */
+ ioc->ret_elem = (uint_t *)&(current_path_count);
+
+ /*
+ * Allocate space for array of path_info structures.
+ * Allocate enough space for # paths from get_pathcount
+ */
+ ioc->ret_buf = (sv_path_info_t *)
+ calloc(initial_path_count,
+ sizeof (sv_path_info_t));
+ if (ioc->ret_buf == NULL) {
+ close(fd);
+ return (L_MALLOC_FAILED);
+ }
+
+ /*
+ * Allocate space for path properties returned by driver
+ */
+ malloc_error = 0;
+ for (i = 0; i < initial_path_count; i++) {
+ ioc->ret_buf[i].ret_prop.buf_size = prop_buf_size;
+ if ((ioc->ret_buf[i].ret_prop.buf =
+ (caddr_t)malloc(prop_buf_size)) == NULL) {
+ malloc_error = 1;
+ break;
+ }
+ if ((ioc->ret_buf[i].ret_prop.ret_buf_size =
+ (uint_t *)malloc(sizeof (uint_t))) == NULL) {
+ malloc_error = 1;
+ break;
+ }
+ }
+ if (malloc_error == 1) {
+ for (i = 0; i < initial_path_count; i++) {
+ free(ioc->ret_buf[i].ret_prop.buf);
+ free(ioc->ret_buf[i].ret_prop.ret_buf_size);
+ }
+ free(ioc->ret_buf);
+ close(fd);
+ return (L_MALLOC_FAILED);
+ }
+
+ retval = ioctl(fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, ioc);
+ if (retval != 0) {
+ for (i = 0; i < initial_path_count; i++) {
+ free(ioc->ret_buf[i].ret_prop.buf);
+ free(ioc->ret_buf[i].ret_prop.ret_buf_size);
+ }
+ free(ioc->ret_buf);
+ close(fd);
+ return (L_SCSI_VHCI_ERROR);
+ }
+ if (initial_path_count < current_path_count) {
+ /* then a new path was added */
+ pathlist_retry_count++;
+ initial_path_count = current_path_count;
+ } else {
+ break;
+ }
+ }
+ /* we are done with ioctl's, lose the fd */
+ close(fd);
+
+ /*
+ * Compare the length num elements from the ioctl response
+ * and the caller's request - use smaller value.
+ *
+ * pathlist_p->path_count now has count returned from ioctl.
+ * ioc.buf_elem has the value the caller provided.
+ */
+ if (initial_path_count < current_path_count) {
+ /* More paths exist than we allocated space for */
+ *path_count = initial_path_count;
+ } else {
+ *path_count = current_path_count;
+ }
+
+ return (0);
+}
+
+int
+get_mode_page(char *path, uchar_t **pg_buf)
+{
+struct mode_header_g1 *mode_header_ptr;
+int status, size, fd;
+
+ /* open controller */
+ if ((fd = open(path, O_NDELAY | O_RDWR)) == -1)
+ return (-1); /* L_OPEN_PATH_FAIL */
+
+ /*
+ * Read the first part of the page to get the page size
+ */
+ size = 20;
+ if ((*pg_buf = (uchar_t *)calloc(1, size)) == NULL) {
+ (void) close(fd);
+ return (L_MALLOC_FAILED);
+ }
+ /* read page */
+ if (status = scsi_mode_sense_cmd(fd, *pg_buf, size,
+ 0, MODEPAGE_ALLPAGES)) {
+ (void) close(fd);
+ (void) free(*pg_buf);
+ return (status);
+ }
+ /* Now get the size for all pages */
+ mode_header_ptr = (struct mode_header_g1 *)(void *)*pg_buf;
+ size = ntohs(mode_header_ptr->length) +
+ sizeof (mode_header_ptr->length);
+ (void) free(*pg_buf);
+ if ((*pg_buf = (uchar_t *)calloc(1, size)) == NULL) {
+ (void) close(fd);
+ return (L_MALLOC_FAILED);
+ }
+ /* read all pages */
+ if (status = scsi_mode_sense_cmd(fd, *pg_buf, size,
+ 0, MODEPAGE_ALLPAGES)) {
+ (void) close(fd);
+ (void) free(*pg_buf);
+ return (status);
+ }
+ (void) close(fd);
+ return (0);
+}
+
+/*
+ * Dump a structure in hexadecimal.
+ */
+void
+dump_hex_data(char *hdr, uchar_t *src, int nbytes, int format)
+{
+ int i;
+ int n;
+ char *p;
+ char s[256];
+
+ assert(format == HEX_ONLY || format == HEX_ASCII);
+
+ (void) strcpy(s, hdr);
+ for (p = s; *p; p++) {
+ *p = ' ';
+ }
+
+ p = hdr;
+ while (nbytes > 0) {
+ (void) fprintf(stdout, "%s", p);
+ p = s;
+ n = MIN(nbytes, BYTES_PER_LINE);
+ for (i = 0; i < n; i++) {
+ (void) fprintf(stdout, "%02x ", src[i] & 0xff);
+ }
+ if (format == HEX_ASCII) {
+ for (i = BYTES_PER_LINE-n; i > 0; i--) {
+ (void) fprintf(stdout, " ");
+ }
+ (void) fprintf(stdout, " ");
+ for (i = 0; i < n; i++) {
+ (void) fprintf(stdout, "%c",
+ isprint(src[i]) ? src[i] : '.');
+ }
+ }
+ (void) fprintf(stdout, "\n");
+ nbytes -= n;
+ src += n;
+ }
+}