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	*/
81char	*dtype[16]; /* setting a global for later use. */
82char			*whoami;
83int	Options;
84const	int OPTION_A	= 0x00000001;
85const	int OPTION_B	= 0x00000002;
86const	int OPTION_C	= 0x00000004;
87const	int OPTION_D	= 0x00000008;
88const	int OPTION_E	= 0x00000010;
89const	int OPTION_F	= 0x00000020;
90const	int OPTION_L	= 0x00000040;
91const	int OPTION_P	= 0x00000080;
92const	int OPTION_R	= 0x00000100;
93const	int OPTION_T	= 0x00000200;
94const	int OPTION_V	= 0x00000400;
95const	int OPTION_Z	= 0x00001000;
96const	int OPTION_Y	= 0x00002000;
97const	int OPTION_CAPF	= 0x00004000;
98const	int PVERBOSE	= 0x00008000;
99const	int SAVE	= 0x00010000;
100const	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 */
112void
113print_chars(uchar_t *buffer, int size, int fill_flag)
114{
115
116int 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 */
139static char *
140memstrstr(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 */
174static	int
175adm_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 */
330struct 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
336static  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
392static	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 */
405static int
406getaction(char *cmd, struct keyword *matches, int  *retval)
407{
408int 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. */
441int
442main(int argc, char **argv)
443{
444register int 	c;
445/* getopt varbs */
446extern char *optarg;
447char		*optstring = NULL;
448int		path_index, err = 0;
449int		cmd = 0;		/* Cmd verb from cmd line */
450int		exit_code = 0;		/* exit code for program */
451int		temp_fd;		/* For -f option */
452char		*file_name = NULL;
453int		option_t_input;
454char		*path_phys = NULL;
455int		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