1/*
2 * CDDL HEADER START
3 *
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.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25
26
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"
50
51#define	MAX_MODE_SENSE_LEN		0xffff
52#define	MAXLEN		1000
53
54#define	RETRY_PATHLIST	1
55#define	BYTES_PER_LINE	16
56#define	SCMD_UNKNOWN	0xff
57
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"
63
64extern	char	*dtype[]; /* from adm.c */
65extern	int	rand_r(unsigned int *);
66
67static int cleanup_dotdot_path(char *path);
68static int wait_random_time(void);
69static char *scsi_find_command_name(int cmd);
70static void scsi_printerr(struct uscsi_cmd *ucmd,
71	    struct scsi_extended_sense *rq, int rqlen,
72	    char msg_string[], char *err_string);
73static void string_dump(char *hdr, uchar_t *src, int nbytes, int format,
74	    char msg_string[]);
75static int issue_uscsi_cmd(int file, struct uscsi_cmd *command, int flag);
76
77
78static int
79wait_random_time(void)
80{
81	time_t		timeval;
82	struct tm	*tmbuf = NULL;
83	struct timeval	tval;
84	unsigned int	seed;
85	int		random;
86	pid_t		pid;
87
88	/*
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.
96	 */
97	if ((timeval = time(NULL)) == -1) {
98		return (errno);
99	}
100	if ((tmbuf = localtime(&timeval)) == NULL) {
101		return (-1); /* L_LOCALTIME_ERROR */
102	}
103
104	pid = getpid();
105
106	/* get a random number. */
107	seed = (unsigned int) tmbuf->tm_sec;
108	seed ^= pid;
109	random = rand_r(&seed);
110
111
112	random = ((random % 500) + 100) * MILLISEC;
113	tval.tv_sec = random / MICROSEC;
114	tval.tv_usec = random % MICROSEC;
115
116	if (select(0, NULL, NULL, NULL, &tval) == -1) {
117		return (-1); /* L_SELECT_ERROR */
118	}
119	return (0);
120}
121
122/*
123 *		Special string dump for error message
124 */
125static	void
126string_dump(char *hdr, uchar_t *src, int nbytes, int format, char msg_string[])
127{
128	int i;
129	int n;
130	char	*p;
131	char	s[256];
132
133	assert(format == HEX_ONLY || format == HEX_ASCII);
134
135	(void) strcpy(s, hdr);
136	for (p = s; *p; p++) {
137		*p = ' ';
138	}
139
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);
148		}
149		if (format == HEX_ASCII) {
150			for (i = BYTES_PER_LINE-n; i > 0; i--) {
151				(void) sprintf(&msg_string[strlen(msg_string)],
152				    "   ");
153			}
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] : '.');
159			}
160		}
161		(void) sprintf(&msg_string[strlen(msg_string)], "\n");
162		nbytes -= n;
163		src += n;
164	}
165}
166/*
167 * Return a pointer to a string telling us the name of the command.
168 */
169static char *
170scsi_find_command_name(int cmd)
171{
172	/*
173	 * Names of commands.  Must have SCMD_UNKNOWN at end of list.
174	 */
175	struct scsi_command_name {
176		int command;
177		char	*name;
178	} scsi_command_names[29];
179
180	register struct scsi_command_name *c;
181
182	scsi_command_names[0].command = SCMD_TEST_UNIT_READY;
183	scsi_command_names[0].name = MSGSTR(61, "Test Unit Ready");
184
185	scsi_command_names[1].command = SCMD_FORMAT;
186	scsi_command_names[1].name = MSGSTR(110, "Format");
187
188	scsi_command_names[2].command = SCMD_REASSIGN_BLOCK;
189	scsi_command_names[2].name = MSGSTR(77, "Reassign Block");
190
191	scsi_command_names[3].command = SCMD_READ;
192	scsi_command_names[3].name = MSGSTR(27, "Read");
193
194	scsi_command_names[4].command = SCMD_WRITE;
195	scsi_command_names[4].name = MSGSTR(54, "Write");
196
197	scsi_command_names[5].command = SCMD_READ_G1;
198	scsi_command_names[5].name = MSGSTR(79, "Read(10 Byte)");
199
200	scsi_command_names[6].command = SCMD_WRITE_G1;
201	scsi_command_names[6].name = MSGSTR(51, "Write(10 Byte)");
202
203	scsi_command_names[7].command = SCMD_MODE_SELECT;
204	scsi_command_names[7].name = MSGSTR(97, "Mode Select");
205
206	scsi_command_names[8].command = SCMD_MODE_SENSE;
207	scsi_command_names[8].name = MSGSTR(95, "Mode Sense");
208
209	scsi_command_names[9].command = SCMD_REASSIGN_BLOCK;
210	scsi_command_names[9].name = MSGSTR(77, "Reassign Block");
211
212	scsi_command_names[10].command = SCMD_REQUEST_SENSE;
213	scsi_command_names[10].name = MSGSTR(74, "Request Sense");
214
215	scsi_command_names[11].command = SCMD_READ_DEFECT_LIST;
216	scsi_command_names[11].name = MSGSTR(80, "Read Defect List");
217
218	scsi_command_names[12].command = SCMD_INQUIRY;
219	scsi_command_names[12].name = MSGSTR(102, "Inquiry");
220
221	scsi_command_names[13].command = SCMD_WRITE_BUFFER;
222	scsi_command_names[13].name = MSGSTR(53, "Write Buffer");
223
224	scsi_command_names[14].command = SCMD_READ_BUFFER;
225	scsi_command_names[14].name = MSGSTR(82, "Read Buffer");
226
227	scsi_command_names[15].command = SCMD_START_STOP;
228	scsi_command_names[15].name = MSGSTR(67, "Start/Stop");
229
230	scsi_command_names[16].command = SCMD_RESERVE;
231	scsi_command_names[16].name = MSGSTR(72, "Reserve");
232
233	scsi_command_names[17].command = SCMD_RELEASE;
234	scsi_command_names[17].name = MSGSTR(75, "Release");
235
236	scsi_command_names[18].command = SCMD_MODE_SENSE_G1;
237	scsi_command_names[18].name = MSGSTR(94, "Mode Sense(10 Byte)");
238
239	scsi_command_names[19].command = SCMD_MODE_SELECT_G1;
240	scsi_command_names[19].name = MSGSTR(96, "Mode Select(10 Byte)");
241
242	scsi_command_names[20].command = SCMD_READ_CAPACITY;
243	scsi_command_names[20].name = MSGSTR(81, "Read Capacity");
244
245	scsi_command_names[21].command = SCMD_SYNC_CACHE;
246	scsi_command_names[21].name = MSGSTR(64, "Synchronize Cache");
247
248	scsi_command_names[22].command = SCMD_READ_DEFECT_LIST;
249	scsi_command_names[22].name = MSGSTR(80, "Read Defect List");
250
251	scsi_command_names[23].command = SCMD_GDIAG;
252	scsi_command_names[23].name = MSGSTR(108, "Get Diagnostic");
253
254	scsi_command_names[24].command = SCMD_SDIAG;
255	scsi_command_names[24].name = MSGSTR(69, "Set Diagnostic");
256
257	scsi_command_names[25].command = SCMD_PERS_RESERV_IN;
258	scsi_command_names[25].name = MSGSTR(10500, "Persistent Reserve In");
259
260	scsi_command_names[26].command = SCMD_PERS_RESERV_OUT;
261	scsi_command_names[26].name = MSGSTR(10501, "Persistent Reserve out");
262
263	scsi_command_names[27].command = SCMD_LOG_SENSE;
264	scsi_command_names[27].name = MSGSTR(10502, "Log Sense");
265
266	scsi_command_names[28].command = SCMD_UNKNOWN;
267	scsi_command_names[28].name = MSGSTR(25, "Unknown");
268
269
270	for (c = scsi_command_names; c->command != SCMD_UNKNOWN; c++)
271		if (c->command == cmd)
272			break;
273	return (c->name);
274}
275
276
277/*
278 *	Function to create error message containing
279 *	scsi request sense information
280 */
281
282static void
283scsi_printerr(struct uscsi_cmd *ucmd, struct scsi_extended_sense *rq,
284    int rqlen, char msg_string[], char *err_string)
285{
286	int		blkno;
287
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;
349	}
350
351	(void) sprintf(&msg_string[strlen(msg_string)],
352	    MSGSTR(10507, " during: %s"),
353	    scsi_find_command_name(ucmd->uscsi_cdb[0]));
354
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);
360	}
361
362	(void) sprintf(&msg_string[strlen(msg_string)], "\n");
363
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);
369		/*
370		 * rq->es_add_info[ADD_SENSE_CODE],
371		 * rq->es_add_info[ADD_SENSE_QUAL_CODE]);
372		 */
373	}
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);
380	}
381	string_dump(MSGSTR(43, " sense:  "),
382	    (uchar_t *)rq, 8 + rq->es_add_len, HEX_ONLY, msg_string);
383	rqlen = rqlen;	/* not used */
384}
385
386
387/*
388 * Execute a command and determine the result.
389 */
390static int
391issue_uscsi_cmd(int file, struct uscsi_cmd *command, int flag)
392{
393	struct scsi_extended_sense	*rqbuf;
394	int				status, i, retry_cnt = 0, err;
395	char				errorMsg[MAXLEN];
396
397	/*
398	 * Set function flags for driver.
399	 *
400	 * Set Automatic request sense enable
401	 *
402	 */
403	command->uscsi_flags = USCSI_RQENABLE;
404	command->uscsi_flags |= flag;
405
406	/* intialize error message array */
407	errorMsg[0] = '\0';
408
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");
416			}
417			if (flag & USCSI_RESET_ALL) {
418				(void) printf("  Issuing a SCSI Reset All.\n");
419			}
420
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));
428			}
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);
434
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);
440			}
441		}
442		(void) fflush(stdout);
443	}
444
445
446	/*
447	 * Default command timeout in case command left it 0
448	 */
449	if (command->uscsi_timeout == 0) {
450		command->uscsi_timeout = 60;
451	}
452	/*	Issue command - finally */
453
454retry:
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);
463			}
464		}
465		return (status);
466	}
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));
472		}
473		return (status);
474	}
475
476	/*
477	 * Just a SCSI error, create error message
478	 * Retry once for Unit Attention,
479	 * Not Ready, and Aborted Command
480	 */
481	if ((command->uscsi_rqbuf != NULL) &&
482	    (((char)command->uscsi_rqlen - (char)command->uscsi_rqresid) > 0)) {
483
484		rqbuf = (struct scsi_extended_sense *)command->uscsi_rqbuf;
485
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");
491
492				if ((err = wait_random_time()) == 0) {
493					goto retry;
494				} else {
495					return (err);
496				}
497			}
498			break;
499
500		case KEY_UNIT_ATTENTION:
501			if (retry_cnt++ < 1) {
502				ER_DPRINTF("  cmd():"
503				" UNIT_ATTENTION: Retrying...\n");
504
505				goto retry;
506			}
507			break;
508
509		case KEY_ABORTED_COMMAND:
510			if (retry_cnt++ < 1) {
511				ER_DPRINTF("Note: Command is aborted."
512				" Retrying...\n");
513
514				goto retry;
515			}
516			break;
517		}
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));
524		}
525
526	} else {
527
528		/*
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.
537		 */
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;
546
547				} else {
548					return (err);
549				}
550			}
551			break;
552
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;
560
561				} else {
562					return (err);
563				}
564			}
565			break;
566
567		case STATUS_TERMINATED:
568			if (retry_cnt++ < 1) {
569				R_DPRINTF("Note: Command Terminated."
570				    " Retrying...\n");
571
572				if ((err = wait_random_time()) == 0) {
573					goto retry;
574				} else {
575					return (err);
576				}
577			}
578			break;
579
580		case STATUS_QFULL:
581			if (retry_cnt++ < 1) {
582				R_DPRINTF("Note: Command Queue is full."
583				" Retrying...\n");
584
585				if ((err = wait_random_time()) == 0) {
586					goto retry;
587				} else {
588					return (err);
589				}
590			}
591			break;
592		}
593
594	}
595	if (((getenv("_LUX_S_DEBUG") != NULL) ||
596	    (getenv("_LUX_ER_DEBUG") != NULL)) &&
597	    (errorMsg[0] != '\0')) {
598		(void) fprintf(stdout, "  %s\n", errorMsg);
599	}
600	return (L_SCSI_ERROR | command->uscsi_status);
601}
602
603/*
604 *		MODE SENSE USCSI command
605 *
606 *
607 *		pc = page control field
608 *		page_code = Pages to return
609 */
610int
611scsi_mode_sense_cmd(int fd, uchar_t *buf_ptr, int buf_len, uchar_t pc,
612    uchar_t page_code)
613{
614	struct uscsi_cmd	ucmd;
615	/* 10 byte Mode Select cmd */
616	union scsi_cdb	cdb =  {SCMD_MODE_SENSE_G1, 0, 0, 0, 0, 0, 0, 0, 0, 0};
617	struct	scsi_extended_sense	sense;
618	int		status;
619	static	int	uscsi_count;
620
621	if ((fd < 0) || (buf_ptr == NULL) || (buf_len < 0)) {
622		return (-1); /* L_INVALID_ARG */
623	}
624
625	(void) memset(buf_ptr, 0, buf_len);
626	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
627	/* Just for me  - a sanity check */
628	if ((page_code > MODEPAGE_ALLPAGES) || (pc > 3) ||
629	    (buf_len > MAX_MODE_SENSE_LEN)) {
630		return (-1); /* L_ILLEGAL_MODE_SENSE_PAGE */
631	}
632	cdb.g1_addr3 = (pc << 6) + page_code;
633	cdb.g1_count1 = buf_len>>8;
634	cdb.g1_count0 = buf_len & 0xff;
635	ucmd.uscsi_cdb = (caddr_t)&cdb;
636	ucmd.uscsi_cdblen = CDB_GROUP1;
637	ucmd.uscsi_bufaddr = (caddr_t)buf_ptr;
638	ucmd.uscsi_buflen = buf_len;
639	ucmd.uscsi_rqbuf = (caddr_t)&sense;
640	ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
641	ucmd.uscsi_timeout = 120;
642
643	status = issue_uscsi_cmd(fd, &ucmd, USCSI_READ);
644	/* Bytes actually transfered */
645	if (status == 0) {
646		uscsi_count = buf_len - ucmd.uscsi_resid;
647		S_DPRINTF("  Number of bytes read on "
648		"Mode Sense 0x%x\n", uscsi_count);
649		if (getenv("_LUX_D_DEBUG") != NULL) {
650			(void) dump_hex_data("  Mode Sense data: ", buf_ptr,
651			    uscsi_count, HEX_ASCII);
652		}
653	}
654	return (status);
655}
656
657int
658scsi_release(char *path)
659{
660	struct uscsi_cmd	ucmd;
661	union scsi_cdb		cdb = {SCMD_RELEASE, 0, 0, 0, 0, 0};
662	struct	scsi_extended_sense	sense;
663	int	fd, status;
664
665	P_DPRINTF("  scsi_release: Release: Path %s\n", path);
666	if ((fd = open(path, O_NDELAY | O_RDONLY)) == -1)
667		return (1);
668
669	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
670
671	ucmd.uscsi_cdb = (caddr_t)&cdb;
672	ucmd.uscsi_cdblen = CDB_GROUP0;
673	ucmd.uscsi_bufaddr = NULL;
674	ucmd.uscsi_buflen = 0;
675	ucmd.uscsi_rqbuf = (caddr_t)&sense;
676	ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
677	ucmd.uscsi_timeout = 60;
678	status = (issue_uscsi_cmd(fd, &ucmd, 0));
679
680	(void) close(fd);
681	return (status);
682}
683
684int
685scsi_reserve(char *path)
686{
687	struct uscsi_cmd	ucmd;
688	union scsi_cdb	cdb = {SCMD_RESERVE, 0, 0, 0, 0, 0};
689	struct	scsi_extended_sense	sense;
690	int	fd, status;
691
692	P_DPRINTF("  scsi_reserve: Reserve: Path %s\n", path);
693	if ((fd = open(path, O_NDELAY | O_RDONLY)) == -1)
694		return (1);
695
696	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
697
698	ucmd.uscsi_cdb = (caddr_t)&cdb;
699	ucmd.uscsi_cdblen = CDB_GROUP0;
700	ucmd.uscsi_bufaddr = NULL;
701	ucmd.uscsi_buflen = 0;
702	ucmd.uscsi_rqbuf = (caddr_t)&sense;
703	ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
704	ucmd.uscsi_timeout = 60;
705	status = (issue_uscsi_cmd(fd, &ucmd, 0));
706
707	(void) close(fd);
708	return (status);
709}
710
711/*
712 * Print out fabric dev dtype
713 */
714void
715print_fabric_dtype_prop(uchar_t *hba_port_wwn, uchar_t *port_wwn,
716    uchar_t dtype_prop)
717{
718	if ((dtype_prop & DTYPE_MASK) < 0x10) {
719		(void) fprintf(stdout, " 0x%-2x (%s)\n",
720		    (dtype_prop & DTYPE_MASK),
721		    dtype[(dtype_prop & DTYPE_MASK)]);
722	} else if ((dtype_prop & DTYPE_MASK) < 0x1f) {
723		(void) fprintf(stdout,
724		    MSGSTR(2096, " 0x%-2x (Reserved)\n"),
725		    (dtype_prop & DTYPE_MASK));
726	} else {
727		/* Check to see if this is the HBA */
728		if (wwnConversion(hba_port_wwn) != wwnConversion(port_wwn)) {
729			(void) fprintf(stdout, MSGSTR(2097,
730			    " 0x%-2x (Unknown Type)\n"),
731			    (dtype_prop & DTYPE_MASK));
732		} else {
733			/* MATCH */
734			(void) fprintf(stdout, MSGSTR(2241,
735			    " 0x%-2x (Unknown Type,Host Bus Adapter)\n"),
736			    (dtype_prop & DTYPE_MASK));
737		}
738	}
739}
740
741
742void
743print_inq_data(char *arg_path, char *path, L_inquiry inq, uchar_t *serial,
744    size_t serial_len)
745{
746	char	**p;
747	uchar_t	*v_parm;
748	int	scsi_3, length;
749	char	byte_number[MAXNAMELEN];
750	static	char *scsi_inquiry_labels_2[21];
751	static	char *scsi_inquiry_labels_3[22];
752#define	MAX_ANSI_VERSION	6
753	static	char	*ansi_version[MAX_ANSI_VERSION];
754	/*
755	 * Intialize scsi_inquiry_labels_2 with i18n strings
756	 */
757	scsi_inquiry_labels_2[0] = MSGSTR(138, "Vendor:                     ");
758	scsi_inquiry_labels_2[1] = MSGSTR(149, "Product:                    ");
759	scsi_inquiry_labels_2[2] = MSGSTR(139, "Revision:                   ");
760	scsi_inquiry_labels_2[3] = MSGSTR(143, "Firmware Revision           ");
761	scsi_inquiry_labels_2[4] = MSGSTR(144, "Serial Number               ");
762	scsi_inquiry_labels_2[5] = MSGSTR(140, "Device type:                ");
763	scsi_inquiry_labels_2[6] = MSGSTR(145, "Removable media:            ");
764	scsi_inquiry_labels_2[7] = MSGSTR(146, "ISO version:                ");
765	scsi_inquiry_labels_2[8] = MSGSTR(147, "ECMA version:               ");
766	scsi_inquiry_labels_2[9] = MSGSTR(148, "ANSI version:               ");
767	scsi_inquiry_labels_2[10] =
768	    MSGSTR(2168, "Async event notification:   ");
769	scsi_inquiry_labels_2[11] =
770	    MSGSTR(2169, "Terminate i/o process msg:  ");
771	scsi_inquiry_labels_2[12] = MSGSTR(150, "Response data format:       ");
772	scsi_inquiry_labels_2[13] = MSGSTR(151, "Additional length:          ");
773	scsi_inquiry_labels_2[14] = MSGSTR(152, "Relative addressing:        ");
774	scsi_inquiry_labels_2[15] =
775	    MSGSTR(2170, "32 bit transfers:           ");
776	scsi_inquiry_labels_2[16] =
777	    MSGSTR(2171, "16 bit transfers:           ");
778	scsi_inquiry_labels_2[17] =
779	    MSGSTR(2172, "Synchronous transfers:      ");
780	scsi_inquiry_labels_2[18] = MSGSTR(153, "Linked commands:            ");
781	scsi_inquiry_labels_2[19] = MSGSTR(154, "Command queueing:           ");
782	scsi_inquiry_labels_2[20] =
783	    MSGSTR(2173, "Soft reset option:          ");
784
785	/*
786	 * Intialize scsi_inquiry_labels_3 with i18n strings
787	 */
788	scsi_inquiry_labels_3[0] = MSGSTR(138, "Vendor:                     ");
789	scsi_inquiry_labels_3[1] = MSGSTR(149, "Product:                    ");
790	scsi_inquiry_labels_3[2] = MSGSTR(139, "Revision:                   ");
791	scsi_inquiry_labels_3[3] = MSGSTR(143, "Firmware Revision           ");
792	scsi_inquiry_labels_3[4] = MSGSTR(144, "Serial Number               ");
793	scsi_inquiry_labels_3[5] = MSGSTR(140, "Device type:                ");
794	scsi_inquiry_labels_3[6] = MSGSTR(145, "Removable media:            ");
795	scsi_inquiry_labels_3[7] = MSGSTR(2174, "Medium Changer Element:     ");
796	scsi_inquiry_labels_3[8] = MSGSTR(146, "ISO version:                ");
797	scsi_inquiry_labels_3[9] = MSGSTR(147, "ECMA version:               ");
798	scsi_inquiry_labels_3[10] = MSGSTR(148, "ANSI version:               ");
799	scsi_inquiry_labels_3[11] =
800	    MSGSTR(2175, "Async event reporting:      ");
801	scsi_inquiry_labels_3[12] =
802	    MSGSTR(2176, "Terminate task:             ");
803	scsi_inquiry_labels_3[13] =
804	    MSGSTR(2177, "Normal ACA Supported:       ");
805	scsi_inquiry_labels_3[14] = MSGSTR(150, "Response data format:       ");
806	scsi_inquiry_labels_3[15] = MSGSTR(151, "Additional length:          ");
807	scsi_inquiry_labels_3[16] =
808	    MSGSTR(2178, "Cmd received on port:       ");
809	scsi_inquiry_labels_3[17] =
810	    MSGSTR(2179, "SIP Bits:                   ");
811	scsi_inquiry_labels_3[18] = MSGSTR(152, "Relative addressing:        ");
812	scsi_inquiry_labels_3[19] = MSGSTR(153, "Linked commands:            ");
813	scsi_inquiry_labels_3[20] =
814	    MSGSTR(2180, "Transfer Disable:           ");
815	scsi_inquiry_labels_3[21] = MSGSTR(154, "Command queueing:           ");
816
817	/*
818	 * Intialize scsi_inquiry_labels_3 with i18n strings
819	 */
820	ansi_version[0] = MSGSTR(2181,
821	    " (Device might or might not comply to an ANSI version)");
822	ansi_version[1] = MSGSTR(2182,
823	    " (This code is reserved for historical uses)");
824	ansi_version[2] = MSGSTR(2183,
825	    " (Device complies to ANSI X3.131-1994 (SCSI-2))");
826	ansi_version[3] = MSGSTR(2184,
827	    " (Device complies to ANSI INCITS 301-1997 (SPC))");
828	ansi_version[4] = MSGSTR(2226,
829	    " (Device complies to ANSI INCITS 351-2001 (SPC-2))");
830	ansi_version[5] = MSGSTR(2227,
831	    " (Device complies to ANSI INCITS 408-2005 (SPC-3))");
832
833	/* print inquiry information */
834
835	(void) fprintf(stdout, MSGSTR(2185, "\nINQUIRY:\n"));
836		/*
837		 * arg_path is the path sent to luxadm by the user.  if arg_path
838		 * is a /devices path, then we do not need to print out physical
839		 * path info
840		 */
841	if (strcmp(arg_path, path) != 0 &&
842	    strstr(arg_path, "/devices/") == NULL) {
843		(void) fprintf(stdout, "  ");
844		(void) fprintf(stdout,
845		    MSGSTR(5, "Physical Path:"));
846		(void) fprintf(stdout, "\n  %s\n", path);
847	}
848	if (inq.inq_ansi < 3) {
849		p = scsi_inquiry_labels_2;
850		scsi_3 = 0;
851	} else {
852		p = scsi_inquiry_labels_3;
853		scsi_3 = 1;
854	}
855	if (inq.inq_len < 11) {
856		p += 1;
857	} else {
858		/* */
859		(void) fprintf(stdout, "%s", *p++);
860		print_chars(inq.inq_vid, sizeof (inq.inq_vid), 0);
861		(void) fprintf(stdout, "\n");
862	}
863	if (inq.inq_len < 27) {
864		p += 1;
865	} else {
866		(void) fprintf(stdout, "%s", *p++);
867		print_chars(inq.inq_pid, sizeof (inq.inq_pid), 0);
868		(void) fprintf(stdout, "\n");
869	}
870	if (inq.inq_len < 31) {
871		p += 1;
872	} else {
873		(void) fprintf(stdout, "%s", *p++);
874		print_chars(inq.inq_revision, sizeof (inq.inq_revision), 0);
875		(void) fprintf(stdout, "\n");
876	}
877	if (inq.inq_len < 39) {
878		p += 2;
879	} else {
880		/*
881		 * If Pluto then print
882		 * firmware rev & serial #.
883		 */
884		if (strstr((char *)inq.inq_pid, "SSA") != 0) {
885			(void) fprintf(stdout, "%s", *p++);
886			print_chars(inq.inq_firmware_rev,
887			    sizeof (inq.inq_firmware_rev), 0);
888			(void) fprintf(stdout, "\n");
889			(void) fprintf(stdout, "%s", *p++);
890			print_chars(serial, serial_len, 0);
891			(void) fprintf(stdout, "\n");
892		} else if ((inq.inq_dtype & DTYPE_MASK) != DTYPE_ESI) {
893			p++;
894			(void) fprintf(stdout, "%s", *p++);
895			print_chars(serial, serial_len, 0);
896			(void) fprintf(stdout, "\n");
897		} else {
898			/* if we miss both the above if's */
899			p += 2;
900		}
901	}
902
903	(void) fprintf(stdout, "%s0x%x (", *p++, (inq.inq_dtype & DTYPE_MASK));
904	if ((inq.inq_dtype & DTYPE_MASK) < 0x10) {
905		(void) fprintf(stdout, "%s", dtype[inq.inq_dtype & DTYPE_MASK]);
906	} else if ((inq.inq_dtype & DTYPE_MASK) < 0x1f) {
907		(void) fprintf(stdout, MSGSTR(71, "Reserved"));
908	} else {
909		(void) fprintf(stdout, MSGSTR(2186, "Unknown device"));
910	}
911	(void) fprintf(stdout, ")\n");
912
913	(void) fprintf(stdout, "%s", *p++);
914	if (inq.inq_rmb != 0) {
915		(void) fprintf(stdout, MSGSTR(40, "yes"));
916	} else {
917		(void) fprintf(stdout, MSGSTR(45, "no"));
918	}
919	(void) fprintf(stdout, "\n");
920
921	if (scsi_3) {
922		(void) fprintf(stdout, "%s", *p++);
923		if (inq.inq_mchngr != 0) {
924			(void) fprintf(stdout, MSGSTR(40, "yes"));
925		} else {
926			(void) fprintf(stdout, MSGSTR(45, "no"));
927		}
928		(void) fprintf(stdout, "\n");
929	}
930	(void) fprintf(stdout, "%s%d\n", *p++, inq.inq_iso);
931	(void) fprintf(stdout, "%s%d\n", *p++, inq.inq_ecma);
932
933	(void) fprintf(stdout, "%s%d", *p++, inq.inq_ansi);
934	if (inq.inq_ansi < MAX_ANSI_VERSION) {
935		(void) fprintf(stdout, "%s", ansi_version[inq.inq_ansi]);
936	} else
937		(void) fprintf(stdout, " (%s)", MSGSTR(71, "Reserved"));
938
939	(void) fprintf(stdout, "\n");
940
941	if (inq.inq_aenc) {
942		(void) fprintf(stdout, "%s", *p++);
943		(void) fprintf(stdout, MSGSTR(40, "yes"));
944		(void) fprintf(stdout, "\n");
945	} else {
946		p++;
947	}
948	if (scsi_3) {
949		(void) fprintf(stdout, "%s", *p++);
950		if (inq.inq_normaca != 0) {
951			(void) fprintf(stdout, MSGSTR(40, "yes"));
952		} else {
953			(void) fprintf(stdout, MSGSTR(45, "no"));
954		}
955		(void) fprintf(stdout, "\n");
956	}
957	if (inq.inq_trmiop) {
958		(void) fprintf(stdout, "%s", *p++);
959		(void) fprintf(stdout, MSGSTR(40, "yes"));
960		(void) fprintf(stdout, "\n");
961	} else {
962		p++;
963	}
964	(void) fprintf(stdout, "%s%d\n", *p++, inq.inq_rdf);
965	(void) fprintf(stdout, "%s0x%x\n", *p++, inq.inq_len);
966	if (scsi_3) {
967		if (inq.inq_dual_p) {
968			if (inq.inq_port != 0) {
969				(void) fprintf(stdout, MSGSTR(2187,
970				    "%sa\n"), *p++);
971			} else {
972				(void) fprintf(stdout, MSGSTR(2188,
973				    "%sb\n"), *p++);
974			}
975		} else {
976			p++;
977		}
978	}
979	if (scsi_3) {
980		if (inq.inq_SIP_1 || inq.ui.inq_3.inq_SIP_2 ||
981		    inq.ui.inq_3.inq_SIP_3) {
982			(void) fprintf(stdout, "%s%d, %d, %d\n", *p,
983			    inq.inq_SIP_1, inq.ui.inq_3.inq_SIP_2,
984			    inq.ui.inq_3.inq_SIP_3);
985		}
986		p++;
987
988	}
989
990	if (inq.ui.inq_2.inq_2_reladdr) {
991		(void) fprintf(stdout, "%s", *p);
992		(void) fprintf(stdout, MSGSTR(40, "yes"));
993		(void) fprintf(stdout, "\n");
994	}
995	p++;
996
997	if (!scsi_3) {
998		if (inq.ui.inq_2.inq_wbus32) {
999			(void) fprintf(stdout, "%s", *p);
1000			(void) fprintf(stdout, MSGSTR(40, "yes"));
1001			(void) fprintf(stdout, "\n");
1002		}
1003		p++;
1004
1005		if (inq.ui.inq_2.inq_wbus16) {
1006			(void) fprintf(stdout, "%s", *p);
1007			(void) fprintf(stdout, MSGSTR(40, "yes"));
1008			(void) fprintf(stdout, "\n");
1009		}
1010		p++;
1011
1012		if (inq.ui.inq_2.inq_sync) {
1013			(void) fprintf(stdout, "%s", *p);
1014			(void) fprintf(stdout, MSGSTR(40, "yes"));
1015			(void) fprintf(stdout, "\n");
1016		}
1017		p++;
1018
1019	}
1020	if (inq.ui.inq_2.inq_linked) {
1021		(void) fprintf(stdout, "%s", *p);
1022		(void) fprintf(stdout, MSGSTR(40, "yes"));
1023		(void) fprintf(stdout, "\n");
1024	}
1025	p++;
1026
1027	if (scsi_3) {
1028		(void) fprintf(stdout, "%s", *p++);
1029		if (inq.ui.inq_3.inq_trandis != 0) {
1030			(void) fprintf(stdout, MSGSTR(40, "yes"));
1031		} else {
1032			(void) fprintf(stdout, MSGSTR(45, "no"));
1033		}
1034		(void) fprintf(stdout, "\n");
1035	}
1036
1037	if (inq.ui.inq_2.inq_cmdque) {
1038		(void) fprintf(stdout, "%s", *p);
1039		(void) fprintf(stdout, MSGSTR(40, "yes"));
1040		(void) fprintf(stdout, "\n");
1041	}
1042	p++;
1043
1044	if (!scsi_3) {
1045		if (inq.ui.inq_2.inq_sftre) {
1046			(void) fprintf(stdout, "%s", *p);
1047			(void) fprintf(stdout, MSGSTR(40, "yes"));
1048			(void) fprintf(stdout, "\n");
1049		}
1050		p++;
1051
1052	}
1053
1054	/*
1055	 * Now print the vendor-specific data.
1056	 */
1057	v_parm = inq.inq_ven_specific_1;
1058	if (inq.inq_len >= 32) {
1059		length = inq.inq_len - 31;
1060		if (strstr((char *)inq.inq_pid, "SSA") != 0) {
1061			(void) fprintf(stdout, MSGSTR(2189,
1062			    "Number of Ports, Targets:   %d,%d\n"),
1063			    inq.inq_ssa_ports, inq.inq_ssa_tgts);
1064			v_parm += 20;
1065			length -= 20;
1066		} else if ((strstr((char *)inq.inq_pid, "SUN") != 0) ||
1067		    (strncmp((char *)inq.inq_vid, "SUN     ",
1068		    sizeof (inq.inq_vid)) == 0)) {
1069			v_parm += 16;
1070			length -= 16;
1071		}
1072		/*
1073		 * Do hex Dump of rest of the data.
1074		 */
1075		if (length > 0) {
1076			(void) fprintf(stdout,
1077			    MSGSTR(2190,
1078			"              VENDOR-SPECIFIC PARAMETERS\n"));
1079			(void) fprintf(stdout,
1080			    MSGSTR(2191,
1081			    "Byte#                  Hex Value            "
1082			    "                 ASCII\n"));
1083			(void) sprintf(byte_number,
1084			    "%d    ", inq.inq_len - length + 5);
1085			dump_hex_data(byte_number, v_parm,
1086			    MIN(length, inq.inq_res3 - v_parm), HEX_ASCII);
1087		}
1088		/*
1089		 * Skip reserved bytes 56-95.
1090		 */
1091		length -= (inq.inq_box_name - v_parm);
1092		if (length > 0) {
1093			(void) sprintf(byte_number, "%d    ",
1094			    inq.inq_len - length + 5);
1095			dump_hex_data(byte_number, inq.inq_box_name,
1096			    MIN(length, sizeof (inq.inq_box_name) +
1097			    sizeof (inq.inq_avu)), HEX_ASCII);
1098		}
1099	}
1100	if (getenv("_LUX_D_DEBUG") != NULL) {
1101		dump_hex_data("\nComplete Inquiry: ",
1102		    (uchar_t *)&inq,
1103		    MIN(inq.inq_len + 5, sizeof (inq)), HEX_ASCII);
1104	}
1105}
1106
1107/*
1108 * Internal routine to clean up ../'s in paths.
1109 * returns 0 if no "../" are left.
1110 *
1111 * Wouldn't it be nice if there was a standard system library
1112 * routine to do this...?
1113 */
1114static int
1115cleanup_dotdot_path(char *path)
1116{
1117	char holder[MAXPATHLEN];
1118	char *dotdot;
1119	char *previous_slash;
1120
1121	/* Find the first "/../" in the string */
1122	dotdot = strstr(path, "/../");
1123	if (dotdot == NULL) {
1124		return (0);
1125	}
1126
1127
1128	/*
1129	 * If the [0] character is '/' and "../" immediatly
1130	 * follows it, then we can strip the ../
1131	 *
1132	 *	/../../foo/bar == /foo/bar
1133	 *
1134	 */
1135	if (dotdot == path) {
1136		strcpy(holder, &path[3]); /* strip "/.." */
1137		strcpy(path, holder);
1138		return (1);
1139	}
1140
1141	/*
1142	 * Now look for the LAST "/" before the "/../"
1143	 * as this is the parent dir we can get rid of.
1144	 * We do this by temporarily truncating the string
1145	 * at the '/' just before "../" using the dotdot pointer.
1146	 */
1147	*dotdot = '\0';
1148	previous_slash = strrchr(path, '/');
1149	if (previous_slash == NULL) {
1150		/*
1151		 * hmm, somethings wrong.  path looks something
1152		 * like "foo/../bar/" so we can't really deal with it.
1153		 */
1154		return (0);
1155	}
1156	/*
1157	 * Now truncate the path just after the previous '/'
1158	 * and slam everything after the "../" back on
1159	 */
1160	*(previous_slash+1) = '\0';
1161	(void) strcat(path, dotdot+4);
1162	return (1); /* We may have more "../"s */
1163}
1164
1165/*
1166 * Follow symbolic links from the logical device name to
1167 * the /devfs physical device name.  To be complete, we
1168 * handle the case of multiple links.  This function
1169 * either returns NULL (no links, or some other error),
1170 * or the physical device name, alloc'ed on the heap.
1171 *
1172 * NOTE: If the path is relative, it will be forced into
1173 * an absolute path by pre-pending the pwd to it.
1174 */
1175char *
1176get_slash_devices_from_osDevName(char *osDevName, int flag)
1177{
1178	struct stat	stbuf;
1179	char		source[MAXPATHLEN];
1180	char		scratch[MAXPATHLEN];
1181	char		pwd[MAXPATHLEN];
1182	char		*tmp, *phys_path;
1183	int		cnt;
1184	boolean_t	is_lstat_failed = B_TRUE;
1185
1186	/* return NULL if path is NULL */
1187	if (osDevName == NULL) {
1188		return (NULL);
1189	}
1190
1191	strcpy(source, osDevName);
1192	for (;;) {
1193
1194		/*
1195		 * First make sure the path is absolute.  If not, make it.
1196		 * If it's already an absolute path, we have no need
1197		 * to determine the cwd, so the program should still
1198		 * function within security-by-obscurity directories.
1199		 */
1200		if (source[0] != '/') {
1201			tmp = getcwd(pwd, MAXPATHLEN);
1202			if (tmp == NULL) {
1203				return (NULL);
1204			}
1205			/*
1206			 * Handle special case of "./foo/bar"
1207			 */
1208			if (source[0] == '.' && source[1] == '/') {
1209				strcpy(scratch, source+2);
1210			} else { /* no "./" so just take everything */
1211				strcpy(scratch, source);
1212			}
1213			strcpy(source, pwd);
1214			(void) strcat(source, "/");
1215			(void) strcat(source, scratch);
1216		}
1217
1218		/*
1219		 * Clean up any "../"s that are in the path
1220		 */
1221		while (cleanup_dotdot_path(source))
1222			;
1223
1224		/*
1225		 * source is now an absolute path to the link we're
1226		 * concerned with
1227		 */
1228		if (flag == NOT_IGNORE_DANGLING_LINK) {
1229			/*
1230			 * In order not to ingore dangling links, check
1231			 * the lstat. If lstat succeeds, return the path
1232			 * from readlink.
1233			 * Note: osDevName input with /devices path from
1234			 * a dangling /dev link doesn't pass lstat so
1235			 * NULL is returned.
1236			 */
1237			if (stat(source, &stbuf) == -1) {
1238				if (!is_lstat_failed &&
1239				    strstr(source, "/devices")) {
1240					/*
1241					 * lstat succeeded previously and source
1242					 * contains "/devices" then it is
1243					 * dangling node.
1244					 */
1245					phys_path = (char *)calloc(1,
1246					    strlen(source) + 1);
1247					if (phys_path != NULL) {
1248						(void) strncpy(phys_path,
1249						    source, strlen(source) + 1);
1250					}
1251					return (phys_path);
1252				} else if (is_lstat_failed) {
1253					/* check lstat result. */
1254					if (lstat(source, &stbuf) == -1) {
1255						return (NULL);
1256					} else {
1257						/* and continue */
1258						is_lstat_failed = B_FALSE;
1259					}
1260				} else {
1261					/*
1262					 * With algorithm that resolves a link
1263					 * and then issues readlink(), should
1264					 * not be reached here.
1265					 */
1266					return (NULL);
1267				}
1268			} else {
1269				if (lstat(source, &stbuf) == -1) {
1270					/*
1271					 * when stat succeeds it is not
1272					 * a dangling node so it is not
1273					 * a special case.
1274					 */
1275					return (NULL);
1276				}
1277			}
1278		} else if (flag == STANDARD_DEVNAME_HANDLING) {
1279			/*
1280			 * See if there's a real file out there.  If not,
1281			 * we have a dangling link and we ignore it.
1282			 */
1283			if (stat(source, &stbuf) == -1) {
1284				return (NULL);
1285			}
1286			if (lstat(source, &stbuf) == -1) {
1287				return (NULL);
1288			}
1289		} else {
1290			/* invalid flag */
1291			return (NULL);
1292		}
1293
1294		/*
1295		 * If the file is not a link, we're done one
1296		 * way or the other.  If there were links,
1297		 * return the full pathname of the resulting
1298		 * file.
1299		 *
1300		 * Note:  All of our temp's are on the stack,
1301		 * so we have to copy the final result to the heap.
1302		 */
1303		if (!S_ISLNK(stbuf.st_mode)) {
1304			phys_path = (char *)calloc(1, strlen(source) + 1);
1305			if (phys_path != NULL) {
1306				(void) strncpy(phys_path, source,
1307				    strlen(source) + 1);
1308			}
1309			return (phys_path);
1310		}
1311		cnt = readlink(source, scratch, sizeof (scratch));
1312		if (cnt < 0) {
1313			return (NULL);
1314		}
1315		/*
1316		 * scratch is on the heap, and for some reason readlink
1317		 * doesn't always terminate things properly so we have
1318		 * to make certain we're properly terminated
1319		 */
1320		scratch[cnt] = '\0';
1321
1322		/*
1323		 * Now check to see if the link is relative.  If so,
1324		 * then we have to append it to the directory
1325		 * which the source was in. (This is non trivial)
1326		 */
1327		if (scratch[0] != '/') {
1328			tmp = strrchr(source, '/');
1329			if (tmp == NULL) { /* Whoa!  Something's hosed! */
1330				O_DPRINTF("Internal error... corrupt path.\n");
1331				return (NULL);
1332			}
1333			/* Now strip off just the directory path */
1334			*(tmp+1) = '\0'; /* Keeping the last '/' */
1335			/* and append the new link */
1336			(void) strcat(source, scratch);
1337			/*
1338			 * Note:  At this point, source should have "../"s
1339			 * but we'll clean it up in the next pass through
1340			 * the loop.
1341			 */
1342		} else {
1343			/* It's an absolute link so no worries */
1344			strcpy(source, scratch);
1345		}
1346	}
1347	/* Never reach here */
1348}
1349
1350/*
1351 * Input - Space for client_path, phci_path and paddr fields of ioc structure
1352 * need to be allocated by the caller of this routine.
1353 */
1354int
1355get_scsi_vhci_pathinfo(char *dev_path, sv_iocdata_t *ioc, int *path_count)
1356{
1357	char	*physical_path, *physical_path_s;
1358	int	retval;
1359	int	fd;
1360	int	initial_path_count;
1361	int	current_path_count;
1362	int	i;
1363	char	*delimiter;
1364	int	malloc_error = 0;
1365	int	prop_buf_size;
1366	int	pathlist_retry_count = 0;
1367
1368	if (strncmp(dev_path, SCSI_VHCI, strlen(SCSI_VHCI)) != 0) {
1369		if ((physical_path = get_slash_devices_from_osDevName(
1370		    dev_path, STANDARD_DEVNAME_HANDLING)) == NULL) {
1371			return (L_INVALID_PATH);
1372		}
1373		if (strncmp(physical_path, SCSI_VHCI,
1374		    strlen(SCSI_VHCI)) != 0) {
1375			free(physical_path);
1376			return (L_INVALID_PATH);
1377		}
1378	} else {
1379		if ((physical_path = calloc(1, MAXPATHLEN)) == NULL) {
1380			return (L_MALLOC_FAILED);
1381		}
1382		(void) strcpy(physical_path, dev_path);
1383	}
1384	physical_path_s = physical_path;
1385
1386	/* move beyond "/devices" prefix */
1387	physical_path += DEV_PREFIX_STRLEN-1;
1388	/* remove  :c,raw suffix */
1389	delimiter = strrchr(physical_path, ':');
1390	/* if we didn't find the ':' fine, else truncate */
1391	if (delimiter != NULL) {
1392		*delimiter = '\0';
1393	}
1394
1395	/*
1396	 * We'll call ioctl SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO
1397	 * at least twice.  The first time will get the path count
1398	 * and the size of the ioctl propoerty buffer.  The second
1399	 * time will get the path_info for each path.
1400	 *
1401	 * It's possible that additional paths are added while this
1402	 * code is running.  If the path count increases between the
1403	 * 2 ioctl's above, then we'll retry (and assume all is well).
1404	 */
1405	(void) strcpy(ioc->client, physical_path);
1406	ioc->buf_elem = 1;
1407	ioc->ret_elem = (uint_t *)&(initial_path_count);
1408	ioc->ret_buf = NULL;
1409
1410	/* free physical path */
1411	free(physical_path_s);
1412
1413	/* 0 buf_size asks driver to return actual size needed */
1414	/* open the ioctl file descriptor */
1415	if ((fd = open("/devices/scsi_vhci:devctl", O_RDWR)) < 0) {
1416		return (L_OPEN_PATH_FAIL);
1417	}
1418
1419	retval = ioctl(fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, ioc);
1420	if (retval != 0) {
1421		close(fd);
1422		return (L_SCSI_VHCI_ERROR);
1423	}
1424	prop_buf_size = SV_PROP_MAX_BUF_SIZE;
1425
1426
1427	while (pathlist_retry_count <= RETRY_PATHLIST) {
1428		ioc->buf_elem = initial_path_count;
1429		/* Make driver put actual # paths in variable */
1430		ioc->ret_elem = (uint_t *)&(current_path_count);
1431
1432		/*
1433		 * Allocate space for array of path_info structures.
1434		 * Allocate enough space for # paths from get_pathcount
1435		 */
1436		ioc->ret_buf = (sv_path_info_t *)
1437		    calloc(initial_path_count, sizeof (sv_path_info_t));
1438		if (ioc->ret_buf == NULL) {
1439			close(fd);
1440			return (L_MALLOC_FAILED);
1441		}
1442
1443		/*
1444		 * Allocate space for path properties returned by driver
1445		 */
1446		malloc_error = 0;
1447		for (i = 0; i < initial_path_count; i++) {
1448			ioc->ret_buf[i].ret_prop.buf_size = prop_buf_size;
1449			if ((ioc->ret_buf[i].ret_prop.buf =
1450			    (caddr_t)malloc(prop_buf_size)) == NULL) {
1451				malloc_error = 1;
1452				break;
1453			}
1454			if ((ioc->ret_buf[i].ret_prop.ret_buf_size =
1455			    (uint_t *)malloc(sizeof (uint_t))) == NULL) {
1456				malloc_error = 1;
1457				break;
1458			}
1459		}
1460		if (malloc_error == 1) {
1461			for (i = 0; i < initial_path_count; i++) {
1462				free(ioc->ret_buf[i].ret_prop.buf);
1463				free(ioc->ret_buf[i].ret_prop.ret_buf_size);
1464			}
1465			free(ioc->ret_buf);
1466			close(fd);
1467			return (L_MALLOC_FAILED);
1468		}
1469
1470		retval = ioctl(fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, ioc);
1471		if (retval != 0) {
1472			for (i = 0; i < initial_path_count; i++) {
1473				free(ioc->ret_buf[i].ret_prop.buf);
1474				free(ioc->ret_buf[i].ret_prop.ret_buf_size);
1475			}
1476			free(ioc->ret_buf);
1477			close(fd);
1478			return (L_SCSI_VHCI_ERROR);
1479		}
1480		if (initial_path_count < current_path_count) {
1481			/* then a new path was added */
1482			pathlist_retry_count++;
1483			initial_path_count = current_path_count;
1484		} else {
1485			break;
1486		}
1487	}
1488	/* we are done with ioctl's, lose the fd */
1489	close(fd);
1490
1491	/*
1492	 * Compare the length num elements from the ioctl response
1493	 *   and the caller's request - use smaller value.
1494	 *
1495	 * pathlist_p->path_count now has count returned from ioctl.
1496	 * ioc.buf_elem has the value the caller provided.
1497	 */
1498	if (initial_path_count < current_path_count) {
1499		/* More paths exist than we allocated space for */
1500		*path_count = initial_path_count;
1501	} else {
1502		*path_count = current_path_count;
1503	}
1504
1505	return (0);
1506}
1507
1508int
1509get_mode_page(char *path, uchar_t **pg_buf)
1510{
1511	struct mode_header_g1	*mode_header_ptr;
1512	int		status, size, fd;
1513
1514	/* open controller */
1515	if ((fd = open(path, O_NDELAY | O_RDWR)) == -1)
1516		return (-1); /* L_OPEN_PATH_FAIL */
1517
1518	/*
1519	 * Read the first part of the page to get the page size
1520	 */
1521	size = 20;
1522	if ((*pg_buf = (uchar_t *)calloc(1, size)) == NULL) {
1523		(void) close(fd);
1524		return (L_MALLOC_FAILED);
1525	}
1526	/* read page */
1527	if (status = scsi_mode_sense_cmd(fd, *pg_buf, size,
1528	    0, MODEPAGE_ALLPAGES)) {
1529		(void) close(fd);
1530		(void) free(*pg_buf);
1531		return (status);
1532	}
1533	/* Now get the size for all pages */
1534	mode_header_ptr = (struct mode_header_g1 *)(void *)*pg_buf;
1535	size = ntohs(mode_header_ptr->length) +
1536	    sizeof (mode_header_ptr->length);
1537	(void) free(*pg_buf);
1538	if ((*pg_buf = (uchar_t *)calloc(1, size)) == NULL) {
1539		(void) close(fd);
1540		return (L_MALLOC_FAILED);
1541	}
1542	/* read all pages */
1543	if (status = scsi_mode_sense_cmd(fd, *pg_buf, size,
1544	    0, MODEPAGE_ALLPAGES)) {
1545		(void) close(fd);
1546		(void) free(*pg_buf);
1547		return (status);
1548	}
1549	(void) close(fd);
1550	return (0);
1551}
1552
1553/*
1554 * Dump a structure in hexadecimal.
1555 */
1556void
1557dump_hex_data(char *hdr, uchar_t *src, int nbytes, int format)
1558{
1559	int i;
1560	int n;
1561	char	*p;
1562	char	s[256];
1563
1564	assert(format == HEX_ONLY || format == HEX_ASCII);
1565
1566	(void) strcpy(s, hdr);
1567	for (p = s; *p; p++) {
1568		*p = ' ';
1569	}
1570
1571	p = hdr;
1572	while (nbytes > 0) {
1573		(void) fprintf(stdout, "%s", p);
1574		p = s;
1575		n = MIN(nbytes, BYTES_PER_LINE);
1576		for (i = 0; i < n; i++) {
1577			(void) fprintf(stdout, "%02x ", src[i] & 0xff);
1578		}
1579		if (format == HEX_ASCII) {
1580			for (i = BYTES_PER_LINE-n; i > 0; i--) {
1581				(void) fprintf(stdout, "   ");
1582			}
1583			(void) fprintf(stdout, "    ");
1584			for (i = 0; i < n; i++) {
1585				(void) fprintf(stdout, "%c",
1586				    isprint(src[i]) ? src[i] : '.');
1587			}
1588		}
1589		(void) fprintf(stdout, "\n");
1590		nbytes -= n;
1591		src += n;
1592	}
1593}
1594