xref: /illumos-gate/usr/src/cmd/luxadm/lux_util.c (revision b6805bf78d2bbbeeaea8909a05623587b42d58b3)
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 
64 extern	char	*dtype[]; /* from adm.c */
65 extern	int	rand_r(unsigned int *);
66 
67 static int cleanup_dotdot_path(char *path);
68 static int wait_random_time(void);
69 static char *scsi_find_command_name(int cmd);
70 static void scsi_printerr(struct uscsi_cmd *ucmd,
71 	    struct scsi_extended_sense *rq, int rqlen,
72 	    char msg_string[], char *err_string);
73 static void string_dump(char *hdr, uchar_t *src, int nbytes, int format,
74 	    char msg_string[]);
75 static int issue_uscsi_cmd(int file, struct uscsi_cmd *command, int flag);
76 
77 
78 static int
79 wait_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  */
125 static	void
126 string_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  */
169 static char *
170 scsi_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 
282 static void
283 scsi_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  */
390 static int
391 issue_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 
454 retry:
455 	status = ioctl(file, USCSICMD, command);
456 	if (status == 0 && command->uscsi_status == 0) {
457 		if (getenv("_LUX_S_DEBUG") != NULL) {
458 			if ((command->uscsi_buflen > 0) &&
459 			    (flag & USCSI_READ)) {
460 				(void) dump_hex_data("\tData read:",
461 				    (uchar_t *)command->uscsi_bufaddr,
462 				    MIN(command->uscsi_buflen, 512), HEX_ASCII);
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  */
610 int
611 scsi_mode_sense_cmd(int fd,
612 	uchar_t *buf_ptr,
613 	int buf_len,
614 	uchar_t pc,
615 	uchar_t page_code)
616 {
617 	struct uscsi_cmd	ucmd;
618 	/* 10 byte Mode Select cmd */
619 	union scsi_cdb	cdb =  {SCMD_MODE_SENSE_G1, 0, 0, 0, 0, 0, 0, 0, 0, 0};
620 	struct	scsi_extended_sense	sense;
621 	int		status;
622 	static	int	uscsi_count;
623 
624 	if ((fd < 0) || (buf_ptr == NULL) || (buf_len < 0)) {
625 		return (-1); /* L_INVALID_ARG */
626 	}
627 
628 	(void) memset(buf_ptr, 0, buf_len);
629 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
630 	/* Just for me  - a sanity check */
631 	if ((page_code > MODEPAGE_ALLPAGES) || (pc > 3) ||
632 	    (buf_len > MAX_MODE_SENSE_LEN)) {
633 		return (-1); /* L_ILLEGAL_MODE_SENSE_PAGE */
634 	}
635 	cdb.g1_addr3 = (pc << 6) + page_code;
636 	cdb.g1_count1 = buf_len>>8;
637 	cdb.g1_count0 = buf_len & 0xff;
638 	ucmd.uscsi_cdb = (caddr_t)&cdb;
639 	ucmd.uscsi_cdblen = CDB_GROUP1;
640 	ucmd.uscsi_bufaddr = (caddr_t)buf_ptr;
641 	ucmd.uscsi_buflen = buf_len;
642 	ucmd.uscsi_rqbuf = (caddr_t)&sense;
643 	ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
644 	ucmd.uscsi_timeout = 120;
645 
646 	status = issue_uscsi_cmd(fd, &ucmd, USCSI_READ);
647 	/* Bytes actually transfered */
648 	if (status == 0) {
649 		uscsi_count = buf_len - ucmd.uscsi_resid;
650 		S_DPRINTF("  Number of bytes read on "
651 		"Mode Sense 0x%x\n", uscsi_count);
652 		if (getenv("_LUX_D_DEBUG") != NULL) {
653 			(void) dump_hex_data("  Mode Sense data: ", buf_ptr,
654 			    uscsi_count, HEX_ASCII);
655 		}
656 	}
657 	return (status);
658 }
659 
660 int
661 scsi_release(char *path)
662 {
663 	struct uscsi_cmd	ucmd;
664 	union scsi_cdb		cdb = {SCMD_RELEASE, 0, 0, 0, 0, 0};
665 	struct	scsi_extended_sense	sense;
666 	int	fd, status;
667 
668 	P_DPRINTF("  scsi_release: Release: Path %s\n", path);
669 	if ((fd = open(path, O_NDELAY | O_RDONLY)) == -1)
670 		return (1);
671 
672 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
673 
674 	ucmd.uscsi_cdb = (caddr_t)&cdb;
675 	ucmd.uscsi_cdblen = CDB_GROUP0;
676 	ucmd.uscsi_bufaddr = NULL;
677 	ucmd.uscsi_buflen = 0;
678 	ucmd.uscsi_rqbuf = (caddr_t)&sense;
679 	ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
680 	ucmd.uscsi_timeout = 60;
681 	status = (issue_uscsi_cmd(fd, &ucmd, 0));
682 
683 	(void) close(fd);
684 	return (status);
685 }
686 
687 int
688 scsi_reserve(char *path)
689 {
690 	struct uscsi_cmd	ucmd;
691 	union scsi_cdb	cdb = {SCMD_RESERVE, 0, 0, 0, 0, 0};
692 	struct	scsi_extended_sense	sense;
693 	int	fd, status;
694 
695 	P_DPRINTF("  scsi_reserve: Reserve: Path %s\n", path);
696 	if ((fd = open(path, O_NDELAY | O_RDONLY)) == -1)
697 		return (1);
698 
699 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
700 
701 	ucmd.uscsi_cdb = (caddr_t)&cdb;
702 	ucmd.uscsi_cdblen = CDB_GROUP0;
703 	ucmd.uscsi_bufaddr = NULL;
704 	ucmd.uscsi_buflen = 0;
705 	ucmd.uscsi_rqbuf = (caddr_t)&sense;
706 	ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
707 	ucmd.uscsi_timeout = 60;
708 	status = (issue_uscsi_cmd(fd, &ucmd, 0));
709 
710 	(void) close(fd);
711 	return (status);
712 }
713 
714 /*
715  * Print out fabric dev dtype
716  */
717 void
718 print_fabric_dtype_prop(uchar_t *hba_port_wwn, uchar_t *port_wwn,
719 	uchar_t dtype_prop)
720 {
721 	if ((dtype_prop & DTYPE_MASK) < 0x10) {
722 		(void) fprintf(stdout, " 0x%-2x (%s)\n",
723 		    (dtype_prop & DTYPE_MASK),
724 		    dtype[(dtype_prop & DTYPE_MASK)]);
725 	} else if ((dtype_prop & DTYPE_MASK) < 0x1f) {
726 		(void) fprintf(stdout,
727 		    MSGSTR(2096, " 0x%-2x (Reserved)\n"),
728 		    (dtype_prop & DTYPE_MASK));
729 	} else {
730 		/* Check to see if this is the HBA */
731 		if (wwnConversion(hba_port_wwn) != wwnConversion(port_wwn)) {
732 			(void) fprintf(stdout, MSGSTR(2097,
733 			    " 0x%-2x (Unknown Type)\n"),
734 			    (dtype_prop & DTYPE_MASK));
735 		} else {
736 			/* MATCH */
737 			(void) fprintf(stdout, MSGSTR(2241,
738 			    " 0x%-2x (Unknown Type,Host Bus Adapter)\n"),
739 			    (dtype_prop & DTYPE_MASK));
740 		}
741 	}
742 }
743 
744 
745 void
746 print_inq_data(char *arg_path, char *path, L_inquiry inq, uchar_t *serial,
747     size_t serial_len)
748 {
749 	char	**p;
750 	uchar_t	*v_parm;
751 	int	scsi_3, length;
752 	char	byte_number[MAXNAMELEN];
753 	static	char *scsi_inquiry_labels_2[21];
754 	static	char *scsi_inquiry_labels_3[22];
755 #define	MAX_ANSI_VERSION	6
756 	static	char	*ansi_version[MAX_ANSI_VERSION];
757 	/*
758 	 * Intialize scsi_inquiry_labels_2 with i18n strings
759 	 */
760 	scsi_inquiry_labels_2[0] = MSGSTR(138, "Vendor:                     ");
761 	scsi_inquiry_labels_2[1] = MSGSTR(149, "Product:                    ");
762 	scsi_inquiry_labels_2[2] = MSGSTR(139, "Revision:                   ");
763 	scsi_inquiry_labels_2[3] = MSGSTR(143, "Firmware Revision           ");
764 	scsi_inquiry_labels_2[4] = MSGSTR(144, "Serial Number               ");
765 	scsi_inquiry_labels_2[5] = MSGSTR(140, "Device type:                ");
766 	scsi_inquiry_labels_2[6] = MSGSTR(145, "Removable media:            ");
767 	scsi_inquiry_labels_2[7] = MSGSTR(146, "ISO version:                ");
768 	scsi_inquiry_labels_2[8] = MSGSTR(147, "ECMA version:               ");
769 	scsi_inquiry_labels_2[9] = MSGSTR(148, "ANSI version:               ");
770 	scsi_inquiry_labels_2[10] =
771 	    MSGSTR(2168, "Async event notification:   ");
772 	scsi_inquiry_labels_2[11] =
773 	    MSGSTR(2169, "Terminate i/o process msg:  ");
774 	scsi_inquiry_labels_2[12] = MSGSTR(150, "Response data format:       ");
775 	scsi_inquiry_labels_2[13] = MSGSTR(151, "Additional length:          ");
776 	scsi_inquiry_labels_2[14] = MSGSTR(152, "Relative addressing:        ");
777 	scsi_inquiry_labels_2[15] =
778 	    MSGSTR(2170, "32 bit transfers:           ");
779 	scsi_inquiry_labels_2[16] =
780 	    MSGSTR(2171, "16 bit transfers:           ");
781 	scsi_inquiry_labels_2[17] =
782 	    MSGSTR(2172, "Synchronous transfers:      ");
783 	scsi_inquiry_labels_2[18] = MSGSTR(153, "Linked commands:            ");
784 	scsi_inquiry_labels_2[19] = MSGSTR(154, "Command queueing:           ");
785 	scsi_inquiry_labels_2[20] =
786 	    MSGSTR(2173, "Soft reset option:          ");
787 
788 	/*
789 	 * Intialize scsi_inquiry_labels_3 with i18n strings
790 	 */
791 	scsi_inquiry_labels_3[0] = MSGSTR(138, "Vendor:                     ");
792 	scsi_inquiry_labels_3[1] = MSGSTR(149, "Product:                    ");
793 	scsi_inquiry_labels_3[2] = MSGSTR(139, "Revision:                   ");
794 	scsi_inquiry_labels_3[3] = MSGSTR(143, "Firmware Revision           ");
795 	scsi_inquiry_labels_3[4] = MSGSTR(144, "Serial Number               ");
796 	scsi_inquiry_labels_3[5] = MSGSTR(140, "Device type:                ");
797 	scsi_inquiry_labels_3[6] = MSGSTR(145, "Removable media:            ");
798 	scsi_inquiry_labels_3[7] = MSGSTR(2174, "Medium Changer Element:     ");
799 	scsi_inquiry_labels_3[8] = MSGSTR(146, "ISO version:                ");
800 	scsi_inquiry_labels_3[9] = MSGSTR(147, "ECMA version:               ");
801 	scsi_inquiry_labels_3[10] = MSGSTR(148, "ANSI version:               ");
802 	scsi_inquiry_labels_3[11] =
803 	    MSGSTR(2175, "Async event reporting:      ");
804 	scsi_inquiry_labels_3[12] =
805 	    MSGSTR(2176, "Terminate task:             ");
806 	scsi_inquiry_labels_3[13] =
807 	    MSGSTR(2177, "Normal ACA Supported:       ");
808 	scsi_inquiry_labels_3[14] = MSGSTR(150, "Response data format:       ");
809 	scsi_inquiry_labels_3[15] = MSGSTR(151, "Additional length:          ");
810 	scsi_inquiry_labels_3[16] =
811 	    MSGSTR(2178, "Cmd received on port:       ");
812 	scsi_inquiry_labels_3[17] =
813 	    MSGSTR(2179, "SIP Bits:                   ");
814 	scsi_inquiry_labels_3[18] = MSGSTR(152, "Relative addressing:        ");
815 	scsi_inquiry_labels_3[19] = MSGSTR(153, "Linked commands:            ");
816 	scsi_inquiry_labels_3[20] =
817 	    MSGSTR(2180, "Transfer Disable:           ");
818 	scsi_inquiry_labels_3[21] = MSGSTR(154, "Command queueing:           ");
819 
820 	/*
821 	 * Intialize scsi_inquiry_labels_3 with i18n strings
822 	 */
823 	ansi_version[0] = MSGSTR(2181,
824 	    " (Device might or might not comply to an ANSI version)");
825 	ansi_version[1] = MSGSTR(2182,
826 	    " (This code is reserved for historical uses)");
827 	ansi_version[2] = MSGSTR(2183,
828 	    " (Device complies to ANSI X3.131-1994 (SCSI-2))");
829 	ansi_version[3] = MSGSTR(2184,
830 	    " (Device complies to ANSI INCITS 301-1997 (SPC))");
831 	ansi_version[4] = MSGSTR(2226,
832 	    " (Device complies to ANSI INCITS 351-2001 (SPC-2))");
833 	ansi_version[5] = MSGSTR(2227,
834 	    " (Device complies to ANSI INCITS 408-2005 (SPC-3))");
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 (", *p++, (inq.inq_dtype & DTYPE_MASK));
907 	if ((inq.inq_dtype & DTYPE_MASK) < 0x10) {
908 		(void) fprintf(stdout, "%s", dtype[inq.inq_dtype & DTYPE_MASK]);
909 	} else if ((inq.inq_dtype & DTYPE_MASK) < 0x1f) {
910 		(void) fprintf(stdout, MSGSTR(71, "Reserved"));
911 	} else {
912 		(void) fprintf(stdout, MSGSTR(2186, "Unknown device"));
913 	}
914 	(void) fprintf(stdout, ")\n");
915 
916 	(void) fprintf(stdout, "%s", *p++);
917 	if (inq.inq_rmb != NULL) {
918 		(void) fprintf(stdout, MSGSTR(40, "yes"));
919 	} else {
920 		(void) fprintf(stdout, MSGSTR(45, "no"));
921 	}
922 	(void) fprintf(stdout, "\n");
923 
924 	if (scsi_3) {
925 		(void) fprintf(stdout, "%s", *p++);
926 		if (inq.inq_mchngr != NULL) {
927 			(void) fprintf(stdout, MSGSTR(40, "yes"));
928 		} else {
929 			(void) fprintf(stdout, MSGSTR(45, "no"));
930 		}
931 		(void) fprintf(stdout, "\n");
932 	}
933 	(void) fprintf(stdout, "%s%d\n", *p++, inq.inq_iso);
934 	(void) fprintf(stdout, "%s%d\n", *p++, inq.inq_ecma);
935 
936 	(void) fprintf(stdout, "%s%d", *p++, inq.inq_ansi);
937 	if (inq.inq_ansi < MAX_ANSI_VERSION) {
938 		(void) fprintf(stdout, "%s", ansi_version[inq.inq_ansi]);
939 	} else
940 		(void) fprintf(stdout, " (%s)", MSGSTR(71, "Reserved"));
941 
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 		if (inq.ui.inq_2.inq_wbus32) {
1002 			(void) fprintf(stdout, "%s", *p);
1003 			(void) fprintf(stdout, MSGSTR(40, "yes"));
1004 			(void) fprintf(stdout, "\n");
1005 		}
1006 		p++;
1007 
1008 		if (inq.ui.inq_2.inq_wbus16) {
1009 			(void) fprintf(stdout, "%s", *p);
1010 			(void) fprintf(stdout, MSGSTR(40, "yes"));
1011 			(void) fprintf(stdout, "\n");
1012 		}
1013 		p++;
1014 
1015 		if (inq.ui.inq_2.inq_sync) {
1016 			(void) fprintf(stdout, "%s", *p);
1017 			(void) fprintf(stdout, MSGSTR(40, "yes"));
1018 			(void) fprintf(stdout, "\n");
1019 		}
1020 		p++;
1021 
1022 	}
1023 	if (inq.ui.inq_2.inq_linked) {
1024 		(void) fprintf(stdout, "%s", *p);
1025 		(void) fprintf(stdout, MSGSTR(40, "yes"));
1026 		(void) fprintf(stdout, "\n");
1027 	}
1028 	p++;
1029 
1030 	if (scsi_3) {
1031 		(void) fprintf(stdout, "%s", *p++);
1032 		if (inq.ui.inq_3.inq_trandis != NULL) {
1033 			(void) fprintf(stdout, MSGSTR(40, "yes"));
1034 		} else {
1035 			(void) fprintf(stdout, MSGSTR(45, "no"));
1036 		}
1037 		(void) fprintf(stdout, "\n");
1038 	}
1039 
1040 	if (inq.ui.inq_2.inq_cmdque) {
1041 		(void) fprintf(stdout, "%s", *p);
1042 		(void) fprintf(stdout, MSGSTR(40, "yes"));
1043 		(void) fprintf(stdout, "\n");
1044 	}
1045 	p++;
1046 
1047 	if (!scsi_3) {
1048 		if (inq.ui.inq_2.inq_sftre) {
1049 			(void) fprintf(stdout, "%s", *p);
1050 			(void) fprintf(stdout, MSGSTR(40, "yes"));
1051 			(void) fprintf(stdout, "\n");
1052 		}
1053 		p++;
1054 
1055 	}
1056 
1057 	/*
1058 	 * Now print the vendor-specific data.
1059 	 */
1060 	v_parm = inq.inq_ven_specific_1;
1061 	if (inq.inq_len >= 32) {
1062 		length = inq.inq_len - 31;
1063 		if (strstr((char *)inq.inq_pid, "SSA") != 0) {
1064 			(void) fprintf(stdout, MSGSTR(2189,
1065 			    "Number of Ports, Targets:   %d,%d\n"),
1066 			    inq.inq_ssa_ports, inq.inq_ssa_tgts);
1067 			v_parm += 20;
1068 			length -= 20;
1069 		} else if ((strstr((char *)inq.inq_pid, "SUN") != 0) ||
1070 		    (strncmp((char *)inq.inq_vid, "SUN     ",
1071 		    sizeof (inq.inq_vid)) == 0)) {
1072 			v_parm += 16;
1073 			length -= 16;
1074 		}
1075 		/*
1076 		 * Do hex Dump of rest of the data.
1077 		 */
1078 		if (length > 0) {
1079 			(void) fprintf(stdout,
1080 			    MSGSTR(2190,
1081 			"              VENDOR-SPECIFIC PARAMETERS\n"));
1082 			(void) fprintf(stdout,
1083 			    MSGSTR(2191,
1084 			    "Byte#                  Hex Value            "
1085 			    "                 ASCII\n"));
1086 			(void) sprintf(byte_number,
1087 			    "%d    ", inq.inq_len - length + 5);
1088 			dump_hex_data(byte_number, v_parm,
1089 			    MIN(length, inq.inq_res3 - v_parm), HEX_ASCII);
1090 		}
1091 		/*
1092 		 * Skip reserved bytes 56-95.
1093 		 */
1094 		length -= (inq.inq_box_name - v_parm);
1095 		if (length > 0) {
1096 			(void) sprintf(byte_number, "%d    ",
1097 			    inq.inq_len - length + 5);
1098 			dump_hex_data(byte_number, inq.inq_box_name,
1099 			    MIN(length, sizeof (inq.inq_box_name) +
1100 			    sizeof (inq.inq_avu)), HEX_ASCII);
1101 		}
1102 	}
1103 	if (getenv("_LUX_D_DEBUG") != NULL) {
1104 		dump_hex_data("\nComplete Inquiry: ",
1105 		    (uchar_t *)&inq,
1106 		    MIN(inq.inq_len + 5, sizeof (inq)), HEX_ASCII);
1107 	}
1108 }
1109 
1110 /*
1111  * Internal routine to clean up ../'s in paths.
1112  * returns 0 if no "../" are left.
1113  *
1114  * Wouldn't it be nice if there was a standard system library
1115  * routine to do this...?
1116  */
1117 static int
1118 cleanup_dotdot_path(char *path)
1119 {
1120 	char holder[MAXPATHLEN];
1121 	char *dotdot;
1122 	char *previous_slash;
1123 
1124 	/* Find the first "/../" in the string */
1125 	dotdot = strstr(path, "/../");
1126 	if (dotdot == NULL) {
1127 		return (0);
1128 	}
1129 
1130 
1131 	/*
1132 	 * If the [0] character is '/' and "../" immediatly
1133 	 * follows it, then we can strip the ../
1134 	 *
1135 	 *	/../../foo/bar == /foo/bar
1136 	 *
1137 	 */
1138 	if (dotdot == path) {
1139 		strcpy(holder, &path[3]); /* strip "/.." */
1140 		strcpy(path, holder);
1141 		return (1);
1142 	}
1143 
1144 	/*
1145 	 * Now look for the LAST "/" before the "/../"
1146 	 * as this is the parent dir we can get rid of.
1147 	 * We do this by temporarily truncating the string
1148 	 * at the '/' just before "../" using the dotdot pointer.
1149 	 */
1150 	*dotdot = '\0';
1151 	previous_slash = strrchr(path, '/');
1152 	if (previous_slash == NULL) {
1153 		/*
1154 		 * hmm, somethings wrong.  path looks something
1155 		 * like "foo/../bar/" so we can't really deal with it.
1156 		 */
1157 		return (0);
1158 	}
1159 	/*
1160 	 * Now truncate the path just after the previous '/'
1161 	 * and slam everything after the "../" back on
1162 	 */
1163 	*(previous_slash+1) = '\0';
1164 	(void) strcat(path, dotdot+4);
1165 	return (1); /* We may have more "../"s */
1166 }
1167 
1168 /*
1169  * Follow symbolic links from the logical device name to
1170  * the /devfs physical device name.  To be complete, we
1171  * handle the case of multiple links.  This function
1172  * either returns NULL (no links, or some other error),
1173  * or the physical device name, alloc'ed on the heap.
1174  *
1175  * NOTE: If the path is relative, it will be forced into
1176  * an absolute path by pre-pending the pwd to it.
1177  */
1178 char *
1179 get_slash_devices_from_osDevName(char *osDevName, int flag)
1180 {
1181 	struct stat	stbuf;
1182 	char		source[MAXPATHLEN];
1183 	char		scratch[MAXPATHLEN];
1184 	char		pwd[MAXPATHLEN];
1185 	char		*tmp, *phys_path;
1186 	int		cnt;
1187 	boolean_t	is_lstat_failed = B_TRUE;
1188 
1189 	/* return NULL if path is NULL */
1190 	if (osDevName == NULL) {
1191 		return (NULL);
1192 	}
1193 
1194 	strcpy(source, osDevName);
1195 	for (;;) {
1196 
1197 		/*
1198 		 * First make sure the path is absolute.  If not, make it.
1199 		 * If it's already an absolute path, we have no need
1200 		 * to determine the cwd, so the program should still
1201 		 * function within security-by-obscurity directories.
1202 		 */
1203 		if (source[0] != '/') {
1204 			tmp = getcwd(pwd, MAXPATHLEN);
1205 			if (tmp == NULL) {
1206 				return (NULL);
1207 			}
1208 			/*
1209 			 * Handle special case of "./foo/bar"
1210 			 */
1211 			if (source[0] == '.' && source[1] == '/') {
1212 				strcpy(scratch, source+2);
1213 			} else { /* no "./" so just take everything */
1214 				strcpy(scratch, source);
1215 			}
1216 			strcpy(source, pwd);
1217 			(void) strcat(source, "/");
1218 			(void) strcat(source, scratch);
1219 		}
1220 
1221 		/*
1222 		 * Clean up any "../"s that are in the path
1223 		 */
1224 		while (cleanup_dotdot_path(source))
1225 			;
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
1246 					 * dangling node.
1247 					 */
1248 					phys_path = (char *)calloc(1,
1249 					    strlen(source) + 1);
1250 					if (phys_path != NULL) {
1251 						(void) strncpy(phys_path,
1252 						    source, strlen(source) + 1);
1253 					}
1254 					return (phys_path);
1255 				} else if (is_lstat_failed) {
1256 					/* check lstat result. */
1257 					if (lstat(source, &stbuf) == -1) {
1258 						return (NULL);
1259 					} else {
1260 						/* and continue */
1261 						is_lstat_failed = B_FALSE;
1262 					}
1263 				} else {
1264 					/*
1265 					 * With algorithm that resolves a link
1266 					 * and then issues readlink(), should
1267 					 * not be reached here.
1268 					 */
1269 					return (NULL);
1270 				}
1271 			} else {
1272 				if (lstat(source, &stbuf) == -1) {
1273 					/*
1274 					 * when stat succeeds it is not
1275 					 * a dangling node so it is not
1276 					 * a special case.
1277 					 */
1278 					return (NULL);
1279 				}
1280 			}
1281 		} else if (flag == STANDARD_DEVNAME_HANDLING) {
1282 			/*
1283 			 * See if there's a real file out there.  If not,
1284 			 * we have a dangling link and we ignore it.
1285 			 */
1286 			if (stat(source, &stbuf) == -1) {
1287 				return (NULL);
1288 			}
1289 			if (lstat(source, &stbuf) == -1) {
1290 				return (NULL);
1291 			}
1292 		} else {
1293 			/* invalid flag */
1294 			return (NULL);
1295 		}
1296 
1297 		/*
1298 		 * If the file is not a link, we're done one
1299 		 * way or the other.  If there were links,
1300 		 * return the full pathname of the resulting
1301 		 * file.
1302 		 *
1303 		 * Note:  All of our temp's are on the stack,
1304 		 * so we have to copy the final result to the heap.
1305 		 */
1306 		if (!S_ISLNK(stbuf.st_mode)) {
1307 			phys_path = (char *)calloc(1, strlen(source) + 1);
1308 			if (phys_path != NULL) {
1309 				(void) strncpy(phys_path, source,
1310 				    strlen(source) + 1);
1311 			}
1312 			return (phys_path);
1313 		}
1314 		cnt = readlink(source, scratch, sizeof (scratch));
1315 		if (cnt < 0) {
1316 			return (NULL);
1317 		}
1318 		/*
1319 		 * scratch is on the heap, and for some reason readlink
1320 		 * doesn't always terminate things properly so we have
1321 		 * to make certain we're properly terminated
1322 		 */
1323 		scratch[cnt] = '\0';
1324 
1325 		/*
1326 		 * Now check to see if the link is relative.  If so,
1327 		 * then we have to append it to the directory
1328 		 * which the source was in. (This is non trivial)
1329 		 */
1330 		if (scratch[0] != '/') {
1331 			tmp = strrchr(source, '/');
1332 			if (tmp == NULL) { /* Whoa!  Something's hosed! */
1333 				O_DPRINTF("Internal error... corrupt path.\n");
1334 				return (NULL);
1335 			}
1336 			/* Now strip off just the directory path */
1337 			*(tmp+1) = '\0'; /* Keeping the last '/' */
1338 			/* and append the new link */
1339 			(void) strcat(source, scratch);
1340 			/*
1341 			 * Note:  At this point, source should have "../"s
1342 			 * but we'll clean it up in the next pass through
1343 			 * the loop.
1344 			 */
1345 		} else {
1346 			/* It's an absolute link so no worries */
1347 			strcpy(source, scratch);
1348 		}
1349 	}
1350 	/* Never reach here */
1351 }
1352 
1353 /*
1354  * Input - Space for client_path, phci_path and paddr fields of ioc structure
1355  * need to be allocated by the caller of this routine.
1356  */
1357 int
1358 get_scsi_vhci_pathinfo(char *dev_path, sv_iocdata_t *ioc, int *path_count)
1359 {
1360 	char	*physical_path, *physical_path_s;
1361 	int	retval;
1362 	int	fd;
1363 	int	initial_path_count;
1364 	int	current_path_count;
1365 	int 	i;
1366 	char	*delimiter;
1367 	int	malloc_error = 0;
1368 	int 	prop_buf_size;
1369 	int	pathlist_retry_count = 0;
1370 
1371 	if (strncmp(dev_path, SCSI_VHCI, strlen(SCSI_VHCI)) != NULL) {
1372 		if ((physical_path = get_slash_devices_from_osDevName(
1373 		    dev_path, STANDARD_DEVNAME_HANDLING)) == NULL) {
1374 			return (L_INVALID_PATH);
1375 		}
1376 		if (strncmp(physical_path, SCSI_VHCI,
1377 		    strlen(SCSI_VHCI)) != NULL) {
1378 			free(physical_path);
1379 			return (L_INVALID_PATH);
1380 		}
1381 	} else {
1382 		if ((physical_path = calloc(1, MAXPATHLEN)) == NULL) {
1383 			return (L_MALLOC_FAILED);
1384 		}
1385 		(void) strcpy(physical_path, dev_path);
1386 	}
1387 	physical_path_s = physical_path;
1388 
1389 	/* move beyond "/devices" prefix */
1390 	physical_path += DEV_PREFIX_STRLEN-1;
1391 	/* remove  :c,raw suffix */
1392 	delimiter = strrchr(physical_path, ':');
1393 	/* if we didn't find the ':' fine, else truncate */
1394 	if (delimiter != NULL) {
1395 		*delimiter = NULL;
1396 	}
1397 
1398 	/*
1399 	 * We'll call ioctl SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO
1400 	 * at least twice.  The first time will get the path count
1401 	 * and the size of the ioctl propoerty buffer.  The second
1402 	 * time will get the path_info for each path.
1403 	 *
1404 	 * It's possible that additional paths are added while this
1405 	 * code is running.  If the path count increases between the
1406 	 * 2 ioctl's above, then we'll retry (and assume all is well).
1407 	 */
1408 	(void) strcpy(ioc->client, physical_path);
1409 	ioc->buf_elem = 1;
1410 	ioc->ret_elem = (uint_t *)&(initial_path_count);
1411 	ioc->ret_buf = NULL;
1412 
1413 	/* free physical path */
1414 	free(physical_path_s);
1415 
1416 	/* 0 buf_size asks driver to return actual size needed */
1417 	/* open the ioctl file descriptor */
1418 	if ((fd = open("/devices/scsi_vhci:devctl", O_RDWR)) < 0) {
1419 		return (L_OPEN_PATH_FAIL);
1420 	}
1421 
1422 	retval = ioctl(fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, ioc);
1423 	if (retval != 0) {
1424 		close(fd);
1425 		return (L_SCSI_VHCI_ERROR);
1426 	}
1427 	prop_buf_size = SV_PROP_MAX_BUF_SIZE;
1428 
1429 
1430 	while (pathlist_retry_count <= RETRY_PATHLIST) {
1431 		ioc->buf_elem = initial_path_count;
1432 		/* Make driver put actual # paths in variable */
1433 		ioc->ret_elem = (uint_t *)&(current_path_count);
1434 
1435 		/*
1436 		 * Allocate space for array of path_info structures.
1437 		 * Allocate enough space for # paths from get_pathcount
1438 		 */
1439 		ioc->ret_buf = (sv_path_info_t *)
1440 		    calloc(initial_path_count, sizeof (sv_path_info_t));
1441 		if (ioc->ret_buf == NULL) {
1442 			close(fd);
1443 			return (L_MALLOC_FAILED);
1444 		}
1445 
1446 		/*
1447 		 * Allocate space for path properties returned by driver
1448 		 */
1449 		malloc_error = 0;
1450 		for (i = 0; i < initial_path_count; i++) {
1451 			ioc->ret_buf[i].ret_prop.buf_size = prop_buf_size;
1452 			if ((ioc->ret_buf[i].ret_prop.buf =
1453 			    (caddr_t)malloc(prop_buf_size)) == NULL) {
1454 				malloc_error = 1;
1455 				break;
1456 			}
1457 			if ((ioc->ret_buf[i].ret_prop.ret_buf_size =
1458 			    (uint_t *)malloc(sizeof (uint_t))) == NULL) {
1459 				malloc_error = 1;
1460 				break;
1461 			}
1462 		}
1463 		if (malloc_error == 1) {
1464 			for (i = 0; i < initial_path_count; i++) {
1465 				free(ioc->ret_buf[i].ret_prop.buf);
1466 				free(ioc->ret_buf[i].ret_prop.ret_buf_size);
1467 			}
1468 			free(ioc->ret_buf);
1469 			close(fd);
1470 			return (L_MALLOC_FAILED);
1471 		}
1472 
1473 		retval = ioctl(fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, ioc);
1474 		if (retval != 0) {
1475 			for (i = 0; i < initial_path_count; i++) {
1476 				free(ioc->ret_buf[i].ret_prop.buf);
1477 				free(ioc->ret_buf[i].ret_prop.ret_buf_size);
1478 			}
1479 			free(ioc->ret_buf);
1480 			close(fd);
1481 			return (L_SCSI_VHCI_ERROR);
1482 		}
1483 		if (initial_path_count < current_path_count) {
1484 			/* then a new path was added */
1485 			pathlist_retry_count++;
1486 			initial_path_count = current_path_count;
1487 		} else {
1488 			break;
1489 		}
1490 	}
1491 	/* we are done with ioctl's, lose the fd */
1492 	close(fd);
1493 
1494 	/*
1495 	 * Compare the length num elements from the ioctl response
1496 	 *   and the caller's request - use smaller value.
1497 	 *
1498 	 * pathlist_p->path_count now has count returned from ioctl.
1499 	 * ioc.buf_elem has the value the caller provided.
1500 	 */
1501 	if (initial_path_count < current_path_count) {
1502 		/* More paths exist than we allocated space for */
1503 		*path_count = initial_path_count;
1504 	} else {
1505 		*path_count = current_path_count;
1506 	}
1507 
1508 	return (0);
1509 }
1510 
1511 int
1512 get_mode_page(char *path, uchar_t **pg_buf)
1513 {
1514 	struct mode_header_g1	*mode_header_ptr;
1515 	int		status, size, fd;
1516 
1517 	/* open controller */
1518 	if ((fd = open(path, O_NDELAY | O_RDWR)) == -1)
1519 		return (-1); /* L_OPEN_PATH_FAIL */
1520 
1521 	/*
1522 	 * Read the first part of the page to get the page size
1523 	 */
1524 	size = 20;
1525 	if ((*pg_buf = (uchar_t *)calloc(1, size)) == NULL) {
1526 		(void) close(fd);
1527 		return (L_MALLOC_FAILED);
1528 	}
1529 	/* read page */
1530 	if (status = scsi_mode_sense_cmd(fd, *pg_buf, size,
1531 	    0, MODEPAGE_ALLPAGES)) {
1532 		(void) close(fd);
1533 		(void) free(*pg_buf);
1534 		return (status);
1535 	}
1536 	/* Now get the size for all pages */
1537 	mode_header_ptr = (struct mode_header_g1 *)(void *)*pg_buf;
1538 	size = ntohs(mode_header_ptr->length) +
1539 	    sizeof (mode_header_ptr->length);
1540 	(void) free(*pg_buf);
1541 	if ((*pg_buf = (uchar_t *)calloc(1, size)) == NULL) {
1542 		(void) close(fd);
1543 		return (L_MALLOC_FAILED);
1544 	}
1545 	/* read all pages */
1546 	if (status = scsi_mode_sense_cmd(fd, *pg_buf, size,
1547 	    0, MODEPAGE_ALLPAGES)) {
1548 		(void) close(fd);
1549 		(void) free(*pg_buf);
1550 		return (status);
1551 	}
1552 	(void) close(fd);
1553 	return (0);
1554 }
1555 
1556 /*
1557  * Dump a structure in hexadecimal.
1558  */
1559 void
1560 dump_hex_data(char *hdr, uchar_t *src, int nbytes, int format)
1561 {
1562 	int i;
1563 	int n;
1564 	char	*p;
1565 	char	s[256];
1566 
1567 	assert(format == HEX_ONLY || format == HEX_ASCII);
1568 
1569 	(void) strcpy(s, hdr);
1570 	for (p = s; *p; p++) {
1571 		*p = ' ';
1572 	}
1573 
1574 	p = hdr;
1575 	while (nbytes > 0) {
1576 		(void) fprintf(stdout, "%s", p);
1577 		p = s;
1578 		n = MIN(nbytes, BYTES_PER_LINE);
1579 		for (i = 0; i < n; i++) {
1580 			(void) fprintf(stdout, "%02x ", src[i] & 0xff);
1581 		}
1582 		if (format == HEX_ASCII) {
1583 			for (i = BYTES_PER_LINE-n; i > 0; i--) {
1584 				(void) fprintf(stdout, "   ");
1585 			}
1586 			(void) fprintf(stdout, "    ");
1587 			for (i = 0; i < n; i++) {
1588 				(void) fprintf(stdout, "%c",
1589 				    isprint(src[i]) ? src[i] : '.');
1590 			}
1591 		}
1592 		(void) fprintf(stdout, "\n");
1593 		nbytes -= n;
1594 		src += n;
1595 	}
1596 }
1597