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