xref: /illumos-gate/usr/src/cmd/luxadm/lux_util.c (revision 24fe0b3b)
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 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 
27 
28 #include	<stdio.h>
29 #include	<unistd.h>
30 #include	<stdlib.h>
31 #include	<sys/param.h>
32 #include	<sys/types.h>
33 #include	<fcntl.h>
34 #include	<sys/stat.h>
35 #include	<string.h>
36 #include	<strings.h>
37 #include	<ctype.h>
38 #include	<errno.h>
39 #include	<assert.h>
40 #include	<sys/scsi/impl/uscsi.h>
41 #include	<sys/scsi/generic/commands.h>
42 #include	<sys/scsi/impl/commands.h>
43 #include	<sys/scsi/generic/sense.h>
44 #include	<sys/scsi/generic/mode.h>
45 #include	<sys/scsi/generic/status.h>
46 #include	<sys/scsi/generic/inquiry.h>
47 #include	<sys/scsi/adapters/scsi_vhci.h>
48 #include	<sys/byteorder.h>
49 #include	"common.h"
50 #include	"errorcodes.h"
51 
52 #define	MAX_MODE_SENSE_LEN		0xffff
53 #define	MAXLEN		1000
54 
55 #define	RETRY_PATHLIST	1
56 #define	BYTES_PER_LINE	16
57 #define	SCMD_UNKNOWN	0xff
58 
59 #define	SCSI_VHCI	"/devices/scsi_vhci/"
60 #define	SLASH		"/"
61 #define	DEV_PREFIX	"/devices/"
62 #define	DEV_PREFIX_STRLEN	strlen(DEV_PREFIX)
63 #define	DEVICES_DIR	"/devices"
64 
65 extern	char	*dtype[]; /* from adm.c */
66 extern	int	rand_r(unsigned int *);
67 
68 static int cleanup_dotdot_path(char *path);
69 static int wait_random_time(void);
70 static char *scsi_find_command_name(int cmd);
71 static void scsi_printerr(struct uscsi_cmd *ucmd,
72 	    struct scsi_extended_sense *rq, int rqlen,
73 	    char msg_string[], char *err_string);
74 static void string_dump(char *hdr, uchar_t *src, int nbytes, int format,
75 	    char msg_string[]);
76 static int issue_uscsi_cmd(int file, struct uscsi_cmd *command, int flag);
77 
78 
79 static int
80 wait_random_time(void)
81 {
82 time_t		timeval;
83 struct tm	*tmbuf = NULL;
84 struct timeval	tval;
85 unsigned int	seed;
86 int		random;
87 pid_t		pid;
88 
89 
90 	/*
91 	 * Get the system time and use "system seconds"
92 	 * as 'seed' to generate a random number. Then,
93 	 * wait between 1/10 - 1/2 seconds before retry.
94 	 * Get the current process id and ex-or it with
95 	 * the seed so that the random number is always
96 	 * different even in case of multiple processes
97 	 * generate a random number at the same time.
98 	 */
99 	if ((timeval = time(NULL)) == -1) {
100 		return (errno);
101 	}
102 	if ((tmbuf = localtime(&timeval)) == NULL) {
103 		return (-1); /* L_LOCALTIME_ERROR */
104 	}
105 
106 	pid = getpid();
107 
108 	/* get a random number. */
109 	seed = (unsigned int) tmbuf->tm_sec;
110 	seed ^= pid;
111 	random = rand_r(&seed);
112 
113 
114 	random = ((random % 500) + 100) * MILLISEC;
115 	tval.tv_sec = random / MICROSEC;
116 	tval.tv_usec = random % MICROSEC;
117 
118 	if (select(0, NULL, NULL, NULL, &tval) == -1) {
119 		return (-1); /* L_SELECT_ERROR */
120 	}
121 	return (0);
122 }
123 
124 /*
125  *		Special string dump for error message
126  */
127 static	void
128 string_dump(char *hdr, uchar_t *src, int nbytes, int format, char msg_string[])
129 {
130 	int i;
131 	int n;
132 	char	*p;
133 	char	s[256];
134 
135 	assert(format == HEX_ONLY || format == HEX_ASCII);
136 
137 	(void) strcpy(s, hdr);
138 	for (p = s; *p; p++) {
139 		*p = ' ';
140 	}
141 
142 	p = hdr;
143 	while (nbytes > 0) {
144 		(void) sprintf(&msg_string[strlen(msg_string)],
145 			"%s", p);
146 		p = s;
147 		n = MIN(nbytes, BYTES_PER_LINE);
148 		for (i = 0; i < n; i++) {
149 			(void) sprintf(&msg_string[strlen(msg_string)],
150 				"%02x ",
151 				src[i] & 0xff);
152 		}
153 		if (format == HEX_ASCII) {
154 			for (i = BYTES_PER_LINE-n; i > 0; i--) {
155 				(void) sprintf(&msg_string[strlen(msg_string)],
156 					"   ");
157 			}
158 			(void) sprintf(&msg_string[strlen(msg_string)],
159 				"    ");
160 			for (i = 0; i < n; i++) {
161 				(void) sprintf(&msg_string[strlen(msg_string)],
162 					"%c",
163 					isprint(src[i]) ? src[i] : '.');
164 			}
165 		}
166 		(void) sprintf(&msg_string[strlen(msg_string)], "\n");
167 		nbytes -= n;
168 		src += n;
169 	}
170 }
171 /*
172  * Return a pointer to a string telling us the name of the command.
173  */
174 static char *
175 scsi_find_command_name(int cmd)
176 {
177 /*
178  * Names of commands.  Must have SCMD_UNKNOWN at end of list.
179  */
180 struct scsi_command_name {
181 	int command;
182 	char	*name;
183 } scsi_command_names[29];
184 
185 register struct scsi_command_name *c;
186 
187 	scsi_command_names[0].command = SCMD_TEST_UNIT_READY;
188 	scsi_command_names[0].name = MSGSTR(61, "Test Unit Ready");
189 
190 	scsi_command_names[1].command = SCMD_FORMAT;
191 	scsi_command_names[1].name = MSGSTR(110, "Format");
192 
193 	scsi_command_names[2].command = SCMD_REASSIGN_BLOCK;
194 	scsi_command_names[2].name = MSGSTR(77, "Reassign Block");
195 
196 	scsi_command_names[3].command = SCMD_READ;
197 	scsi_command_names[3].name = MSGSTR(27, "Read");
198 
199 	scsi_command_names[4].command = SCMD_WRITE;
200 	scsi_command_names[4].name = MSGSTR(54, "Write");
201 
202 	scsi_command_names[5].command = SCMD_READ_G1;
203 	scsi_command_names[5].name = MSGSTR(79, "Read(10 Byte)");
204 
205 	scsi_command_names[6].command = SCMD_WRITE_G1;
206 	scsi_command_names[6].name = MSGSTR(51, "Write(10 Byte)");
207 
208 	scsi_command_names[7].command = SCMD_MODE_SELECT;
209 	scsi_command_names[7].name = MSGSTR(97, "Mode Select");
210 
211 	scsi_command_names[8].command = SCMD_MODE_SENSE;
212 	scsi_command_names[8].name = MSGSTR(95, "Mode Sense");
213 
214 	scsi_command_names[9].command = SCMD_REASSIGN_BLOCK;
215 	scsi_command_names[9].name = MSGSTR(77, "Reassign Block");
216 
217 	scsi_command_names[10].command = SCMD_REQUEST_SENSE;
218 	scsi_command_names[10].name = MSGSTR(74, "Request Sense");
219 
220 	scsi_command_names[11].command = SCMD_READ_DEFECT_LIST;
221 	scsi_command_names[11].name = MSGSTR(80, "Read Defect List");
222 
223 	scsi_command_names[12].command = SCMD_INQUIRY;
224 	scsi_command_names[12].name = MSGSTR(102, "Inquiry");
225 
226 	scsi_command_names[13].command = SCMD_WRITE_BUFFER;
227 	scsi_command_names[13].name = MSGSTR(53, "Write Buffer");
228 
229 	scsi_command_names[14].command = SCMD_READ_BUFFER;
230 	scsi_command_names[14].name = MSGSTR(82, "Read Buffer");
231 
232 	scsi_command_names[15].command = SCMD_START_STOP;
233 	scsi_command_names[15].name = MSGSTR(67, "Start/Stop");
234 
235 	scsi_command_names[16].command = SCMD_RESERVE;
236 	scsi_command_names[16].name = MSGSTR(72, "Reserve");
237 
238 	scsi_command_names[17].command = SCMD_RELEASE;
239 	scsi_command_names[17].name = MSGSTR(75, "Release");
240 
241 	scsi_command_names[18].command = SCMD_MODE_SENSE_G1;
242 	scsi_command_names[18].name = MSGSTR(94, "Mode Sense(10 Byte)");
243 
244 	scsi_command_names[19].command = SCMD_MODE_SELECT_G1;
245 	scsi_command_names[19].name = MSGSTR(96, "Mode Select(10 Byte)");
246 
247 	scsi_command_names[20].command = SCMD_READ_CAPACITY;
248 	scsi_command_names[20].name = MSGSTR(81, "Read Capacity");
249 
250 	scsi_command_names[21].command = SCMD_SYNC_CACHE;
251 	scsi_command_names[21].name = MSGSTR(64, "Synchronize Cache");
252 
253 	scsi_command_names[22].command = SCMD_READ_DEFECT_LIST;
254 	scsi_command_names[22].name = MSGSTR(80, "Read Defect List");
255 
256 	scsi_command_names[23].command = SCMD_GDIAG;
257 	scsi_command_names[23].name = MSGSTR(108, "Get Diagnostic");
258 
259 	scsi_command_names[24].command = SCMD_SDIAG;
260 	scsi_command_names[24].name = MSGSTR(69, "Set Diagnostic");
261 
262 	scsi_command_names[25].command = SCMD_PERS_RESERV_IN;
263 	scsi_command_names[25].name = MSGSTR(10500, "Persistent Reserve In");
264 
265 	scsi_command_names[26].command = SCMD_PERS_RESERV_OUT;
266 	scsi_command_names[26].name = MSGSTR(10501, "Persistent Reserve out");
267 
268 	scsi_command_names[27].command = SCMD_LOG_SENSE;
269 	scsi_command_names[27].name = MSGSTR(10502, "Log Sense");
270 
271 	scsi_command_names[28].command = SCMD_UNKNOWN;
272 	scsi_command_names[28].name = MSGSTR(25, "Unknown");
273 
274 
275 	for (c = scsi_command_names; c->command != SCMD_UNKNOWN; c++)
276 		if (c->command == cmd)
277 			break;
278 	return (c->name);
279 }
280 
281 
282 /*
283  *	Function to create error message containing
284  *	scsi request sense information
285  */
286 
287 static void
288 scsi_printerr(struct uscsi_cmd *ucmd, struct scsi_extended_sense *rq,
289 		int rqlen, char msg_string[], char *err_string)
290 {
291 	int		blkno;
292 
293 	switch (rq->es_key) {
294 	case KEY_NO_SENSE:
295 		(void) sprintf(msg_string, MSGSTR(91, "No sense error"));
296 		break;
297 	case KEY_RECOVERABLE_ERROR:
298 		(void) sprintf(msg_string, MSGSTR(76, "Recoverable error"));
299 		break;
300 	case KEY_NOT_READY:
301 		(void) sprintf(msg_string,
302 			MSGSTR(10503,
303 			"Device Not ready."
304 			" Error: Random Retry Failed: %s\n."),
305 			err_string);
306 		break;
307 	case KEY_MEDIUM_ERROR:
308 		(void) sprintf(msg_string, MSGSTR(99, "Medium error"));
309 		break;
310 	case KEY_HARDWARE_ERROR:
311 		(void) sprintf(msg_string, MSGSTR(106, "Hardware error"));
312 		break;
313 	case KEY_ILLEGAL_REQUEST:
314 		(void) sprintf(msg_string, MSGSTR(103, "Illegal request"));
315 		break;
316 	case KEY_UNIT_ATTENTION:
317 		(void) sprintf(msg_string,
318 			MSGSTR(10504,
319 			"Unit attention."
320 			"Error: Random Retry Failed.\n"));
321 		break;
322 	case KEY_WRITE_PROTECT:
323 		(void) sprintf(msg_string, MSGSTR(52, "Write protect error"));
324 		break;
325 	case KEY_BLANK_CHECK:
326 		(void) sprintf(msg_string, MSGSTR(131, "Blank check error"));
327 		break;
328 	case KEY_VENDOR_UNIQUE:
329 		(void) sprintf(msg_string, MSGSTR(58, "Vendor unique error"));
330 		break;
331 	case KEY_COPY_ABORTED:
332 		(void) sprintf(msg_string, MSGSTR(123, "Copy aborted error"));
333 		break;
334 	case KEY_ABORTED_COMMAND:
335 		(void) sprintf(msg_string,
336 			MSGSTR(10505,
337 			"Aborted command."
338 			" Error: Random Retry Failed.\n"));
339 		break;
340 	case KEY_EQUAL:
341 		(void) sprintf(msg_string, MSGSTR(117, "Equal error"));
342 		break;
343 	case KEY_VOLUME_OVERFLOW:
344 		(void) sprintf(msg_string, MSGSTR(57, "Volume overflow"));
345 		break;
346 	case KEY_MISCOMPARE:
347 		(void) sprintf(msg_string, MSGSTR(98, "Miscompare error"));
348 		break;
349 	case KEY_RESERVED:
350 		(void) sprintf(msg_string, MSGSTR(10506,
351 			"Reserved value found"));
352 		break;
353 	default:
354 		(void) sprintf(msg_string, MSGSTR(59, "Unknown error"));
355 		break;
356 	}
357 
358 	(void) sprintf(&msg_string[strlen(msg_string)],
359 		MSGSTR(10507, " during: %s"),
360 		scsi_find_command_name(ucmd->uscsi_cdb[0]));
361 
362 	if (rq->es_valid) {
363 		blkno = (rq->es_info_1 << 24) | (rq->es_info_2 << 16) |
364 			(rq->es_info_3 << 8) | rq->es_info_4;
365 		(void) sprintf(&msg_string[strlen(msg_string)],
366 			MSGSTR(49, ": block %d (0x%x)"), blkno, blkno);
367 	}
368 
369 	(void) sprintf(&msg_string[strlen(msg_string)], "\n");
370 
371 	if (rq->es_add_len >= 6) {
372 		(void) sprintf(&msg_string[strlen(msg_string)],
373 		MSGSTR(132, "  Additional sense: 0x%x   ASC Qualifier: 0x%x\n"),
374 			rq->es_add_code, rq->es_qual_code);
375 			/*
376 			 * rq->es_add_info[ADD_SENSE_CODE],
377 			 * rq->es_add_info[ADD_SENSE_QUAL_CODE]);
378 			 */
379 	}
380 	if (rq->es_key == KEY_ILLEGAL_REQUEST) {
381 		string_dump(MSGSTR(47, " cmd:   "), (uchar_t *)ucmd,
382 			sizeof (struct uscsi_cmd), HEX_ONLY, msg_string);
383 		string_dump(MSGSTR(48, " cdb:   "),
384 			(uchar_t *)ucmd->uscsi_cdb,
385 			ucmd->uscsi_cdblen, HEX_ONLY, msg_string);
386 	}
387 	string_dump(MSGSTR(43, " sense:  "),
388 		(uchar_t *)rq, 8 + rq->es_add_len, HEX_ONLY,
389 		msg_string);
390 	rqlen = rqlen;	/* not used */
391 }
392 
393 
394 /*
395  * Execute a command and determine the result.
396  */
397 static int
398 issue_uscsi_cmd(int file, struct uscsi_cmd *command, int flag)
399 {
400 struct scsi_extended_sense	*rqbuf;
401 int				status, i, retry_cnt = 0, err;
402 char				errorMsg[MAXLEN];
403 
404 	/*
405 	 * Set function flags for driver.
406 	 *
407 	 * Set Automatic request sense enable
408 	 *
409 	 */
410 	command->uscsi_flags = USCSI_RQENABLE;
411 	command->uscsi_flags |= flag;
412 
413 	/* intialize error message array */
414 	errorMsg[0] = '\0';
415 
416 	/* print command for debug */
417 	if (getenv("_LUX_S_DEBUG") != NULL) {
418 		if ((command->uscsi_cdb == NULL) ||
419 			(flag & USCSI_RESET) ||
420 			(flag & USCSI_RESET_ALL)) {
421 			if (flag & USCSI_RESET) {
422 				(void) printf("  Issuing a SCSI Reset.\n");
423 			}
424 			if (flag & USCSI_RESET_ALL) {
425 				(void) printf("  Issuing a SCSI Reset All.\n");
426 			}
427 
428 		} else {
429 			(void) printf("  Issuing the following "
430 				"SCSI command: %s\n",
431 			scsi_find_command_name(command->uscsi_cdb[0]));
432 			(void) printf("	fd=0x%x cdb=", file);
433 			for (i = 0; i < (int)command->uscsi_cdblen; i++) {
434 				(void) printf("%x ", *(command->uscsi_cdb + i));
435 			}
436 			(void) printf("\n\tlen=0x%x bufaddr=0x%x buflen=0x%x"
437 				" flags=0x%x\n",
438 			command->uscsi_cdblen,
439 			command->uscsi_bufaddr,
440 			command->uscsi_buflen, command->uscsi_flags);
441 
442 			if ((command->uscsi_buflen > 0) &&
443 				((flag & USCSI_READ) == 0)) {
444 				(void) dump_hex_data("  Buffer data: ",
445 				(uchar_t *)command->uscsi_bufaddr,
446 				MIN(command->uscsi_buflen, 512), HEX_ASCII);
447 			}
448 		}
449 		(void) fflush(stdout);
450 	}
451 
452 
453 	/*
454 	 * Default command timeout in case command left it 0
455 	 */
456 	if (command->uscsi_timeout == 0) {
457 		command->uscsi_timeout = 60;
458 	}
459 	/*	Issue command - finally */
460 
461 retry:
462 	status = ioctl(file, USCSICMD, command);
463 	if (status == 0 && command->uscsi_status == 0) {
464 		if (getenv("_LUX_S_DEBUG") != NULL) {
465 			if ((command->uscsi_buflen > 0) &&
466 				(flag & USCSI_READ)) {
467 				(void) dump_hex_data("\tData read:",
468 				(uchar_t *)command->uscsi_bufaddr,
469 				MIN(command->uscsi_buflen, 512), HEX_ASCII);
470 			}
471 		}
472 		return (status);
473 	}
474 	if ((status != 0) && (command->uscsi_status == 0)) {
475 		if ((getenv("_LUX_S_DEBUG") != NULL) ||
476 			(getenv("_LUX_ER_DEBUG") != NULL)) {
477 			(void) printf("Unexpected USCSICMD ioctl error: %s\n",
478 				strerror(errno));
479 		}
480 		return (status);
481 	}
482 
483 	/*
484 	 * Just a SCSI error, create error message
485 	 * Retry once for Unit Attention,
486 	 * Not Ready, and Aborted Command
487 	 */
488 	if ((command->uscsi_rqbuf != NULL) &&
489 	    (((char)command->uscsi_rqlen - (char)command->uscsi_rqresid) > 0)) {
490 
491 		rqbuf = (struct scsi_extended_sense *)command->uscsi_rqbuf;
492 
493 		switch (rqbuf->es_key) {
494 		case KEY_NOT_READY:
495 			if (retry_cnt++ < 1) {
496 				ER_DPRINTF("Note: Device Not Ready."
497 						" Retrying...\n");
498 
499 				if ((err = wait_random_time()) == 0) {
500 					goto retry;
501 				} else {
502 					return (err);
503 				}
504 			}
505 			break;
506 
507 		case KEY_UNIT_ATTENTION:
508 			if (retry_cnt++ < 1) {
509 				ER_DPRINTF("  cmd():"
510 				" UNIT_ATTENTION: Retrying...\n");
511 
512 				goto retry;
513 			}
514 			break;
515 
516 		case KEY_ABORTED_COMMAND:
517 			if (retry_cnt++ < 1) {
518 				ER_DPRINTF("Note: Command is aborted."
519 				" Retrying...\n");
520 
521 				goto retry;
522 			}
523 			break;
524 		}
525 		if ((getenv("_LUX_S_DEBUG") != NULL) ||
526 			(getenv("_LUX_ER_DEBUG") != NULL)) {
527 			scsi_printerr(command,
528 			(struct scsi_extended_sense *)command->uscsi_rqbuf,
529 			(command->uscsi_rqlen - command->uscsi_rqresid),
530 				errorMsg, strerror(errno));
531 		}
532 
533 	} else {
534 
535 		/*
536 		 * Retry 5 times in case of BUSY, and only
537 		 * once for Reservation-conflict, Command
538 		 * Termination and Queue Full. Wait for
539 		 * random amount of time (between 1/10 - 1/2 secs.)
540 		 * between each retry. This random wait is to avoid
541 		 * the multiple threads being executed at the same time
542 		 * and also the constraint in Photon IB, where the
543 		 * command queue has a depth of one command.
544 		 */
545 		switch ((uchar_t)command->uscsi_status & STATUS_MASK) {
546 		case STATUS_BUSY:
547 			if (retry_cnt++ < 5) {
548 				if ((err = wait_random_time()) == 0) {
549 					R_DPRINTF("  cmd(): No. of retries %d."
550 					" STATUS_BUSY: Retrying...\n",
551 					retry_cnt);
552 					goto retry;
553 
554 				} else {
555 					return (err);
556 				}
557 			}
558 			break;
559 
560 		case STATUS_RESERVATION_CONFLICT:
561 			if (retry_cnt++ < 1) {
562 				if ((err = wait_random_time()) == 0) {
563 					R_DPRINTF("  cmd():"
564 					" RESERVATION_CONFLICT:"
565 					" Retrying...\n");
566 					goto retry;
567 
568 				} else {
569 					return (err);
570 				}
571 			}
572 			break;
573 
574 		case STATUS_TERMINATED:
575 			if (retry_cnt++ < 1) {
576 				R_DPRINTF("Note: Command Terminated."
577 					" Retrying...\n");
578 
579 				if ((err = wait_random_time()) == 0) {
580 					goto retry;
581 				} else {
582 					return (err);
583 				}
584 			}
585 			break;
586 
587 		case STATUS_QFULL:
588 			if (retry_cnt++ < 1) {
589 				R_DPRINTF("Note: Command Queue is full."
590 				" Retrying...\n");
591 
592 				if ((err = wait_random_time()) == 0) {
593 					goto retry;
594 				} else {
595 					return (err);
596 				}
597 			}
598 			break;
599 		}
600 
601 	}
602 	if (((getenv("_LUX_S_DEBUG") != NULL) ||
603 		(getenv("_LUX_ER_DEBUG") != NULL)) &&
604 		(errorMsg[0] != '\0')) {
605 		(void) fprintf(stdout, "  %s\n", errorMsg);
606 	}
607 	return (L_SCSI_ERROR | command->uscsi_status);
608 }
609 
610 /*
611  *		MODE SENSE USCSI command
612  *
613  *
614  *		pc = page control field
615  *		page_code = Pages to return
616  */
617 int
618 scsi_mode_sense_cmd(int fd,
619 	uchar_t *buf_ptr,
620 	int buf_len,
621 	uchar_t pc,
622 	uchar_t page_code)
623 {
624 struct uscsi_cmd	ucmd;
625 /* 10 byte Mode Select cmd */
626 union scsi_cdb	cdb =  {SCMD_MODE_SENSE_G1, 0, 0, 0, 0, 0, 0, 0, 0, 0};
627 struct	scsi_extended_sense	sense;
628 int		status;
629 static	int	uscsi_count;
630 
631 	if ((fd < 0) || (buf_ptr == NULL) || (buf_len < 0)) {
632 		return (-1); /* L_INVALID_ARG */
633 	}
634 
635 	(void) memset(buf_ptr, 0, buf_len);
636 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
637 	/* Just for me  - a sanity check */
638 	if ((page_code > MODEPAGE_ALLPAGES) || (pc > 3) ||
639 		(buf_len > MAX_MODE_SENSE_LEN)) {
640 		return (-1); /* L_ILLEGAL_MODE_SENSE_PAGE */
641 	}
642 	cdb.g1_addr3 = (pc << 6) + page_code;
643 	cdb.g1_count1 = buf_len>>8;
644 	cdb.g1_count0 = buf_len & 0xff;
645 	ucmd.uscsi_cdb = (caddr_t)&cdb;
646 	ucmd.uscsi_cdblen = CDB_GROUP1;
647 	ucmd.uscsi_bufaddr = (caddr_t)buf_ptr;
648 	ucmd.uscsi_buflen = buf_len;
649 	ucmd.uscsi_rqbuf = (caddr_t)&sense;
650 	ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
651 	ucmd.uscsi_timeout = 120;
652 
653 	status = issue_uscsi_cmd(fd, &ucmd, USCSI_READ);
654 	/* Bytes actually transfered */
655 	if (status == 0) {
656 		uscsi_count = buf_len - ucmd.uscsi_resid;
657 		S_DPRINTF("  Number of bytes read on "
658 		"Mode Sense 0x%x\n", uscsi_count);
659 		if (getenv("_LUX_D_DEBUG") != NULL) {
660 		    (void) dump_hex_data("  Mode Sense data: ", buf_ptr,
661 		    uscsi_count, HEX_ASCII);
662 		}
663 	}
664 	return (status);
665 }
666 
667 int
668 scsi_release(char *path)
669 {
670 struct uscsi_cmd	ucmd;
671 union scsi_cdb		cdb = {SCMD_RELEASE, 0, 0, 0, 0, 0};
672 struct	scsi_extended_sense	sense;
673 int	fd, status;
674 
675 	P_DPRINTF("  scsi_release: Release: Path %s\n", path);
676 	if ((fd = open(path, O_NDELAY | O_RDONLY)) == -1)
677 	    return (1);
678 
679 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
680 
681 	ucmd.uscsi_cdb = (caddr_t)&cdb;
682 	ucmd.uscsi_cdblen = CDB_GROUP0;
683 	ucmd.uscsi_bufaddr = NULL;
684 	ucmd.uscsi_buflen = 0;
685 	ucmd.uscsi_rqbuf = (caddr_t)&sense;
686 	ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
687 	ucmd.uscsi_timeout = 60;
688 	status = (issue_uscsi_cmd(fd, &ucmd, 0));
689 
690 	(void) close(fd);
691 	return (status);
692 }
693 
694 int
695 scsi_reserve(char *path)
696 {
697 struct uscsi_cmd	ucmd;
698 union scsi_cdb	cdb = {SCMD_RESERVE, 0, 0, 0, 0, 0};
699 struct	scsi_extended_sense	sense;
700 int	fd, status;
701 
702 	P_DPRINTF("  scsi_reserve: Reserve: Path %s\n", path);
703 	if ((fd = open(path, O_NDELAY | O_RDONLY)) == -1)
704 	    return (1);
705 
706 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
707 
708 	ucmd.uscsi_cdb = (caddr_t)&cdb;
709 	ucmd.uscsi_cdblen = CDB_GROUP0;
710 	ucmd.uscsi_bufaddr = NULL;
711 	ucmd.uscsi_buflen = 0;
712 	ucmd.uscsi_rqbuf = (caddr_t)&sense;
713 	ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
714 	ucmd.uscsi_timeout = 60;
715 	status = (issue_uscsi_cmd(fd, &ucmd, 0));
716 
717 	(void) close(fd);
718 	return (status);
719 }
720 
721 /*
722  * Print out fabric dev dtype
723  */
724 void
725 print_fabric_dtype_prop(uchar_t *hba_port_wwn, uchar_t *port_wwn,
726 	uchar_t dtype_prop)
727 {
728 	if ((dtype_prop & DTYPE_MASK) < 0x10) {
729 		(void) fprintf(stdout, " 0x%-2x (%s)\n",
730 		(dtype_prop & DTYPE_MASK), dtype[(dtype_prop & DTYPE_MASK)]);
731 	} else if ((dtype_prop & DTYPE_MASK) < 0x1f) {
732 		(void) fprintf(stdout,
733 		MSGSTR(2096, " 0x%-2x (Reserved)\n"),
734 		(dtype_prop & DTYPE_MASK));
735 	} else {
736 		/* Check to see if this is the HBA */
737 		if (wwnConversion(hba_port_wwn) != wwnConversion(port_wwn)) {
738 			(void) fprintf(stdout, MSGSTR(2097,
739 			" 0x%-2x (Unknown Type)\n"), (dtype_prop & DTYPE_MASK));
740 		} else {
741 			/* MATCH */
742 			(void) fprintf(stdout, MSGSTR(2241,
743 			" 0x%-2x (Unknown Type,Host Bus Adapter)\n"),
744 			(dtype_prop & DTYPE_MASK));
745 		}
746 	}
747 }
748 
749 
750 void
751 print_inq_data(char *arg_path, char *path, L_inquiry inq, uchar_t *serial,
752     size_t serial_len)
753 {
754 char	**p;
755 uchar_t	*v_parm;
756 int	scsi_3, length;
757 char	byte_number[MAXNAMELEN];
758 static	char *scsi_inquiry_labels_2[21];
759 static	char *scsi_inquiry_labels_3[22];
760 static	char	*ansi_version[4];
761 	/*
762 	 * Intialize scsi_inquiry_labels_2 with i18n strings
763 	 */
764 	scsi_inquiry_labels_2[0] = MSGSTR(138, "Vendor:                     ");
765 	scsi_inquiry_labels_2[1] = MSGSTR(149, "Product:                    ");
766 	scsi_inquiry_labels_2[2] = MSGSTR(139, "Revision:                   ");
767 	scsi_inquiry_labels_2[3] = MSGSTR(143, "Firmware Revision           ");
768 	scsi_inquiry_labels_2[4] = MSGSTR(144, "Serial Number               ");
769 	scsi_inquiry_labels_2[5] = MSGSTR(140, "Device type:                ");
770 	scsi_inquiry_labels_2[6] = MSGSTR(145, "Removable media:            ");
771 	scsi_inquiry_labels_2[7] = MSGSTR(146, "ISO version:                ");
772 	scsi_inquiry_labels_2[8] = MSGSTR(147, "ECMA version:               ");
773 	scsi_inquiry_labels_2[9] = MSGSTR(148, "ANSI version:               ");
774 	scsi_inquiry_labels_2[10] =
775 		MSGSTR(2168, "Async event notification:   ");
776 	scsi_inquiry_labels_2[11] =
777 		MSGSTR(2169, "Terminate i/o process msg:  ");
778 	scsi_inquiry_labels_2[12] = MSGSTR(150, "Response data format:       ");
779 	scsi_inquiry_labels_2[13] = MSGSTR(151, "Additional length:          ");
780 	scsi_inquiry_labels_2[14] = MSGSTR(152, "Relative addressing:        ");
781 	scsi_inquiry_labels_2[15] =
782 		MSGSTR(2170, "32 bit transfers:           ");
783 	scsi_inquiry_labels_2[16] =
784 		MSGSTR(2171, "16 bit transfers:           ");
785 	scsi_inquiry_labels_2[17] =
786 		MSGSTR(2172, "Synchronous transfers:      ");
787 	scsi_inquiry_labels_2[18] = MSGSTR(153, "Linked commands:            ");
788 	scsi_inquiry_labels_2[19] = MSGSTR(154, "Command queueing:           ");
789 	scsi_inquiry_labels_2[20] =
790 		MSGSTR(2173, "Soft reset option:          ");
791 
792 	/*
793 	 * Intialize scsi_inquiry_labels_3 with i18n strings
794 	 */
795 	scsi_inquiry_labels_3[0] = MSGSTR(138, "Vendor:                     ");
796 	scsi_inquiry_labels_3[1] = MSGSTR(149, "Product:                    ");
797 	scsi_inquiry_labels_3[2] = MSGSTR(139, "Revision:                   ");
798 	scsi_inquiry_labels_3[3] = MSGSTR(143, "Firmware Revision           ");
799 	scsi_inquiry_labels_3[4] = MSGSTR(144, "Serial Number               ");
800 	scsi_inquiry_labels_3[5] = MSGSTR(140, "Device type:                ");
801 	scsi_inquiry_labels_3[6] = MSGSTR(145, "Removable media:            ");
802 	scsi_inquiry_labels_3[7] = MSGSTR(2174, "Medium Changer Element:     ");
803 	scsi_inquiry_labels_3[8] = MSGSTR(146, "ISO version:                ");
804 	scsi_inquiry_labels_3[9] = MSGSTR(147, "ECMA version:               ");
805 	scsi_inquiry_labels_3[10] = MSGSTR(148, "ANSI version:               ");
806 	scsi_inquiry_labels_3[11] =
807 		MSGSTR(2175, "Async event reporting:      ");
808 	scsi_inquiry_labels_3[12] =
809 		MSGSTR(2176, "Terminate task:             ");
810 	scsi_inquiry_labels_3[13] =
811 		MSGSTR(2177, "Normal ACA Supported:       ");
812 	scsi_inquiry_labels_3[14] = MSGSTR(150, "Response data format:       ");
813 	scsi_inquiry_labels_3[15] = MSGSTR(151, "Additional length:          ");
814 	scsi_inquiry_labels_3[16] =
815 		MSGSTR(2178, "Cmd received on port:       ");
816 	scsi_inquiry_labels_3[17] =
817 		MSGSTR(2179, "SIP Bits:                   ");
818 	scsi_inquiry_labels_3[18] = MSGSTR(152, "Relative addressing:        ");
819 	scsi_inquiry_labels_3[19] = MSGSTR(153, "Linked commands:            ");
820 	scsi_inquiry_labels_3[20] =
821 		MSGSTR(2180, "Transfer Disable:           ");
822 	scsi_inquiry_labels_3[21] = MSGSTR(154, "Command queueing:           ");
823 
824 	/*
825 	 * Intialize scsi_inquiry_labels_3 with i18n strings
826 	 */
827 	ansi_version[0] = MSGSTR(2181,
828 		" (Device might or might not comply to an ANSI version)");
829 	ansi_version[1] = MSGSTR(2182,
830 		" (This code is reserved for historical uses)");
831 	ansi_version[2] = MSGSTR(2183,
832 		" (Device complies to ANSI X3.131-1994 (SCSI-2))");
833 	ansi_version[3] = MSGSTR(2184,
834 		" (Device complies to SCSI-3)");
835 
836 	    /* print inquiry information */
837 
838 	    (void) fprintf(stdout, MSGSTR(2185, "\nINQUIRY:\n"));
839 		/*
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
843 		 */
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);
850 	    }
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;
857 	    }
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");
865 	    }
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");
872 	    }
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");
879 	    }
880 	    if (inq.inq_len < 39) {
881 		p += 2;
882 	    } else {
883 		/*
884 		 * If Pluto then print
885 		 * firmware rev & serial #.
886 		 */
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;
903 		}
904 	    }
905 
906 	    (void) fprintf(stdout, "%s0x%x (",
907 			*p++, (inq.inq_dtype & DTYPE_MASK));
908 	    if ((inq.inq_dtype & DTYPE_MASK) < 0x10) {
909 		(void) fprintf(stdout, "%s", dtype[inq.inq_dtype & DTYPE_MASK]);
910 	    } else if ((inq.inq_dtype & DTYPE_MASK) < 0x1f) {
911 		(void) fprintf(stdout, MSGSTR(71, "Reserved"));
912 	    } else {
913 		(void) fprintf(stdout, MSGSTR(2186, "Unknown device"));
914 	    }
915 	    (void) fprintf(stdout, ")\n");
916 
917 	    (void) fprintf(stdout, "%s", *p++);
918 	    if (inq.inq_rmb != NULL) {
919 		(void) fprintf(stdout, MSGSTR(40, "yes"));
920 	    } else {
921 		(void) fprintf(stdout, MSGSTR(45, "no"));
922 	    }
923 	    (void) fprintf(stdout, "\n");
924 
925 	    if (scsi_3) {
926 		(void) fprintf(stdout, "%s", *p++);
927 		if (inq.inq_mchngr != NULL) {
928 			(void) fprintf(stdout, MSGSTR(40, "yes"));
929 		} else {
930 			(void) fprintf(stdout, MSGSTR(45, "no"));
931 		}
932 		(void) fprintf(stdout, "\n");
933 	    }
934 	    (void) fprintf(stdout, "%s%d\n", *p++, inq.inq_iso);
935 	    (void) fprintf(stdout, "%s%d\n", *p++, inq.inq_ecma);
936 
937 	    (void) fprintf(stdout, "%s%d", *p++, inq.inq_ansi);
938 	    if (inq.inq_ansi < 0x4) {
939 		(void) fprintf(stdout, "%s", ansi_version[inq.inq_ansi]);
940 	    } else
941 		(void) fprintf(stdout, MSGSTR(71, "Reserved"));
942 	    (void) fprintf(stdout, "\n");
943 
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++;
950 	    }
951 	    if (scsi_3) {
952 		(void) fprintf(stdout, "%s", *p++);
953 		if (inq.inq_normaca != NULL) {
954 			(void) fprintf(stdout, MSGSTR(40, "yes"));
955 		} else {
956 			(void) fprintf(stdout, MSGSTR(45, "no"));
957 		}
958 		(void) fprintf(stdout, "\n");
959 	    }
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++;
966 	    }
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 != NULL) {
972 				(void) fprintf(stdout, MSGSTR(2187,
973 					"%sa\n"), *p++);
974 			} else {
975 				(void) fprintf(stdout, MSGSTR(2188,
976 					"%sb\n"), *p++);
977 			}
978 		} else {
979 		    p++;
980 		}
981 	    }
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);
988 		}
989 		p++;
990 
991 	    }
992 
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");
997 	    }
998 	    p++;
999 
1000 	    if (!scsi_3) {
1001 
1002 		    if (inq.ui.inq_2.inq_wbus32) {
1003 			(void) fprintf(stdout, "%s", *p);
1004 			(void) fprintf(stdout, MSGSTR(40, "yes"));
1005 			(void) fprintf(stdout, "\n");
1006 		    }
1007 		    p++;
1008 
1009 		    if (inq.ui.inq_2.inq_wbus16) {
1010 			(void) fprintf(stdout, "%s", *p);
1011 			(void) fprintf(stdout, MSGSTR(40, "yes"));
1012 			(void) fprintf(stdout, "\n");
1013 		    }
1014 		    p++;
1015 
1016 		    if (inq.ui.inq_2.inq_sync) {
1017 			(void) fprintf(stdout, "%s", *p);
1018 			(void) fprintf(stdout, MSGSTR(40, "yes"));
1019 			(void) fprintf(stdout, "\n");
1020 		    }
1021 		    p++;
1022 
1023 	    }
1024 	    if (inq.ui.inq_2.inq_linked) {
1025 		(void) fprintf(stdout, "%s", *p);
1026 		(void) fprintf(stdout, MSGSTR(40, "yes"));
1027 		(void) fprintf(stdout, "\n");
1028 	    }
1029 	    p++;
1030 
1031 	    if (scsi_3) {
1032 		(void) fprintf(stdout, "%s", *p++);
1033 		if (inq.ui.inq_3.inq_trandis != NULL) {
1034 			(void) fprintf(stdout, MSGSTR(40, "yes"));
1035 		} else {
1036 			(void) fprintf(stdout, MSGSTR(45, "no"));
1037 		}
1038 		(void) fprintf(stdout, "\n");
1039 	    }
1040 
1041 	    if (inq.ui.inq_2.inq_cmdque) {
1042 		(void) fprintf(stdout, "%s", *p);
1043 		(void) fprintf(stdout, MSGSTR(40, "yes"));
1044 		(void) fprintf(stdout, "\n");
1045 	    }
1046 	    p++;
1047 
1048 	    if (!scsi_3) {
1049 		    if (inq.ui.inq_2.inq_sftre) {
1050 			(void) fprintf(stdout, "%s", *p);
1051 			(void) fprintf(stdout, MSGSTR(40, "yes"));
1052 			(void) fprintf(stdout, "\n");
1053 		    }
1054 		    p++;
1055 
1056 	    }
1057 
1058 		/*
1059 		 * Now print the vendor-specific data.
1060 		 */
1061 	    v_parm = inq.inq_ven_specific_1;
1062 	    if (inq.inq_len >= 32) {
1063 		length = inq.inq_len - 31;
1064 		if (strstr((char *)inq.inq_pid, "SSA") != 0) {
1065 		(void) fprintf(stdout, MSGSTR(2189,
1066 					"Number of Ports, Targets:   %d,%d\n"),
1067 			inq.inq_ssa_ports, inq.inq_ssa_tgts);
1068 			v_parm += 20;
1069 			length -= 20;
1070 		} else if ((strstr((char *)inq.inq_pid, "SUN") != 0) ||
1071 			(strncmp((char *)inq.inq_vid, "SUN     ",
1072 			sizeof (inq.inq_vid)) == 0)) {
1073 			v_parm += 16;
1074 			length -= 16;
1075 		}
1076 		/*
1077 		 * Do hex Dump of rest of the data.
1078 		 */
1079 		if (length > 0) {
1080 			(void) fprintf(stdout,
1081 				MSGSTR(2190,
1082 			"              VENDOR-SPECIFIC PARAMETERS\n"));
1083 			(void) fprintf(stdout,
1084 				MSGSTR(2191,
1085 				"Byte#                  Hex Value            "
1086 				"                 ASCII\n"));
1087 			(void) sprintf(byte_number,
1088 			"%d    ", inq.inq_len - length + 5);
1089 			dump_hex_data(byte_number, v_parm,
1090 				MIN(length, inq.inq_res3 - v_parm), HEX_ASCII);
1091 		}
1092 		/*
1093 		 * Skip reserved bytes 56-95.
1094 		 */
1095 		length -= (inq.inq_box_name - v_parm);
1096 		if (length > 0) {
1097 			(void) sprintf(byte_number, "%d    ",
1098 				inq.inq_len - length + 5);
1099 			dump_hex_data(byte_number, inq.inq_box_name,
1100 				MIN(length, sizeof (inq.inq_box_name) +
1101 				sizeof (inq.inq_avu)), HEX_ASCII);
1102 		}
1103 	    }
1104 	    if (getenv("_LUX_D_DEBUG") != NULL) {
1105 		dump_hex_data("\nComplete Inquiry: ",
1106 			(uchar_t *)&inq,
1107 			MIN(inq.inq_len + 5, sizeof (inq)), HEX_ASCII);
1108 	    }
1109 }
1110 
1111 /*
1112  * Internal routine to clean up ../'s in paths.
1113  * returns 0 if no "../" are left.
1114  *
1115  * Wouldn't it be nice if there was a standard system library
1116  * routine to do this...?
1117  */
1118 static int
1119 cleanup_dotdot_path(char *path)
1120 {
1121 	char holder[MAXPATHLEN];
1122 	char *dotdot;
1123 	char *previous_slash;
1124 
1125 	/* Find the first "/../" in the string */
1126 	dotdot = strstr(path, "/../");
1127 	if (dotdot == NULL) {
1128 		return (0);
1129 	}
1130 
1131 
1132 	/*
1133 	 * If the [0] character is '/' and "../" immediatly
1134 	 * follows it, then we can strip the ../
1135 	 *
1136 	 *	/../../foo/bar == /foo/bar
1137 	 *
1138 	 */
1139 	if (dotdot == path) {
1140 		strcpy(holder, &path[3]); /* strip "/.." */
1141 		strcpy(path, holder);
1142 		return (1);
1143 	}
1144 
1145 	/*
1146 	 * Now look for the LAST "/" before the "/../"
1147 	 * as this is the parent dir we can get rid of.
1148 	 * We do this by temporarily truncating the string
1149 	 * at the '/' just before "../" using the dotdot pointer.
1150 	 */
1151 	*dotdot = '\0';
1152 	previous_slash = strrchr(path, '/');
1153 	if (previous_slash == NULL) {
1154 		/*
1155 		 * hmm, somethings wrong.  path looks something
1156 		 * like "foo/../bar/" so we can't really deal with it.
1157 		 */
1158 		return (0);
1159 	}
1160 	/*
1161 	 * Now truncate the path just after the previous '/'
1162 	 * and slam everything after the "../" back on
1163 	 */
1164 	*(previous_slash+1) = '\0';
1165 	(void) strcat(path, dotdot+4);
1166 	return (1); /* We may have more "../"s */
1167 }
1168 
1169 /*
1170  * Follow symbolic links from the logical device name to
1171  * the /devfs physical device name.  To be complete, we
1172  * handle the case of multiple links.  This function
1173  * either returns NULL (no links, or some other error),
1174  * or the physical device name, alloc'ed on the heap.
1175  *
1176  * NOTE: If the path is relative, it will be forced into
1177  * an absolute path by pre-pending the pwd to it.
1178  */
1179 char *
1180 get_slash_devices_from_osDevName(char *osDevName, int flag)
1181 {
1182 	struct stat	stbuf;
1183 	char		source[MAXPATHLEN];
1184 	char		scratch[MAXPATHLEN];
1185 	char		pwd[MAXPATHLEN];
1186 	char		*tmp, *phys_path;
1187 	int		cnt;
1188 	boolean_t	is_lstat_failed = B_TRUE;
1189 
1190 	/* return NULL if path is NULL */
1191 	if (osDevName == NULL) {
1192 		return (NULL);
1193 	}
1194 
1195 	strcpy(source, osDevName);
1196 	for (;;) {
1197 
1198 		/*
1199 		 * First make sure the path is absolute.  If not, make it.
1200 		 * If it's already an absolute path, we have no need
1201 		 * to determine the cwd, so the program should still
1202 		 * function within security-by-obscurity directories.
1203 		 */
1204 		if (source[0] != '/') {
1205 			tmp = getcwd(pwd, MAXPATHLEN);
1206 			if (tmp == NULL) {
1207 				return (NULL);
1208 			}
1209 			/*
1210 			 * Handle special case of "./foo/bar"
1211 			 */
1212 			if (source[0] == '.' && source[1] == '/') {
1213 				strcpy(scratch, source+2);
1214 			} else { /* no "./" so just take everything */
1215 				strcpy(scratch, source);
1216 			}
1217 			strcpy(source, pwd);
1218 			(void) strcat(source, "/");
1219 			(void) strcat(source, scratch);
1220 		}
1221 
1222 		/*
1223 		 * Clean up any "../"s that are in the path
1224 		 */
1225 		while (cleanup_dotdot_path(source));
1226 
1227 		/*
1228 		 * source is now an absolute path to the link we're
1229 		 * concerned with
1230 		 */
1231 		if (flag == NOT_IGNORE_DANGLING_LINK) {
1232 			/*
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.
1239 			 */
1240 		    if (stat(source, &stbuf) == -1) {
1241 			if (!is_lstat_failed &&
1242 			    strstr(source, "/devices")) {
1243 				/*
1244 				 * lstat succeeded previously and source
1245 				 * contains "/devices" then it is dangling node.
1246 				 */
1247 			    phys_path = (char *)calloc(1, strlen(source) + 1);
1248 			    if (phys_path != NULL) {
1249 				(void) strncpy(phys_path, source,
1250 				strlen(source) + 1);
1251 			    }
1252 			    return (phys_path);
1253 			} else if (is_lstat_failed) {
1254 			    /* check lstat result. */
1255 			    if (lstat(source, &stbuf) == -1) {
1256 				return (NULL);
1257 			    } else {
1258 				is_lstat_failed = B_FALSE; /* and coninue */
1259 			    }
1260 			} else {
1261 				/*
1262 				 * With algorithm that resolves a link and
1263 				 * then issues readlink(), should not be
1264 				 * reached here.
1265 				 */
1266 			    return (NULL);
1267 			}
1268 		    } else {
1269 			if (lstat(source, &stbuf) == -1) {
1270 			/*
1271 			 * when stat succeeds it is not a dangling node
1272 			 * so it is not a special case.
1273 			 */
1274 			    return (NULL);
1275 			}
1276 		    }
1277 		} else if (flag == STANDARD_DEVNAME_HANDLING) {
1278 			/*
1279 			 * See if there's a real file out there.  If not,
1280 			 * we have a dangling link and we ignore it.
1281 			 */
1282 		    if (stat(source, &stbuf) == -1) {
1283 			return (NULL);
1284 		    }
1285 		    if (lstat(source, &stbuf) == -1) {
1286 			return (NULL);
1287 		    }
1288 		} else {
1289 		    /* invalid flag */
1290 		    return (NULL);
1291 		}
1292 
1293 		/*
1294 		 * If the file is not a link, we're done one
1295 		 * way or the other.  If there were links,
1296 		 * return the full pathname of the resulting
1297 		 * file.
1298 		 *
1299 		 * Note:  All of our temp's are on the stack,
1300 		 * so we have to copy the final result to the heap.
1301 		 */
1302 		if (!S_ISLNK(stbuf.st_mode)) {
1303 			phys_path = (char *)calloc(1, strlen(source) + 1);
1304 			if (phys_path != NULL) {
1305 			    (void) strncpy(phys_path, source,
1306 			    strlen(source) + 1);
1307 			}
1308 			return (phys_path);
1309 		}
1310 		cnt = readlink(source, scratch, sizeof (scratch));
1311 		if (cnt < 0) {
1312 			return (NULL);
1313 		}
1314 		/*
1315 		 * scratch is on the heap, and for some reason readlink
1316 		 * doesn't always terminate things properly so we have
1317 		 * to make certain we're properly terminated
1318 		 */
1319 		scratch[cnt] = '\0';
1320 
1321 		/*
1322 		 * Now check to see if the link is relative.  If so,
1323 		 * then we have to append it to the directory
1324 		 * which the source was in. (This is non trivial)
1325 		 */
1326 		if (scratch[0] != '/') {
1327 			tmp = strrchr(source, '/');
1328 			if (tmp == NULL) { /* Whoa!  Something's hosed! */
1329 				O_DPRINTF("Internal error... corrupt path.\n");
1330 				return (NULL);
1331 			}
1332 			/* Now strip off just the directory path */
1333 			*(tmp+1) = '\0'; /* Keeping the last '/' */
1334 			/* and append the new link */
1335 			(void) strcat(source, scratch);
1336 			/*
1337 			 * Note:  At this point, source should have "../"s
1338 			 * but we'll clean it up in the next pass through
1339 			 * the loop.
1340 			 */
1341 		} else {
1342 			/* It's an absolute link so no worries */
1343 			strcpy(source, scratch);
1344 		}
1345 	}
1346 	/* Never reach here */
1347 }
1348 
1349 /*
1350  * Input - Space for client_path, phci_path and paddr fields of ioc structure
1351  * need to be allocated by the caller of this routine.
1352  */
1353 int
1354 get_scsi_vhci_pathinfo(char *dev_path, sv_iocdata_t *ioc, int *path_count)
1355 {
1356 	char	*physical_path, *physical_path_s;
1357 	int	retval;
1358 	int	fd;
1359 	int	initial_path_count;
1360 	int	current_path_count;
1361 	int 	i;
1362 	char	*delimiter;
1363 	int	malloc_error = 0;
1364 	int 	prop_buf_size;
1365 	int	pathlist_retry_count = 0;
1366 
1367 	if (strncmp(dev_path, SCSI_VHCI,
1368 			strlen(SCSI_VHCI)) != NULL) {
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)) != NULL) {
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 = NULL;
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,
1438 					sizeof (sv_path_info_t));
1439 		if (ioc->ret_buf == NULL) {
1440 			close(fd);
1441 			return (L_MALLOC_FAILED);
1442 		}
1443 
1444 		/*
1445 		 * Allocate space for path properties returned by driver
1446 		 */
1447 		malloc_error = 0;
1448 		for (i = 0; i < initial_path_count; i++) {
1449 			ioc->ret_buf[i].ret_prop.buf_size = prop_buf_size;
1450 			if ((ioc->ret_buf[i].ret_prop.buf =
1451 			    (caddr_t)malloc(prop_buf_size)) == NULL) {
1452 				malloc_error = 1;
1453 				break;
1454 			}
1455 			if ((ioc->ret_buf[i].ret_prop.ret_buf_size =
1456 				(uint_t *)malloc(sizeof (uint_t))) == NULL) {
1457 				malloc_error = 1;
1458 				break;
1459 			}
1460 		}
1461 		if (malloc_error == 1) {
1462 			for (i = 0; i < initial_path_count; i++) {
1463 				free(ioc->ret_buf[i].ret_prop.buf);
1464 				free(ioc->ret_buf[i].ret_prop.ret_buf_size);
1465 			}
1466 			free(ioc->ret_buf);
1467 			close(fd);
1468 			return (L_MALLOC_FAILED);
1469 		}
1470 
1471 		retval = ioctl(fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, ioc);
1472 		if (retval != 0) {
1473 			for (i = 0; i < initial_path_count; i++) {
1474 				free(ioc->ret_buf[i].ret_prop.buf);
1475 				free(ioc->ret_buf[i].ret_prop.ret_buf_size);
1476 			}
1477 			free(ioc->ret_buf);
1478 			close(fd);
1479 			return (L_SCSI_VHCI_ERROR);
1480 		}
1481 		if (initial_path_count < current_path_count) {
1482 			/* then a new path was added */
1483 			pathlist_retry_count++;
1484 			initial_path_count = current_path_count;
1485 		} else {
1486 			break;
1487 		}
1488 	}
1489 	/* we are done with ioctl's, lose the fd */
1490 	close(fd);
1491 
1492 	/*
1493 	 * Compare the length num elements from the ioctl response
1494 	 *   and the caller's request - use smaller value.
1495 	 *
1496 	 * pathlist_p->path_count now has count returned from ioctl.
1497 	 * ioc.buf_elem has the value the caller provided.
1498 	 */
1499 	if (initial_path_count < current_path_count) {
1500 		/* More paths exist than we allocated space for */
1501 		*path_count = initial_path_count;
1502 	} else {
1503 		*path_count = current_path_count;
1504 	}
1505 
1506 	return (0);
1507 }
1508 
1509 int
1510 get_mode_page(char *path, uchar_t **pg_buf)
1511 {
1512 struct mode_header_g1	*mode_header_ptr;
1513 int		status, size, fd;
1514 
1515 	/* open controller */
1516 	if ((fd = open(path, O_NDELAY | O_RDWR)) == -1)
1517 		return (-1); /* L_OPEN_PATH_FAIL */
1518 
1519 	/*
1520 	 * Read the first part of the page to get the page size
1521 	 */
1522 	size = 20;
1523 	if ((*pg_buf = (uchar_t *)calloc(1, size)) == NULL) {
1524 	    (void) close(fd);
1525 	    return (L_MALLOC_FAILED);
1526 	}
1527 	/* read page */
1528 	if (status = scsi_mode_sense_cmd(fd, *pg_buf, size,
1529 	    0, MODEPAGE_ALLPAGES)) {
1530 	    (void) close(fd);
1531 	    (void) free(*pg_buf);
1532 	    return (status);
1533 	}
1534 	/* Now get the size for all pages */
1535 	mode_header_ptr = (struct mode_header_g1 *)(void *)*pg_buf;
1536 	size = ntohs(mode_header_ptr->length) +
1537 		sizeof (mode_header_ptr->length);
1538 	(void) free(*pg_buf);
1539 	if ((*pg_buf = (uchar_t *)calloc(1, size)) == NULL) {
1540 	    (void) close(fd);
1541 	    return (L_MALLOC_FAILED);
1542 	}
1543 	/* read all pages */
1544 	if (status = scsi_mode_sense_cmd(fd, *pg_buf, size,
1545 					0, MODEPAGE_ALLPAGES)) {
1546 	    (void) close(fd);
1547 	    (void) free(*pg_buf);
1548 	    return (status);
1549 	}
1550 	(void) close(fd);
1551 	return (0);
1552 }
1553 
1554 /*
1555  * Dump a structure in hexadecimal.
1556  */
1557 void
1558 dump_hex_data(char *hdr, uchar_t *src, int nbytes, int format)
1559 {
1560 	int i;
1561 	int n;
1562 	char	*p;
1563 	char	s[256];
1564 
1565 	assert(format == HEX_ONLY || format == HEX_ASCII);
1566 
1567 	(void) strcpy(s, hdr);
1568 	for (p = s; *p; p++) {
1569 		*p = ' ';
1570 	}
1571 
1572 	p = hdr;
1573 	while (nbytes > 0) {
1574 		(void) fprintf(stdout, "%s", p);
1575 		p = s;
1576 		n = MIN(nbytes, BYTES_PER_LINE);
1577 		for (i = 0; i < n; i++) {
1578 			(void) fprintf(stdout, "%02x ", src[i] & 0xff);
1579 		}
1580 		if (format == HEX_ASCII) {
1581 			for (i = BYTES_PER_LINE-n; i > 0; i--) {
1582 				(void) fprintf(stdout, "   ");
1583 			}
1584 			(void) fprintf(stdout, "    ");
1585 			for (i = 0; i < n; i++) {
1586 				(void) fprintf(stdout, "%c",
1587 					isprint(src[i]) ? src[i] : '.');
1588 			}
1589 		}
1590 		(void) fprintf(stdout, "\n");
1591 		nbytes -= n;
1592 		src += n;
1593 	}
1594 }
1595