xref: /illumos-gate/usr/src/lib/storage/libg_fc/common/genf.c (revision b41e2fb6b2658a36accc73b3b86d99375e700dba)
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 
29 /*LINTLIBRARY*/
30 
31 /*
32  *	This module is part of the Fibre Channel Interface library.
33  *
34  */
35 
36 /*
37  * I18N message number ranges
38  *  This file: 10500 - 10999
39  *  Shared common messages: 1 - 1999
40  */
41 
42 /*	Includes	*/
43 #include	<stdlib.h>
44 #include 	<stdio.h>
45 #include	<sys/file.h>
46 #include	<sys/types.h>
47 #include	<sys/stat.h>
48 #include	<sys/param.h>
49 #include	<fcntl.h>
50 #include	<string.h>
51 #include	<errno.h>
52 #include	<assert.h>
53 #include	<unistd.h>
54 #include	<sys/types.h>
55 #include	<sys/param.h>
56 #include	<sys/dklabel.h>
57 #include	<sys/autoconf.h>
58 #include	<sys/utsname.h>
59 #include 	<sys/ddi.h>		/* for min */
60 #include	<ctype.h>		/* for isprint */
61 #include	<sys/scsi/scsi.h>
62 #include	<dirent.h>		/* for DIR */
63 #include	<nl_types.h>
64 #include	<locale.h>
65 #include	<thread.h>
66 #include	<synch.h>
67 #include	<l_common.h>
68 #include	<stgcom.h>
69 #include	<l_error.h>
70 #include	<g_state.h>
71 #include	<libdevinfo.h>
72 
73 
74 /*	Defines		*/
75 #define	BYTES_PER_LINE		16	/* # of bytes to dump per line */
76 #define	SCMD_UNKNOWN		0xff
77 
78 /* Bus strings - for internal use by g_get_path_type() only */
79 #define	PCI_BUS			1
80 #define	SBUS			2
81 #define	FCOE			3
82 
83 struct str_type {
84 	char *string;
85 	uint_t type;
86 };
87 
88 static struct str_type ValidBusStrings[] = {
89 	{"pci@", PCI_BUS},
90 	{"sbus@", SBUS},
91 	{"fcoe", FCOE},
92 	{NULL, 0}
93 };
94 
95 
96 /*
97  *	Strings below were used before cougar driver(qlc) was proposed.
98  *	{"scsi/", FC_PCI_FCA},
99  *	{"fibre-channel/", FC_PCI_FCA},
100  */
101 static struct str_type ValidFCAstrings[] = {
102 	{"SUNW,ifp@", FC4_PCI_FCA | FC4_IFP_XPORT},
103 	{"SUNW,socal@", FC4_SOCAL_FCA},
104 	{NULL, 0}
105 };
106 
107 static struct str_type ValidXportStrings[] = {
108 	{"/sf@", FC4_SF_XPORT},
109 	{"/fp@", FC_GEN_XPORT},
110 	{NULL, 0}
111 };
112 
113 struct _enclDisk {
114 	char *vid;
115 	char *pid;
116 };
117 
118 /*
119  * SENA/SUNWGS type enclosure disk table. This table contains vendor IDs and
120  * the non-unique portion of the product identifier sufficient for
121  * comparison. This table needs to be updated as new drives are supported
122  * in the SENA/SUNWGS type enclosures that do not have a corresponding match
123  * in this table. Currently, the v880 and v890 are the only shipping products
124  * that utilize the SUNWGS type enclosure. SENA is EOL'd. The risk of new
125  * devices being added that do not match an entry in this table is small but it
126  * does exist.
127  */
128 static struct _enclDisk enclDiskTbl[] = {
129 	{"SUN", "SENA"},
130 	{"SUN", "SUNWGS"},
131 	{"FUJITSU", "MA"},
132 	{"HITACHI", "DK"},
133 	{"HITACHI", "HU"},
134 	{"SEAGATE", "ST"},
135 	{NULL, NULL}
136 };
137 
138 
139 /* i18n */
140 nl_catd		l_catd;
141 
142 
143 /*	Internal Functions	*/
144 static	void	string_dump(char *, uchar_t *, int, int, char msg_string[]);
145 
146 /*
147  * Allocate space for and return a pointer to a string
148  * on the stack.  If the string is null, create
149  * an empty string.
150  * Use g_destroy_data() to free when no longer used.
151  */
152 char *
153 g_alloc_string(char *s)
154 {
155 	char	*ns;
156 
157 	if (s == (char *)NULL) {
158 		ns = (char *)g_zalloc(1);
159 	} else {
160 		ns = (char *)g_zalloc(strlen(s) + 1);
161 		if (ns != NULL) {
162 			(void) strncpy(ns, s, (strlen(s) + 1));
163 		}
164 	}
165 	return (ns);
166 }
167 
168 
169 /*
170  * This routine is a wrapper for free.
171  */
172 void
173 g_destroy_data(void *data)
174 {
175 	A_DPRINTF("  g_destroy_data: Free\'ed buffer at 0x%x\n",
176 		data);
177 	free((void *)data);
178 }
179 
180 
181 /*
182  * Dump a structure in hexadecimal.
183  */
184 void
185 g_dump(char *hdr, uchar_t *src, int nbytes, int format)
186 {
187 	int i;
188 	int n;
189 	char	*p;
190 	char	s[256];
191 
192 	assert(format == HEX_ONLY || format == HEX_ASCII);
193 
194 	(void) strcpy(s, hdr);
195 	for (p = s; *p; p++) {
196 		*p = ' ';
197 	}
198 
199 	p = hdr;
200 	while (nbytes > 0) {
201 		(void) fprintf(stdout, "%s", p);
202 		p = s;
203 		n = min(nbytes, BYTES_PER_LINE);
204 		for (i = 0; i < n; i++) {
205 			(void) fprintf(stdout, "%02x ", src[i] & 0xff);
206 		}
207 		if (format == HEX_ASCII) {
208 			for (i = BYTES_PER_LINE-n; i > 0; i--) {
209 				(void) fprintf(stdout, "   ");
210 			}
211 			(void) fprintf(stdout, "    ");
212 			for (i = 0; i < n; i++) {
213 				(void) fprintf(stdout, "%c",
214 					isprint(src[i]) ? src[i] : '.');
215 			}
216 		}
217 		(void) fprintf(stdout, "\n");
218 		nbytes -= n;
219 		src += n;
220 	}
221 }
222 
223 /*
224  * Internal routine to clean up ../'s in paths.
225  * returns 0 if no "../" are left.
226  *
227  * Wouldn't it be nice if there was a standard system library
228  * routine to do this...?
229  */
230 static int
231 cleanup_dotdot_path(char *path)
232 {
233 	char holder[MAXPATHLEN];
234 	char *dotdot;
235 	char *previous_slash;
236 
237 	/* Find the first "/../" in the string */
238 	dotdot = strstr(path, "/../");
239 	if (dotdot == NULL) {
240 		return (0);
241 	}
242 
243 
244 	/*
245 	 * If the [0] character is '/' and "../" immediatly
246 	 * follows it, then we can strip the ../
247 	 *
248 	 *	/../../foo/bar == /foo/bar
249 	 *
250 	 */
251 	if (dotdot == path) {
252 		strcpy(holder, &path[3]); /* strip "/.." */
253 		strcpy(path, holder);
254 		return (1);
255 	}
256 
257 	/*
258 	 * Now look for the LAST "/" before the "/../"
259 	 * as this is the parent dir we can get rid of.
260 	 * We do this by temporarily truncating the string
261 	 * at the '/' just before "../" using the dotdot pointer.
262 	 */
263 	*dotdot = '\0';
264 	previous_slash = strrchr(path, '/');
265 	if (previous_slash == NULL) {
266 		/*
267 		 * hmm, somethings wrong.  path looks something
268 		 * like "foo/../bar/" so we can't really deal with it.
269 		 */
270 		return (0);
271 	}
272 	/*
273 	 * Now truncate the path just after the previous '/'
274 	 * and slam everything after the "../" back on
275 	 */
276 	*(previous_slash+1) = '\0';
277 	strcat(path, dotdot+4);
278 	return (1); /* We may have more "../"s */
279 }
280 
281 
282 /*
283  * Follow symbolic links from the logical device name to
284  * the /devfs physical device name.  To be complete, we
285  * handle the case of multiple links.  This function
286  * either returns NULL (no links, or some other error),
287  * or the physical device name, alloc'ed on the heap.
288  *
289  * NOTE: If the path is relative, it will be forced into
290  * an absolute path by pre-pending the pwd to it.
291  */
292 char *
293 g_get_physical_name_from_link(char *path)
294 {
295 	struct stat	stbuf;
296 	char		source[MAXPATHLEN];
297 	char		scratch[MAXPATHLEN];
298 	char		pwd[MAXPATHLEN];
299 	char		*tmp;
300 	int			cnt;
301 
302 	/* return NULL if path is NULL */
303 	if (path == NULL) {
304 		return (NULL);
305 	}
306 
307 	strcpy(source, path);
308 	for (;;) {
309 
310 		/*
311 		 * First make sure the path is absolute.  If not, make it.
312 		 * If it's already an absolute path, we have no need
313 		 * to determine the cwd, so the program should still
314 		 * function within security-by-obscurity directories.
315 		 */
316 		if (source[0] != '/') {
317 			tmp = getcwd(pwd, MAXPATHLEN);
318 			if (tmp == NULL) {
319 				O_DPRINTF("getcwd() failed - %s\n",
320 					strerror(errno));
321 				return (NULL);
322 			}
323 			/*
324 			 * Handle special case of "./foo/bar"
325 			 */
326 			if (source[0] == '.' && source[1] == '/') {
327 				strcpy(scratch, source+2);
328 			} else { /* no "./" so just take everything */
329 				strcpy(scratch, source);
330 			}
331 			strcpy(source, pwd);
332 			strcat(source, "/");
333 			strcat(source, scratch);
334 		}
335 
336 		/*
337 		 * Clean up any "../"s that are in the path
338 		 */
339 		while (cleanup_dotdot_path(source));
340 
341 		/*
342 		 * source is now an absolute path to the link we're
343 		 * concerned with
344 		 *
345 		 * See if there's a real file out there.  If not,
346 		 * we have a dangling link and we ignore it.
347 		 */
348 
349 		if (stat(source, &stbuf) == -1) {
350 			O_DPRINTF("stat() failed for %s- %s\n",
351 				source, strerror(errno));
352 			return (NULL);
353 		}
354 		if (lstat(source, &stbuf) == -1) {
355 			O_DPRINTF("lstat() failed for - %s\n",
356 				source, strerror(errno));
357 			return (NULL);
358 		}
359 		/*
360 		 * If the file is not a link, we're done one
361 		 * way or the other.  If there were links,
362 		 * return the full pathname of the resulting
363 		 * file.
364 		 *
365 		 * Note:  All of our temp's are on the stack,
366 		 * so we have to copy the final result to the heap.
367 		 */
368 		if (!S_ISLNK(stbuf.st_mode)) {
369 			return (g_alloc_string(source));
370 		}
371 		cnt = readlink(source, scratch, sizeof (scratch));
372 		if (cnt < 0) {
373 			O_DPRINTF("readlink() failed - %s\n",
374 				strerror(errno));
375 			return (NULL);
376 		}
377 		/*
378 		 * scratch is on the heap, and for some reason readlink
379 		 * doesn't always terminate things properly so we have
380 		 * to make certain we're properly terminated
381 		 */
382 		scratch[cnt] = '\0';
383 
384 		/*
385 		 * Now check to see if the link is relative.  If so,
386 		 * then we have to append it to the directory
387 		 * which the source was in. (This is non trivial)
388 		 */
389 		if (scratch[0] != '/') {
390 			tmp = strrchr(source, '/');
391 			if (tmp == NULL) { /* Whoa!  Something's hosed! */
392 				O_DPRINTF("Internal error... corrupt path.\n");
393 				return (NULL);
394 			}
395 			/* Now strip off just the directory path */
396 			*(tmp+1) = '\0'; /* Keeping the last '/' */
397 			/* and append the new link */
398 			strcat(source, scratch);
399 			/*
400 			 * Note:  At this point, source should have "../"s
401 			 * but we'll clean it up in the next pass through
402 			 * the loop.
403 			 */
404 		} else {
405 			/* It's an absolute link so no worries */
406 			strcpy(source, scratch);
407 		}
408 	}
409 	/* Never reach here */
410 }
411 
412 /*
413  * Function for getting physical pathnames
414  *
415  * This function can handle 3 different inputs.
416  *
417  * 1) Inputs of the form cN
418  *	This requires the program  to search the /dev/rdsk
419  *	directory for a device that is conected to the
420  *	controller with number 'N' and then getting
421  *	the physical pathname of the controller.
422  *	The format of the controller pathname is
423  *	/devices/.../.../SUNW,soc@x,x/SUNW,pln@xxxx,xxxxxxxx:ctlr
424  *	The physical pathname is returned.
425  *
426  * 2) Inputs of the form /dev/rdsk/cNtNdNsN
427  *	These are identified by being a link
428  *	The physical path they are linked to is returned.
429  *
430  * 3) Inputs of the form /devices/...
431  *	These are actual physical names.
432  *	They are not converted.
433  */
434 char *
435 g_get_physical_name(char *path)
436 {
437 	struct stat	stbuf;
438 	char		s[MAXPATHLEN];
439 	char		namebuf[MAXPATHLEN];
440 	char		savedir[MAXPATHLEN];
441 	char		*result = NULL;
442 	DIR		*dirp;
443 	struct dirent	*entp;
444 	char		*dev_name, *char_ptr;
445 	struct stat	sb;
446 	int		found_flag = 0;
447 	int		status = 0;
448 	int		i;
449 
450 	/* return invalid path if path NULL */
451 	if (path == NULL) {
452 		return (NULL);
453 	}
454 
455 	(void) strcpy(s, path);
456 	/*
457 	 * See if the form is cN
458 	 * Must handle scenaro where there is a file cN in this directory
459 	 * Bug ID: 1184633
460 	 *
461 	 * We could be in the /dev/rdsk directory and the file could be of
462 	 * the form cNdNsN (See man disks).
463 	 */
464 	status = stat(s, &stbuf);
465 	if (((status == -1) && (errno == ENOENT)) ||
466 	    ((s[0] == 'c') && ((int)strlen(s) > 1) && ((int)strlen(s) < 5))) {
467 		/*
468 		 * Further qualify cN entry
469 		 */
470 		if ((s[0] != 'c') || ((int)strlen(s) <= 1) ||
471 		((int)strlen(s) >= 5)) {
472 			goto exit;
473 		}
474 		for (i = 1; i < (int)strlen(s); i++) {
475 			if ((s[i] < '0') || (s[i] > '9')) {
476 				goto exit;
477 			}
478 		}
479 		/*
480 		 * path does not point to a file or file is of form cN
481 		 */
482 		P_DPRINTF("  g_get_physical_name: "
483 			"Found entry of the form cN n=%s len=%d\n",
484 			&s[1], strlen(s));
485 
486 		dev_name = g_zalloc(sizeof ("/dev/rdsk"));
487 		sprintf((char *)dev_name, "/dev/rdsk");
488 
489 		if ((dirp = opendir(dev_name)) == NULL) {
490 			g_destroy_data(dev_name);
491 			goto exit;
492 		}
493 
494 		while ((entp = readdir(dirp)) != NULL) {
495 		    if (strcmp(entp->d_name, ".") == 0 ||
496 			strcmp(entp->d_name, "..") == 0)
497 			continue;
498 
499 		    if (entp->d_name[0] != 'c')
500 			/*
501 			 * Silently Ignore for now any names
502 			 * not stating with c
503 			 */
504 			continue;
505 
506 		    sprintf(namebuf, "%s/%s", dev_name, entp->d_name);
507 
508 		    if ((lstat(namebuf, &sb)) < 0) {
509 				L_WARNINGS(MSGSTR(55,
510 					"Warning: Cannot stat %s\n"),
511 					namebuf);
512 			continue;
513 		    }
514 
515 		    if (!S_ISLNK(sb.st_mode)) {
516 				L_WARNINGS(MSGSTR(56,
517 					"Warning: %s is not a symbolic link\n"),
518 					namebuf);
519 			continue;
520 		    }
521 
522 		    if (strstr(entp->d_name, s) != NULL) {
523 			/*
524 			 * found link to device in /devices
525 			 *
526 			 * Further qualify to be sure I have
527 			 * not found entry of the form c10
528 			 * when I am searching for c1
529 			 */
530 			if (atoi(&s[1]) == atoi(&entp->d_name[1])) {
531 			    P_DPRINTF("  g_get_physical_name: "
532 			    "Found entry in /dev/rdsk matching %s: %s\n",
533 				s, entp->d_name);
534 				found_flag = 1;
535 				break;
536 			}
537 		    }
538 		}
539 		closedir(dirp);
540 		g_destroy_data(dev_name);
541 
542 		if (found_flag) {
543 		    result = g_get_physical_name_from_link(namebuf);
544 		    if (result == NULL) {
545 			goto exit;
546 		    }
547 			/*
548 			 * Convert from device name to controller name
549 			 */
550 		    char_ptr = strrchr(result, '/');
551 		    *char_ptr = '\0';   /* Terminate sting  */
552 		    (void) strcat(result, CTLR_POSTFIX);
553 		}
554 		goto exit;
555 	}
556 	if (status == -1)
557 		goto exit;
558 
559 	if (lstat(s, &stbuf) == -1) {
560 			L_WARNINGS(MSGSTR(134,
561 				"%s: lstat() failed - %s\n"),
562 				s, strerror(errno));
563 		goto exit;
564 	}
565 	/*
566 	 */
567 	if (!S_ISLNK(stbuf.st_mode)) {
568 		/*
569 		 * Path is not a linked file so must be
570 		 * a physical path
571 		 */
572 		if (S_ISCHR(stbuf.st_mode) || S_ISDIR(stbuf.st_mode)) {
573 			/* Make sure a full path as that is required. */
574 			if (strstr(s, "/devices")) {
575 				result = g_alloc_string(s);
576 			} else {
577 				if (getcwd(savedir,
578 					sizeof (savedir)) == NULL) {
579 					return (NULL);
580 				}
581 				/*
582 				 * Check for this format:
583 				 * ./ssd@0,1:g,raw
584 				 */
585 				if (s[0] == '.') {
586 					strcat(savedir, &s[1]);
587 				} else {
588 					strcat(savedir, "/");
589 					strcat(savedir, s);
590 				}
591 				result = g_alloc_string(savedir);
592 			}
593 		}
594 	} else {
595 		/*
596 		 * Entry is linked file
597 		 * so follow link to physical name
598 		 */
599 		result = g_get_physical_name_from_link(path);
600 	}
601 
602 exit:
603 	return (result);
604 }
605 
606 /*
607  *	Function to open a device
608  */
609 int
610 g_object_open(char *path, int flag)
611 {
612 int fd = -1, retry = 0;
613 	if (getenv("_LUX_O_DEBUG") != NULL) {
614 		(void) printf("  Object_open:%s ", path);
615 		if (flag & O_WRONLY) {
616 			(void) printf("O_WRONLY,");
617 		} else if (flag & O_RDWR) {
618 			(void) printf("O_RDWR,");
619 		} else {
620 			(void) printf("O_RDONLY,");
621 		}
622 		if (flag & O_NDELAY) {
623 			(void) printf("O_NDELAY,");
624 		}
625 		if (flag & O_APPEND) {
626 			(void) printf("O_APPEND,");
627 		}
628 		if (flag & O_DSYNC) {
629 			(void) printf("O_DSYNC,");
630 		}
631 		if (flag & O_RSYNC) {
632 			(void) printf("O_RSYNC,");
633 		}
634 		if (flag & O_SYNC) {
635 			(void) printf("O_SYNC,");
636 		}
637 		if (flag & O_NOCTTY) {
638 			(void) printf("O_NOCTTY,");
639 		}
640 		if (flag & O_CREAT) {
641 			(void) printf("O_CREAT,");
642 		}
643 		if (flag & O_EXCL) {
644 			(void) printf("O_EXCL,");
645 		}
646 		if (flag & O_TRUNC) {
647 			(void) printf("O_TRUNC,");
648 		}
649 		(void) printf("\n");
650 	}
651 
652 	/* Open retries introduced due to bugid 4473337	*/
653 	errno	= 0;
654 	fd	= open(path, flag);
655 	while (fd < 0 && retry++ < RETRY_OBJECT_OPEN && (
656 			errno == EBUSY || errno == EAGAIN)) {
657 		O_DPRINTF("  Object_open: Retried:%d %d %s\n",
658 			retry, errno, path);
659 		(void) usleep(WAIT_OBJECT_OPEN);
660 		fd = open(path, flag);
661 	}
662 	if (fd < 0) {
663 		O_DPRINTF("  Object_open: Open failed:%s\n", path);
664 	}
665 	return (fd);
666 }
667 
668 
669 /*
670  * Return a pointer to a string telling us the name of the command.
671  */
672 char *
673 g_scsi_find_command_name(int cmd)
674 {
675 /*
676  * Names of commands.  Must have SCMD_UNKNOWN at end of list.
677  */
678 struct scsi_command_name {
679 	int command;
680 	char	*name;
681 } scsi_command_names[29];
682 
683 register struct scsi_command_name *c;
684 
685 	scsi_command_names[0].command = SCMD_TEST_UNIT_READY;
686 	scsi_command_names[0].name = MSGSTR(61, "Test Unit Ready");
687 
688 	scsi_command_names[1].command = SCMD_FORMAT;
689 	scsi_command_names[1].name = MSGSTR(110, "Format");
690 
691 	scsi_command_names[2].command = SCMD_REASSIGN_BLOCK;
692 	scsi_command_names[2].name = MSGSTR(77, "Reassign Block");
693 
694 	scsi_command_names[3].command = SCMD_READ;
695 	scsi_command_names[3].name = MSGSTR(27, "Read");
696 
697 	scsi_command_names[4].command = SCMD_WRITE;
698 	scsi_command_names[4].name = MSGSTR(54, "Write");
699 
700 	scsi_command_names[5].command = SCMD_READ_G1;
701 	scsi_command_names[5].name = MSGSTR(79, "Read(10 Byte)");
702 
703 	scsi_command_names[6].command = SCMD_WRITE_G1;
704 	scsi_command_names[6].name = MSGSTR(51, "Write(10 Byte)");
705 
706 	scsi_command_names[7].command = SCMD_MODE_SELECT;
707 	scsi_command_names[7].name = MSGSTR(97, "Mode Select");
708 
709 	scsi_command_names[8].command = SCMD_MODE_SENSE;
710 	scsi_command_names[8].name = MSGSTR(95, "Mode Sense");
711 
712 	scsi_command_names[9].command = SCMD_REASSIGN_BLOCK;
713 	scsi_command_names[9].name = MSGSTR(77, "Reassign Block");
714 
715 	scsi_command_names[10].command = SCMD_REQUEST_SENSE;
716 	scsi_command_names[10].name = MSGSTR(74, "Request Sense");
717 
718 	scsi_command_names[11].command = SCMD_READ_DEFECT_LIST;
719 	scsi_command_names[11].name = MSGSTR(80, "Read Defect List");
720 
721 	scsi_command_names[12].command = SCMD_INQUIRY;
722 	scsi_command_names[12].name = MSGSTR(102, "Inquiry");
723 
724 	scsi_command_names[13].command = SCMD_WRITE_BUFFER;
725 	scsi_command_names[13].name = MSGSTR(53, "Write Buffer");
726 
727 	scsi_command_names[14].command = SCMD_READ_BUFFER;
728 	scsi_command_names[14].name = MSGSTR(82, "Read Buffer");
729 
730 	scsi_command_names[15].command = SCMD_START_STOP;
731 	scsi_command_names[15].name = MSGSTR(67, "Start/Stop");
732 
733 	scsi_command_names[16].command = SCMD_RESERVE;
734 	scsi_command_names[16].name = MSGSTR(72, "Reserve");
735 
736 	scsi_command_names[17].command = SCMD_RELEASE;
737 	scsi_command_names[17].name = MSGSTR(75, "Release");
738 
739 	scsi_command_names[18].command = SCMD_MODE_SENSE_G1;
740 	scsi_command_names[18].name = MSGSTR(94, "Mode Sense(10 Byte)");
741 
742 	scsi_command_names[19].command = SCMD_MODE_SELECT_G1;
743 	scsi_command_names[19].name = MSGSTR(96, "Mode Select(10 Byte)");
744 
745 	scsi_command_names[20].command = SCMD_READ_CAPACITY;
746 	scsi_command_names[20].name = MSGSTR(81, "Read Capacity");
747 
748 	scsi_command_names[21].command = SCMD_SYNC_CACHE;
749 	scsi_command_names[21].name = MSGSTR(64, "Synchronize Cache");
750 
751 	scsi_command_names[22].command = SCMD_READ_DEFECT_LIST;
752 	scsi_command_names[22].name = MSGSTR(80, "Read Defect List");
753 
754 	scsi_command_names[23].command = SCMD_GDIAG;
755 	scsi_command_names[23].name = MSGSTR(108, "Get Diagnostic");
756 
757 	scsi_command_names[24].command = SCMD_SDIAG;
758 	scsi_command_names[24].name = MSGSTR(69, "Set Diagnostic");
759 
760 	scsi_command_names[25].command = SCMD_PERS_RESERV_IN;
761 	scsi_command_names[25].name = MSGSTR(10500, "Persistent Reserve In");
762 
763 	scsi_command_names[26].command = SCMD_PERS_RESERV_OUT;
764 	scsi_command_names[26].name = MSGSTR(10501, "Persistent Reserve out");
765 
766 	scsi_command_names[27].command = SCMD_LOG_SENSE;
767 	scsi_command_names[27].name = MSGSTR(10502, "Log Sense");
768 
769 	scsi_command_names[28].command = SCMD_UNKNOWN;
770 	scsi_command_names[28].name = MSGSTR(25, "Unknown");
771 
772 
773 	for (c = scsi_command_names; c->command != SCMD_UNKNOWN; c++)
774 		if (c->command == cmd)
775 			break;
776 	return (c->name);
777 }
778 
779 
780 /*
781  *	Function to create error message containing
782  *	scsi request sense information
783  */
784 
785 void
786 g_scsi_printerr(struct uscsi_cmd *ucmd, struct scsi_extended_sense *rq,
787 		int rqlen, char msg_string[], char *err_string)
788 {
789 	int		blkno;
790 
791 	switch (rq->es_key) {
792 	case KEY_NO_SENSE:
793 		(void) sprintf(msg_string, MSGSTR(91, "No sense error"));
794 		break;
795 	case KEY_RECOVERABLE_ERROR:
796 		(void) sprintf(msg_string, MSGSTR(76, "Recoverable error"));
797 		break;
798 	case KEY_NOT_READY:
799 		(void) sprintf(msg_string,
800 			MSGSTR(10503,
801 			"Device Not ready."
802 			" Error: Random Retry Failed: %s\n."),
803 			err_string);
804 		break;
805 	case KEY_MEDIUM_ERROR:
806 		(void) sprintf(msg_string, MSGSTR(99, "Medium error"));
807 		break;
808 	case KEY_HARDWARE_ERROR:
809 		(void) sprintf(msg_string, MSGSTR(106, "Hardware error"));
810 		break;
811 	case KEY_ILLEGAL_REQUEST:
812 		(void) sprintf(msg_string, MSGSTR(103, "Illegal request"));
813 		break;
814 	case KEY_UNIT_ATTENTION:
815 		(void) sprintf(msg_string,
816 			MSGSTR(10504,
817 			"Unit attention."
818 			"Error: Random Retry Failed.\n"));
819 		break;
820 	case KEY_WRITE_PROTECT:
821 		(void) sprintf(msg_string, MSGSTR(52, "Write protect error"));
822 		break;
823 	case KEY_BLANK_CHECK:
824 		(void) sprintf(msg_string, MSGSTR(131, "Blank check error"));
825 		break;
826 	case KEY_VENDOR_UNIQUE:
827 		(void) sprintf(msg_string, MSGSTR(58, "Vendor unique error"));
828 		break;
829 	case KEY_COPY_ABORTED:
830 		(void) sprintf(msg_string, MSGSTR(123, "Copy aborted error"));
831 		break;
832 	case KEY_ABORTED_COMMAND:
833 		(void) sprintf(msg_string,
834 			MSGSTR(10505,
835 			"Aborted command."
836 			" Error: Random Retry Failed.\n"));
837 		break;
838 	case KEY_EQUAL:
839 		(void) sprintf(msg_string, MSGSTR(117, "Equal error"));
840 		break;
841 	case KEY_VOLUME_OVERFLOW:
842 		(void) sprintf(msg_string, MSGSTR(57, "Volume overflow"));
843 		break;
844 	case KEY_MISCOMPARE:
845 		(void) sprintf(msg_string, MSGSTR(98, "Miscompare error"));
846 		break;
847 	case KEY_RESERVED:
848 		(void) sprintf(msg_string, MSGSTR(10506,
849 			"Reserved value found"));
850 		break;
851 	default:
852 		(void) sprintf(msg_string, MSGSTR(59, "Unknown error"));
853 		break;
854 	}
855 
856 	(void) sprintf(&msg_string[strlen(msg_string)],
857 		MSGSTR(10507, " during: %s"),
858 		g_scsi_find_command_name(ucmd->uscsi_cdb[0]));
859 
860 	if (rq->es_valid) {
861 		blkno = (rq->es_info_1 << 24) | (rq->es_info_2 << 16) |
862 			(rq->es_info_3 << 8) | rq->es_info_4;
863 		(void) sprintf(&msg_string[strlen(msg_string)],
864 			MSGSTR(49, ": block %d (0x%x)"), blkno, blkno);
865 	}
866 
867 	(void) sprintf(&msg_string[strlen(msg_string)], "\n");
868 
869 	if (rq->es_add_len >= 6) {
870 		(void) sprintf(&msg_string[strlen(msg_string)],
871 		MSGSTR(132, "  Additional sense: 0x%x   ASC Qualifier: 0x%x\n"),
872 			rq->es_add_code, rq->es_qual_code);
873 			/*
874 			 * rq->es_add_info[ADD_SENSE_CODE],
875 			 * rq->es_add_info[ADD_SENSE_QUAL_CODE]);
876 			 */
877 	}
878 	if (rq->es_key == KEY_ILLEGAL_REQUEST) {
879 		string_dump(MSGSTR(47, " cmd:   "), (uchar_t *)ucmd,
880 			sizeof (struct uscsi_cmd), HEX_ONLY, msg_string);
881 		string_dump(MSGSTR(48, " cdb:   "),
882 			(uchar_t *)ucmd->uscsi_cdb,
883 			ucmd->uscsi_cdblen, HEX_ONLY, msg_string);
884 	}
885 	string_dump(MSGSTR(43, " sense:  "),
886 		(uchar_t *)rq, 8 + rq->es_add_len, HEX_ONLY,
887 		msg_string);
888 	rqlen = rqlen;	/* not used */
889 }
890 
891 
892 /*
893  *		Special string dump for error message
894  */
895 static	void
896 string_dump(char *hdr, uchar_t *src, int nbytes, int format, char msg_string[])
897 {
898 	int i;
899 	int n;
900 	char	*p;
901 	char	s[256];
902 
903 	assert(format == HEX_ONLY || format == HEX_ASCII);
904 
905 	(void) strcpy(s, hdr);
906 	for (p = s; *p; p++) {
907 		*p = ' ';
908 	}
909 
910 	p = hdr;
911 	while (nbytes > 0) {
912 		(void) sprintf(&msg_string[strlen(msg_string)],
913 			"%s", p);
914 		p = s;
915 		n = min(nbytes, BYTES_PER_LINE);
916 		for (i = 0; i < n; i++) {
917 			(void) sprintf(&msg_string[strlen(msg_string)],
918 				"%02x ",
919 				src[i] & 0xff);
920 		}
921 		if (format == HEX_ASCII) {
922 			for (i = BYTES_PER_LINE-n; i > 0; i--) {
923 				(void) sprintf(&msg_string[strlen(msg_string)],
924 					"   ");
925 			}
926 			(void) sprintf(&msg_string[strlen(msg_string)],
927 				"    ");
928 			for (i = 0; i < n; i++) {
929 				(void) sprintf(&msg_string[strlen(msg_string)],
930 					"%c",
931 					isprint(src[i]) ? src[i] : '.');
932 			}
933 		}
934 		(void) sprintf(&msg_string[strlen(msg_string)], "\n");
935 		nbytes -= n;
936 		src += n;
937 	}
938 }
939 
940 
941 
942 /*
943  * This routine is a wrapper for malloc.  It allocates pre-zeroed space,
944  * and checks the return value so the caller doesn't have to.
945  */
946 void *
947 g_zalloc(int count)
948 {
949 	void	*ptr;
950 
951 	ptr = (void *) calloc(1, (unsigned)count);
952 	A_DPRINTF("  g_zalloc: Allocated 0x%x bytes "
953 			"at 0x%x\n", count, ptr);
954 
955 	return (ptr);
956 }
957 
958 /*
959  * Open up the i18n catalog.
960  * Returns:
961  *  0 = O.K.
962  * -1 = Failed (Will revert to default strings)
963  */
964 int
965 g_i18n_catopen(void)
966 {
967 	static int fileopen = 0;
968 	static mutex_t mp;
969 
970 	if (setlocale(LC_ALL, "") == NULL) {
971 	    (void) fprintf(stderr,
972 		"Cannot operate in the locale requested. "
973 		"Continuing in the default C locale\n");
974 	}
975 	if (mutex_lock(&mp) != 0) {
976 		return (-1);
977 	}
978 	if (!fileopen) {
979 		l_catd = catopen("a5k_g_fc_i18n_cat", NL_CAT_LOCALE);
980 		if (l_catd == (nl_catd)-1) {
981 			(void) mutex_unlock(&mp);
982 			return (-1);
983 		}
984 		fileopen = 1;
985 	}
986 	(void) mutex_unlock(&mp);
987 	return (0);
988 }
989 
990 /* Macro used by g_get_path_type() */
991 #define	GetMatch(s_ptr)	\
992 	for (found = 0, search_arr_ptr = s_ptr; \
993 		search_arr_ptr->string != NULL; \
994 			search_arr_ptr++) {\
995 		if (strstr(path_ptr, search_arr_ptr->string) != NULL) {\
996 			found = 1;\
997 			break;\
998 		}\
999 	}
1000 
1001 /*
1002  * Input  : A NULL terminated string
1003  *          This string is checked to be an absolute device path
1004  * Output :
1005  * 	The FCA type and Xport type if found in the path on success
1006  *	0 on Failure
1007  *
1008  * Examples of valid device strings :
1009  *
1010  * Non Fabric FC driver :
1011  * /devices/io-unit@f,e0200000/sbi@0,0/SUNW,socal@1,0/sf@1,0:ctlr
1012  * /devices/io-unit@f,e2200000/sbi@0,0/SUNW,socal@3,0/sf@0,0/ssd@20,0:c,raw
1013  * /devices/sbus@1f,0/SUNW,socal@0,0/sf@0,0:devctl
1014  * /devices/sbus@1f,0/SUNW,socal@2,0/sf@1,0/ssd@w2200002037110cbf,0:b,raw
1015  * /devices/pci@1f,4000/SUNW,ifp@4:devctl
1016  * /devices/pci@1f,4000/SUNW,ifp@2/ssd@w2100002037049ba0,0:c,raw
1017  * /devices/pci@6,4000/pci@2/SUNW,ifp@5/ssd@w210000203708b44f,0:c,raw
1018  *
1019  * Fabric FC driver (fp) :
1020  * 	- offical device path for Qlogic 2202 with proper FCODE
1021  *	  as of 12/99.
1022  * /devices/pci@1f,2000/pci@1/SUNW,qlc@5/fp@0,0:devctl
1023  * /devices/pci@e,2000/pci@2/SUNW,qlc@4/fp@0,0:devctl
1024  *
1025  */
1026 uint_t
1027 g_get_path_type(char *path)
1028 {
1029 	uint_t path_type = 0;
1030 	int	i = 0, pathcnt = 1;
1031 	char *path_ptr = path;
1032 	struct str_type *search_arr_ptr; /* updated by GetMatch macro */
1033 	char found;			 /* Updated by GetMatch marco */
1034 	char		drvr_path1[MAXPATHLEN];
1035 	mp_pathlist_t	pathlist;
1036 	int		p_on = 0, p_st = 0;
1037 
1038 	/* Path passed must be an absolute device path */
1039 	if (strncmp(path_ptr, DEV_PREFIX, DEV_PREFIX_LEN) ||
1040 				(strlen(path_ptr) == DEV_PREFIX_LEN)) {
1041 		return (0);	/* Invalid path */
1042 	}
1043 
1044 	/* if mpxio device, need to convert from vhci to phci */
1045 	if (strstr(path, SCSI_VHCI)) {
1046 		(void) strcpy(drvr_path1, path);
1047 		if (g_get_pathlist(drvr_path1, &pathlist)) {
1048 			return (0);
1049 		}
1050 		pathcnt = pathlist.path_count;
1051 		p_on = p_st = 0;
1052 		for (i = 0; i < pathcnt; i++) {
1053 			if (pathlist.path_info[i].path_state < MAXPATHSTATE) {
1054 				if (pathlist.path_info[i].path_state ==
1055 					MDI_PATHINFO_STATE_ONLINE) {
1056 					p_on = i;
1057 					break;
1058 				} else if (pathlist.path_info[i].path_state ==
1059 					MDI_PATHINFO_STATE_STANDBY) {
1060 					p_st = i;
1061 				}
1062 			}
1063 		}
1064 		if (pathlist.path_info[p_on].path_state ==
1065 		    MDI_PATHINFO_STATE_ONLINE) {
1066 			/* on_line path */
1067 			(void) strcpy(drvr_path1,
1068 				pathlist.path_info[p_on].path_hba);
1069 		} else {
1070 			/* standby or path0 */
1071 			(void) strcpy(drvr_path1,
1072 				pathlist.path_info[p_st].path_hba);
1073 		}
1074 		free(pathlist.path_info);
1075 		path_ptr = drvr_path1;
1076 	}
1077 
1078 	GetMatch(ValidBusStrings);
1079 	if (found == 0) {
1080 		/* No valid bus string - so not a valid path */
1081 		return (0);
1082 	}
1083 
1084 	GetMatch(ValidFCAstrings);	/* Check for a valid FCA string */
1085 	if (found != 0) {
1086 		path_type |= search_arr_ptr->type;
1087 	}
1088 
1089 	/*
1090 	 * continue to check xport even without valid FCA string.
1091 	 * This is to support 3rd party FCA vendor on Leadville stack.
1092 	 */
1093 	GetMatch(ValidXportStrings);	/* Check for a valid transport str */
1094 	if (found == 0) {
1095 		return (path_type);
1096 	} else {
1097 		/*
1098 		 * if leadville tranport is detected and fca is not set yet,
1099 		 * set fca flag to generic FC_FCA_MASK.
1100 		 */
1101 		if ((search_arr_ptr->type == FC_GEN_XPORT) &&
1102 			(!(path_type & FC_FCA_MASK))) {
1103 			path_type |= FC_FCA_MASK;
1104 		}
1105 	}
1106 	path_type |= search_arr_ptr->type;
1107 
1108 	/*
1109 	 * A quick sanity check to make sure that we dont have
1110 	 * a combination that is not possible
1111 	 */
1112 	if (((path_type & (FC4_FCA_MASK | FC_XPORT_MASK)) ==
1113 			path_type) ||
1114 		((path_type & (FC_FCA_MASK | FC4_XPORT_MASK)) ==
1115 			path_type)) {
1116 		path_type = 0;
1117 	}
1118 
1119 	return (path_type);
1120 }
1121 
1122 
1123 /*
1124  * g_get_port_path(char *, portlist_t *)
1125  * Purpose: Find all port nexus paths for a particular driver
1126  * Input:   portdrvr
1127  *		set to name of driver for which to find the paths
1128  * Output:  portlist
1129  *		allocated structure to hold paths found
1130  *		user must call g_free_portlist(portlist_t *) to
1131  *		free allocated memory
1132  */
1133 int
1134 g_get_port_path(char *portdrvr, portlist_t *portlist)
1135 {
1136 	di_node_t root;
1137 	di_node_t node;
1138 	di_minor_t minor_node;
1139 	char hbapathfound[MAXPATHLEN];
1140 	char *tmppath;
1141 	struct stat buf;
1142 
1143 	/* return invalid argument if *portdrvr or *portlist is NULL */
1144 	if ((portdrvr == NULL) || (portlist == NULL)) {
1145 		return (L_INVALID_ARG);
1146 	}
1147 
1148 	/* Create a snapshot of the kernel device tree */
1149 	root = di_init("/", DINFOCPYALL);
1150 	if (root == DI_NODE_NIL) {
1151 		return (L_DEV_SNAPSHOT_FAILED);
1152 	}
1153 
1154 	/* point to first node which matches portdrvr */
1155 	node = di_drv_first_node(portdrvr, root);
1156 	if (node == DI_NODE_NIL) {
1157 		/*
1158 		 * Could not find driver node
1159 		 */
1160 		(void) di_fini(root);
1161 		if (errno == EINVAL)
1162 			return (L_PORT_DRIVER_NOT_FOUND);
1163 		else
1164 			return (L_PHYS_PATH_NOT_FOUND);
1165 	}
1166 
1167 	while (node) {
1168 		/* point to first minor node which matches node */
1169 		minor_node = di_minor_next(node, DI_MINOR_NIL);
1170 
1171 		/* if we have a minor node use it */
1172 		while (minor_node) {
1173 			/*
1174 			 * Is this a devctl or pseudo node?
1175 			 * If not, skip it.
1176 			 * Soc+ HBA port device paths such as:
1177 			 * 	/devices/sbus@2,0/SUNW,socal@d,10000:0
1178 			 * are pseudo nodes as of S9 so we need to
1179 			 * include those as well.
1180 			 */
1181 			if (di_minor_nodetype(minor_node) &&
1182 				(strcmp(di_minor_nodetype(minor_node),
1183 					DDI_NT_NEXUS) &&
1184 				strcmp(di_minor_nodetype(minor_node),
1185 					DDI_PSEUDO))) {
1186 				minor_node = di_minor_next(node, minor_node);
1187 				continue;
1188 			}
1189 			/*
1190 			 * Prepend '/devices' to path
1191 			 * Note: The path returned from di_devfs_path
1192 			 * does NOT begin with '/devices'.
1193 			 * '/devices' is considered a mount point
1194 			 */
1195 			strcpy(hbapathfound, "/devices");
1196 			tmppath = di_devfs_path(node);
1197 			strcat(hbapathfound, tmppath);
1198 			(void) free(tmppath);
1199 			strcat(hbapathfound, ":");
1200 			strcat(hbapathfound, di_minor_name(minor_node));
1201 			/*
1202 			 * Verify that the path is validly constructed
1203 			 */
1204 			if ((stat(hbapathfound, (struct stat *)&buf)) < 0) {
1205 				(void) di_fini(root);
1206 				return (L_STAT_ERROR);
1207 			}
1208 			/* allocate memory and copy constructed path */
1209 			if ((portlist->hbacnt > MAX_HBA_PORT - 1) ||
1210 			    ((portlist->physpath[portlist->hbacnt] =
1211 				(char *)malloc(MAXPATHLEN)) == NULL)) {
1212 				(void) di_fini(root);
1213 				return (L_MALLOC_FAILED);
1214 			}
1215 			strcpy(portlist->physpath[portlist->hbacnt],
1216 				hbapathfound);
1217 			portlist->hbacnt++;
1218 			minor_node = di_minor_next(node, minor_node);
1219 		}
1220 		node = di_drv_next_node(node);
1221 	}
1222 	/*
1223 	 * Destroy the snapshot and return
1224 	 */
1225 	(void) di_fini(root);
1226 	return (0);
1227 }
1228 
1229 /*
1230  * Free the allocated portlist structure
1231  */
1232 void
1233 g_free_portlist(portlist_t *portlist)
1234 {
1235 	int x = 0;
1236 
1237 	/* return if portlist is NULL */
1238 	if (portlist == NULL) {
1239 		return;
1240 	}
1241 
1242 	for (x = 0; x < portlist->hbacnt; x++) {
1243 		if (portlist->physpath[x] != NULL) {
1244 			free(portlist->physpath[x]);
1245 		}
1246 	}
1247 }
1248 
1249 /*
1250  * Check VID/PID against enclosure disk table
1251  */
1252 boolean_t
1253 g_enclDiskChk(char *vid, char *pid)
1254 {
1255 	int i;
1256 	for (i = 0; enclDiskTbl[i].vid; i++) {
1257 		if ((strncmp(vid, enclDiskTbl[i].vid,
1258 		    strlen(enclDiskTbl[i].vid)) == 0) &&
1259 		    (strncmp(pid, enclDiskTbl[i].pid,
1260 		    strlen(enclDiskTbl[i].pid)) == 0)) {
1261 			return (B_TRUE);
1262 		}
1263 	}
1264 	return (B_FALSE);
1265 }
1266