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