xref: /illumos-gate/usr/src/cmd/luxadm/adm.c (revision 826703e5)
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  * Administration program for SENA
28  * subsystems and individual FC_AL devices.
29  */
30 
31 /*
32  * I18N message number ranges
33  *  This file: 2000 - 2999
34  *  Shared common messages: 1 - 1999
35  */
36 
37 /* #define		 _POSIX_SOURCE 1 */
38 
39 /*
40  * These defines are used to map instance number from sf minor node.
41  * They are copied from SF_INST_SHIFT4MINOR and SF_MINOR2INST in sfvar.h.
42  * sfvar.h is not clean for userland use.
43  * When it is cleaned up, these defines will be removed and sfvar.h
44  * will be included in luxadm.h header file.
45  */
46 #define		LUX_SF_INST_SHIFT4MINOR	6
47 #define		LUX_SF_MINOR2INST(x)	(x >> LUX_SF_INST_SHIFT4MINOR)
48 
49 /*	Includes	*/
50 #include	<stdlib.h>
51 #include	<stdio.h>
52 #include	<sys/file.h>
53 #include	<sys/errno.h>
54 #include	<sys/types.h>
55 #include	<sys/stat.h>
56 #include	<sys/param.h>
57 #include	<fcntl.h>
58 #include	<unistd.h>
59 #include	<errno.h>
60 #include	<string.h>
61 #include	<ctype.h>
62 #include	<strings.h>
63 #include	<sys/stat.h>
64 #include	<dirent.h>
65 #include	<limits.h>
66 #include	<stdarg.h>
67 #include	<termio.h>		/* For password */
68 #include	<sys/scsi/scsi.h>
69 
70 #include	"common.h"
71 #include	"luxadm.h"
72 
73 
74 /*	Global variables	*/
75 char	*dtype[16]; /* setting a global for later use. */
76 char			*whoami;
77 int	Options;
78 const	int OPTION_A	= 0x00000001;
79 const	int OPTION_B	= 0x00000002;
80 const	int OPTION_C	= 0x00000004;
81 const	int OPTION_D	= 0x00000008;
82 const	int OPTION_E	= 0x00000010;
83 const	int OPTION_F	= 0x00000020;
84 const	int OPTION_L	= 0x00000040;
85 const	int OPTION_P	= 0x00000080;
86 const	int OPTION_R	= 0x00000100;
87 const	int OPTION_T	= 0x00000200;
88 const	int OPTION_V	= 0x00000400;
89 const	int OPTION_Z	= 0x00001000;
90 const	int OPTION_Y	= 0x00002000;
91 const	int OPTION_CAPF	= 0x00004000;
92 const	int PVERBOSE	= 0x00008000;
93 const	int SAVE	= 0x00010000;
94 const	int EXPERT	= 0x00020000;
95 
96 /*
97  * Given a pointer to a character array, print the character array.
98  * the character array will not necesarily be NULL terminated.
99  *
100  * Inputs:
101  *	size - the max number of characters to print
102  *	fill_flag - flag when set fills all NULL characters with spaces
103  * Returns:
104  *	N/A
105  */
106 void
print_chars(uchar_t * buffer,int size,int fill_flag)107 print_chars(uchar_t *buffer, int size, int fill_flag)
108 {
109 
110 int i;
111 
112 	for (i = 0; i < size; i++) {
113 		if (buffer[i])
114 			(void) fprintf(stdout, "%c", buffer[i]);
115 		else if (fill_flag)
116 			(void) fprintf(stdout, " ");
117 		else
118 			return;
119 	}
120 }
121 
122 /*
123  * Name    : memstrstr
124  * Input   : pointer to buf1, pointer to buf2, size of buf1, size of buf2
125  * Returns :
126  *	Pointer to start of contents-of-buf2 in buf1 if it is found
127  *	NULL if buf1 does not contain contents of buf2
128  * Synopsis:
129  * This function works similar to strstr(). The difference is that null
130  * characters in the buffer are treated like any other character. So, buf1
131  * and buf2 can have embedded null characters in them.
132  */
133 static char *
memstrstr(char * s1,char * s2,int size1,int size2)134 memstrstr(char *s1, char *s2, int size1, int size2)
135 {
136 	int count1, count2;
137 	char *s1_ptr, *s2_ptr;
138 
139 	count1 = size1; count2 = size2;
140 	s1_ptr = s1; s2_ptr = s2;
141 
142 	if (size2 == 0)
143 		return (s1);
144 
145 	while (count1--) {
146 		if (*s1_ptr++ == *s2_ptr++) {
147 			if (--count2 == 0)
148 				return (s1_ptr - size2);
149 			continue;
150 		}
151 		count2 = size2;
152 		s2_ptr = s2;
153 	}
154 
155 	return (NULL);
156 }
157 
158 
159 /*
160  *	Download host bus adapter FCode to all supported cards.
161  *
162  *	Specify a directory that holds the FCode files, or
163  *	it will use the default dir.  Each file is dealt to
164  *	the appropriate function.
165  *
166  *	-p prints current versions only, -d specifies a directory to load
167  */
168 static	int
adm_fcode(int verbose,char * dir)169 adm_fcode(int verbose, char *dir)
170 {
171 	struct stat statbuf;
172 	struct dirent *dirp;
173 	DIR	*dp;
174 	int	fp;
175 	char	fbuf[BUFSIZ];
176 	char	file[MAXPATHLEN];
177 	int	retval = 0, strfound = 0;
178 	char	manf[BUFSIZ];
179 
180 	/* Find all adapters and print the current FCode version */
181 	if (Options & OPTION_P) {
182 
183 /* SOCAL (SBus) adapters are not supported on x86 */
184 #ifndef __x86
185 		if (verbose) {
186 			(void) fprintf(stdout,
187 			    MSGSTR(2215, "\n  Searching for FC100/S cards:\n"));
188 		}
189 		retval += fcal_update(Options & PVERBOSE, NULL);
190 #endif
191 
192 		if (verbose) {
193 			(void) fprintf(stdout,
194 		MSGSTR(2216, "\n  Searching for FC100/P, FC100/2P cards:\n"));
195 		}
196 		retval += q_qlgc_update(Options & PVERBOSE, NULL);
197 		if (verbose) {
198 			(void) fprintf(stdout,
199 			    MSGSTR(2503, "\n  Searching for Emulex cards:\n"));
200 		}
201 		retval += emulex_update(NULL);
202 
203 	/* Send files to the correct function for loading to the HBA */
204 	} else {
205 
206 		if (!dir) {
207 			(void) fprintf(stdout, MSGSTR(2251,
208 			    "  Location of Fcode not specified.\n"));
209 			return (1);
210 
211 		} else if (verbose) {
212 			(void) fprintf(stdout, MSGSTR(2217,
213 			    "  Using directory %s"), dir);
214 		}
215 		if (lstat(dir, &statbuf) < 0) {
216 			(void) fprintf(stderr, MSGSTR(134,
217 			    "%s: lstat() failed - %s\n"),
218 			    dir, strerror(errno));
219 			return (1);
220 		}
221 		if (S_ISDIR(statbuf.st_mode) == 0) {
222 		(void) fprintf(stderr,
223 		    MSGSTR(2218, "Error: %s is not a directory.\n"), dir);
224 			return (1);
225 		}
226 		if ((dp = opendir(dir)) == NULL) {
227 			(void) fprintf(stderr, MSGSTR(2219,
228 			    "  Error Cannot open directory %s\n"), dir);
229 			return (1);
230 		}
231 
232 		while ((dirp = readdir(dp)) != NULL) {
233 			if (strcmp(dirp->d_name, ".") == 0 ||
234 			    strcmp(dirp->d_name, "..") == 0) {
235 				continue;
236 			}
237 			sprintf(file, "%s/%s", dir, dirp->d_name);
238 
239 			if ((fp = open(file, O_RDONLY)) < 0) {
240 				(void) fprintf(stderr,
241 				    MSGSTR(2220,
242 					"Error: open() failed to open file "
243 					"%s\n"), file);
244 				/*
245 				 * We should just issue an error message and
246 				 * make an attempt on the next file,
247 				 * and the open error is still an error
248 				 * so the retval should be incremented
249 				 */
250 				retval++;
251 				continue;
252 			}
253 			while ((read(fp, fbuf, BUFSIZ)) > 0) {
254 				if (memstrstr(fbuf, "SUNW,socal",
255 					BUFSIZ, strlen("SUNW,socal"))
256 								!= NULL) {
257 					(void) fprintf(stdout, MSGSTR(2221,
258 					    "\n  Using file: %s\n"), file);
259 					retval += fcal_update(
260 						Options & PVERBOSE, file);
261 					strfound++;
262 					break;
263 				} else if ((memstrstr(fbuf, "SUNW,ifp",
264 						BUFSIZ, strlen("SUNW,ifp"))
265 								!= NULL) ||
266 				    (memstrstr(fbuf, "SUNW,qlc",
267 					    BUFSIZ, strlen("SUNW,qlc"))
268 								    != NULL)) {
269 					(void) fprintf(stdout, MSGSTR(2221,
270 					    "\n  Using file: %s\n"), file);
271 					retval += q_qlgc_update(
272 						Options & PVERBOSE, file);
273 					strfound++;
274 					break;
275 				}
276 			}
277 			if (!strfound) {
278 				/* check to see if this is an emulex fcode */
279 				memset(manf, 0, sizeof (manf));
280 				if ((emulex_fcode_reader(fp, "manufacturer",
281 						    manf,
282 						    sizeof (manf)) == 0) &&
283 				    (strncmp(manf, "Emulex", sizeof (manf))
284 									== 0)) {
285 					retval += emulex_update(file);
286 					strfound = 0;
287 				} else {
288 					(void) fprintf(stderr, MSGSTR(2222,
289 					    "\nError: %s is not a valid Fcode "
290 					    "file.\n"), file);
291 					retval++;
292 				}
293 			} else {
294 				strfound = 0;
295 			}
296 			close(fp);
297 		}
298 		closedir(dp);
299 	}
300 	return (retval);
301 }
302 
303 /*
304  * Definition of getaction() routine which does keyword parsing
305  *
306  * Operation: A character string containing the ascii cmd to be
307  * parsed is passed in along with an array of structures.
308  * The 1st struct element is a recognizable cmd string, the second
309  * is the minimum number of characters from the start of this string
310  * to succeed on a match. For example, { "offline", 3, ONLINE }
311  * will match "off", "offli", "offline", but not "of" nor "offlinebarf"
312  * The third element is the {usually but not necessarily unique}
313  * integer to return on a successful match. Note: compares are cAsE insensitive.
314  *
315  * To change, extend or use this utility, just add or remove appropriate
316  * lines in the structure initializer below and in the #define	s for the
317  * return values.
318  *
319  *                              N O T E
320  * Do not change the minimum number of characters to produce
321  * a match as someone may be building scripts that use this
322  * feature.
323  */
324 struct keyword {
325 	char *match;		/* Character String to match against */
326 	int  num_match;		/* Minimum chars to produce a match */
327 	int  ret_code;		/* Value to return on a match */
328 };
329 
330 static  struct keyword Keywords[] = {
331 	{"display",		2, DISPLAY},
332 	{"download",		3, DOWNLOAD},
333 	{"enclosure_names",	2, ENCLOSURE_NAMES},
334 	{"failover",		3, FAILOVER},
335 	{"fcal_s_download",	4, FCAL_UPDATE},
336 	{"fcode_download",	4, FCODE_UPDATE},
337 	{"inquiry",		2, INQUIRY},
338 	{"insert_device",	3, INSERT_DEVICE},
339 	{"led",			3, LED},
340 	{"led_on",		5, LED_ON},
341 	{"led_off",		5, LED_OFF},
342 	{"led_blink",		5, LED_BLINK},
343 	{"password",		2, PASSWORD},
344 	{"power_on",		8, POWER_ON},
345 	{"power_off",		9, POWER_OFF},
346 	{"probe",		2, PROBE},
347 	{"qlgc_s_download",	4, QLGC_UPDATE},
348 	{"remove_device",	3, REMOVE_DEVICE},
349 	{"reserve",		5, RESERVE},
350 	{"release",		3, RELEASE},
351 	{"set_boot_dev",	5, SET_BOOT_DEV},
352 	{"start",		3, START},
353 	{"stop",		3, STOP},
354 	{"rdls",		2, RDLS},
355 	{"bypass",		3, BYPASS},
356 	{"enable",		3, ENABLE},
357 	{"p_offline",		4, LUX_P_OFFLINE},
358 	{"p_online",		4, LUX_P_ONLINE},
359 	{"forcelip",		2, FORCELIP},
360 	{"dump",		2, DUMP},
361 	{"check_file",		2, CHECK_FILE},
362 	{"dump_map",		2, DUMP_MAP},
363 	{"sysdump",		5, SYSDUMP},
364 	{"port",		4, PORT},
365 	{"external_loopback",	12, EXT_LOOPBACK},
366 	{"internal_loopback",	12, INT_LOOPBACK},
367 	{"no_loopback",		11, NO_LOOPBACK},
368 	{"version",		2, VERSION},
369 	{"create_fabric_device",	2,	CREATE_FAB},
370 	/* hotplugging device operations */
371 	{"online",		2, DEV_ONLINE},
372 	{"offline",		2, DEV_OFFLINE},
373 	{"dev_getstate",	5, DEV_GETSTATE},
374 	{"dev_reset",		5, DEV_RESET},
375 	/* hotplugging bus operations */
376 	{"bus_quiesce",		5, BUS_QUIESCE},
377 	{"bus_unquiesce",	5, BUS_UNQUIESCE},
378 	{"bus_getstate",	5, BUS_GETSTATE},
379 	{"bus_reset",		9, BUS_RESET},
380 	{"bus_resetall",	12, BUS_RESETALL},
381 	/* hotplugging "helper" subcommands */
382 	{ NULL,			0, 0}
383 };
384 
385 #ifndef	EOK
386 static	const	int EOK	= 0;	/* errno.h type success return code */
387 #endif
388 
389 
390 /*
391  * function getaction() takes a character string, cmd, and
392  * tries to match it against a passed structure of known cmd
393  * character strings. If a match is found, corresponding code
394  * is returned in retval. Status returns as follows:
395  *   EOK	= Match found, look for cmd's code in retval
396  *   EFAULT = One of passed parameters was bad
397  *   EINVAL = cmd did not match any in list
398  */
399 static int
getaction(char * cmd,struct keyword * matches,int * retval)400 getaction(char *cmd, struct keyword *matches, int  *retval)
401 {
402 	int actlen;
403 
404 	/* Idiot checking of pointers */
405 	if (! cmd || ! matches || ! retval ||
406 	    ! (actlen = strlen(cmd)))	/* Is there an cmd ? */
407 	    return (EFAULT);
408 
409 	/* Keep looping until NULL match string (end of list) */
410 	while (matches->match) {
411 		/*
412 		 * Precedence: Make sure target is no longer than
413 		 * current match string
414 		 * and target is at least as long as
415 		 * minimum # match chars,
416 		 * then do case insensitive match
417 		 * based on actual target size
418 		 */
419 		if ((((int)strlen(matches->match)) >= actlen) &&
420 		    (actlen >= matches->num_match) &&
421 		    /* can't get strncasecmp to work on SCR4 */
422 		    /* (strncasecmp(matches->match, cmd, actlen) == 0) */
423 		    (strncmp(matches->match, cmd, actlen) == 0)) {
424 		    *retval = matches->ret_code;	/* Found our match */
425 		    return (EOK);
426 		} else {
427 		    matches++;		/* Next match string/struct */
428 		}
429 	}	/* End of matches loop */
430 	return (EINVAL);
431 
432 }	/* End of getaction() */
433 
434 /* main functions. */
435 int
main(int argc,char ** argv)436 main(int argc, char **argv)
437 {
438 register int	c;
439 /* getopt varbs */
440 extern char *optarg;
441 char		*optstring = NULL;
442 int		path_index, err = 0;
443 int		cmd = 0;		/* Cmd verb from cmd line */
444 int		exit_code = 0;		/* exit code for program */
445 int		temp_fd;		/* For -f option */
446 char		*file_name = NULL;
447 int		option_t_input = 0;
448 char		*path_phys = NULL;
449 int		USE_FCHBA = 0;
450 
451 	whoami = argv[0];
452 
453 
454 	/*
455 	 * Enable locale announcement
456 	 */
457 	i18n_catopen();
458 
459 	while ((c = getopt(argc, argv, "ve"))
460 	    != EOF) {
461 	    switch (c) {
462 		case 'v':
463 		    Options |= PVERBOSE;
464 		    break;
465 		case 'e':
466 		    Options |= EXPERT;
467 		    break;
468 		default:
469 		    /* Note: getopt prints an error if invalid option */
470 		    USEAGE()
471 		    exit(-1);
472 	    } /* End of switch(c) */
473 	}
474 	setbuf(stdout, NULL);	/* set stdout unbuffered. */
475 
476 	/*
477 	 * Build any i18n global variables
478 	 */
479 	dtype[0] = MSGSTR(2192, "Disk device");
480 	dtype[1] = MSGSTR(2193, "Tape device");
481 	dtype[2] = MSGSTR(2194, "Printer device");
482 	dtype[3] = MSGSTR(2195, "Processor device");
483 	dtype[4] = MSGSTR(2196, "WORM device");
484 	dtype[5] = MSGSTR(2197, "CD-ROM device");
485 	dtype[6] = MSGSTR(2198, "Scanner device");
486 	dtype[7] = MSGSTR(2199, "Optical memory device");
487 	dtype[8] = MSGSTR(2200, "Medium changer device");
488 	dtype[9] = MSGSTR(2201, "Communications device");
489 	dtype[10] = MSGSTR(107, "Graphic arts device");
490 	dtype[11] = MSGSTR(107, "Graphic arts device");
491 	dtype[12] = MSGSTR(2202, "Array controller device");
492 	dtype[13] = MSGSTR(2203, "SES device");
493 	dtype[14] = MSGSTR(71, "Reserved");
494 	dtype[15] = MSGSTR(71, "Reserved");
495 
496 
497 
498 	/*
499 	 * Get subcommand.
500 	 */
501 	if ((getaction(argv[optind], Keywords, &cmd)) == EOK) {
502 		optind++;
503 		if ((cmd != PROBE) && (cmd != FCAL_UPDATE) &&
504 		(cmd != QLGC_UPDATE) && (cmd != FCODE_UPDATE) &&
505 		(cmd != INSERT_DEVICE) && (cmd != SYSDUMP) && (cmd != AU) &&
506 		(cmd != PORT) && (cmd != CREATE_FAB) && (optind >= argc)) {
507 			(void) fprintf(stderr,
508 			MSGSTR(2204,
509 			"Error: enclosure or pathname not specified.\n"));
510 			USEAGE();
511 			exit(-1);
512 		}
513 	} else {
514 		(void) fprintf(stderr,
515 		MSGSTR(2205, "%s: subcommand not specified.\n"),
516 		whoami);
517 		USEAGE();
518 		exit(-1);
519 	}
520 
521 	/* Extract & Save subcommand options */
522 	if ((cmd == ENABLE) || (cmd == BYPASS)) {
523 		optstring = "Ffrab";
524 	} else if (cmd == FCODE_UPDATE) {
525 		optstring = "pd:";
526 	} else if (cmd == REMOVE_DEVICE) {
527 		optstring = "F";
528 	} else if (cmd == CREATE_FAB) {
529 		optstring = "f:";
530 	} else {
531 		optstring = "Fryszabepcdlvt:f:w:";
532 	}
533 	while ((c = getopt(argc, argv, optstring)) != EOF) {
534 	    switch (c) {
535 		case 'a':
536 			Options |= OPTION_A;
537 			break;
538 	    case 'b':
539 			Options |= OPTION_B;
540 			break;
541 		case 'c':
542 			Options |= OPTION_C;
543 			break;
544 		case 'd':
545 			Options |= OPTION_D;
546 			if (cmd == FCODE_UPDATE) {
547 			    file_name = optarg;
548 			}
549 			break;
550 		case 'e':
551 			Options |= OPTION_E;
552 			break;
553 		case 'f':
554 			Options |= OPTION_F;
555 			if (!((cmd == ENABLE) || (cmd == BYPASS))) {
556 				file_name = optarg;
557 			}
558 			break;
559 		case 'F':
560 			Options |= OPTION_CAPF;
561 			break;
562 		case 'l':
563 		    Options |= OPTION_L;
564 		    break;
565 		case 'p':
566 		    Options |= OPTION_P;
567 		    break;
568 		case 'r':
569 		    Options |= OPTION_R;
570 		    break;
571 		case 's':
572 		    Options |= SAVE;
573 		    break;
574 		case 't':
575 		    Options |= OPTION_T;
576 		    option_t_input = atoi(optarg);
577 		    break;
578 		case 'v':
579 		    Options |= OPTION_V;
580 		    break;
581 		case 'z':
582 		    Options |= OPTION_Z;
583 		    break;
584 		case 'y':
585 		    Options |= OPTION_Y;
586 		    break;
587 		default:
588 		    /* Note: getopt prints an error if invalid option */
589 		    USEAGE()
590 		    exit(-1);
591 	    } /* End of switch(c) */
592 	}
593 	if ((cmd != PROBE) && (cmd != FCAL_UPDATE) &&
594 	    (cmd != QLGC_UPDATE) && (cmd != FCODE_UPDATE) &&
595 	    (cmd != INSERT_DEVICE) && (cmd != SYSDUMP) &&
596 	    (cmd != AU) && (cmd != PORT) &&
597 	    (cmd != CREATE_FAB) && (optind >= argc)) {
598 	    (void) fprintf(stderr,
599 		MSGSTR(2206,
600 		"Error: enclosure or pathname not specified.\n"));
601 	    USEAGE();
602 	    exit(-1);
603 	}
604 	path_index = optind;
605 
606 	/*
607 	 * Check if the file supplied with the -f option is valid
608 	 * Some sub commands (bypass for example) use the -f option
609 	 * for other reasons. In such cases, "file_name" should be
610 	 * NULL.
611 	 */
612 	if ((file_name != NULL) && (Options & OPTION_F)) {
613 		if ((temp_fd = open(file_name, O_RDONLY)) == -1) {
614 			perror(file_name);
615 			exit(-1);
616 		} else {
617 			close(temp_fd);
618 		}
619 	}
620 
621 	/* Determine which mode to operate in (FC-HBA or original) */
622 	USE_FCHBA = use_fchba();
623 
624 	switch (cmd)	{
625 	    case	DISPLAY:
626 		if (Options &
627 		    ~(PVERBOSE | OPTION_A | OPTION_Z | OPTION_R |
628 		    OPTION_P | OPTION_V | OPTION_L | OPTION_E | OPTION_T)) {
629 		    USEAGE();
630 		    exit(-1);
631 		}
632 		/* Display object(s) */
633 		if (USE_FCHBA) {
634 		    exit_code = fchba_display_config(&argv[path_index],
635 			    option_t_input, argc - path_index);
636 		} else {
637 		    exit_code = adm_display_config(&argv[path_index]);
638 		}
639 		break;
640 
641 	    case	DOWNLOAD:
642 		    if (Options &
643 			~(PVERBOSE | OPTION_F | SAVE)) {
644 			USEAGE();
645 			exit(-1);
646 		    }
647 		    adm_download(&argv[path_index], file_name);
648 		    break;
649 
650 	    case	ENCLOSURE_NAMES:
651 		    if (Options & ~PVERBOSE) {
652 			USEAGE();
653 			exit(-1);
654 		    }
655 		    up_encl_name(&argv[path_index], argc);
656 		    break;
657 
658 	    case	FAILOVER:
659 		    if (Options & ~PVERBOSE) {
660 			USEAGE();
661 			exit(-1);
662 		    }
663 		    adm_failover(&argv[path_index]);
664 		    break;
665 
666 	    case	INQUIRY:
667 		if (Options & ~(PVERBOSE)) {
668 			USEAGE();
669 			exit(-1);
670 		}
671 		if (USE_FCHBA) {
672 		    exit_code = fchba_inquiry(&argv[path_index]);
673 		} else {
674 		    exit_code = adm_inquiry(&argv[path_index]);
675 		}
676 		break;
677 
678 	    case	PROBE:
679 		if (Options & ~(PVERBOSE | OPTION_P)) {
680 			USEAGE();
681 			exit(-1);
682 		}
683 		/*
684 		 * A special check just in case someone entered
685 		 * any characters after the -p or the probe.
686 		 *
687 		 * (I know, a nit.)
688 		 */
689 		if (((Options & PVERBOSE) && (Options & OPTION_P) &&
690 			(argc != 4)) ||
691 			(!(Options & PVERBOSE) && (Options & OPTION_P) &&
692 			(argc != 3)) ||
693 			((Options & PVERBOSE) && (!(Options & OPTION_P)) &&
694 			(argc != 3)) ||
695 			(!(Options & PVERBOSE) && (!(Options & OPTION_P)) &&
696 			(argc != 2))) {
697 			(void) fprintf(stderr,
698 			MSGSTR(114, "Error: Incorrect number of arguments.\n"));
699 			(void) fprintf(stderr,  MSGSTR(2208,
700 			"Usage: %s [-v] subcommand [option]\n"), whoami);
701 			exit(-1);
702 		}
703 		if (USE_FCHBA) {
704 		    exit_code = fchba_non_encl_probe();
705 		} else {
706 		    pho_probe();
707 		    non_encl_probe();
708 		}
709 		break;
710 
711 	    case	FCODE_UPDATE:	/* Update Fcode in all cards */
712 			if ((Options & ~(PVERBOSE)) &
713 			    ~(OPTION_P | OPTION_D) || argv[path_index]) {
714 				USEAGE();
715 				exit(-1);
716 			}
717 			if (!((Options & (OPTION_P | OPTION_D)) &&
718 			    !((Options & OPTION_P) && (Options & OPTION_D)))) {
719 				USEAGE();
720 				exit(-1);
721 			}
722 			if (adm_fcode(Options & PVERBOSE, file_name) != 0) {
723 				exit(-1);
724 			}
725 			break;
726 
727 	    case	QLGC_UPDATE:	/* Update Fcode in PCI HBA card(s) */
728 			if ((Options & ~(PVERBOSE)) & ~(OPTION_F) ||
729 			    argv[path_index]) {
730 				USEAGE();
731 				exit(-1);
732 			}
733 			if (q_qlgc_update(Options & PVERBOSE, file_name) != 0) {
734 				exit(-1);
735 			}
736 			break;
737 
738 	    case	FCAL_UPDATE:	/* Update Fcode in Sbus soc+ card */
739 			if ((Options & ~(PVERBOSE)) & ~(OPTION_F) ||
740 			    argv[path_index]) {
741 				USEAGE();
742 				exit(-1);
743 			}
744 			exit_code = fcal_update(Options & PVERBOSE, file_name);
745 			break;
746 
747 	    case	SET_BOOT_DEV:   /* Set boot-device variable in nvram */
748 			exit_code = setboot(Options & OPTION_Y,
749 				Options & PVERBOSE, argv[path_index]);
750 		break;
751 
752 	    case	LED:
753 		if (Options & ~(PVERBOSE)) {
754 			USEAGE();
755 			exit(-1);
756 		}
757 		adm_led(&argv[path_index], L_LED_STATUS);
758 		break;
759 	    case	LED_ON:
760 		if (Options & ~(PVERBOSE)) {
761 			USEAGE();
762 			exit(-1);
763 		}
764 		adm_led(&argv[path_index], L_LED_ON);
765 		break;
766 	    case	LED_OFF:
767 		if (Options & ~(PVERBOSE)) {
768 			USEAGE();
769 			exit(-1);
770 		}
771 		adm_led(&argv[path_index], L_LED_OFF);
772 		break;
773 	    case	LED_BLINK:
774 		if (Options & ~(PVERBOSE)) {
775 			USEAGE();
776 			exit(-1);
777 		}
778 		adm_led(&argv[path_index], L_LED_RQST_IDENTIFY);
779 		break;
780 	    case	PASSWORD:
781 		if (Options & ~(PVERBOSE))  {
782 			USEAGE();
783 			exit(-1);
784 		}
785 		up_password(&argv[path_index]);
786 		break;
787 
788 	    case	RESERVE:
789 
790 		if (Options & (~PVERBOSE)) {
791 			USEAGE();
792 			exit(-1);
793 		}
794 		VERBPRINT(MSGSTR(2209,
795 			"  Reserving: \n %s\n"), argv[path_index]);
796 		if (USE_FCHBA) {
797 		    struct stat sbuf;
798 		    /* Just stat the argument and make sure it exists */
799 		    if (stat(argv[path_index], &sbuf) < 0) {
800 			(void) fprintf(stderr, "%s: ", whoami);
801 			(void) fprintf(stderr,
802 				MSGSTR(112, "Error: Invalid pathname (%s)"),
803 				argv[path_index]);
804 			(void) fprintf(stderr, "\n");
805 			exit(-1);
806 		    }
807 		    path_phys = argv[path_index];
808 		    if (err = scsi_reserve(path_phys)) {
809 			(void) print_errString(err, argv[path_index]);
810 			exit(-1);
811 		    }
812 		} else {
813 		    exit_code = adm_reserve(argv[path_index]);
814 		}
815 		break;
816 
817 	    case	RELEASE:
818 		if (Options & (~PVERBOSE)) {
819 			USEAGE();
820 			exit(-1);
821 		}
822 		VERBPRINT(MSGSTR(2210, "  Canceling Reservation for:\n %s\n"),
823 		    argv[path_index]);
824 		if (USE_FCHBA) {
825 		    struct stat sbuf;
826 		    /* Just stat the argument and make sure it exists */
827 		    if (stat(argv[path_index], &sbuf) < 0) {
828 			(void) fprintf(stderr, "%s: ", whoami);
829 			(void) fprintf(stderr,
830 				MSGSTR(112, "Error: Invalid pathname (%s)"),
831 				argv[path_index]);
832 			(void) fprintf(stderr, "\n");
833 			exit(-1);
834 		    }
835 		    path_phys = argv[path_index];
836 		    if (err = scsi_release(path_phys)) {
837 			(void) print_errString(err, argv[path_index]);
838 			exit(-1);
839 		    }
840 		} else {
841 		    exit_code = adm_release(argv[path_index]);
842 		}
843 		break;
844 
845 	    case	START:
846 		if (Options & ~(PVERBOSE)) {
847 			USEAGE();
848 			exit(-1);
849 		}
850 		exit_code = adm_start(&argv[path_index]);
851 		break;
852 
853 	    case	STOP:
854 		if (Options & ~(PVERBOSE)) {
855 			USEAGE();
856 			exit(-1);
857 		}
858 		exit_code = adm_stop(&argv[path_index]);
859 		break;
860 
861 	    case	POWER_OFF:
862 		if (Options & ~(PVERBOSE | OPTION_CAPF)) {
863 			USEAGE();
864 			exit(-1);
865 		}
866 		exit_code = adm_power_off(&argv[path_index], 1);
867 		break;
868 
869 	    case	POWER_ON:
870 		if (Options & (~PVERBOSE)) {
871 			USEAGE();
872 			exit(-1);
873 		}
874 		exit_code = adm_power_off(&argv[path_index], 0);
875 		break;
876 
877 	/*
878 	 * EXPERT commands.
879 	 */
880 
881 	    case	FORCELIP:
882 		if (!(Options & EXPERT) || (Options & ~(PVERBOSE | EXPERT))) {
883 			E_USEAGE();
884 			exit(-1);
885 		}
886 		exit_code = adm_forcelip(&argv[path_index]);
887 		break;
888 
889 	    case	BYPASS:
890 		if (!(Options & EXPERT) || (Options & ~(PVERBOSE | EXPERT |
891 			OPTION_CAPF | OPTION_A | OPTION_B | OPTION_F |
892 			OPTION_R)) || !(Options & (OPTION_A | OPTION_B)) ||
893 			((Options & OPTION_A) && (Options & OPTION_B))) {
894 			E_USEAGE();
895 			exit(-1);
896 		}
897 		adm_bypass_enable(&argv[path_index], 1);
898 		break;
899 
900 	    case	ENABLE:
901 		if (!(Options & EXPERT) || (Options & ~(PVERBOSE | EXPERT |
902 			OPTION_CAPF | OPTION_A | OPTION_B | OPTION_F |
903 			OPTION_R)) || !(Options & (OPTION_A | OPTION_B)) ||
904 			((Options & OPTION_A) && (Options & OPTION_B))) {
905 			E_USEAGE();
906 			exit(-1);
907 		}
908 		adm_bypass_enable(&argv[path_index], 0);
909 		break;
910 	    case	LUX_P_OFFLINE:	/* Offline a port */
911 		if (!(Options & EXPERT) || (Options & ~(PVERBOSE | EXPERT))) {
912 			E_USEAGE();
913 			exit(-1);
914 		}
915 		exit_code = adm_port_offline_online(&argv[path_index],
916 		    LUX_P_OFFLINE);
917 		break;
918 
919 	    case	LUX_P_ONLINE:	/* Online a port */
920 		if (!(Options & EXPERT) || (Options & ~(PVERBOSE | EXPERT))) {
921 			E_USEAGE();
922 			exit(-1);
923 		}
924 		exit_code = adm_port_offline_online(&argv[path_index],
925 		    LUX_P_ONLINE);
926 		break;
927 
928 	    case	RDLS:
929 		if (!(Options & EXPERT) || (Options & ~(PVERBOSE | EXPERT))) {
930 			E_USEAGE();
931 			exit(-1);
932 		}
933 		if (USE_FCHBA) {
934 		    exit_code = fchba_display_link_status(&argv[path_index]);
935 		} else {
936 		    display_link_status(&argv[path_index]);
937 		}
938 		break;
939 
940 	    case	CREATE_FAB:
941 		if (!(Options & (EXPERT | OPTION_F)) ||
942 			(Options & ~(PVERBOSE | EXPERT | OPTION_F))) {
943 			E_USEAGE();
944 			exit(-1);
945 		}
946 		if (read_repos_file(file_name) != 0) {
947 			exit(-1);
948 		}
949 		break;
950 
951 	/*
952 	 * Undocumented commands.
953 	 */
954 
955 	    case	CHECK_FILE:	/* Undocumented Cmd */
956 		if (Options & ~(PVERBOSE)) {
957 			USEAGE();
958 			exit(-1);
959 		}
960 		exit_code = adm_check_file(&argv[path_index],
961 		    (Options & PVERBOSE));
962 		break;
963 
964 	    case	DUMP:		/* Undocumented Cmd */
965 		if (!(Options & EXPERT) || (Options & ~(PVERBOSE | EXPERT))) {
966 			USEAGE();
967 			exit(-1);
968 		}
969 		dump(&argv[path_index]);
970 		break;
971 
972 	    case	DUMP_MAP:	/* Undocumented Cmd */
973 		if (!(Options & EXPERT) || (Options & ~(PVERBOSE | EXPERT))) {
974 			USEAGE();
975 			exit(-1);
976 		}
977 		if (USE_FCHBA) {
978 		    exit_code = fchba_dump_map(&argv[path_index]);
979 		} else {
980 		    dump_map(&argv[path_index]);
981 		}
982 		break;
983 
984 	    case	SYSDUMP:
985 			if (Options & ~(PVERBOSE)) {
986 			USEAGE();
987 			exit(-1);
988 		}
989 		if (err = sysdump(Options & PVERBOSE)) {
990 		    (void) print_errString(err, NULL);
991 		    exit(-1);
992 		}
993 		break;
994 
995 	    case	PORT: /* Undocumented command */
996 		if (!(Options & EXPERT) || (Options & ~(PVERBOSE | EXPERT))) {
997 			USEAGE();
998 			exit(-1);
999 		}
1000 		if (USE_FCHBA) {
1001 		    exit_code = fchba_display_port(Options & PVERBOSE);
1002 		} else {
1003 		    exit_code = adm_display_port(Options & PVERBOSE);
1004 		}
1005 		break;
1006 
1007 	    case	EXT_LOOPBACK:
1008 		if (!(Options & EXPERT) || (Options & ~(PVERBOSE | EXPERT))) {
1009 			USEAGE();
1010 			exit(-1);
1011 		}
1012 		if (adm_port_loopback(argv[path_index], EXT_LOOPBACK) < 0) {
1013 			exit(-1);
1014 		}
1015 		break;
1016 
1017 	    case	INT_LOOPBACK:
1018 		if (!(Options & EXPERT) || (Options & ~(PVERBOSE | EXPERT))) {
1019 			USEAGE();
1020 			exit(-1);
1021 		}
1022 		if (adm_port_loopback(argv[path_index], INT_LOOPBACK) < 0) {
1023 			exit(-1);
1024 		}
1025 		break;
1026 
1027 	    case	NO_LOOPBACK:
1028 		if (!(Options & EXPERT) || (Options & ~(PVERBOSE | EXPERT))) {
1029 			USEAGE();
1030 			exit(-1);
1031 		}
1032 		if (adm_port_loopback(argv[path_index], NO_LOOPBACK) < 0) {
1033 			exit(-1);
1034 		}
1035 		break;
1036 
1037 	    case	VERSION:
1038 		break;
1039 
1040 
1041 	    case	INSERT_DEVICE:
1042 			if (argv[path_index] == NULL) {
1043 				if ((err = h_insertSena_fcdev()) != 0) {
1044 					(void) print_errString(err, NULL);
1045 					exit(-1);
1046 				}
1047 			} else if ((err = hotplug(INSERT_DEVICE,
1048 					&argv[path_index],
1049 					Options & PVERBOSE,
1050 					Options & OPTION_CAPF)) != 0) {
1051 				(void) print_errString(err, argv[path_index]);
1052 				exit(-1);
1053 			}
1054 			break;
1055 	    case	REMOVE_DEVICE:
1056 			if (err = hotplug(REMOVE_DEVICE, &argv[path_index],
1057 			    Options & PVERBOSE, Options & OPTION_CAPF)) {
1058 			    (void) print_errString(err, argv[path_index]);
1059 			    exit(-1);
1060 			}
1061 			break;
1062 
1063 	/* for hotplug device operations */
1064 	    case	DEV_ONLINE:
1065 	    case	DEV_OFFLINE:
1066 	    case	DEV_GETSTATE:
1067 	    case	DEV_RESET:
1068 	    case	BUS_QUIESCE:
1069 	    case	BUS_UNQUIESCE:
1070 	    case	BUS_GETSTATE:
1071 	    case	BUS_RESET:
1072 	    case	BUS_RESETALL:
1073 		if (!(Options & EXPERT) || (Options & ~(PVERBOSE | EXPERT))) {
1074 			E_USEAGE();
1075 			exit(-1);
1076 		}
1077 		if (USE_FCHBA) {
1078 		    if (fchba_hotplug_e(cmd, &argv[path_index],
1079 			    Options & PVERBOSE, Options & OPTION_CAPF) != 0) {
1080 			exit(-1);
1081 		    }
1082 		} else {
1083 		    if (hotplug_e(cmd, &argv[path_index],
1084 			    Options & PVERBOSE, Options & OPTION_CAPF) != 0) {
1085 			exit(-1);
1086 		    }
1087 		}
1088 		break;
1089 
1090 	    default:
1091 		(void) fprintf(stderr,
1092 		    MSGSTR(2213, "%s: subcommand decode failed.\n"),
1093 		    whoami);
1094 		USEAGE();
1095 		exit(-1);
1096 	}
1097 	return (exit_code);
1098 }
1099