xref: /illumos-gate/usr/src/cmd/luxadm/lux_util.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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 2009 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 	 * Get the system time and use "system seconds"
91 	 * as 'seed' to generate a random number. Then,
92 	 * wait between 1/10 - 1/2 seconds before retry.
93 	 * Get the current process id and ex-or it with
94 	 * the seed so that the random number is always
95 	 * different even in case of multiple processes
96 	 * generate a random number at the same time.
97 	 */
98 	if ((timeval = time(NULL)) == -1) {
99 		return (errno);
100 	}
101 	if ((tmbuf = localtime(&timeval)) == NULL) {
102 		return (-1); /* L_LOCALTIME_ERROR */
103 	}
104 
105 	pid = getpid();
106 
107 	/* get a random number. */
108 	seed = (unsigned int) tmbuf->tm_sec;
109 	seed ^= pid;
110 	random = rand_r(&seed);
111 
112 
113 	random = ((random % 500) + 100) * MILLISEC;
114 	tval.tv_sec = random / MICROSEC;
115 	tval.tv_usec = random % MICROSEC;
116 
117 	if (select(0, NULL, NULL, NULL, &tval) == -1) {
118 		return (-1); /* L_SELECT_ERROR */
119 	}
120 	return (0);
121 }
122 
123 /*
124  *		Special string dump for error message
125  */
126 static	void
127 string_dump(char *hdr, uchar_t *src, int nbytes, int format, char msg_string[])
128 {
129 	int i;
130 	int n;
131 	char	*p;
132 	char	s[256];
133 
134 	assert(format == HEX_ONLY || format == HEX_ASCII);
135 
136 	(void) strcpy(s, hdr);
137 	for (p = s; *p; p++) {
138 		*p = ' ';
139 	}
140 
141 	p = hdr;
142 	while (nbytes > 0) {
143 		(void) sprintf(&msg_string[strlen(msg_string)], "%s", p);
144 		p = s;
145 		n = MIN(nbytes, BYTES_PER_LINE);
146 		for (i = 0; i < n; i++) {
147 			(void) sprintf(&msg_string[strlen(msg_string)],
148 			    "%02x ", src[i] & 0xff);
149 		}
150 		if (format == HEX_ASCII) {
151 			for (i = BYTES_PER_LINE-n; i > 0; i--) {
152 				(void) sprintf(&msg_string[strlen(msg_string)],
153 				    "   ");
154 			}
155 			(void) sprintf(&msg_string[strlen(msg_string)],
156 			    "    ");
157 			for (i = 0; i < n; i++) {
158 				(void) sprintf(&msg_string[strlen(msg_string)],
159 				    "%c", isprint(src[i]) ? src[i] : '.');
160 			}
161 		}
162 		(void) sprintf(&msg_string[strlen(msg_string)], "\n");
163 		nbytes -= n;
164 		src += n;
165 	}
166 }
167 /*
168  * Return a pointer to a string telling us the name of the command.
169  */
170 static char *
171 scsi_find_command_name(int cmd)
172 {
173 	/*
174 	 * Names of commands.  Must have SCMD_UNKNOWN at end of list.
175 	 */
176 	struct scsi_command_name {
177 		int command;
178 		char	*name;
179 	} scsi_command_names[29];
180 
181 	register struct scsi_command_name *c;
182 
183 	scsi_command_names[0].command = SCMD_TEST_UNIT_READY;
184 	scsi_command_names[0].name = MSGSTR(61, "Test Unit Ready");
185 
186 	scsi_command_names[1].command = SCMD_FORMAT;
187 	scsi_command_names[1].name = MSGSTR(110, "Format");
188 
189 	scsi_command_names[2].command = SCMD_REASSIGN_BLOCK;
190 	scsi_command_names[2].name = MSGSTR(77, "Reassign Block");
191 
192 	scsi_command_names[3].command = SCMD_READ;
193 	scsi_command_names[3].name = MSGSTR(27, "Read");
194 
195 	scsi_command_names[4].command = SCMD_WRITE;
196 	scsi_command_names[4].name = MSGSTR(54, "Write");
197 
198 	scsi_command_names[5].command = SCMD_READ_G1;
199 	scsi_command_names[5].name = MSGSTR(79, "Read(10 Byte)");
200 
201 	scsi_command_names[6].command = SCMD_WRITE_G1;
202 	scsi_command_names[6].name = MSGSTR(51, "Write(10 Byte)");
203 
204 	scsi_command_names[7].command = SCMD_MODE_SELECT;
205 	scsi_command_names[7].name = MSGSTR(97, "Mode Select");
206 
207 	scsi_command_names[8].command = SCMD_MODE_SENSE;
208 	scsi_command_names[8].name = MSGSTR(95, "Mode Sense");
209 
210 	scsi_command_names[9].command = SCMD_REASSIGN_BLOCK;
211 	scsi_command_names[9].name = MSGSTR(77, "Reassign Block");
212 
213 	scsi_command_names[10].command = SCMD_REQUEST_SENSE;
214 	scsi_command_names[10].name = MSGSTR(74, "Request Sense");
215 
216 	scsi_command_names[11].command = SCMD_READ_DEFECT_LIST;
217 	scsi_command_names[11].name = MSGSTR(80, "Read Defect List");
218 
219 	scsi_command_names[12].command = SCMD_INQUIRY;
220 	scsi_command_names[12].name = MSGSTR(102, "Inquiry");
221 
222 	scsi_command_names[13].command = SCMD_WRITE_BUFFER;
223 	scsi_command_names[13].name = MSGSTR(53, "Write Buffer");
224 
225 	scsi_command_names[14].command = SCMD_READ_BUFFER;
226 	scsi_command_names[14].name = MSGSTR(82, "Read Buffer");
227 
228 	scsi_command_names[15].command = SCMD_START_STOP;
229 	scsi_command_names[15].name = MSGSTR(67, "Start/Stop");
230 
231 	scsi_command_names[16].command = SCMD_RESERVE;
232 	scsi_command_names[16].name = MSGSTR(72, "Reserve");
233 
234 	scsi_command_names[17].command = SCMD_RELEASE;
235 	scsi_command_names[17].name = MSGSTR(75, "Release");
236 
237 	scsi_command_names[18].command = SCMD_MODE_SENSE_G1;
238 	scsi_command_names[18].name = MSGSTR(94, "Mode Sense(10 Byte)");
239 
240 	scsi_command_names[19].command = SCMD_MODE_SELECT_G1;
241 	scsi_command_names[19].name = MSGSTR(96, "Mode Select(10 Byte)");
242 
243 	scsi_command_names[20].command = SCMD_READ_CAPACITY;
244 	scsi_command_names[20].name = MSGSTR(81, "Read Capacity");
245 
246 	scsi_command_names[21].command = SCMD_SYNC_CACHE;
247 	scsi_command_names[21].name = MSGSTR(64, "Synchronize Cache");
248 
249 	scsi_command_names[22].command = SCMD_READ_DEFECT_LIST;
250 	scsi_command_names[22].name = MSGSTR(80, "Read Defect List");
251 
252 	scsi_command_names[23].command = SCMD_GDIAG;
253 	scsi_command_names[23].name = MSGSTR(108, "Get Diagnostic");
254 
255 	scsi_command_names[24].command = SCMD_SDIAG;
256 	scsi_command_names[24].name = MSGSTR(69, "Set Diagnostic");
257 
258 	scsi_command_names[25].command = SCMD_PERS_RESERV_IN;
259 	scsi_command_names[25].name = MSGSTR(10500, "Persistent Reserve In");
260 
261 	scsi_command_names[26].command = SCMD_PERS_RESERV_OUT;
262 	scsi_command_names[26].name = MSGSTR(10501, "Persistent Reserve out");
263 
264 	scsi_command_names[27].command = SCMD_LOG_SENSE;
265 	scsi_command_names[27].name = MSGSTR(10502, "Log Sense");
266 
267 	scsi_command_names[28].command = SCMD_UNKNOWN;
268 	scsi_command_names[28].name = MSGSTR(25, "Unknown");
269 
270 
271 	for (c = scsi_command_names; c->command != SCMD_UNKNOWN; c++)
272 		if (c->command == cmd)
273 			break;
274 	return (c->name);
275 }
276 
277 
278 /*
279  *	Function to create error message containing
280  *	scsi request sense information
281  */
282 
283 static void
284 scsi_printerr(struct uscsi_cmd *ucmd, struct scsi_extended_sense *rq,
285 		int rqlen, char msg_string[], char *err_string)
286 {
287 	int		blkno;
288 
289 	switch (rq->es_key) {
290 	case KEY_NO_SENSE:
291 		(void) sprintf(msg_string, MSGSTR(91, "No sense error"));
292 		break;
293 	case KEY_RECOVERABLE_ERROR:
294 		(void) sprintf(msg_string, MSGSTR(76, "Recoverable error"));
295 		break;
296 	case KEY_NOT_READY:
297 		(void) sprintf(msg_string,
298 		    MSGSTR(10503,
299 		    "Device Not ready. Error: Random Retry Failed: %s\n."),
300 		    err_string);
301 		break;
302 	case KEY_MEDIUM_ERROR:
303 		(void) sprintf(msg_string, MSGSTR(99, "Medium error"));
304 		break;
305 	case KEY_HARDWARE_ERROR:
306 		(void) sprintf(msg_string, MSGSTR(106, "Hardware error"));
307 		break;
308 	case KEY_ILLEGAL_REQUEST:
309 		(void) sprintf(msg_string, MSGSTR(103, "Illegal request"));
310 		break;
311 	case KEY_UNIT_ATTENTION:
312 		(void) sprintf(msg_string,
313 		    MSGSTR(10504,
314 		    "Unit attention."
315 		    "Error: Random Retry Failed.\n"));
316 		break;
317 	case KEY_WRITE_PROTECT:
318 		(void) sprintf(msg_string, MSGSTR(52, "Write protect error"));
319 		break;
320 	case KEY_BLANK_CHECK:
321 		(void) sprintf(msg_string, MSGSTR(131, "Blank check error"));
322 		break;
323 	case KEY_VENDOR_UNIQUE:
324 		(void) sprintf(msg_string, MSGSTR(58, "Vendor unique error"));
325 		break;
326 	case KEY_COPY_ABORTED:
327 		(void) sprintf(msg_string, MSGSTR(123, "Copy aborted error"));
328 		break;
329 	case KEY_ABORTED_COMMAND:
330 		(void) sprintf(msg_string,
331 		    MSGSTR(10505,
332 		    "Aborted command. Error: Random Retry Failed.\n"));
333 		break;
334 	case KEY_EQUAL:
335 		(void) sprintf(msg_string, MSGSTR(117, "Equal error"));
336 		break;
337 	case KEY_VOLUME_OVERFLOW:
338 		(void) sprintf(msg_string, MSGSTR(57, "Volume overflow"));
339 		break;
340 	case KEY_MISCOMPARE:
341 		(void) sprintf(msg_string, MSGSTR(98, "Miscompare error"));
342 		break;
343 	case KEY_RESERVED:
344 		(void) sprintf(msg_string, MSGSTR(10506,
345 		    "Reserved value found"));
346 		break;
347 	default:
348 		(void) sprintf(msg_string, MSGSTR(59, "Unknown error"));
349 		break;
350 	}
351 
352 	(void) sprintf(&msg_string[strlen(msg_string)],
353 	    MSGSTR(10507, " during: %s"),
354 	    scsi_find_command_name(ucmd->uscsi_cdb[0]));
355 
356 	if (rq->es_valid) {
357 		blkno = (rq->es_info_1 << 24) | (rq->es_info_2 << 16) |
358 		    (rq->es_info_3 << 8) | rq->es_info_4;
359 		(void) sprintf(&msg_string[strlen(msg_string)],
360 		    MSGSTR(49, ": block %d (0x%x)"), blkno, blkno);
361 	}
362 
363 	(void) sprintf(&msg_string[strlen(msg_string)], "\n");
364 
365 	if (rq->es_add_len >= 6) {
366 		(void) sprintf(&msg_string[strlen(msg_string)],
367 		    MSGSTR(132, "  Additional sense: 0x%x   "
368 		    "ASC Qualifier: 0x%x\n"),
369 		    rq->es_add_code, rq->es_qual_code);
370 		/*
371 		 * rq->es_add_info[ADD_SENSE_CODE],
372 		 * rq->es_add_info[ADD_SENSE_QUAL_CODE]);
373 		 */
374 	}
375 	if (rq->es_key == KEY_ILLEGAL_REQUEST) {
376 		string_dump(MSGSTR(47, " cmd:   "), (uchar_t *)ucmd,
377 		    sizeof (struct uscsi_cmd), HEX_ONLY, msg_string);
378 		string_dump(MSGSTR(48, " cdb:   "),
379 		    (uchar_t *)ucmd->uscsi_cdb,
380 		    ucmd->uscsi_cdblen, HEX_ONLY, msg_string);
381 	}
382 	string_dump(MSGSTR(43, " sense:  "),
383 	    (uchar_t *)rq, 8 + rq->es_add_len, HEX_ONLY, msg_string);
384 	rqlen = rqlen;	/* not used */
385 }
386 
387 
388 /*
389  * Execute a command and determine the result.
390  */
391 static int
392 issue_uscsi_cmd(int file, struct uscsi_cmd *command, int flag)
393 {
394 	struct scsi_extended_sense	*rqbuf;
395 	int				status, i, retry_cnt = 0, err;
396 	char				errorMsg[MAXLEN];
397 
398 	/*
399 	 * Set function flags for driver.
400 	 *
401 	 * Set Automatic request sense enable
402 	 *
403 	 */
404 	command->uscsi_flags = USCSI_RQENABLE;
405 	command->uscsi_flags |= flag;
406 
407 	/* intialize error message array */
408 	errorMsg[0] = '\0';
409 
410 	/* print command for debug */
411 	if (getenv("_LUX_S_DEBUG") != NULL) {
412 		if ((command->uscsi_cdb == NULL) ||
413 		    (flag & USCSI_RESET) ||
414 		    (flag & USCSI_RESET_ALL)) {
415 			if (flag & USCSI_RESET) {
416 				(void) printf("  Issuing a SCSI Reset.\n");
417 			}
418 			if (flag & USCSI_RESET_ALL) {
419 				(void) printf("  Issuing a SCSI Reset All.\n");
420 			}
421 
422 		} else {
423 			(void) printf("  Issuing the following "
424 			    "SCSI command: %s\n",
425 			    scsi_find_command_name(command->uscsi_cdb[0]));
426 			(void) printf("	fd=0x%x cdb=", file);
427 			for (i = 0; i < (int)command->uscsi_cdblen; i++) {
428 				(void) printf("%x ", *(command->uscsi_cdb + i));
429 			}
430 			(void) printf("\n\tlen=0x%x bufaddr=0x%x buflen=0x%x"
431 			    " flags=0x%x\n",
432 			    command->uscsi_cdblen,
433 			    command->uscsi_bufaddr,
434 			    command->uscsi_buflen, command->uscsi_flags);
435 
436 			if ((command->uscsi_buflen > 0) &&
437 			    ((flag & USCSI_READ) == 0)) {
438 				(void) dump_hex_data("  Buffer data: ",
439 				    (uchar_t *)command->uscsi_bufaddr,
440 				    MIN(command->uscsi_buflen, 512), HEX_ASCII);
441 			}
442 		}
443 		(void) fflush(stdout);
444 	}
445 
446 
447 	/*
448 	 * Default command timeout in case command left it 0
449 	 */
450 	if (command->uscsi_timeout == 0) {
451 		command->uscsi_timeout = 60;
452 	}
453 	/*	Issue command - finally */
454 
455 retry:
456 	status = ioctl(file, USCSICMD, command);
457 	if (status == 0 && command->uscsi_status == 0) {
458 		if (getenv("_LUX_S_DEBUG") != NULL) {
459 			if ((command->uscsi_buflen > 0) &&
460 			    (flag & USCSI_READ)) {
461 				(void) dump_hex_data("\tData read:",
462 				    (uchar_t *)command->uscsi_bufaddr,
463 				    MIN(command->uscsi_buflen, 512), HEX_ASCII);
464 			}
465 		}
466 		return (status);
467 	}
468 	if ((status != 0) && (command->uscsi_status == 0)) {
469 		if ((getenv("_LUX_S_DEBUG") != NULL) ||
470 		    (getenv("_LUX_ER_DEBUG") != NULL)) {
471 			(void) printf("Unexpected USCSICMD ioctl error: %s\n",
472 			    strerror(errno));
473 		}
474 		return (status);
475 	}
476 
477 	/*
478 	 * Just a SCSI error, create error message
479 	 * Retry once for Unit Attention,
480 	 * Not Ready, and Aborted Command
481 	 */
482 	if ((command->uscsi_rqbuf != NULL) &&
483 	    (((char)command->uscsi_rqlen - (char)command->uscsi_rqresid) > 0)) {
484 
485 		rqbuf = (struct scsi_extended_sense *)command->uscsi_rqbuf;
486 
487 		switch (rqbuf->es_key) {
488 		case KEY_NOT_READY:
489 			if (retry_cnt++ < 1) {
490 				ER_DPRINTF("Note: Device Not Ready."
491 				    " Retrying...\n");
492 
493 				if ((err = wait_random_time()) == 0) {
494 					goto retry;
495 				} else {
496 					return (err);
497 				}
498 			}
499 			break;
500 
501 		case KEY_UNIT_ATTENTION:
502 			if (retry_cnt++ < 1) {
503 				ER_DPRINTF("  cmd():"
504 				" UNIT_ATTENTION: Retrying...\n");
505 
506 				goto retry;
507 			}
508 			break;
509 
510 		case KEY_ABORTED_COMMAND:
511 			if (retry_cnt++ < 1) {
512 				ER_DPRINTF("Note: Command is aborted."
513 				" Retrying...\n");
514 
515 				goto retry;
516 			}
517 			break;
518 		}
519 		if ((getenv("_LUX_S_DEBUG") != NULL) ||
520 		    (getenv("_LUX_ER_DEBUG") != NULL)) {
521 			scsi_printerr(command,
522 			    (struct scsi_extended_sense *)command->uscsi_rqbuf,
523 			    (command->uscsi_rqlen - command->uscsi_rqresid),
524 			    errorMsg, strerror(errno));
525 		}
526 
527 	} else {
528 
529 		/*
530 		 * Retry 5 times in case of BUSY, and only
531 		 * once for Reservation-conflict, Command
532 		 * Termination and Queue Full. Wait for
533 		 * random amount of time (between 1/10 - 1/2 secs.)
534 		 * between each retry. This random wait is to avoid
535 		 * the multiple threads being executed at the same time
536 		 * and also the constraint in Photon IB, where the
537 		 * command queue has a depth of one command.
538 		 */
539 		switch ((uchar_t)command->uscsi_status & STATUS_MASK) {
540 		case STATUS_BUSY:
541 			if (retry_cnt++ < 5) {
542 				if ((err = wait_random_time()) == 0) {
543 					R_DPRINTF("  cmd(): No. of retries %d."
544 					    " STATUS_BUSY: Retrying...\n",
545 					    retry_cnt);
546 					goto retry;
547 
548 				} else {
549 					return (err);
550 				}
551 			}
552 			break;
553 
554 		case STATUS_RESERVATION_CONFLICT:
555 			if (retry_cnt++ < 1) {
556 				if ((err = wait_random_time()) == 0) {
557 					R_DPRINTF("  cmd():"
558 					" RESERVATION_CONFLICT:"
559 					" Retrying...\n");
560 					goto retry;
561 
562 				} else {
563 					return (err);
564 				}
565 			}
566 			break;
567 
568 		case STATUS_TERMINATED:
569 			if (retry_cnt++ < 1) {
570 				R_DPRINTF("Note: Command Terminated."
571 				    " Retrying...\n");
572 
573 				if ((err = wait_random_time()) == 0) {
574 					goto retry;
575 				} else {
576 					return (err);
577 				}
578 			}
579 			break;
580 
581 		case STATUS_QFULL:
582 			if (retry_cnt++ < 1) {
583 				R_DPRINTF("Note: Command Queue is full."
584 				" Retrying...\n");
585 
586 				if ((err = wait_random_time()) == 0) {
587 					goto retry;
588 				} else {
589 					return (err);
590 				}
591 			}
592 			break;
593 		}
594 
595 	}
596 	if (((getenv("_LUX_S_DEBUG") != NULL) ||
597 	    (getenv("_LUX_ER_DEBUG") != NULL)) &&
598 	    (errorMsg[0] != '\0')) {
599 		(void) fprintf(stdout, "  %s\n", errorMsg);
600 	}
601 	return (L_SCSI_ERROR | command->uscsi_status);
602 }
603 
604 /*
605  *		MODE SENSE USCSI command
606  *
607  *
608  *		pc = page control field
609  *		page_code = Pages to return
610  */
611 int
612 scsi_mode_sense_cmd(int fd,
613 	uchar_t *buf_ptr,
614 	int buf_len,
615 	uchar_t pc,
616 	uchar_t page_code)
617 {
618 	struct uscsi_cmd	ucmd;
619 	/* 10 byte Mode Select cmd */
620 	union scsi_cdb	cdb =  {SCMD_MODE_SENSE_G1, 0, 0, 0, 0, 0, 0, 0, 0, 0};
621 	struct	scsi_extended_sense	sense;
622 	int		status;
623 	static	int	uscsi_count;
624 
625 	if ((fd < 0) || (buf_ptr == NULL) || (buf_len < 0)) {
626 		return (-1); /* L_INVALID_ARG */
627 	}
628 
629 	(void) memset(buf_ptr, 0, buf_len);
630 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
631 	/* Just for me  - a sanity check */
632 	if ((page_code > MODEPAGE_ALLPAGES) || (pc > 3) ||
633 	    (buf_len > MAX_MODE_SENSE_LEN)) {
634 		return (-1); /* L_ILLEGAL_MODE_SENSE_PAGE */
635 	}
636 	cdb.g1_addr3 = (pc << 6) + page_code;
637 	cdb.g1_count1 = buf_len>>8;
638 	cdb.g1_count0 = buf_len & 0xff;
639 	ucmd.uscsi_cdb = (caddr_t)&cdb;
640 	ucmd.uscsi_cdblen = CDB_GROUP1;
641 	ucmd.uscsi_bufaddr = (caddr_t)buf_ptr;
642 	ucmd.uscsi_buflen = buf_len;
643 	ucmd.uscsi_rqbuf = (caddr_t)&sense;
644 	ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
645 	ucmd.uscsi_timeout = 120;
646 
647 	status = issue_uscsi_cmd(fd, &ucmd, USCSI_READ);
648 	/* Bytes actually transfered */
649 	if (status == 0) {
650 		uscsi_count = buf_len - ucmd.uscsi_resid;
651 		S_DPRINTF("  Number of bytes read on "
652 		"Mode Sense 0x%x\n", uscsi_count);
653 		if (getenv("_LUX_D_DEBUG") != NULL) {
654 			(void) dump_hex_data("  Mode Sense data: ", buf_ptr,
655 			    uscsi_count, HEX_ASCII);
656 		}
657 	}
658 	return (status);
659 }
660 
661 int
662 scsi_release(char *path)
663 {
664 	struct uscsi_cmd	ucmd;
665 	union scsi_cdb		cdb = {SCMD_RELEASE, 0, 0, 0, 0, 0};
666 	struct	scsi_extended_sense	sense;
667 	int	fd, status;
668 
669 	P_DPRINTF("  scsi_release: Release: Path %s\n", path);
670 	if ((fd = open(path, O_NDELAY | O_RDONLY)) == -1)
671 		return (1);
672 
673 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
674 
675 	ucmd.uscsi_cdb = (caddr_t)&cdb;
676 	ucmd.uscsi_cdblen = CDB_GROUP0;
677 	ucmd.uscsi_bufaddr = NULL;
678 	ucmd.uscsi_buflen = 0;
679 	ucmd.uscsi_rqbuf = (caddr_t)&sense;
680 	ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
681 	ucmd.uscsi_timeout = 60;
682 	status = (issue_uscsi_cmd(fd, &ucmd, 0));
683 
684 	(void) close(fd);
685 	return (status);
686 }
687 
688 int
689 scsi_reserve(char *path)
690 {
691 	struct uscsi_cmd	ucmd;
692 	union scsi_cdb	cdb = {SCMD_RESERVE, 0, 0, 0, 0, 0};
693 	struct	scsi_extended_sense	sense;
694 	int	fd, status;
695 
696 	P_DPRINTF("  scsi_reserve: Reserve: Path %s\n", path);
697 	if ((fd = open(path, O_NDELAY | O_RDONLY)) == -1)
698 		return (1);
699 
700 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
701 
702 	ucmd.uscsi_cdb = (caddr_t)&cdb;
703 	ucmd.uscsi_cdblen = CDB_GROUP0;
704 	ucmd.uscsi_bufaddr = NULL;
705 	ucmd.uscsi_buflen = 0;
706 	ucmd.uscsi_rqbuf = (caddr_t)&sense;
707 	ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
708 	ucmd.uscsi_timeout = 60;
709 	status = (issue_uscsi_cmd(fd, &ucmd, 0));
710 
711 	(void) close(fd);
712 	return (status);
713 }
714 
715 /*
716  * Print out fabric dev dtype
717  */
718 void
719 print_fabric_dtype_prop(uchar_t *hba_port_wwn, uchar_t *port_wwn,
720 	uchar_t dtype_prop)
721 {
722 	if ((dtype_prop & DTYPE_MASK) < 0x10) {
723 		(void) fprintf(stdout, " 0x%-2x (%s)\n",
724 		    (dtype_prop & DTYPE_MASK),
725 		    dtype[(dtype_prop & DTYPE_MASK)]);
726 	} else if ((dtype_prop & DTYPE_MASK) < 0x1f) {
727 		(void) fprintf(stdout,
728 		    MSGSTR(2096, " 0x%-2x (Reserved)\n"),
729 		    (dtype_prop & DTYPE_MASK));
730 	} else {
731 		/* Check to see if this is the HBA */
732 		if (wwnConversion(hba_port_wwn) != wwnConversion(port_wwn)) {
733 			(void) fprintf(stdout, MSGSTR(2097,
734 			    " 0x%-2x (Unknown Type)\n"),
735 			    (dtype_prop & DTYPE_MASK));
736 		} else {
737 			/* MATCH */
738 			(void) fprintf(stdout, MSGSTR(2241,
739 			    " 0x%-2x (Unknown Type,Host Bus Adapter)\n"),
740 			    (dtype_prop & DTYPE_MASK));
741 		}
742 	}
743 }
744 
745 
746 void
747 print_inq_data(char *arg_path, char *path, L_inquiry inq, uchar_t *serial,
748     size_t serial_len)
749 {
750 	char	**p;
751 	uchar_t	*v_parm;
752 	int	scsi_3, length;
753 	char	byte_number[MAXNAMELEN];
754 	static	char *scsi_inquiry_labels_2[21];
755 	static	char *scsi_inquiry_labels_3[22];
756 #define	MAX_ANSI_VERSION	6
757 	static	char	*ansi_version[MAX_ANSI_VERSION];
758 	/*
759 	 * Intialize scsi_inquiry_labels_2 with i18n strings
760 	 */
761 	scsi_inquiry_labels_2[0] = MSGSTR(138, "Vendor:                     ");
762 	scsi_inquiry_labels_2[1] = MSGSTR(149, "Product:                    ");
763 	scsi_inquiry_labels_2[2] = MSGSTR(139, "Revision:                   ");
764 	scsi_inquiry_labels_2[3] = MSGSTR(143, "Firmware Revision           ");
765 	scsi_inquiry_labels_2[4] = MSGSTR(144, "Serial Number               ");
766 	scsi_inquiry_labels_2[5] = MSGSTR(140, "Device type:                ");
767 	scsi_inquiry_labels_2[6] = MSGSTR(145, "Removable media:            ");
768 	scsi_inquiry_labels_2[7] = MSGSTR(146, "ISO version:                ");
769 	scsi_inquiry_labels_2[8] = MSGSTR(147, "ECMA version:               ");
770 	scsi_inquiry_labels_2[9] = MSGSTR(148, "ANSI version:               ");
771 	scsi_inquiry_labels_2[10] =
772 	    MSGSTR(2168, "Async event notification:   ");
773 	scsi_inquiry_labels_2[11] =
774 	    MSGSTR(2169, "Terminate i/o process msg:  ");
775 	scsi_inquiry_labels_2[12] = MSGSTR(150, "Response data format:       ");
776 	scsi_inquiry_labels_2[13] = MSGSTR(151, "Additional length:          ");
777 	scsi_inquiry_labels_2[14] = MSGSTR(152, "Relative addressing:        ");
778 	scsi_inquiry_labels_2[15] =
779 	    MSGSTR(2170, "32 bit transfers:           ");
780 	scsi_inquiry_labels_2[16] =
781 	    MSGSTR(2171, "16 bit transfers:           ");
782 	scsi_inquiry_labels_2[17] =
783 	    MSGSTR(2172, "Synchronous transfers:      ");
784 	scsi_inquiry_labels_2[18] = MSGSTR(153, "Linked commands:            ");
785 	scsi_inquiry_labels_2[19] = MSGSTR(154, "Command queueing:           ");
786 	scsi_inquiry_labels_2[20] =
787 	    MSGSTR(2173, "Soft reset option:          ");
788 
789 	/*
790 	 * Intialize scsi_inquiry_labels_3 with i18n strings
791 	 */
792 	scsi_inquiry_labels_3[0] = MSGSTR(138, "Vendor:                     ");
793 	scsi_inquiry_labels_3[1] = MSGSTR(149, "Product:                    ");
794 	scsi_inquiry_labels_3[2] = MSGSTR(139, "Revision:                   ");
795 	scsi_inquiry_labels_3[3] = MSGSTR(143, "Firmware Revision           ");
796 	scsi_inquiry_labels_3[4] = MSGSTR(144, "Serial Number               ");
797 	scsi_inquiry_labels_3[5] = MSGSTR(140, "Device type:                ");
798 	scsi_inquiry_labels_3[6] = MSGSTR(145, "Removable media:            ");
799 	scsi_inquiry_labels_3[7] = MSGSTR(2174, "Medium Changer Element:     ");
800 	scsi_inquiry_labels_3[8] = MSGSTR(146, "ISO version:                ");
801 	scsi_inquiry_labels_3[9] = MSGSTR(147, "ECMA version:               ");
802 	scsi_inquiry_labels_3[10] = MSGSTR(148, "ANSI version:               ");
803 	scsi_inquiry_labels_3[11] =
804 	    MSGSTR(2175, "Async event reporting:      ");
805 	scsi_inquiry_labels_3[12] =
806 	    MSGSTR(2176, "Terminate task:             ");
807 	scsi_inquiry_labels_3[13] =
808 	    MSGSTR(2177, "Normal ACA Supported:       ");
809 	scsi_inquiry_labels_3[14] = MSGSTR(150, "Response data format:       ");
810 	scsi_inquiry_labels_3[15] = MSGSTR(151, "Additional length:          ");
811 	scsi_inquiry_labels_3[16] =
812 	    MSGSTR(2178, "Cmd received on port:       ");
813 	scsi_inquiry_labels_3[17] =
814 	    MSGSTR(2179, "SIP Bits:                   ");
815 	scsi_inquiry_labels_3[18] = MSGSTR(152, "Relative addressing:        ");
816 	scsi_inquiry_labels_3[19] = MSGSTR(153, "Linked commands:            ");
817 	scsi_inquiry_labels_3[20] =
818 	    MSGSTR(2180, "Transfer Disable:           ");
819 	scsi_inquiry_labels_3[21] = MSGSTR(154, "Command queueing:           ");
820 
821 	/*
822 	 * Intialize scsi_inquiry_labels_3 with i18n strings
823 	 */
824 	ansi_version[0] = MSGSTR(2181,
825 	    " (Device might or might not comply to an ANSI version)");
826 	ansi_version[1] = MSGSTR(2182,
827 	    " (This code is reserved for historical uses)");
828 	ansi_version[2] = MSGSTR(2183,
829 	    " (Device complies to ANSI X3.131-1994 (SCSI-2))");
830 	ansi_version[3] = MSGSTR(2184,
831 	    " (Device complies to ANSI INCITS 301-1997 (SPC))");
832 	ansi_version[4] = MSGSTR(2226,
833 	    " (Device complies to ANSI INCITS 351-2001 (SPC-2))");
834 	ansi_version[5] = MSGSTR(2227,
835 	    " (Device complies to ANSI INCITS 408-2005 (SPC-3))");
836 
837 	/* print inquiry information */
838 
839 	(void) fprintf(stdout, MSGSTR(2185, "\nINQUIRY:\n"));
840 		/*
841 		 * arg_path is the path sent to luxadm by the user.  if arg_path
842 		 * is a /devices path, then we do not need to print out physical
843 		 * path info
844 		 */
845 	if (strcmp(arg_path, path) != 0 &&
846 	    strstr(arg_path, "/devices/") == NULL) {
847 		(void) fprintf(stdout, "  ");
848 		(void) fprintf(stdout,
849 		    MSGSTR(5, "Physical Path:"));
850 		(void) fprintf(stdout, "\n  %s\n", path);
851 	}
852 	if (inq.inq_ansi < 3) {
853 		p = scsi_inquiry_labels_2;
854 		scsi_3 = 0;
855 	} else {
856 		p = scsi_inquiry_labels_3;
857 		scsi_3 = 1;
858 	}
859 	if (inq.inq_len < 11) {
860 		p += 1;
861 	} else {
862 		/* */
863 		(void) fprintf(stdout, "%s", *p++);
864 		print_chars(inq.inq_vid, sizeof (inq.inq_vid), 0);
865 		(void) fprintf(stdout, "\n");
866 	}
867 	if (inq.inq_len < 27) {
868 		p += 1;
869 	} else {
870 		(void) fprintf(stdout, "%s", *p++);
871 		print_chars(inq.inq_pid, sizeof (inq.inq_pid), 0);
872 		(void) fprintf(stdout, "\n");
873 	}
874 	if (inq.inq_len < 31) {
875 		p += 1;
876 	} else {
877 		(void) fprintf(stdout, "%s", *p++);
878 		print_chars(inq.inq_revision, sizeof (inq.inq_revision), 0);
879 		(void) fprintf(stdout, "\n");
880 	}
881 	if (inq.inq_len < 39) {
882 		p += 2;
883 	} else {
884 		/*
885 		 * If Pluto then print
886 		 * firmware rev & serial #.
887 		 */
888 		if (strstr((char *)inq.inq_pid, "SSA") != 0) {
889 			(void) fprintf(stdout, "%s", *p++);
890 			print_chars(inq.inq_firmware_rev,
891 			    sizeof (inq.inq_firmware_rev), 0);
892 			(void) fprintf(stdout, "\n");
893 			(void) fprintf(stdout, "%s", *p++);
894 			print_chars(serial, serial_len, 0);
895 			(void) fprintf(stdout, "\n");
896 		} else if ((inq.inq_dtype & DTYPE_MASK) != DTYPE_ESI) {
897 			p++;
898 			(void) fprintf(stdout, "%s", *p++);
899 			print_chars(serial, serial_len, 0);
900 			(void) fprintf(stdout, "\n");
901 		} else {
902 			/* if we miss both the above if's */
903 			p += 2;
904 		}
905 	}
906 
907 	(void) fprintf(stdout, "%s0x%x (", *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 < MAX_ANSI_VERSION) {
939 		(void) fprintf(stdout, "%s", ansi_version[inq.inq_ansi]);
940 	} else
941 		(void) fprintf(stdout, MSGSTR(71, " (Reserved)"));
942 
943 	(void) fprintf(stdout, "\n");
944 
945 	if (inq.inq_aenc) {
946 		(void) fprintf(stdout, "%s", *p++);
947 		(void) fprintf(stdout, MSGSTR(40, "yes"));
948 		(void) fprintf(stdout, "\n");
949 	} else {
950 		p++;
951 	}
952 	if (scsi_3) {
953 		(void) fprintf(stdout, "%s", *p++);
954 		if (inq.inq_normaca != NULL) {
955 			(void) fprintf(stdout, MSGSTR(40, "yes"));
956 		} else {
957 			(void) fprintf(stdout, MSGSTR(45, "no"));
958 		}
959 		(void) fprintf(stdout, "\n");
960 	}
961 	if (inq.inq_trmiop) {
962 		(void) fprintf(stdout, "%s", *p++);
963 		(void) fprintf(stdout, MSGSTR(40, "yes"));
964 		(void) fprintf(stdout, "\n");
965 	} else {
966 		p++;
967 	}
968 	(void) fprintf(stdout, "%s%d\n", *p++, inq.inq_rdf);
969 	(void) fprintf(stdout, "%s0x%x\n", *p++, inq.inq_len);
970 	if (scsi_3) {
971 		if (inq.inq_dual_p) {
972 			if (inq.inq_port != NULL) {
973 				(void) fprintf(stdout, MSGSTR(2187,
974 				    "%sa\n"), *p++);
975 			} else {
976 				(void) fprintf(stdout, MSGSTR(2188,
977 				    "%sb\n"), *p++);
978 			}
979 		} else {
980 			p++;
981 		}
982 	}
983 	if (scsi_3) {
984 		if (inq.inq_SIP_1 || inq.ui.inq_3.inq_SIP_2 ||
985 		    inq.ui.inq_3.inq_SIP_3) {
986 			(void) fprintf(stdout, "%s%d, %d, %d\n", *p,
987 			    inq.inq_SIP_1, inq.ui.inq_3.inq_SIP_2,
988 			    inq.ui.inq_3.inq_SIP_3);
989 		}
990 		p++;
991 
992 	}
993 
994 	if (inq.ui.inq_2.inq_2_reladdr) {
995 		(void) fprintf(stdout, "%s", *p);
996 		(void) fprintf(stdout, MSGSTR(40, "yes"));
997 		(void) fprintf(stdout, "\n");
998 	}
999 	p++;
1000 
1001 	if (!scsi_3) {
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 		/*
1229 		 * source is now an absolute path to the link we're
1230 		 * concerned with
1231 		 */
1232 		if (flag == NOT_IGNORE_DANGLING_LINK) {
1233 			/*
1234 			 * In order not to ingore dangling links, check
1235 			 * the lstat. If lstat succeeds, return the path
1236 			 * from readlink.
1237 			 * Note: osDevName input with /devices path from
1238 			 * a dangling /dev link doesn't pass lstat so
1239 			 * NULL is returned.
1240 			 */
1241 			if (stat(source, &stbuf) == -1) {
1242 				if (!is_lstat_failed &&
1243 				    strstr(source, "/devices")) {
1244 					/*
1245 					 * lstat succeeded previously and source
1246 					 * contains "/devices" then it is
1247 					 * dangling node.
1248 					 */
1249 					phys_path = (char *)calloc(1,
1250 					    strlen(source) + 1);
1251 					if (phys_path != NULL) {
1252 						(void) strncpy(phys_path,
1253 						    source, strlen(source) + 1);
1254 					}
1255 					return (phys_path);
1256 				} else if (is_lstat_failed) {
1257 					/* check lstat result. */
1258 					if (lstat(source, &stbuf) == -1) {
1259 						return (NULL);
1260 					} else {
1261 						/* and continue */
1262 						is_lstat_failed = B_FALSE;
1263 					}
1264 				} else {
1265 					/*
1266 					 * With algorithm that resolves a link
1267 					 * and then issues readlink(), should
1268 					 * not be reached here.
1269 					 */
1270 					return (NULL);
1271 				}
1272 			} else {
1273 				if (lstat(source, &stbuf) == -1) {
1274 					/*
1275 					 * when stat succeeds it is not
1276 					 * a dangling node so it is not
1277 					 * a special case.
1278 					 */
1279 					return (NULL);
1280 				}
1281 			}
1282 		} else if (flag == STANDARD_DEVNAME_HANDLING) {
1283 			/*
1284 			 * See if there's a real file out there.  If not,
1285 			 * we have a dangling link and we ignore it.
1286 			 */
1287 			if (stat(source, &stbuf) == -1) {
1288 				return (NULL);
1289 			}
1290 			if (lstat(source, &stbuf) == -1) {
1291 				return (NULL);
1292 			}
1293 		} else {
1294 			/* invalid flag */
1295 			return (NULL);
1296 		}
1297 
1298 		/*
1299 		 * If the file is not a link, we're done one
1300 		 * way or the other.  If there were links,
1301 		 * return the full pathname of the resulting
1302 		 * file.
1303 		 *
1304 		 * Note:  All of our temp's are on the stack,
1305 		 * so we have to copy the final result to the heap.
1306 		 */
1307 		if (!S_ISLNK(stbuf.st_mode)) {
1308 			phys_path = (char *)calloc(1, strlen(source) + 1);
1309 			if (phys_path != NULL) {
1310 				(void) strncpy(phys_path, source,
1311 				    strlen(source) + 1);
1312 			}
1313 			return (phys_path);
1314 		}
1315 		cnt = readlink(source, scratch, sizeof (scratch));
1316 		if (cnt < 0) {
1317 			return (NULL);
1318 		}
1319 		/*
1320 		 * scratch is on the heap, and for some reason readlink
1321 		 * doesn't always terminate things properly so we have
1322 		 * to make certain we're properly terminated
1323 		 */
1324 		scratch[cnt] = '\0';
1325 
1326 		/*
1327 		 * Now check to see if the link is relative.  If so,
1328 		 * then we have to append it to the directory
1329 		 * which the source was in. (This is non trivial)
1330 		 */
1331 		if (scratch[0] != '/') {
1332 			tmp = strrchr(source, '/');
1333 			if (tmp == NULL) { /* Whoa!  Something's hosed! */
1334 				O_DPRINTF("Internal error... corrupt path.\n");
1335 				return (NULL);
1336 			}
1337 			/* Now strip off just the directory path */
1338 			*(tmp+1) = '\0'; /* Keeping the last '/' */
1339 			/* and append the new link */
1340 			(void) strcat(source, scratch);
1341 			/*
1342 			 * Note:  At this point, source should have "../"s
1343 			 * but we'll clean it up in the next pass through
1344 			 * the loop.
1345 			 */
1346 		} else {
1347 			/* It's an absolute link so no worries */
1348 			strcpy(source, scratch);
1349 		}
1350 	}
1351 	/* Never reach here */
1352 }
1353 
1354 /*
1355  * Input - Space for client_path, phci_path and paddr fields of ioc structure
1356  * need to be allocated by the caller of this routine.
1357  */
1358 int
1359 get_scsi_vhci_pathinfo(char *dev_path, sv_iocdata_t *ioc, int *path_count)
1360 {
1361 	char	*physical_path, *physical_path_s;
1362 	int	retval;
1363 	int	fd;
1364 	int	initial_path_count;
1365 	int	current_path_count;
1366 	int 	i;
1367 	char	*delimiter;
1368 	int	malloc_error = 0;
1369 	int 	prop_buf_size;
1370 	int	pathlist_retry_count = 0;
1371 
1372 	if (strncmp(dev_path, SCSI_VHCI, strlen(SCSI_VHCI)) != NULL) {
1373 		if ((physical_path = get_slash_devices_from_osDevName(
1374 		    dev_path, STANDARD_DEVNAME_HANDLING)) == NULL) {
1375 			return (L_INVALID_PATH);
1376 		}
1377 		if (strncmp(physical_path, SCSI_VHCI,
1378 		    strlen(SCSI_VHCI)) != NULL) {
1379 			free(physical_path);
1380 			return (L_INVALID_PATH);
1381 		}
1382 	} else {
1383 		if ((physical_path = calloc(1, MAXPATHLEN)) == NULL) {
1384 			return (L_MALLOC_FAILED);
1385 		}
1386 		(void) strcpy(physical_path, dev_path);
1387 	}
1388 	physical_path_s = physical_path;
1389 
1390 	/* move beyond "/devices" prefix */
1391 	physical_path += DEV_PREFIX_STRLEN-1;
1392 	/* remove  :c,raw suffix */
1393 	delimiter = strrchr(physical_path, ':');
1394 	/* if we didn't find the ':' fine, else truncate */
1395 	if (delimiter != NULL) {
1396 		*delimiter = NULL;
1397 	}
1398 
1399 	/*
1400 	 * We'll call ioctl SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO
1401 	 * at least twice.  The first time will get the path count
1402 	 * and the size of the ioctl propoerty buffer.  The second
1403 	 * time will get the path_info for each path.
1404 	 *
1405 	 * It's possible that additional paths are added while this
1406 	 * code is running.  If the path count increases between the
1407 	 * 2 ioctl's above, then we'll retry (and assume all is well).
1408 	 */
1409 	(void) strcpy(ioc->client, physical_path);
1410 	ioc->buf_elem = 1;
1411 	ioc->ret_elem = (uint_t *)&(initial_path_count);
1412 	ioc->ret_buf = NULL;
1413 
1414 	/* free physical path */
1415 	free(physical_path_s);
1416 
1417 	/* 0 buf_size asks driver to return actual size needed */
1418 	/* open the ioctl file descriptor */
1419 	if ((fd = open("/devices/scsi_vhci:devctl", O_RDWR)) < 0) {
1420 		return (L_OPEN_PATH_FAIL);
1421 	}
1422 
1423 	retval = ioctl(fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, ioc);
1424 	if (retval != 0) {
1425 		close(fd);
1426 		return (L_SCSI_VHCI_ERROR);
1427 	}
1428 	prop_buf_size = SV_PROP_MAX_BUF_SIZE;
1429 
1430 
1431 	while (pathlist_retry_count <= RETRY_PATHLIST) {
1432 		ioc->buf_elem = initial_path_count;
1433 		/* Make driver put actual # paths in variable */
1434 		ioc->ret_elem = (uint_t *)&(current_path_count);
1435 
1436 		/*
1437 		 * Allocate space for array of path_info structures.
1438 		 * Allocate enough space for # paths from get_pathcount
1439 		 */
1440 		ioc->ret_buf = (sv_path_info_t *)
1441 		    calloc(initial_path_count, sizeof (sv_path_info_t));
1442 		if (ioc->ret_buf == NULL) {
1443 			close(fd);
1444 			return (L_MALLOC_FAILED);
1445 		}
1446 
1447 		/*
1448 		 * Allocate space for path properties returned by driver
1449 		 */
1450 		malloc_error = 0;
1451 		for (i = 0; i < initial_path_count; i++) {
1452 			ioc->ret_buf[i].ret_prop.buf_size = prop_buf_size;
1453 			if ((ioc->ret_buf[i].ret_prop.buf =
1454 			    (caddr_t)malloc(prop_buf_size)) == NULL) {
1455 				malloc_error = 1;
1456 				break;
1457 			}
1458 			if ((ioc->ret_buf[i].ret_prop.ret_buf_size =
1459 			    (uint_t *)malloc(sizeof (uint_t))) == NULL) {
1460 				malloc_error = 1;
1461 				break;
1462 			}
1463 		}
1464 		if (malloc_error == 1) {
1465 			for (i = 0; i < initial_path_count; i++) {
1466 				free(ioc->ret_buf[i].ret_prop.buf);
1467 				free(ioc->ret_buf[i].ret_prop.ret_buf_size);
1468 			}
1469 			free(ioc->ret_buf);
1470 			close(fd);
1471 			return (L_MALLOC_FAILED);
1472 		}
1473 
1474 		retval = ioctl(fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, ioc);
1475 		if (retval != 0) {
1476 			for (i = 0; i < initial_path_count; i++) {
1477 				free(ioc->ret_buf[i].ret_prop.buf);
1478 				free(ioc->ret_buf[i].ret_prop.ret_buf_size);
1479 			}
1480 			free(ioc->ret_buf);
1481 			close(fd);
1482 			return (L_SCSI_VHCI_ERROR);
1483 		}
1484 		if (initial_path_count < current_path_count) {
1485 			/* then a new path was added */
1486 			pathlist_retry_count++;
1487 			initial_path_count = current_path_count;
1488 		} else {
1489 			break;
1490 		}
1491 	}
1492 	/* we are done with ioctl's, lose the fd */
1493 	close(fd);
1494 
1495 	/*
1496 	 * Compare the length num elements from the ioctl response
1497 	 *   and the caller's request - use smaller value.
1498 	 *
1499 	 * pathlist_p->path_count now has count returned from ioctl.
1500 	 * ioc.buf_elem has the value the caller provided.
1501 	 */
1502 	if (initial_path_count < current_path_count) {
1503 		/* More paths exist than we allocated space for */
1504 		*path_count = initial_path_count;
1505 	} else {
1506 		*path_count = current_path_count;
1507 	}
1508 
1509 	return (0);
1510 }
1511 
1512 int
1513 get_mode_page(char *path, uchar_t **pg_buf)
1514 {
1515 	struct mode_header_g1	*mode_header_ptr;
1516 	int		status, size, fd;
1517 
1518 	/* open controller */
1519 	if ((fd = open(path, O_NDELAY | O_RDWR)) == -1)
1520 		return (-1); /* L_OPEN_PATH_FAIL */
1521 
1522 	/*
1523 	 * Read the first part of the page to get the page size
1524 	 */
1525 	size = 20;
1526 	if ((*pg_buf = (uchar_t *)calloc(1, size)) == NULL) {
1527 		(void) close(fd);
1528 		return (L_MALLOC_FAILED);
1529 	}
1530 	/* read page */
1531 	if (status = scsi_mode_sense_cmd(fd, *pg_buf, size,
1532 	    0, MODEPAGE_ALLPAGES)) {
1533 		(void) close(fd);
1534 		(void) free(*pg_buf);
1535 		return (status);
1536 	}
1537 	/* Now get the size for all pages */
1538 	mode_header_ptr = (struct mode_header_g1 *)(void *)*pg_buf;
1539 	size = ntohs(mode_header_ptr->length) +
1540 	    sizeof (mode_header_ptr->length);
1541 	(void) free(*pg_buf);
1542 	if ((*pg_buf = (uchar_t *)calloc(1, size)) == NULL) {
1543 		(void) close(fd);
1544 		return (L_MALLOC_FAILED);
1545 	}
1546 	/* read all pages */
1547 	if (status = scsi_mode_sense_cmd(fd, *pg_buf, size,
1548 	    0, MODEPAGE_ALLPAGES)) {
1549 		(void) close(fd);
1550 		(void) free(*pg_buf);
1551 		return (status);
1552 	}
1553 	(void) close(fd);
1554 	return (0);
1555 }
1556 
1557 /*
1558  * Dump a structure in hexadecimal.
1559  */
1560 void
1561 dump_hex_data(char *hdr, uchar_t *src, int nbytes, int format)
1562 {
1563 	int i;
1564 	int n;
1565 	char	*p;
1566 	char	s[256];
1567 
1568 	assert(format == HEX_ONLY || format == HEX_ASCII);
1569 
1570 	(void) strcpy(s, hdr);
1571 	for (p = s; *p; p++) {
1572 		*p = ' ';
1573 	}
1574 
1575 	p = hdr;
1576 	while (nbytes > 0) {
1577 		(void) fprintf(stdout, "%s", p);
1578 		p = s;
1579 		n = MIN(nbytes, BYTES_PER_LINE);
1580 		for (i = 0; i < n; i++) {
1581 			(void) fprintf(stdout, "%02x ", src[i] & 0xff);
1582 		}
1583 		if (format == HEX_ASCII) {
1584 			for (i = BYTES_PER_LINE-n; i > 0; i--) {
1585 				(void) fprintf(stdout, "   ");
1586 			}
1587 			(void) fprintf(stdout, "    ");
1588 			for (i = 0; i < n; i++) {
1589 				(void) fprintf(stdout, "%c",
1590 				    isprint(src[i]) ? src[i] : '.');
1591 			}
1592 		}
1593 		(void) fprintf(stdout, "\n");
1594 		nbytes -= n;
1595 		src += n;
1596 	}
1597 }
1598