Unleashed v1.4
[unleashed.git] / usr / src / cmd / luxadm / lux_util.c
blob6536ee3634b750e33e6400f3fad99881bd4b7676
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
22 * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
27 #include <stdio.h>
28 #include <unistd.h>
29 #include <stdlib.h>
30 #include <sys/param.h>
31 #include <sys/types.h>
32 #include <fcntl.h>
33 #include <sys/stat.h>
34 #include <string.h>
35 #include <strings.h>
36 #include <ctype.h>
37 #include <errno.h>
38 #include <assert.h>
39 #include <sys/scsi/impl/uscsi.h>
40 #include <sys/scsi/generic/commands.h>
41 #include <sys/scsi/impl/commands.h>
42 #include <sys/scsi/generic/sense.h>
43 #include <sys/scsi/generic/mode.h>
44 #include <sys/scsi/generic/status.h>
45 #include <sys/scsi/generic/inquiry.h>
46 #include <sys/scsi/adapters/scsi_vhci.h>
47 #include <sys/byteorder.h>
48 #include "common.h"
49 #include "errorcodes.h"
51 #define MAX_MODE_SENSE_LEN 0xffff
52 #define MAXLEN 1000
54 #define RETRY_PATHLIST 1
55 #define BYTES_PER_LINE 16
56 #define SCMD_UNKNOWN 0xff
58 #define SCSI_VHCI "/devices/scsi_vhci/"
59 #define SLASH "/"
60 #define DEV_PREFIX "/devices/"
61 #define DEV_PREFIX_STRLEN strlen(DEV_PREFIX)
62 #define DEVICES_DIR "/devices"
64 extern char *dtype[]; /* from adm.c */
65 extern int rand_r(unsigned int *);
67 static int cleanup_dotdot_path(char *path);
68 static int wait_random_time(void);
69 static char *scsi_find_command_name(int cmd);
70 static void scsi_printerr(struct uscsi_cmd *ucmd,
71 struct scsi_extended_sense *rq, int rqlen,
72 char msg_string[], char *err_string);
73 static void string_dump(char *hdr, uchar_t *src, int nbytes, int format,
74 char msg_string[]);
75 static int issue_uscsi_cmd(int file, struct uscsi_cmd *command, int flag);
78 static int
79 wait_random_time(void)
81 time_t timeval;
82 struct tm *tmbuf = NULL;
83 struct timeval tval;
84 unsigned int seed;
85 int random;
86 pid_t pid;
89 * Get the system time and use "system seconds"
90 * as 'seed' to generate a random number. Then,
91 * wait between 1/10 - 1/2 seconds before retry.
92 * Get the current process id and ex-or it with
93 * the seed so that the random number is always
94 * different even in case of multiple processes
95 * generate a random number at the same time.
97 if ((timeval = time(NULL)) == -1) {
98 return (errno);
100 if ((tmbuf = localtime(&timeval)) == NULL) {
101 return (-1); /* L_LOCALTIME_ERROR */
104 pid = getpid();
106 /* get a random number. */
107 seed = (unsigned int) tmbuf->tm_sec;
108 seed ^= pid;
109 random = rand_r(&seed);
112 random = ((random % 500) + 100) * MILLISEC;
113 tval.tv_sec = random / MICROSEC;
114 tval.tv_usec = random % MICROSEC;
116 if (select(0, NULL, NULL, NULL, &tval) == -1) {
117 return (-1); /* L_SELECT_ERROR */
119 return (0);
123 * Special string dump for error message
125 static void
126 string_dump(char *hdr, uchar_t *src, int nbytes, int format, char msg_string[])
128 int i;
129 int n;
130 char *p;
131 char s[256];
133 assert(format == HEX_ONLY || format == HEX_ASCII);
135 (void) strcpy(s, hdr);
136 for (p = s; *p; p++) {
137 *p = ' ';
140 p = hdr;
141 while (nbytes > 0) {
142 (void) sprintf(&msg_string[strlen(msg_string)], "%s", p);
143 p = s;
144 n = MIN(nbytes, BYTES_PER_LINE);
145 for (i = 0; i < n; i++) {
146 (void) sprintf(&msg_string[strlen(msg_string)],
147 "%02x ", src[i] & 0xff);
149 if (format == HEX_ASCII) {
150 for (i = BYTES_PER_LINE-n; i > 0; i--) {
151 (void) sprintf(&msg_string[strlen(msg_string)],
152 " ");
154 (void) sprintf(&msg_string[strlen(msg_string)],
155 " ");
156 for (i = 0; i < n; i++) {
157 (void) sprintf(&msg_string[strlen(msg_string)],
158 "%c", isprint(src[i]) ? src[i] : '.');
161 (void) sprintf(&msg_string[strlen(msg_string)], "\n");
162 nbytes -= n;
163 src += n;
167 * Return a pointer to a string telling us the name of the command.
169 static char *
170 scsi_find_command_name(int cmd)
173 * Names of commands. Must have SCMD_UNKNOWN at end of list.
175 struct scsi_command_name {
176 int command;
177 char *name;
178 } scsi_command_names[29];
180 register struct scsi_command_name *c;
182 scsi_command_names[0].command = SCMD_TEST_UNIT_READY;
183 scsi_command_names[0].name = MSGSTR(61, "Test Unit Ready");
185 scsi_command_names[1].command = SCMD_FORMAT;
186 scsi_command_names[1].name = MSGSTR(110, "Format");
188 scsi_command_names[2].command = SCMD_REASSIGN_BLOCK;
189 scsi_command_names[2].name = MSGSTR(77, "Reassign Block");
191 scsi_command_names[3].command = SCMD_READ;
192 scsi_command_names[3].name = MSGSTR(27, "Read");
194 scsi_command_names[4].command = SCMD_WRITE;
195 scsi_command_names[4].name = MSGSTR(54, "Write");
197 scsi_command_names[5].command = SCMD_READ_G1;
198 scsi_command_names[5].name = MSGSTR(79, "Read(10 Byte)");
200 scsi_command_names[6].command = SCMD_WRITE_G1;
201 scsi_command_names[6].name = MSGSTR(51, "Write(10 Byte)");
203 scsi_command_names[7].command = SCMD_MODE_SELECT;
204 scsi_command_names[7].name = MSGSTR(97, "Mode Select");
206 scsi_command_names[8].command = SCMD_MODE_SENSE;
207 scsi_command_names[8].name = MSGSTR(95, "Mode Sense");
209 scsi_command_names[9].command = SCMD_REASSIGN_BLOCK;
210 scsi_command_names[9].name = MSGSTR(77, "Reassign Block");
212 scsi_command_names[10].command = SCMD_REQUEST_SENSE;
213 scsi_command_names[10].name = MSGSTR(74, "Request Sense");
215 scsi_command_names[11].command = SCMD_READ_DEFECT_LIST;
216 scsi_command_names[11].name = MSGSTR(80, "Read Defect List");
218 scsi_command_names[12].command = SCMD_INQUIRY;
219 scsi_command_names[12].name = MSGSTR(102, "Inquiry");
221 scsi_command_names[13].command = SCMD_WRITE_BUFFER;
222 scsi_command_names[13].name = MSGSTR(53, "Write Buffer");
224 scsi_command_names[14].command = SCMD_READ_BUFFER;
225 scsi_command_names[14].name = MSGSTR(82, "Read Buffer");
227 scsi_command_names[15].command = SCMD_START_STOP;
228 scsi_command_names[15].name = MSGSTR(67, "Start/Stop");
230 scsi_command_names[16].command = SCMD_RESERVE;
231 scsi_command_names[16].name = MSGSTR(72, "Reserve");
233 scsi_command_names[17].command = SCMD_RELEASE;
234 scsi_command_names[17].name = MSGSTR(75, "Release");
236 scsi_command_names[18].command = SCMD_MODE_SENSE_G1;
237 scsi_command_names[18].name = MSGSTR(94, "Mode Sense(10 Byte)");
239 scsi_command_names[19].command = SCMD_MODE_SELECT_G1;
240 scsi_command_names[19].name = MSGSTR(96, "Mode Select(10 Byte)");
242 scsi_command_names[20].command = SCMD_READ_CAPACITY;
243 scsi_command_names[20].name = MSGSTR(81, "Read Capacity");
245 scsi_command_names[21].command = SCMD_SYNC_CACHE;
246 scsi_command_names[21].name = MSGSTR(64, "Synchronize Cache");
248 scsi_command_names[22].command = SCMD_READ_DEFECT_LIST;
249 scsi_command_names[22].name = MSGSTR(80, "Read Defect List");
251 scsi_command_names[23].command = SCMD_GDIAG;
252 scsi_command_names[23].name = MSGSTR(108, "Get Diagnostic");
254 scsi_command_names[24].command = SCMD_SDIAG;
255 scsi_command_names[24].name = MSGSTR(69, "Set Diagnostic");
257 scsi_command_names[25].command = SCMD_PERS_RESERV_IN;
258 scsi_command_names[25].name = MSGSTR(10500, "Persistent Reserve In");
260 scsi_command_names[26].command = SCMD_PERS_RESERV_OUT;
261 scsi_command_names[26].name = MSGSTR(10501, "Persistent Reserve out");
263 scsi_command_names[27].command = SCMD_LOG_SENSE;
264 scsi_command_names[27].name = MSGSTR(10502, "Log Sense");
266 scsi_command_names[28].command = SCMD_UNKNOWN;
267 scsi_command_names[28].name = MSGSTR(25, "Unknown");
270 for (c = scsi_command_names; c->command != SCMD_UNKNOWN; c++)
271 if (c->command == cmd)
272 break;
273 return (c->name);
278 * Function to create error message containing
279 * scsi request sense information
282 static void
283 scsi_printerr(struct uscsi_cmd *ucmd, struct scsi_extended_sense *rq,
284 int rqlen, char msg_string[], char *err_string)
286 int blkno;
288 switch (rq->es_key) {
289 case KEY_NO_SENSE:
290 (void) sprintf(msg_string, MSGSTR(91, "No sense error"));
291 break;
292 case KEY_RECOVERABLE_ERROR:
293 (void) sprintf(msg_string, MSGSTR(76, "Recoverable error"));
294 break;
295 case KEY_NOT_READY:
296 (void) sprintf(msg_string,
297 MSGSTR(10503,
298 "Device Not ready. Error: Random Retry Failed: %s\n."),
299 err_string);
300 break;
301 case KEY_MEDIUM_ERROR:
302 (void) sprintf(msg_string, MSGSTR(99, "Medium error"));
303 break;
304 case KEY_HARDWARE_ERROR:
305 (void) sprintf(msg_string, MSGSTR(106, "Hardware error"));
306 break;
307 case KEY_ILLEGAL_REQUEST:
308 (void) sprintf(msg_string, MSGSTR(103, "Illegal request"));
309 break;
310 case KEY_UNIT_ATTENTION:
311 (void) sprintf(msg_string,
312 MSGSTR(10504,
313 "Unit attention."
314 "Error: Random Retry Failed.\n"));
315 break;
316 case KEY_WRITE_PROTECT:
317 (void) sprintf(msg_string, MSGSTR(52, "Write protect error"));
318 break;
319 case KEY_BLANK_CHECK:
320 (void) sprintf(msg_string, MSGSTR(131, "Blank check error"));
321 break;
322 case KEY_VENDOR_UNIQUE:
323 (void) sprintf(msg_string, MSGSTR(58, "Vendor unique error"));
324 break;
325 case KEY_COPY_ABORTED:
326 (void) sprintf(msg_string, MSGSTR(123, "Copy aborted error"));
327 break;
328 case KEY_ABORTED_COMMAND:
329 (void) sprintf(msg_string,
330 MSGSTR(10505,
331 "Aborted command. Error: Random Retry Failed.\n"));
332 break;
333 case KEY_EQUAL:
334 (void) sprintf(msg_string, MSGSTR(117, "Equal error"));
335 break;
336 case KEY_VOLUME_OVERFLOW:
337 (void) sprintf(msg_string, MSGSTR(57, "Volume overflow"));
338 break;
339 case KEY_MISCOMPARE:
340 (void) sprintf(msg_string, MSGSTR(98, "Miscompare error"));
341 break;
342 case KEY_RESERVED:
343 (void) sprintf(msg_string, MSGSTR(10506,
344 "Reserved value found"));
345 break;
346 default:
347 (void) sprintf(msg_string, MSGSTR(59, "Unknown error"));
348 break;
351 (void) sprintf(&msg_string[strlen(msg_string)],
352 MSGSTR(10507, " during: %s"),
353 scsi_find_command_name(ucmd->uscsi_cdb[0]));
355 if (rq->es_valid) {
356 blkno = (rq->es_info_1 << 24) | (rq->es_info_2 << 16) |
357 (rq->es_info_3 << 8) | rq->es_info_4;
358 (void) sprintf(&msg_string[strlen(msg_string)],
359 MSGSTR(49, ": block %d (0x%x)"), blkno, blkno);
362 (void) sprintf(&msg_string[strlen(msg_string)], "\n");
364 if (rq->es_add_len >= 6) {
365 (void) sprintf(&msg_string[strlen(msg_string)],
366 MSGSTR(132, " Additional sense: 0x%x "
367 "ASC Qualifier: 0x%x\n"),
368 rq->es_add_code, rq->es_qual_code);
370 * rq->es_add_info[ADD_SENSE_CODE],
371 * rq->es_add_info[ADD_SENSE_QUAL_CODE]);
374 if (rq->es_key == KEY_ILLEGAL_REQUEST) {
375 string_dump(MSGSTR(47, " cmd: "), (uchar_t *)ucmd,
376 sizeof (struct uscsi_cmd), HEX_ONLY, msg_string);
377 string_dump(MSGSTR(48, " cdb: "),
378 (uchar_t *)ucmd->uscsi_cdb,
379 ucmd->uscsi_cdblen, HEX_ONLY, msg_string);
381 string_dump(MSGSTR(43, " sense: "),
382 (uchar_t *)rq, 8 + rq->es_add_len, HEX_ONLY, msg_string);
383 rqlen = rqlen; /* not used */
388 * Execute a command and determine the result.
390 static int
391 issue_uscsi_cmd(int file, struct uscsi_cmd *command, int flag)
393 struct scsi_extended_sense *rqbuf;
394 int status, i, retry_cnt = 0, err;
395 char errorMsg[MAXLEN];
398 * Set function flags for driver.
400 * Set Automatic request sense enable
403 command->uscsi_flags = USCSI_RQENABLE;
404 command->uscsi_flags |= flag;
406 /* intialize error message array */
407 errorMsg[0] = '\0';
409 /* print command for debug */
410 if (getenv("_LUX_S_DEBUG") != NULL) {
411 if ((command->uscsi_cdb == NULL) ||
412 (flag & USCSI_RESET) ||
413 (flag & USCSI_RESET_ALL)) {
414 if (flag & USCSI_RESET) {
415 (void) printf(" Issuing a SCSI Reset.\n");
417 if (flag & USCSI_RESET_ALL) {
418 (void) printf(" Issuing a SCSI Reset All.\n");
421 } else {
422 (void) printf(" Issuing the following "
423 "SCSI command: %s\n",
424 scsi_find_command_name(command->uscsi_cdb[0]));
425 (void) printf(" fd=0x%x cdb=", file);
426 for (i = 0; i < (int)command->uscsi_cdblen; i++) {
427 (void) printf("%x ", *(command->uscsi_cdb + i));
429 (void) printf("\n\tlen=0x%x bufaddr=0x%x buflen=0x%x"
430 " flags=0x%x\n",
431 command->uscsi_cdblen,
432 command->uscsi_bufaddr,
433 command->uscsi_buflen, command->uscsi_flags);
435 if ((command->uscsi_buflen > 0) &&
436 ((flag & USCSI_READ) == 0)) {
437 (void) dump_hex_data(" Buffer data: ",
438 (uchar_t *)command->uscsi_bufaddr,
439 MIN(command->uscsi_buflen, 512), HEX_ASCII);
442 (void) fflush(stdout);
447 * Default command timeout in case command left it 0
449 if (command->uscsi_timeout == 0) {
450 command->uscsi_timeout = 60;
452 /* Issue command - finally */
454 retry:
455 status = ioctl(file, USCSICMD, command);
456 if (status == 0 && command->uscsi_status == 0) {
457 if (getenv("_LUX_S_DEBUG") != NULL) {
458 if ((command->uscsi_buflen > 0) &&
459 (flag & USCSI_READ)) {
460 (void) dump_hex_data("\tData read:",
461 (uchar_t *)command->uscsi_bufaddr,
462 MIN(command->uscsi_buflen, 512), HEX_ASCII);
465 return (status);
467 if ((status != 0) && (command->uscsi_status == 0)) {
468 if ((getenv("_LUX_S_DEBUG") != NULL) ||
469 (getenv("_LUX_ER_DEBUG") != NULL)) {
470 (void) printf("Unexpected USCSICMD ioctl error: %s\n",
471 strerror(errno));
473 return (status);
477 * Just a SCSI error, create error message
478 * Retry once for Unit Attention,
479 * Not Ready, and Aborted Command
481 if ((command->uscsi_rqbuf != NULL) &&
482 (((char)command->uscsi_rqlen - (char)command->uscsi_rqresid) > 0)) {
484 rqbuf = (struct scsi_extended_sense *)command->uscsi_rqbuf;
486 switch (rqbuf->es_key) {
487 case KEY_NOT_READY:
488 if (retry_cnt++ < 1) {
489 ER_DPRINTF("Note: Device Not Ready."
490 " Retrying...\n");
492 if ((err = wait_random_time()) == 0) {
493 goto retry;
494 } else {
495 return (err);
498 break;
500 case KEY_UNIT_ATTENTION:
501 if (retry_cnt++ < 1) {
502 ER_DPRINTF(" cmd():"
503 " UNIT_ATTENTION: Retrying...\n");
505 goto retry;
507 break;
509 case KEY_ABORTED_COMMAND:
510 if (retry_cnt++ < 1) {
511 ER_DPRINTF("Note: Command is aborted."
512 " Retrying...\n");
514 goto retry;
516 break;
518 if ((getenv("_LUX_S_DEBUG") != NULL) ||
519 (getenv("_LUX_ER_DEBUG") != NULL)) {
520 scsi_printerr(command,
521 (struct scsi_extended_sense *)command->uscsi_rqbuf,
522 (command->uscsi_rqlen - command->uscsi_rqresid),
523 errorMsg, strerror(errno));
526 } else {
529 * Retry 5 times in case of BUSY, and only
530 * once for Reservation-conflict, Command
531 * Termination and Queue Full. Wait for
532 * random amount of time (between 1/10 - 1/2 secs.)
533 * between each retry. This random wait is to avoid
534 * the multiple threads being executed at the same time
535 * and also the constraint in Photon IB, where the
536 * command queue has a depth of one command.
538 switch ((uchar_t)command->uscsi_status & STATUS_MASK) {
539 case STATUS_BUSY:
540 if (retry_cnt++ < 5) {
541 if ((err = wait_random_time()) == 0) {
542 R_DPRINTF(" cmd(): No. of retries %d."
543 " STATUS_BUSY: Retrying...\n",
544 retry_cnt);
545 goto retry;
547 } else {
548 return (err);
551 break;
553 case STATUS_RESERVATION_CONFLICT:
554 if (retry_cnt++ < 1) {
555 if ((err = wait_random_time()) == 0) {
556 R_DPRINTF(" cmd():"
557 " RESERVATION_CONFLICT:"
558 " Retrying...\n");
559 goto retry;
561 } else {
562 return (err);
565 break;
567 case STATUS_TERMINATED:
568 if (retry_cnt++ < 1) {
569 R_DPRINTF("Note: Command Terminated."
570 " Retrying...\n");
572 if ((err = wait_random_time()) == 0) {
573 goto retry;
574 } else {
575 return (err);
578 break;
580 case STATUS_QFULL:
581 if (retry_cnt++ < 1) {
582 R_DPRINTF("Note: Command Queue is full."
583 " Retrying...\n");
585 if ((err = wait_random_time()) == 0) {
586 goto retry;
587 } else {
588 return (err);
591 break;
595 if (((getenv("_LUX_S_DEBUG") != NULL) ||
596 (getenv("_LUX_ER_DEBUG") != NULL)) &&
597 (errorMsg[0] != '\0')) {
598 (void) fprintf(stdout, " %s\n", errorMsg);
600 return (L_SCSI_ERROR | command->uscsi_status);
604 * MODE SENSE USCSI command
607 * pc = page control field
608 * page_code = Pages to return
611 scsi_mode_sense_cmd(int fd,
612 uchar_t *buf_ptr,
613 int buf_len,
614 uchar_t pc,
615 uchar_t page_code)
617 struct uscsi_cmd ucmd;
618 /* 10 byte Mode Select cmd */
619 union scsi_cdb cdb = {SCMD_MODE_SENSE_G1, 0, 0, 0, 0, 0, 0, 0, 0, 0};
620 struct scsi_extended_sense sense;
621 int status;
622 static int uscsi_count;
624 if ((fd < 0) || (buf_ptr == NULL) || (buf_len < 0)) {
625 return (-1); /* L_INVALID_ARG */
628 (void) memset(buf_ptr, 0, buf_len);
629 (void) memset((char *)&ucmd, 0, sizeof (ucmd));
630 /* Just for me - a sanity check */
631 if ((page_code > MODEPAGE_ALLPAGES) || (pc > 3) ||
632 (buf_len > MAX_MODE_SENSE_LEN)) {
633 return (-1); /* L_ILLEGAL_MODE_SENSE_PAGE */
635 cdb.g1_addr3 = (pc << 6) + page_code;
636 cdb.g1_count1 = buf_len>>8;
637 cdb.g1_count0 = buf_len & 0xff;
638 ucmd.uscsi_cdb = (caddr_t)&cdb;
639 ucmd.uscsi_cdblen = CDB_GROUP1;
640 ucmd.uscsi_bufaddr = (caddr_t)buf_ptr;
641 ucmd.uscsi_buflen = buf_len;
642 ucmd.uscsi_rqbuf = (caddr_t)&sense;
643 ucmd.uscsi_rqlen = sizeof (struct scsi_extended_sense);
644 ucmd.uscsi_timeout = 120;
646 status = issue_uscsi_cmd(fd, &ucmd, USCSI_READ);
647 /* Bytes actually transfered */
648 if (status == 0) {
649 uscsi_count = buf_len - ucmd.uscsi_resid;
650 S_DPRINTF(" Number of bytes read on "
651 "Mode Sense 0x%x\n", uscsi_count);
652 if (getenv("_LUX_D_DEBUG") != NULL) {
653 (void) dump_hex_data(" Mode Sense data: ", buf_ptr,
654 uscsi_count, HEX_ASCII);
657 return (status);
661 scsi_release(char *path)
663 struct uscsi_cmd ucmd;
664 union scsi_cdb cdb = {SCMD_RELEASE, 0, 0, 0, 0, 0};
665 struct scsi_extended_sense sense;
666 int fd, status;
668 P_DPRINTF(" scsi_release: Release: Path %s\n", path);
669 if ((fd = open(path, O_NDELAY | O_RDONLY)) == -1)
670 return (1);
672 (void) memset((char *)&ucmd, 0, sizeof (ucmd));
674 ucmd.uscsi_cdb = (caddr_t)&cdb;
675 ucmd.uscsi_cdblen = CDB_GROUP0;
676 ucmd.uscsi_bufaddr = NULL;
677 ucmd.uscsi_buflen = 0;
678 ucmd.uscsi_rqbuf = (caddr_t)&sense;
679 ucmd.uscsi_rqlen = sizeof (struct scsi_extended_sense);
680 ucmd.uscsi_timeout = 60;
681 status = (issue_uscsi_cmd(fd, &ucmd, 0));
683 (void) close(fd);
684 return (status);
688 scsi_reserve(char *path)
690 struct uscsi_cmd ucmd;
691 union scsi_cdb cdb = {SCMD_RESERVE, 0, 0, 0, 0, 0};
692 struct scsi_extended_sense sense;
693 int fd, status;
695 P_DPRINTF(" scsi_reserve: Reserve: Path %s\n", path);
696 if ((fd = open(path, O_NDELAY | O_RDONLY)) == -1)
697 return (1);
699 (void) memset((char *)&ucmd, 0, sizeof (ucmd));
701 ucmd.uscsi_cdb = (caddr_t)&cdb;
702 ucmd.uscsi_cdblen = CDB_GROUP0;
703 ucmd.uscsi_bufaddr = NULL;
704 ucmd.uscsi_buflen = 0;
705 ucmd.uscsi_rqbuf = (caddr_t)&sense;
706 ucmd.uscsi_rqlen = sizeof (struct scsi_extended_sense);
707 ucmd.uscsi_timeout = 60;
708 status = (issue_uscsi_cmd(fd, &ucmd, 0));
710 (void) close(fd);
711 return (status);
715 * Print out fabric dev dtype
717 void
718 print_fabric_dtype_prop(uchar_t *hba_port_wwn, uchar_t *port_wwn,
719 uchar_t dtype_prop)
721 if ((dtype_prop & DTYPE_MASK) < 0x10) {
722 (void) fprintf(stdout, " 0x%-2x (%s)\n",
723 (dtype_prop & DTYPE_MASK),
724 dtype[(dtype_prop & DTYPE_MASK)]);
725 } else if ((dtype_prop & DTYPE_MASK) < 0x1f) {
726 (void) fprintf(stdout,
727 MSGSTR(2096, " 0x%-2x (Reserved)\n"),
728 (dtype_prop & DTYPE_MASK));
729 } else {
730 /* Check to see if this is the HBA */
731 if (wwnConversion(hba_port_wwn) != wwnConversion(port_wwn)) {
732 (void) fprintf(stdout, MSGSTR(2097,
733 " 0x%-2x (Unknown Type)\n"),
734 (dtype_prop & DTYPE_MASK));
735 } else {
736 /* MATCH */
737 (void) fprintf(stdout, MSGSTR(2241,
738 " 0x%-2x (Unknown Type,Host Bus Adapter)\n"),
739 (dtype_prop & DTYPE_MASK));
745 void
746 print_inq_data(char *arg_path, char *path, L_inquiry inq, uchar_t *serial,
747 size_t serial_len)
749 char **p;
750 uchar_t *v_parm;
751 int scsi_3, length;
752 char byte_number[MAXNAMELEN];
753 static char *scsi_inquiry_labels_2[21];
754 static char *scsi_inquiry_labels_3[22];
755 #define MAX_ANSI_VERSION 6
756 static char *ansi_version[MAX_ANSI_VERSION];
758 * Intialize scsi_inquiry_labels_2 with i18n strings
760 scsi_inquiry_labels_2[0] = MSGSTR(138, "Vendor: ");
761 scsi_inquiry_labels_2[1] = MSGSTR(149, "Product: ");
762 scsi_inquiry_labels_2[2] = MSGSTR(139, "Revision: ");
763 scsi_inquiry_labels_2[3] = MSGSTR(143, "Firmware Revision ");
764 scsi_inquiry_labels_2[4] = MSGSTR(144, "Serial Number ");
765 scsi_inquiry_labels_2[5] = MSGSTR(140, "Device type: ");
766 scsi_inquiry_labels_2[6] = MSGSTR(145, "Removable media: ");
767 scsi_inquiry_labels_2[7] = MSGSTR(146, "ISO version: ");
768 scsi_inquiry_labels_2[8] = MSGSTR(147, "ECMA version: ");
769 scsi_inquiry_labels_2[9] = MSGSTR(148, "ANSI version: ");
770 scsi_inquiry_labels_2[10] =
771 MSGSTR(2168, "Async event notification: ");
772 scsi_inquiry_labels_2[11] =
773 MSGSTR(2169, "Terminate i/o process msg: ");
774 scsi_inquiry_labels_2[12] = MSGSTR(150, "Response data format: ");
775 scsi_inquiry_labels_2[13] = MSGSTR(151, "Additional length: ");
776 scsi_inquiry_labels_2[14] = MSGSTR(152, "Relative addressing: ");
777 scsi_inquiry_labels_2[15] =
778 MSGSTR(2170, "32 bit transfers: ");
779 scsi_inquiry_labels_2[16] =
780 MSGSTR(2171, "16 bit transfers: ");
781 scsi_inquiry_labels_2[17] =
782 MSGSTR(2172, "Synchronous transfers: ");
783 scsi_inquiry_labels_2[18] = MSGSTR(153, "Linked commands: ");
784 scsi_inquiry_labels_2[19] = MSGSTR(154, "Command queueing: ");
785 scsi_inquiry_labels_2[20] =
786 MSGSTR(2173, "Soft reset option: ");
789 * Intialize scsi_inquiry_labels_3 with i18n strings
791 scsi_inquiry_labels_3[0] = MSGSTR(138, "Vendor: ");
792 scsi_inquiry_labels_3[1] = MSGSTR(149, "Product: ");
793 scsi_inquiry_labels_3[2] = MSGSTR(139, "Revision: ");
794 scsi_inquiry_labels_3[3] = MSGSTR(143, "Firmware Revision ");
795 scsi_inquiry_labels_3[4] = MSGSTR(144, "Serial Number ");
796 scsi_inquiry_labels_3[5] = MSGSTR(140, "Device type: ");
797 scsi_inquiry_labels_3[6] = MSGSTR(145, "Removable media: ");
798 scsi_inquiry_labels_3[7] = MSGSTR(2174, "Medium Changer Element: ");
799 scsi_inquiry_labels_3[8] = MSGSTR(146, "ISO version: ");
800 scsi_inquiry_labels_3[9] = MSGSTR(147, "ECMA version: ");
801 scsi_inquiry_labels_3[10] = MSGSTR(148, "ANSI version: ");
802 scsi_inquiry_labels_3[11] =
803 MSGSTR(2175, "Async event reporting: ");
804 scsi_inquiry_labels_3[12] =
805 MSGSTR(2176, "Terminate task: ");
806 scsi_inquiry_labels_3[13] =
807 MSGSTR(2177, "Normal ACA Supported: ");
808 scsi_inquiry_labels_3[14] = MSGSTR(150, "Response data format: ");
809 scsi_inquiry_labels_3[15] = MSGSTR(151, "Additional length: ");
810 scsi_inquiry_labels_3[16] =
811 MSGSTR(2178, "Cmd received on port: ");
812 scsi_inquiry_labels_3[17] =
813 MSGSTR(2179, "SIP Bits: ");
814 scsi_inquiry_labels_3[18] = MSGSTR(152, "Relative addressing: ");
815 scsi_inquiry_labels_3[19] = MSGSTR(153, "Linked commands: ");
816 scsi_inquiry_labels_3[20] =
817 MSGSTR(2180, "Transfer Disable: ");
818 scsi_inquiry_labels_3[21] = MSGSTR(154, "Command queueing: ");
821 * Intialize scsi_inquiry_labels_3 with i18n strings
823 ansi_version[0] = MSGSTR(2181,
824 " (Device might or might not comply to an ANSI version)");
825 ansi_version[1] = MSGSTR(2182,
826 " (This code is reserved for historical uses)");
827 ansi_version[2] = MSGSTR(2183,
828 " (Device complies to ANSI X3.131-1994 (SCSI-2))");
829 ansi_version[3] = MSGSTR(2184,
830 " (Device complies to ANSI INCITS 301-1997 (SPC))");
831 ansi_version[4] = MSGSTR(2226,
832 " (Device complies to ANSI INCITS 351-2001 (SPC-2))");
833 ansi_version[5] = MSGSTR(2227,
834 " (Device complies to ANSI INCITS 408-2005 (SPC-3))");
836 /* print inquiry information */
838 (void) fprintf(stdout, MSGSTR(2185, "\nINQUIRY:\n"));
840 * arg_path is the path sent to luxadm by the user. if arg_path
841 * is a /devices path, then we do not need to print out physical
842 * path info
844 if (strcmp(arg_path, path) != 0 &&
845 strstr(arg_path, "/devices/") == NULL) {
846 (void) fprintf(stdout, " ");
847 (void) fprintf(stdout,
848 MSGSTR(5, "Physical Path:"));
849 (void) fprintf(stdout, "\n %s\n", path);
851 if (inq.inq_ansi < 3) {
852 p = scsi_inquiry_labels_2;
853 scsi_3 = 0;
854 } else {
855 p = scsi_inquiry_labels_3;
856 scsi_3 = 1;
858 if (inq.inq_len < 11) {
859 p += 1;
860 } else {
861 /* */
862 (void) fprintf(stdout, "%s", *p++);
863 print_chars(inq.inq_vid, sizeof (inq.inq_vid), 0);
864 (void) fprintf(stdout, "\n");
866 if (inq.inq_len < 27) {
867 p += 1;
868 } else {
869 (void) fprintf(stdout, "%s", *p++);
870 print_chars(inq.inq_pid, sizeof (inq.inq_pid), 0);
871 (void) fprintf(stdout, "\n");
873 if (inq.inq_len < 31) {
874 p += 1;
875 } else {
876 (void) fprintf(stdout, "%s", *p++);
877 print_chars(inq.inq_revision, sizeof (inq.inq_revision), 0);
878 (void) fprintf(stdout, "\n");
880 if (inq.inq_len < 39) {
881 p += 2;
882 } else {
884 * If Pluto then print
885 * firmware rev & serial #.
887 if (strstr((char *)inq.inq_pid, "SSA") != 0) {
888 (void) fprintf(stdout, "%s", *p++);
889 print_chars(inq.inq_firmware_rev,
890 sizeof (inq.inq_firmware_rev), 0);
891 (void) fprintf(stdout, "\n");
892 (void) fprintf(stdout, "%s", *p++);
893 print_chars(serial, serial_len, 0);
894 (void) fprintf(stdout, "\n");
895 } else if ((inq.inq_dtype & DTYPE_MASK) != DTYPE_ESI) {
896 p++;
897 (void) fprintf(stdout, "%s", *p++);
898 print_chars(serial, serial_len, 0);
899 (void) fprintf(stdout, "\n");
900 } else {
901 /* if we miss both the above if's */
902 p += 2;
906 (void) fprintf(stdout, "%s0x%x (", *p++, (inq.inq_dtype & DTYPE_MASK));
907 if ((inq.inq_dtype & DTYPE_MASK) < 0x10) {
908 (void) fprintf(stdout, "%s", dtype[inq.inq_dtype & DTYPE_MASK]);
909 } else if ((inq.inq_dtype & DTYPE_MASK) < 0x1f) {
910 (void) fprintf(stdout, MSGSTR(71, "Reserved"));
911 } else {
912 (void) fprintf(stdout, MSGSTR(2186, "Unknown device"));
914 (void) fprintf(stdout, ")\n");
916 (void) fprintf(stdout, "%s", *p++);
917 if (inq.inq_rmb != 0) {
918 (void) fprintf(stdout, MSGSTR(40, "yes"));
919 } else {
920 (void) fprintf(stdout, MSGSTR(45, "no"));
922 (void) fprintf(stdout, "\n");
924 if (scsi_3) {
925 (void) fprintf(stdout, "%s", *p++);
926 if (inq.inq_mchngr != 0) {
927 (void) fprintf(stdout, MSGSTR(40, "yes"));
928 } else {
929 (void) fprintf(stdout, MSGSTR(45, "no"));
931 (void) fprintf(stdout, "\n");
933 (void) fprintf(stdout, "%s%d\n", *p++, inq.inq_iso);
934 (void) fprintf(stdout, "%s%d\n", *p++, inq.inq_ecma);
936 (void) fprintf(stdout, "%s%d", *p++, inq.inq_ansi);
937 if (inq.inq_ansi < MAX_ANSI_VERSION) {
938 (void) fprintf(stdout, "%s", ansi_version[inq.inq_ansi]);
939 } else
940 (void) fprintf(stdout, " (%s)", MSGSTR(71, "Reserved"));
942 (void) fprintf(stdout, "\n");
944 if (inq.inq_aenc) {
945 (void) fprintf(stdout, "%s", *p++);
946 (void) fprintf(stdout, MSGSTR(40, "yes"));
947 (void) fprintf(stdout, "\n");
948 } else {
949 p++;
951 if (scsi_3) {
952 (void) fprintf(stdout, "%s", *p++);
953 if (inq.inq_normaca != 0) {
954 (void) fprintf(stdout, MSGSTR(40, "yes"));
955 } else {
956 (void) fprintf(stdout, MSGSTR(45, "no"));
958 (void) fprintf(stdout, "\n");
960 if (inq.inq_trmiop) {
961 (void) fprintf(stdout, "%s", *p++);
962 (void) fprintf(stdout, MSGSTR(40, "yes"));
963 (void) fprintf(stdout, "\n");
964 } else {
965 p++;
967 (void) fprintf(stdout, "%s%d\n", *p++, inq.inq_rdf);
968 (void) fprintf(stdout, "%s0x%x\n", *p++, inq.inq_len);
969 if (scsi_3) {
970 if (inq.inq_dual_p) {
971 if (inq.inq_port != 0) {
972 (void) fprintf(stdout, MSGSTR(2187,
973 "%sa\n"), *p++);
974 } else {
975 (void) fprintf(stdout, MSGSTR(2188,
976 "%sb\n"), *p++);
978 } else {
979 p++;
982 if (scsi_3) {
983 if (inq.inq_SIP_1 || inq.ui.inq_3.inq_SIP_2 ||
984 inq.ui.inq_3.inq_SIP_3) {
985 (void) fprintf(stdout, "%s%d, %d, %d\n", *p,
986 inq.inq_SIP_1, inq.ui.inq_3.inq_SIP_2,
987 inq.ui.inq_3.inq_SIP_3);
989 p++;
993 if (inq.ui.inq_2.inq_2_reladdr) {
994 (void) fprintf(stdout, "%s", *p);
995 (void) fprintf(stdout, MSGSTR(40, "yes"));
996 (void) fprintf(stdout, "\n");
998 p++;
1000 if (!scsi_3) {
1001 if (inq.ui.inq_2.inq_wbus32) {
1002 (void) fprintf(stdout, "%s", *p);
1003 (void) fprintf(stdout, MSGSTR(40, "yes"));
1004 (void) fprintf(stdout, "\n");
1006 p++;
1008 if (inq.ui.inq_2.inq_wbus16) {
1009 (void) fprintf(stdout, "%s", *p);
1010 (void) fprintf(stdout, MSGSTR(40, "yes"));
1011 (void) fprintf(stdout, "\n");
1013 p++;
1015 if (inq.ui.inq_2.inq_sync) {
1016 (void) fprintf(stdout, "%s", *p);
1017 (void) fprintf(stdout, MSGSTR(40, "yes"));
1018 (void) fprintf(stdout, "\n");
1020 p++;
1023 if (inq.ui.inq_2.inq_linked) {
1024 (void) fprintf(stdout, "%s", *p);
1025 (void) fprintf(stdout, MSGSTR(40, "yes"));
1026 (void) fprintf(stdout, "\n");
1028 p++;
1030 if (scsi_3) {
1031 (void) fprintf(stdout, "%s", *p++);
1032 if (inq.ui.inq_3.inq_trandis != 0) {
1033 (void) fprintf(stdout, MSGSTR(40, "yes"));
1034 } else {
1035 (void) fprintf(stdout, MSGSTR(45, "no"));
1037 (void) fprintf(stdout, "\n");
1040 if (inq.ui.inq_2.inq_cmdque) {
1041 (void) fprintf(stdout, "%s", *p);
1042 (void) fprintf(stdout, MSGSTR(40, "yes"));
1043 (void) fprintf(stdout, "\n");
1045 p++;
1047 if (!scsi_3) {
1048 if (inq.ui.inq_2.inq_sftre) {
1049 (void) fprintf(stdout, "%s", *p);
1050 (void) fprintf(stdout, MSGSTR(40, "yes"));
1051 (void) fprintf(stdout, "\n");
1053 p++;
1058 * Now print the vendor-specific data.
1060 v_parm = inq.inq_ven_specific_1;
1061 if (inq.inq_len >= 32) {
1062 length = inq.inq_len - 31;
1063 if (strstr((char *)inq.inq_pid, "SSA") != 0) {
1064 (void) fprintf(stdout, MSGSTR(2189,
1065 "Number of Ports, Targets: %d,%d\n"),
1066 inq.inq_ssa_ports, inq.inq_ssa_tgts);
1067 v_parm += 20;
1068 length -= 20;
1069 } else if ((strstr((char *)inq.inq_pid, "SUN") != 0) ||
1070 (strncmp((char *)inq.inq_vid, "SUN ",
1071 sizeof (inq.inq_vid)) == 0)) {
1072 v_parm += 16;
1073 length -= 16;
1076 * Do hex Dump of rest of the data.
1078 if (length > 0) {
1079 (void) fprintf(stdout,
1080 MSGSTR(2190,
1081 " VENDOR-SPECIFIC PARAMETERS\n"));
1082 (void) fprintf(stdout,
1083 MSGSTR(2191,
1084 "Byte# Hex Value "
1085 " ASCII\n"));
1086 (void) sprintf(byte_number,
1087 "%d ", inq.inq_len - length + 5);
1088 dump_hex_data(byte_number, v_parm,
1089 MIN(length, inq.inq_res3 - v_parm), HEX_ASCII);
1092 * Skip reserved bytes 56-95.
1094 length -= (inq.inq_box_name - v_parm);
1095 if (length > 0) {
1096 (void) sprintf(byte_number, "%d ",
1097 inq.inq_len - length + 5);
1098 dump_hex_data(byte_number, inq.inq_box_name,
1099 MIN(length, sizeof (inq.inq_box_name) +
1100 sizeof (inq.inq_avu)), HEX_ASCII);
1103 if (getenv("_LUX_D_DEBUG") != NULL) {
1104 dump_hex_data("\nComplete Inquiry: ",
1105 (uchar_t *)&inq,
1106 MIN(inq.inq_len + 5, sizeof (inq)), HEX_ASCII);
1111 * Internal routine to clean up ../'s in paths.
1112 * returns 0 if no "../" are left.
1114 * Wouldn't it be nice if there was a standard system library
1115 * routine to do this...?
1117 static int
1118 cleanup_dotdot_path(char *path)
1120 char holder[MAXPATHLEN];
1121 char *dotdot;
1122 char *previous_slash;
1124 /* Find the first "/../" in the string */
1125 dotdot = strstr(path, "/../");
1126 if (dotdot == NULL) {
1127 return (0);
1132 * If the [0] character is '/' and "../" immediatly
1133 * follows it, then we can strip the ../
1135 * /../../foo/bar == /foo/bar
1138 if (dotdot == path) {
1139 strcpy(holder, &path[3]); /* strip "/.." */
1140 strcpy(path, holder);
1141 return (1);
1145 * Now look for the LAST "/" before the "/../"
1146 * as this is the parent dir we can get rid of.
1147 * We do this by temporarily truncating the string
1148 * at the '/' just before "../" using the dotdot pointer.
1150 *dotdot = '\0';
1151 previous_slash = strrchr(path, '/');
1152 if (previous_slash == NULL) {
1154 * hmm, somethings wrong. path looks something
1155 * like "foo/../bar/" so we can't really deal with it.
1157 return (0);
1160 * Now truncate the path just after the previous '/'
1161 * and slam everything after the "../" back on
1163 *(previous_slash+1) = '\0';
1164 (void) strcat(path, dotdot+4);
1165 return (1); /* We may have more "../"s */
1169 * Follow symbolic links from the logical device name to
1170 * the /devfs physical device name. To be complete, we
1171 * handle the case of multiple links. This function
1172 * either returns NULL (no links, or some other error),
1173 * or the physical device name, alloc'ed on the heap.
1175 * NOTE: If the path is relative, it will be forced into
1176 * an absolute path by pre-pending the pwd to it.
1178 char *
1179 get_slash_devices_from_osDevName(char *osDevName, int flag)
1181 struct stat stbuf;
1182 char source[MAXPATHLEN];
1183 char scratch[MAXPATHLEN];
1184 char pwd[MAXPATHLEN];
1185 char *tmp, *phys_path;
1186 int cnt;
1187 boolean_t is_lstat_failed = B_TRUE;
1189 /* return NULL if path is NULL */
1190 if (osDevName == NULL) {
1191 return (NULL);
1194 strcpy(source, osDevName);
1195 for (;;) {
1198 * First make sure the path is absolute. If not, make it.
1199 * If it's already an absolute path, we have no need
1200 * to determine the cwd, so the program should still
1201 * function within security-by-obscurity directories.
1203 if (source[0] != '/') {
1204 tmp = getcwd(pwd, MAXPATHLEN);
1205 if (tmp == NULL) {
1206 return (NULL);
1209 * Handle special case of "./foo/bar"
1211 if (source[0] == '.' && source[1] == '/') {
1212 strcpy(scratch, source+2);
1213 } else { /* no "./" so just take everything */
1214 strcpy(scratch, source);
1216 strcpy(source, pwd);
1217 (void) strcat(source, "/");
1218 (void) strcat(source, scratch);
1222 * Clean up any "../"s that are in the path
1224 while (cleanup_dotdot_path(source))
1228 * source is now an absolute path to the link we're
1229 * concerned with
1231 if (flag == NOT_IGNORE_DANGLING_LINK) {
1233 * In order not to ingore dangling links, check
1234 * the lstat. If lstat succeeds, return the path
1235 * from readlink.
1236 * Note: osDevName input with /devices path from
1237 * a dangling /dev link doesn't pass lstat so
1238 * NULL is returned.
1240 if (stat(source, &stbuf) == -1) {
1241 if (!is_lstat_failed &&
1242 strstr(source, "/devices")) {
1244 * lstat succeeded previously and source
1245 * contains "/devices" then it is
1246 * dangling node.
1248 phys_path = (char *)calloc(1,
1249 strlen(source) + 1);
1250 if (phys_path != NULL) {
1251 (void) strncpy(phys_path,
1252 source, strlen(source) + 1);
1254 return (phys_path);
1255 } else if (is_lstat_failed) {
1256 /* check lstat result. */
1257 if (lstat(source, &stbuf) == -1) {
1258 return (NULL);
1259 } else {
1260 /* and continue */
1261 is_lstat_failed = B_FALSE;
1263 } else {
1265 * With algorithm that resolves a link
1266 * and then issues readlink(), should
1267 * not be reached here.
1269 return (NULL);
1271 } else {
1272 if (lstat(source, &stbuf) == -1) {
1274 * when stat succeeds it is not
1275 * a dangling node so it is not
1276 * a special case.
1278 return (NULL);
1281 } else if (flag == STANDARD_DEVNAME_HANDLING) {
1283 * See if there's a real file out there. If not,
1284 * we have a dangling link and we ignore it.
1286 if (stat(source, &stbuf) == -1) {
1287 return (NULL);
1289 if (lstat(source, &stbuf) == -1) {
1290 return (NULL);
1292 } else {
1293 /* invalid flag */
1294 return (NULL);
1298 * If the file is not a link, we're done one
1299 * way or the other. If there were links,
1300 * return the full pathname of the resulting
1301 * file.
1303 * Note: All of our temp's are on the stack,
1304 * so we have to copy the final result to the heap.
1306 if (!S_ISLNK(stbuf.st_mode)) {
1307 phys_path = (char *)calloc(1, strlen(source) + 1);
1308 if (phys_path != NULL) {
1309 (void) strncpy(phys_path, source,
1310 strlen(source) + 1);
1312 return (phys_path);
1314 cnt = readlink(source, scratch, sizeof (scratch));
1315 if (cnt < 0) {
1316 return (NULL);
1319 * scratch is on the heap, and for some reason readlink
1320 * doesn't always terminate things properly so we have
1321 * to make certain we're properly terminated
1323 scratch[cnt] = '\0';
1326 * Now check to see if the link is relative. If so,
1327 * then we have to append it to the directory
1328 * which the source was in. (This is non trivial)
1330 if (scratch[0] != '/') {
1331 tmp = strrchr(source, '/');
1332 if (tmp == NULL) { /* Whoa! Something's hosed! */
1333 O_DPRINTF("Internal error... corrupt path.\n");
1334 return (NULL);
1336 /* Now strip off just the directory path */
1337 *(tmp+1) = '\0'; /* Keeping the last '/' */
1338 /* and append the new link */
1339 (void) strcat(source, scratch);
1341 * Note: At this point, source should have "../"s
1342 * but we'll clean it up in the next pass through
1343 * the loop.
1345 } else {
1346 /* It's an absolute link so no worries */
1347 strcpy(source, scratch);
1350 /* Never reach here */
1354 * Input - Space for client_path, phci_path and paddr fields of ioc structure
1355 * need to be allocated by the caller of this routine.
1358 get_scsi_vhci_pathinfo(char *dev_path, sv_iocdata_t *ioc, int *path_count)
1360 char *physical_path, *physical_path_s;
1361 int retval;
1362 int fd;
1363 int initial_path_count;
1364 int current_path_count;
1365 int i;
1366 char *delimiter;
1367 int malloc_error = 0;
1368 int prop_buf_size;
1369 int pathlist_retry_count = 0;
1371 if (strncmp(dev_path, SCSI_VHCI, strlen(SCSI_VHCI)) != 0) {
1372 if ((physical_path = get_slash_devices_from_osDevName(
1373 dev_path, STANDARD_DEVNAME_HANDLING)) == NULL) {
1374 return (L_INVALID_PATH);
1376 if (strncmp(physical_path, SCSI_VHCI,
1377 strlen(SCSI_VHCI)) != 0) {
1378 free(physical_path);
1379 return (L_INVALID_PATH);
1381 } else {
1382 if ((physical_path = calloc(1, MAXPATHLEN)) == NULL) {
1383 return (L_MALLOC_FAILED);
1385 (void) strcpy(physical_path, dev_path);
1387 physical_path_s = physical_path;
1389 /* move beyond "/devices" prefix */
1390 physical_path += DEV_PREFIX_STRLEN-1;
1391 /* remove :c,raw suffix */
1392 delimiter = strrchr(physical_path, ':');
1393 /* if we didn't find the ':' fine, else truncate */
1394 if (delimiter != NULL) {
1395 *delimiter = 0;
1399 * We'll call ioctl SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO
1400 * at least twice. The first time will get the path count
1401 * and the size of the ioctl propoerty buffer. The second
1402 * time will get the path_info for each path.
1404 * It's possible that additional paths are added while this
1405 * code is running. If the path count increases between the
1406 * 2 ioctl's above, then we'll retry (and assume all is well).
1408 (void) strcpy(ioc->client, physical_path);
1409 ioc->buf_elem = 1;
1410 ioc->ret_elem = (uint_t *)&(initial_path_count);
1411 ioc->ret_buf = NULL;
1413 /* free physical path */
1414 free(physical_path_s);
1416 /* 0 buf_size asks driver to return actual size needed */
1417 /* open the ioctl file descriptor */
1418 if ((fd = open("/devices/scsi_vhci:devctl", O_RDWR)) < 0) {
1419 return (L_OPEN_PATH_FAIL);
1422 retval = ioctl(fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, ioc);
1423 if (retval != 0) {
1424 close(fd);
1425 return (L_SCSI_VHCI_ERROR);
1427 prop_buf_size = SV_PROP_MAX_BUF_SIZE;
1430 while (pathlist_retry_count <= RETRY_PATHLIST) {
1431 ioc->buf_elem = initial_path_count;
1432 /* Make driver put actual # paths in variable */
1433 ioc->ret_elem = (uint_t *)&(current_path_count);
1436 * Allocate space for array of path_info structures.
1437 * Allocate enough space for # paths from get_pathcount
1439 ioc->ret_buf = (sv_path_info_t *)
1440 calloc(initial_path_count, sizeof (sv_path_info_t));
1441 if (ioc->ret_buf == NULL) {
1442 close(fd);
1443 return (L_MALLOC_FAILED);
1447 * Allocate space for path properties returned by driver
1449 malloc_error = 0;
1450 for (i = 0; i < initial_path_count; i++) {
1451 ioc->ret_buf[i].ret_prop.buf_size = prop_buf_size;
1452 if ((ioc->ret_buf[i].ret_prop.buf =
1453 (caddr_t)malloc(prop_buf_size)) == NULL) {
1454 malloc_error = 1;
1455 break;
1457 if ((ioc->ret_buf[i].ret_prop.ret_buf_size =
1458 (uint_t *)malloc(sizeof (uint_t))) == NULL) {
1459 malloc_error = 1;
1460 break;
1463 if (malloc_error == 1) {
1464 for (i = 0; i < initial_path_count; i++) {
1465 free(ioc->ret_buf[i].ret_prop.buf);
1466 free(ioc->ret_buf[i].ret_prop.ret_buf_size);
1468 free(ioc->ret_buf);
1469 close(fd);
1470 return (L_MALLOC_FAILED);
1473 retval = ioctl(fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, ioc);
1474 if (retval != 0) {
1475 for (i = 0; i < initial_path_count; i++) {
1476 free(ioc->ret_buf[i].ret_prop.buf);
1477 free(ioc->ret_buf[i].ret_prop.ret_buf_size);
1479 free(ioc->ret_buf);
1480 close(fd);
1481 return (L_SCSI_VHCI_ERROR);
1483 if (initial_path_count < current_path_count) {
1484 /* then a new path was added */
1485 pathlist_retry_count++;
1486 initial_path_count = current_path_count;
1487 } else {
1488 break;
1491 /* we are done with ioctl's, lose the fd */
1492 close(fd);
1495 * Compare the length num elements from the ioctl response
1496 * and the caller's request - use smaller value.
1498 * pathlist_p->path_count now has count returned from ioctl.
1499 * ioc.buf_elem has the value the caller provided.
1501 if (initial_path_count < current_path_count) {
1502 /* More paths exist than we allocated space for */
1503 *path_count = initial_path_count;
1504 } else {
1505 *path_count = current_path_count;
1508 return (0);
1512 get_mode_page(char *path, uchar_t **pg_buf)
1514 struct mode_header_g1 *mode_header_ptr;
1515 int status, size, fd;
1517 /* open controller */
1518 if ((fd = open(path, O_NDELAY | O_RDWR)) == -1)
1519 return (-1); /* L_OPEN_PATH_FAIL */
1522 * Read the first part of the page to get the page size
1524 size = 20;
1525 if ((*pg_buf = (uchar_t *)calloc(1, size)) == NULL) {
1526 (void) close(fd);
1527 return (L_MALLOC_FAILED);
1529 /* read page */
1530 if (status = scsi_mode_sense_cmd(fd, *pg_buf, size,
1531 0, MODEPAGE_ALLPAGES)) {
1532 (void) close(fd);
1533 (void) free(*pg_buf);
1534 return (status);
1536 /* Now get the size for all pages */
1537 mode_header_ptr = (struct mode_header_g1 *)(void *)*pg_buf;
1538 size = ntohs(mode_header_ptr->length) +
1539 sizeof (mode_header_ptr->length);
1540 (void) free(*pg_buf);
1541 if ((*pg_buf = (uchar_t *)calloc(1, size)) == NULL) {
1542 (void) close(fd);
1543 return (L_MALLOC_FAILED);
1545 /* read all pages */
1546 if (status = scsi_mode_sense_cmd(fd, *pg_buf, size,
1547 0, MODEPAGE_ALLPAGES)) {
1548 (void) close(fd);
1549 (void) free(*pg_buf);
1550 return (status);
1552 (void) close(fd);
1553 return (0);
1557 * Dump a structure in hexadecimal.
1559 void
1560 dump_hex_data(char *hdr, uchar_t *src, int nbytes, int format)
1562 int i;
1563 int n;
1564 char *p;
1565 char s[256];
1567 assert(format == HEX_ONLY || format == HEX_ASCII);
1569 (void) strcpy(s, hdr);
1570 for (p = s; *p; p++) {
1571 *p = ' ';
1574 p = hdr;
1575 while (nbytes > 0) {
1576 (void) fprintf(stdout, "%s", p);
1577 p = s;
1578 n = MIN(nbytes, BYTES_PER_LINE);
1579 for (i = 0; i < n; i++) {
1580 (void) fprintf(stdout, "%02x ", src[i] & 0xff);
1582 if (format == HEX_ASCII) {
1583 for (i = BYTES_PER_LINE-n; i > 0; i--) {
1584 (void) fprintf(stdout, " ");
1586 (void) fprintf(stdout, " ");
1587 for (i = 0; i < n; i++) {
1588 (void) fprintf(stdout, "%c",
1589 isprint(src[i]) ? src[i] : '.');
1592 (void) fprintf(stdout, "\n");
1593 nbytes -= n;
1594 src += n;