diff options
| author | John Forte <John.Forte@Sun.COM> | 2008-10-14 15:09:13 -0700 |
|---|---|---|
| committer | John Forte <John.Forte@Sun.COM> | 2008-10-14 15:09:13 -0700 |
| commit | fcf3ce441efd61da9bb2884968af01cb7c1452cc (patch) | |
| tree | 0e80d59ad41702571586195bf099ccc14222ce02 /usr/src/cmd/luxadm | |
| parent | 247b82a1f1cb5ebd2d163bd9afdb1a3065611962 (diff) | |
| download | illumos-joyent-fcf3ce441efd61da9bb2884968af01cb7c1452cc.tar.gz | |
6745433 Merge NWS consolidation into OS/Net consolidation
Diffstat (limited to 'usr/src/cmd/luxadm')
| -rw-r--r-- | usr/src/cmd/luxadm/Makefile | 107 | ||||
| -rw-r--r-- | usr/src/cmd/luxadm/adm.c | 1104 | ||||
| -rw-r--r-- | usr/src/cmd/luxadm/common.h | 495 | ||||
| -rw-r--r-- | usr/src/cmd/luxadm/diag.c | 262 | ||||
| -rw-r--r-- | usr/src/cmd/luxadm/errorcodes.h | 536 | ||||
| -rw-r--r-- | usr/src/cmd/luxadm/errormsgs.c | 1108 | ||||
| -rw-r--r-- | usr/src/cmd/luxadm/fabric_conf.c | 335 | ||||
| -rw-r--r-- | usr/src/cmd/luxadm/fcalupdate.c | 948 | ||||
| -rw-r--r-- | usr/src/cmd/luxadm/fchba.c | 2151 | ||||
| -rw-r--r-- | usr/src/cmd/luxadm/g_adm.c | 6673 | ||||
| -rw-r--r-- | usr/src/cmd/luxadm/hot.h | 78 | ||||
| -rw-r--r-- | usr/src/cmd/luxadm/hotplug.c | 3037 | ||||
| -rw-r--r-- | usr/src/cmd/luxadm/ibfirmware | bin | 0 -> 389496 bytes | |||
| -rw-r--r-- | usr/src/cmd/luxadm/lux_util.c | 1594 | ||||
| -rw-r--r-- | usr/src/cmd/luxadm/luxadm.h | 110 | ||||
| -rw-r--r-- | usr/src/cmd/luxadm/qlgcupdate.c | 1591 | ||||
| -rw-r--r-- | usr/src/cmd/luxadm/setboot.c | 257 | ||||
| -rw-r--r-- | usr/src/cmd/luxadm/x86_adm.c | 533 |
18 files changed, 20919 insertions, 0 deletions
diff --git a/usr/src/cmd/luxadm/Makefile b/usr/src/cmd/luxadm/Makefile new file mode 100644 index 0000000000..dc58795386 --- /dev/null +++ b/usr/src/cmd/luxadm/Makefile @@ -0,0 +1,107 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# + +PROG = luxadm + +COBJS = adm.o fchba.o lux_util.o fabric_conf.o +OBJS_sparc= g_adm.o diag.o fcalupdate.o hotplug.o qlgcupdate.o setboot.o +OBJS_i386 = x86_adm.o errormsgs.o +OBJS = $(OBJS_$(MACH)) $(COBJS) + +SRCS = $(OBJS:%.o=%.c) + +POFILES = $(OBJS:%.o=%.po) +POFILE = luxadm.po + +include ../Makefile.cmd + +sparc_LDLIBS = -la5k -lg_fc -ldevid -lkstat -ldevinfo +i386_LDLIBS = +LDLIBS += $($(MACH)_LDLIBS) +LDLIBS += -lnvpair +LDLIBS += -ldevice +LDLIBS += -lHBAAPI + +INCS += -I$(SRC)/lib/sun_fc/common + +CPPFLAGS += -D_LARGEFILE64_SOURCE=1 -D_REENTRANT $(INCS) + +LINTFLAGS += -erroff=E_FUNC_RET_MAYBE_IGNORED2 +LINTFLAGS += -erroff=E_SEC_PRINTF_VAR_FMT +LINTFLAGS += -erroff=E_SEC_SPRINTF_UNBOUNDED_COPY +LINTFLAGS += -erroff=E_NAME_USED_NOT_DEF2 +LINTFLAGS += -erroff=E_BAD_FORMAT_ARG_TYPE2 +LINTFLAGS += -erroff=E_SEC_FORBIDDEN_WARN_GETS +LINTFLAGS += -erroff=E_SEC_SCANF_UNBOUNDED_COPY + +ROOTUSRSBINLINKS = $(PROG:%=$(ROOTUSRSBIN)/%) + +.KEEP_STATE: + +all: $(PROG) + +$(PROG): $(OBJS) + $(LINK.c) -o $@ $(OBJS) $(LDLIBS) + $(POST_PROCESS) + +GENCAT_PROG=a5k_g_fc_i18n_cat +GENMSG_LUX= $(PROG).msg +LUXFIRMWARED= $(ROOTLIB)/locale/C/LC_MESSAGES +LUXIBFIRMWARE_sparc= $(LUXFIRMWARED)/ibfirmware +LUXIBFIRMWARE= $(LUXIBFIRMWARE_$(MACH)) +LUXGENCAT_PROG_sparc= $(LUXFIRMWARED)/$(GENCAT_PROG) +LUXGENCAT_PROG= $(LUXGENCAT_PROG_$(MACH)) +$(LUXIBFIRMWARE):= FILEMODE= 0644 +$(LUXGENCAT_PROG):= FILEMODE= 0644 + +$(LUXFIRMWARED): + $(INS.dir) + +$(LUXIBFIRMWARE): $(LUXFIRMWARED) ibfirmware + $(INS.file) ibfirmware + +$(GENMSG_LUX): $(SRCS) + /usr/bin/genmsg -d -p '$(COMPILE.cpp)' -o $@ $(SRCS) + +$(GENCAT_PROG): $(GENMSG_LUX) + /usr/bin/gencat $@ $(GENMSG_LUX) + +$(LUXGENCAT_PROG): $(LUXFIRMWARED) $(GENCAT_PROG) + $(INS.file) $(GENCAT_PROG) + +install: all $(ROOTUSRSBINPROG) $(LUXIBFIRMWARE) $(LUXGENCAT_PROG) + +$(POFILE): $(POFILES) + $(RM) $@ + $(CAT) $(POFILES) > $@ + +clean: + $(RM) $(OBJS) $(PROG) $(GENCAT_PROG) $(GENMSG_LUX) + +lint: lint_SRCS + +include ../Makefile.targ diff --git a/usr/src/cmd/luxadm/adm.c b/usr/src/cmd/luxadm/adm.c new file mode 100644 index 0000000000..cb2ad51b43 --- /dev/null +++ b/usr/src/cmd/luxadm/adm.c @@ -0,0 +1,1104 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + + + +/*LINTLIBRARY*/ + + +/* + * Administration program for SENA + * subsystems and individual FC_AL devices. + */ + +/* + * I18N message number ranges + * This file: 2000 - 2999 + * Shared common messages: 1 - 1999 + */ + +/* #define _POSIX_SOURCE 1 */ + +/* + * These defines are used to map instance number from sf minor node. + * They are copied from SF_INST_SHIFT4MINOR and SF_MINOR2INST in sfvar.h. + * sfvar.h is not clean for userland use. + * When it is cleaned up, these defines will be removed and sfvar.h + * will be included in luxadm.h header file. + */ +#define LUX_SF_INST_SHIFT4MINOR 6 +#define LUX_SF_MINOR2INST(x) (x >> LUX_SF_INST_SHIFT4MINOR) + +/* Includes */ +#include <stdlib.h> +#include <stdio.h> +#include <sys/file.h> +#include <sys/errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/param.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <ctype.h> +#include <strings.h> +#include <sys/stat.h> +#include <dirent.h> +#include <limits.h> +#include <stdarg.h> +#include <termio.h> /* For password */ +#include <sys/scsi/scsi.h> + +#include "common.h" +#include "luxadm.h" + + +/* Global variables */ +char *dtype[16]; /* setting a global for later use. */ +char *whoami; +int Options; +const int OPTION_A = 0x00000001; +const int OPTION_B = 0x00000002; +const int OPTION_C = 0x00000004; +const int OPTION_D = 0x00000008; +const int OPTION_E = 0x00000010; +const int OPTION_F = 0x00000020; +const int OPTION_L = 0x00000040; +const int OPTION_P = 0x00000080; +const int OPTION_R = 0x00000100; +const int OPTION_T = 0x00000200; +const int OPTION_V = 0x00000400; +const int OPTION_Z = 0x00001000; +const int OPTION_Y = 0x00002000; +const int OPTION_CAPF = 0x00004000; +const int PVERBOSE = 0x00008000; +const int SAVE = 0x00010000; +const int EXPERT = 0x00020000; + +/* + * Given a pointer to a character array, print the character array. + * the character array will not necesarily be NULL terminated. + * + * Inputs: + * size - the max number of characters to print + * fill_flag - flag when set fills all NULL characters with spaces + * Returns: + * N/A + */ +void +print_chars(uchar_t *buffer, int size, int fill_flag) +{ + +int i; + + for (i = 0; i < size; i++) { + if (buffer[i]) + (void) fprintf(stdout, "%c", buffer[i]); + else if (fill_flag) + (void) fprintf(stdout, " "); + else + return; + } +} + +/* + * Name : memstrstr + * Input : pointer to buf1, pointer to buf2, size of buf1, size of buf2 + * Returns : + * Pointer to start of contents-of-buf2 in buf1 if it is found + * NULL if buf1 does not contain contents of buf2 + * Synopsis: + * This function works similar to strstr(). The difference is that null + * characters in the buffer are treated like any other character. So, buf1 + * and buf2 can have embedded null characters in them. + */ +static char * +memstrstr(char *s1, char *s2, int size1, int size2) +{ + int count1, count2; + char *s1_ptr, *s2_ptr; + + count1 = size1; count2 = size2; + s1_ptr = s1; s2_ptr = s2; + + if (size2 == 0) + return (s1); + + while (count1--) { + if (*s1_ptr++ == *s2_ptr++) { + if (--count2 == 0) + return (s1_ptr - size2); + continue; + } + count2 = size2; + s2_ptr = s2; + } + + return (NULL); +} + + +/* + * Download host bus adapter FCode to all supported cards. + * + * Specify a directory that holds the FCode files, or + * it will use the default dir. Each file is dealt to + * the appropriate function. + * + * -p prints current versions only, -d specifies a directory to load + */ +static int +adm_fcode(int verbose, char *dir) +{ + struct stat statbuf; + struct dirent *dirp; + DIR *dp; + int fp; + char fbuf[BUFSIZ]; + char file[MAXPATHLEN]; + int retval = 0, strfound = 0; + char manf[BUFSIZ]; + + /* Find all adapters and print the current FCode version */ + if (Options & OPTION_P) { + +/* SOCAL (SBus) adapters are not supported on x86 */ +#ifndef __x86 + if (verbose) { + (void) fprintf(stdout, + MSGSTR(2215, "\n Searching for FC100/S cards:\n")); + } + retval += fcal_update(Options & PVERBOSE, NULL); +#endif + + if (verbose) { + (void) fprintf(stdout, + MSGSTR(2216, "\n Searching for FC100/P, FC100/2P cards:\n")); + } + retval += q_qlgc_update(Options & PVERBOSE, NULL); + if (verbose) { + (void) fprintf(stdout, + MSGSTR(2503, "\n Searching for Emulex cards:\n")); + } + retval += emulex_update(NULL); + + /* Send files to the correct function for loading to the HBA */ + } else { + + if (!dir) { + (void) fprintf(stdout, MSGSTR(2251, + " Location of Fcode not specified.\n")); + return (1); + + } else if (verbose) { + (void) fprintf(stdout, MSGSTR(2217, + " Using directory %s"), dir); + } + if (lstat(dir, &statbuf) < 0) { + (void) fprintf(stderr, MSGSTR(134, + "%s: lstat() failed - %s\n"), + dir, strerror(errno)); + return (1); + } + if (S_ISDIR(statbuf.st_mode) == 0) { + (void) fprintf(stderr, + MSGSTR(2218, "Error: %s is not a directory.\n"), dir); + return (1); + } + if ((dp = opendir(dir)) == NULL) { + (void) fprintf(stderr, MSGSTR(2219, + " Error Cannot open directory %s\n"), dir); + return (1); + } + + while ((dirp = readdir(dp)) != NULL) { + if (strcmp(dirp->d_name, ".") == 0 || + strcmp(dirp->d_name, "..") == 0) { + continue; + } + sprintf(file, "%s/%s", dir, dirp->d_name); + + if ((fp = open(file, O_RDONLY)) < 0) { + (void) fprintf(stderr, + MSGSTR(2220, + "Error: open() failed to open file " + "%s\n"), file); + /* + * We should just issue an error message and + * make an attempt on the next file, + * and the open error is still an error + * so the retval should be incremented + */ + retval++; + continue; + } + while ((read(fp, fbuf, BUFSIZ)) > 0) { + if (memstrstr(fbuf, "SUNW,socal", + BUFSIZ, strlen("SUNW,socal")) + != NULL) { + (void) fprintf(stdout, MSGSTR(2221, + "\n Using file: %s\n"), file); + retval += fcal_update( + Options & PVERBOSE, file); + strfound++; + break; + } else if ((memstrstr(fbuf, "SUNW,ifp", + BUFSIZ, strlen("SUNW,ifp")) + != NULL) || + (memstrstr(fbuf, "SUNW,qlc", + BUFSIZ, strlen("SUNW,qlc")) + != NULL)) { + (void) fprintf(stdout, MSGSTR(2221, + "\n Using file: %s\n"), file); + retval += q_qlgc_update( + Options & PVERBOSE, file); + strfound++; + break; + } + } + if (!strfound) { + /* check to see if this is an emulex fcode */ + memset(manf, 0, sizeof (manf)); + if ((emulex_fcode_reader(fp, "manufacturer", + manf, + sizeof (manf)) == 0) && + (strncmp(manf, "Emulex", sizeof (manf)) + == 0)) { + retval += emulex_update(file); + strfound = 0; + } else { + (void) fprintf(stderr, MSGSTR(2222, + "\nError: %s is not a valid Fcode " + "file.\n"), file); + retval++; + } + } else { + strfound = 0; + } + close(fp); + } + closedir(dp); + } + return (retval); +} + +/* + * Definition of getaction() routine which does keyword parsing + * + * Operation: A character string containing the ascii cmd to be + * parsed is passed in along with an array of structures. + * The 1st struct element is a recognizable cmd string, the second + * is the minimum number of characters from the start of this string + * to succeed on a match. For example, { "offline", 3, ONLINE } + * will match "off", "offli", "offline", but not "of" nor "offlinebarf" + * The third element is the {usually but not necessarily unique} + * integer to return on a successful match. Note: compares are cAsE insensitive. + * + * To change, extend or use this utility, just add or remove appropriate + * lines in the structure initializer below and in the #define s for the + * return values. + * + * N O T E + * Do not change the minimum number of characters to produce + * a match as someone may be building scripts that use this + * feature. + */ +struct keyword { + char *match; /* Character String to match against */ + int num_match; /* Minimum chars to produce a match */ + int ret_code; /* Value to return on a match */ +}; + +static struct keyword Keywords[] = { + {"display", 2, DISPLAY}, + {"download", 3, DOWNLOAD}, + {"enclosure_names", 2, ENCLOSURE_NAMES}, + {"failover", 3, FAILOVER}, + {"fcal_s_download", 4, FCAL_UPDATE}, + {"fcode_download", 4, FCODE_UPDATE}, + {"inquiry", 2, INQUIRY}, + {"insert_device", 3, INSERT_DEVICE}, + {"led", 3, LED}, + {"led_on", 5, LED_ON}, + {"led_off", 5, LED_OFF}, + {"led_blink", 5, LED_BLINK}, + {"password", 2, PASSWORD}, + {"power_on", 8, POWER_ON}, + {"power_off", 9, POWER_OFF}, + {"probe", 2, PROBE}, + {"qlgc_s_download", 4, QLGC_UPDATE}, + {"remove_device", 3, REMOVE_DEVICE}, + {"reserve", 5, RESERVE}, + {"release", 3, RELEASE}, + {"set_boot_dev", 5, SET_BOOT_DEV}, + {"start", 3, START}, + {"stop", 3, STOP}, + {"rdls", 2, RDLS}, + {"bypass", 3, BYPASS}, + {"enable", 3, ENABLE}, + {"p_offline", 4, LUX_P_OFFLINE}, + {"p_online", 4, LUX_P_ONLINE}, + {"forcelip", 2, FORCELIP}, + {"dump", 2, DUMP}, + {"check_file", 2, CHECK_FILE}, + {"dump_map", 2, DUMP_MAP}, + {"sysdump", 5, SYSDUMP}, + {"port", 4, PORT}, + {"external_loopback", 12, EXT_LOOPBACK}, + {"internal_loopback", 12, INT_LOOPBACK}, + {"no_loopback", 11, NO_LOOPBACK}, + {"version", 2, VERSION}, + {"create_fabric_device", 2, CREATE_FAB}, + /* hotplugging device operations */ + {"online", 2, DEV_ONLINE}, + {"offline", 2, DEV_OFFLINE}, + {"dev_getstate", 5, DEV_GETSTATE}, + {"dev_reset", 5, DEV_RESET}, + /* hotplugging bus operations */ + {"bus_quiesce", 5, BUS_QUIESCE}, + {"bus_unquiesce", 5, BUS_UNQUIESCE}, + {"bus_getstate", 5, BUS_GETSTATE}, + {"bus_reset", 9, BUS_RESET}, + {"bus_resetall", 12, BUS_RESETALL}, + /* hotplugging "helper" subcommands */ + { NULL, 0, 0} +}; + +#ifndef EOK +static const int EOK = 0; /* errno.h type success return code */ +#endif + + +/* + * function getaction() takes a character string, cmd, and + * tries to match it against a passed structure of known cmd + * character strings. If a match is found, corresponding code + * is returned in retval. Status returns as follows: + * EOK = Match found, look for cmd's code in retval + * EFAULT = One of passed parameters was bad + * EINVAL = cmd did not match any in list + */ +static int +getaction(char *cmd, struct keyword *matches, int *retval) +{ +int actlen; + + /* Idiot checking of pointers */ + if (! cmd || ! matches || ! retval || + ! (actlen = strlen(cmd))) /* Is there an cmd ? */ + return (EFAULT); + + /* Keep looping until NULL match string (end of list) */ + while (matches->match) { + /* + * Precedence: Make sure target is no longer than + * current match string + * and target is at least as long as + * minimum # match chars, + * then do case insensitive match + * based on actual target size + */ + if ((((int)strlen(matches->match)) >= actlen) && + (actlen >= matches->num_match) && + /* can't get strncasecmp to work on SCR4 */ + /* (strncasecmp(matches->match, cmd, actlen) == 0) */ + (strncmp(matches->match, cmd, actlen) == 0)) { + *retval = matches->ret_code; /* Found our match */ + return (EOK); + } else { + matches++; /* Next match string/struct */ + } + } /* End of matches loop */ + return (EINVAL); + +} /* End of getaction() */ + +/* main functions. */ +int +main(int argc, char **argv) +{ +register int c; +/* getopt varbs */ +extern char *optarg; +char *optstring = NULL; +int path_index, err = 0; +int cmd = 0; /* Cmd verb from cmd line */ +int exit_code = 0; /* exit code for program */ +int temp_fd; /* For -f option */ +char *file_name = NULL; +int option_t_input; +char *path_phys = NULL; +int USE_FCHBA = 0; + + whoami = argv[0]; + + + /* + * Enable locale announcement + */ + i18n_catopen(); + + while ((c = getopt(argc, argv, "ve")) + != EOF) { + switch (c) { + case 'v': + Options |= PVERBOSE; + break; + case 'e': + Options |= EXPERT; + break; + default: + /* Note: getopt prints an error if invalid option */ + USEAGE() + exit(-1); + } /* End of switch(c) */ + } + setbuf(stdout, NULL); /* set stdout unbuffered. */ + + /* + * Build any i18n global variables + */ + dtype[0] = MSGSTR(2192, "Disk device"); + dtype[1] = MSGSTR(2193, "Tape device"); + dtype[2] = MSGSTR(2194, "Printer device"); + dtype[3] = MSGSTR(2195, "Processor device"); + dtype[4] = MSGSTR(2196, "WORM device"); + dtype[5] = MSGSTR(2197, "CD-ROM device"); + dtype[6] = MSGSTR(2198, "Scanner device"); + dtype[7] = MSGSTR(2199, "Optical memory device"); + dtype[8] = MSGSTR(2200, "Medium changer device"); + dtype[9] = MSGSTR(2201, "Communications device"); + dtype[10] = MSGSTR(107, "Graphic arts device"); + dtype[11] = MSGSTR(107, "Graphic arts device"); + dtype[12] = MSGSTR(2202, "Array controller device"); + dtype[13] = MSGSTR(2203, "SES device"); + dtype[14] = MSGSTR(71, "Reserved"); + dtype[15] = MSGSTR(71, "Reserved"); + + + + /* + * Get subcommand. + */ + if ((getaction(argv[optind], Keywords, &cmd)) == EOK) { + optind++; + if ((cmd != PROBE) && (cmd != FCAL_UPDATE) && + (cmd != QLGC_UPDATE) && (cmd != FCODE_UPDATE) && + (cmd != INSERT_DEVICE) && (cmd != SYSDUMP) && (cmd != AU) && + (cmd != PORT) && (cmd != CREATE_FAB) && (optind >= argc)) { + (void) fprintf(stderr, + MSGSTR(2204, + "Error: enclosure or pathname not specified.\n")); + USEAGE(); + exit(-1); + } + } else { + (void) fprintf(stderr, + MSGSTR(2205, "%s: subcommand not specified.\n"), + whoami); + USEAGE(); + exit(-1); + } + + /* Extract & Save subcommand options */ + if ((cmd == ENABLE) || (cmd == BYPASS)) { + optstring = "Ffrab"; + } else if (cmd == FCODE_UPDATE) { + optstring = "pd:"; + } else if (cmd == REMOVE_DEVICE) { + optstring = "F"; + } else if (cmd == CREATE_FAB) { + optstring = "f:"; + } else { + optstring = "Fryszabepcdlvt:f:w:"; + } + while ((c = getopt(argc, argv, optstring)) != EOF) { + switch (c) { + case 'a': + Options |= OPTION_A; + break; + case 'b': + Options |= OPTION_B; + break; + case 'c': + Options |= OPTION_C; + break; + case 'd': + Options |= OPTION_D; + if (cmd == FCODE_UPDATE) { + file_name = optarg; + } + break; + case 'e': + Options |= OPTION_E; + break; + case 'f': + Options |= OPTION_F; + if (!((cmd == ENABLE) || (cmd == BYPASS))) { + file_name = optarg; + } + break; + case 'F': + Options |= OPTION_CAPF; + break; + case 'l': + Options |= OPTION_L; + break; + case 'p': + Options |= OPTION_P; + break; + case 'r': + Options |= OPTION_R; + break; + case 's': + Options |= SAVE; + break; + case 't': + Options |= OPTION_T; + option_t_input = atoi(optarg); + break; + case 'v': + Options |= OPTION_V; + break; + case 'z': + Options |= OPTION_Z; + break; + case 'y': + Options |= OPTION_Y; + break; + default: + /* Note: getopt prints an error if invalid option */ + USEAGE() + exit(-1); + } /* End of switch(c) */ + } + if ((cmd != PROBE) && (cmd != FCAL_UPDATE) && + (cmd != QLGC_UPDATE) && (cmd != FCODE_UPDATE) && + (cmd != INSERT_DEVICE) && (cmd != SYSDUMP) && + (cmd != AU) && (cmd != PORT) && + (cmd != CREATE_FAB) && (optind >= argc)) { + (void) fprintf(stderr, + MSGSTR(2206, + "Error: enclosure or pathname not specified.\n")); + USEAGE(); + exit(-1); + } + path_index = optind; + + /* + * Check if the file supplied with the -f option is valid + * Some sub commands (bypass for example) use the -f option + * for other reasons. In such cases, "file_name" should be + * NULL. + */ + if ((file_name != NULL) && (Options & OPTION_F)) { + if ((temp_fd = open(file_name, O_RDONLY)) == -1) { + perror(file_name); + exit(-1); + } else { + close(temp_fd); + } + } + + /* Determine which mode to operate in (FC-HBA or original) */ + USE_FCHBA = use_fchba(); + + switch (cmd) { + case DISPLAY: + if (Options & + ~(PVERBOSE | OPTION_A | OPTION_Z | OPTION_R | + OPTION_P | OPTION_V | OPTION_L | OPTION_E | OPTION_T)) { + USEAGE(); + exit(-1); + } + /* Display object(s) */ + if (USE_FCHBA) { + exit_code = fchba_display_config(&argv[path_index], + option_t_input, argc - path_index); + } else { + exit_code = adm_display_config(&argv[path_index]); + } + break; + + case DOWNLOAD: + if (Options & + ~(PVERBOSE | OPTION_F | SAVE)) { + USEAGE(); + exit(-1); + } + adm_download(&argv[path_index], file_name); + break; + + case ENCLOSURE_NAMES: + if (Options & ~PVERBOSE) { + USEAGE(); + exit(-1); + } + up_encl_name(&argv[path_index], argc); + break; + + case FAILOVER: + if (Options & ~PVERBOSE) { + USEAGE(); + exit(-1); + } + adm_failover(&argv[path_index]); + break; + + case INQUIRY: + if (Options & ~(PVERBOSE)) { + USEAGE(); + exit(-1); + } + if (USE_FCHBA) { + exit_code = fchba_inquiry(&argv[path_index]); + } else { + exit_code = adm_inquiry(&argv[path_index]); + } + break; + + case PROBE: + if (Options & ~(PVERBOSE | OPTION_P)) { + USEAGE(); + exit(-1); + } + /* + * A special check just in case someone entered + * any characters after the -p or the probe. + * + * (I know, a nit.) + */ + if (((Options & PVERBOSE) && (Options & OPTION_P) && + (argc != 4)) || + (!(Options & PVERBOSE) && (Options & OPTION_P) && + (argc != 3)) || + ((Options & PVERBOSE) && (!(Options & OPTION_P)) && + (argc != 3)) || + (!(Options & PVERBOSE) && (!(Options & OPTION_P)) && + (argc != 2))) { + (void) fprintf(stderr, + MSGSTR(114, "Error: Incorrect number of arguments.\n")); + (void) fprintf(stderr, MSGSTR(2208, + "Usage: %s [-v] subcommand [option]\n"), whoami); + exit(-1); + } + if (USE_FCHBA) { + exit_code = fchba_non_encl_probe(); + } else { + pho_probe(); + non_encl_probe(); + } + break; + + case FCODE_UPDATE: /* Update Fcode in all cards */ + if ((Options & ~(PVERBOSE)) & + ~(OPTION_P | OPTION_D) || argv[path_index]) { + USEAGE(); + exit(-1); + } + if (!((Options & (OPTION_P | OPTION_D)) && + !((Options & OPTION_P) && (Options & OPTION_D)))) { + USEAGE(); + exit(-1); + } + if (adm_fcode(Options & PVERBOSE, file_name) != 0) { + exit(-1); + } + break; + + case QLGC_UPDATE: /* Update Fcode in PCI HBA card(s) */ + if ((Options & ~(PVERBOSE)) & ~(OPTION_F) || + argv[path_index]) { + USEAGE(); + exit(-1); + } + if (q_qlgc_update(Options & PVERBOSE, file_name) != 0) { + exit(-1); + } + break; + + case FCAL_UPDATE: /* Update Fcode in Sbus soc+ card */ + if ((Options & ~(PVERBOSE)) & ~(OPTION_F) || + argv[path_index]) { + USEAGE(); + exit(-1); + } + exit_code = fcal_update(Options & PVERBOSE, file_name); + break; + + case SET_BOOT_DEV: /* Set boot-device variable in nvram */ + exit_code = setboot(Options & OPTION_Y, + Options & PVERBOSE, argv[path_index]); + break; + + case LED: + if (Options & ~(PVERBOSE)) { + USEAGE(); + exit(-1); + } + adm_led(&argv[path_index], L_LED_STATUS); + break; + case LED_ON: + if (Options & ~(PVERBOSE)) { + USEAGE(); + exit(-1); + } + adm_led(&argv[path_index], L_LED_ON); + break; + case LED_OFF: + if (Options & ~(PVERBOSE)) { + USEAGE(); + exit(-1); + } + adm_led(&argv[path_index], L_LED_OFF); + break; + case LED_BLINK: + if (Options & ~(PVERBOSE)) { + USEAGE(); + exit(-1); + } + adm_led(&argv[path_index], L_LED_RQST_IDENTIFY); + break; + case PASSWORD: + if (Options & ~(PVERBOSE)) { + USEAGE(); + exit(-1); + } + up_password(&argv[path_index]); + break; + + case RESERVE: + + if (Options & (~PVERBOSE)) { + USEAGE(); + exit(-1); + } + VERBPRINT(MSGSTR(2209, + " Reserving: \n %s\n"), argv[path_index]); + if (USE_FCHBA) { + struct stat sbuf; + /* Just stat the argument and make sure it exists */ + if (stat(argv[path_index], &sbuf) < 0) { + (void) fprintf(stderr, "%s: ", whoami); + (void) fprintf(stderr, + MSGSTR(112, "Error: Invalid pathname (%s)"), + argv[path_index]); + (void) fprintf(stderr, "\n"); + exit(-1); + } + path_phys = argv[path_index]; + if (err = scsi_reserve(path_phys)) { + (void) print_errString(err, argv[path_index]); + exit(-1); + } + } else { + exit_code = adm_reserve(argv[path_index]); + } + break; + + case RELEASE: + if (Options & (~PVERBOSE)) { + USEAGE(); + exit(-1); + } + VERBPRINT(MSGSTR(2210, " Canceling Reservation for:\n %s\n"), + argv[path_index]); + if (USE_FCHBA) { + struct stat sbuf; + /* Just stat the argument and make sure it exists */ + if (stat(argv[path_index], &sbuf) < 0) { + (void) fprintf(stderr, "%s: ", whoami); + (void) fprintf(stderr, + MSGSTR(112, "Error: Invalid pathname (%s)"), + argv[path_index]); + (void) fprintf(stderr, "\n"); + exit(-1); + } + path_phys = argv[path_index]; + if (err = scsi_release(path_phys)) { + (void) print_errString(err, argv[path_index]); + exit(-1); + } + } else { + exit_code = adm_release(argv[path_index]); + } + break; + + case START: + if (Options & ~(PVERBOSE)) { + USEAGE(); + exit(-1); + } + exit_code = adm_start(&argv[path_index]); + break; + + case STOP: + if (Options & ~(PVERBOSE)) { + USEAGE(); + exit(-1); + } + exit_code = adm_stop(&argv[path_index]); + break; + + case POWER_OFF: + if (Options & ~(PVERBOSE | OPTION_CAPF)) { + USEAGE(); + exit(-1); + } + exit_code = adm_power_off(&argv[path_index], 1); + break; + + case POWER_ON: + if (Options & (~PVERBOSE)) { + USEAGE(); + exit(-1); + } + exit_code = adm_power_off(&argv[path_index], 0); + break; + + /* + * EXPERT commands. + */ + + case FORCELIP: + if (!(Options & EXPERT) || (Options & ~(PVERBOSE | EXPERT))) { + E_USEAGE(); + exit(-1); + } + exit_code = adm_forcelip(&argv[path_index]); + break; + + case BYPASS: + if (!(Options & EXPERT) || (Options & ~(PVERBOSE | EXPERT | + OPTION_CAPF | OPTION_A | OPTION_B | OPTION_F | + OPTION_R)) || !(Options & (OPTION_A | OPTION_B)) || + ((Options & OPTION_A) && (Options & OPTION_B))) { + E_USEAGE(); + exit(-1); + } + adm_bypass_enable(&argv[path_index], 1); + break; + + case ENABLE: + if (!(Options & EXPERT) || (Options & ~(PVERBOSE | EXPERT | + OPTION_CAPF | OPTION_A | OPTION_B | OPTION_F | + OPTION_R)) || !(Options & (OPTION_A | OPTION_B)) || + ((Options & OPTION_A) && (Options & OPTION_B))) { + E_USEAGE(); + exit(-1); + } + adm_bypass_enable(&argv[path_index], 0); + break; + case LUX_P_OFFLINE: /* Offline a port */ + if (!(Options & EXPERT) || (Options & ~(PVERBOSE | EXPERT))) { + E_USEAGE(); + exit(-1); + } + exit_code = adm_port_offline_online(&argv[path_index], + LUX_P_OFFLINE); + break; + + case LUX_P_ONLINE: /* Online a port */ + if (!(Options & EXPERT) || (Options & ~(PVERBOSE | EXPERT))) { + E_USEAGE(); + exit(-1); + } + exit_code = adm_port_offline_online(&argv[path_index], + LUX_P_ONLINE); + break; + + case RDLS: + if (!(Options & EXPERT) || (Options & ~(PVERBOSE | EXPERT))) { + E_USEAGE(); + exit(-1); + } + if (USE_FCHBA) { + exit_code = fchba_display_link_status(&argv[path_index]); + } else { + display_link_status(&argv[path_index]); + } + break; + + case CREATE_FAB: + if (!(Options & (EXPERT | OPTION_F)) || + (Options & ~(PVERBOSE | EXPERT | OPTION_F))) { + E_USEAGE(); + exit(-1); + } + if (read_repos_file(file_name) != 0) { + exit(-1); + } + break; + + /* + * Undocumented commands. + */ + + case CHECK_FILE: /* Undocumented Cmd */ + if (Options & ~(PVERBOSE)) { + USEAGE(); + exit(-1); + } + exit_code = adm_check_file(&argv[path_index], + (Options & PVERBOSE)); + break; + + case DUMP: /* Undocumented Cmd */ + if (!(Options & EXPERT) || (Options & ~(PVERBOSE | EXPERT))) { + USEAGE(); + exit(-1); + } + dump(&argv[path_index]); + break; + + case DUMP_MAP: /* Undocumented Cmd */ + if (!(Options & EXPERT) || (Options & ~(PVERBOSE | EXPERT))) { + USEAGE(); + exit(-1); + } + if (USE_FCHBA) { + exit_code = fchba_dump_map(&argv[path_index]); + } else { + dump_map(&argv[path_index]); + } + break; + + case SYSDUMP: + if (Options & ~(PVERBOSE)) { + USEAGE(); + exit(-1); + } + if (err = sysdump(Options & PVERBOSE)) { + (void) print_errString(err, NULL); + exit(-1); + } + break; + + case PORT: /* Undocumented command */ + if (!(Options & EXPERT) || (Options & ~(PVERBOSE | EXPERT))) { + USEAGE(); + exit(-1); + } + if (USE_FCHBA) { + exit_code = fchba_display_port(Options & PVERBOSE); + } else { + exit_code = adm_display_port(Options & PVERBOSE); + } + break; + + case EXT_LOOPBACK: + if (!(Options & EXPERT) || (Options & ~(PVERBOSE | EXPERT))) { + USEAGE(); + exit(-1); + } + if (adm_port_loopback(argv[path_index], EXT_LOOPBACK) < 0) { + exit(-1); + } + break; + + case INT_LOOPBACK: + if (!(Options & EXPERT) || (Options & ~(PVERBOSE | EXPERT))) { + USEAGE(); + exit(-1); + } + if (adm_port_loopback(argv[path_index], INT_LOOPBACK) < 0) { + exit(-1); + } + break; + + case NO_LOOPBACK: + if (!(Options & EXPERT) || (Options & ~(PVERBOSE | EXPERT))) { + USEAGE(); + exit(-1); + } + if (adm_port_loopback(argv[path_index], NO_LOOPBACK) < 0) { + exit(-1); + } + break; + + case VERSION: + break; + + + case INSERT_DEVICE: + if (argv[path_index] == NULL) { + if ((err = h_insertSena_fcdev()) != 0) { + (void) print_errString(err, NULL); + exit(-1); + } + } else if ((err = hotplug(INSERT_DEVICE, + &argv[path_index], + Options & PVERBOSE, + Options & OPTION_CAPF)) != 0) { + (void) print_errString(err, argv[path_index]); + exit(-1); + } + break; + case REMOVE_DEVICE: + if (err = hotplug(REMOVE_DEVICE, &argv[path_index], + Options & PVERBOSE, Options & OPTION_CAPF)) { + (void) print_errString(err, argv[path_index]); + exit(-1); + } + break; + + /* for hotplug device operations */ + case DEV_ONLINE: + case DEV_OFFLINE: + case DEV_GETSTATE: + case DEV_RESET: + case BUS_QUIESCE: + case BUS_UNQUIESCE: + case BUS_GETSTATE: + case BUS_RESET: + case BUS_RESETALL: + if (!(Options & EXPERT) || (Options & ~(PVERBOSE | EXPERT))) { + E_USEAGE(); + exit(-1); + } + if (USE_FCHBA) { + if (fchba_hotplug_e(cmd, &argv[path_index], + Options & PVERBOSE, Options & OPTION_CAPF) != 0) { + exit(-1); + } + } else { + if (hotplug_e(cmd, &argv[path_index], + Options & PVERBOSE, Options & OPTION_CAPF) != 0) { + exit(-1); + } + } + break; + + default: + (void) fprintf(stderr, + MSGSTR(2213, "%s: subcommand decode failed.\n"), + whoami); + USEAGE(); + exit(-1); + } + return (exit_code); +} diff --git a/usr/src/cmd/luxadm/common.h b/usr/src/cmd/luxadm/common.h new file mode 100644 index 0000000000..1a96198d25 --- /dev/null +++ b/usr/src/cmd/luxadm/common.h @@ -0,0 +1,495 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * PHOTON CONFIGURATION MANAGER + * Common definitions + */ + +/* + * I18N message number ranges + * This file: 12500 - 12999 + * Shared common messages: 1 - 1999 + */ + +#ifndef _COMMON_H +#define _COMMON_H + + + + +/* + * Include any headers you depend on. + */ +#include <sys/types.h> +#include <sys/scsi/adapters/scsi_vhci.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/*#ifdef _BIG_ENDIAN +#define htonll(x) (x) +#define ntohll(x) (x) +#else +#define htonll(x) ((((unsigned long long)htonl(x)) << 32) + htonl(x >> 32)) +#define ntohll(x) ((((unsigned long long)ntohl(x)) << 32) + ntohl(x >> 32)) +#endif*/ + + +extern char *p_error_msg_ptr; + +#ifdef __x86 +#include <nl_types.h> +extern nl_catd l_catd; +#define L_SET1 1 /* catalog set number */ +#define MSGSTR(Num, Str) catgets(l_catd, L_SET1, Num, Str) +#endif + + +/* Defines */ +#define USEAGE() {(void) fprintf(stderr, MSGSTR(12500, \ + "Usage: %s [-v] subcommand [option...]" \ + " {enclosure[,dev]... | pathname...}\n"), \ + whoami); \ + (void) fflush(stderr); } + +#define E_USEAGE() {(void) fprintf(stderr, MSGSTR(12501, \ + "Usage: %s [-v] -e subcommand [option...]" \ + " {enclosure[,dev]... | pathname...}\n"), \ + whoami); \ + (void) fflush(stderr); } + +#define VERBPRINT if (Options & PVERBOSE) (void) printf + +#define L_ERR_PRINT \ + if (p_error_msg_ptr == NULL) { \ + perror(MSGSTR(12502, "Error")); \ + } else { \ + (void) fprintf(stderr, MSGSTR(12503, "Error: %s"), p_error_msg_ptr); \ + } \ + p_error_msg_ptr = NULL; + +#define P_ERR_PRINT if (p_error_msg_ptr == NULL) { \ + perror(whoami); \ + } else { \ + (void) fprintf(stderr, MSGSTR(12504, "Error: %s"), p_error_msg_ptr); \ + } \ + p_error_msg_ptr = NULL; + + +/* Display extended mode page information. */ +#ifndef MODEPAGE_CACHING +#undef MODEPAGE_CACHING +#define MODEPAGE_CACHING 0x08 +#endif + + +/* Primary commands */ +#define ENCLOSURE_NAMES 100 +#define DISPLAY 101 +#define DOWNLOAD 102 +#define FAST_WRITE 400 /* SSA */ +#define FAILOVER 500 +#define FC_UPDATE 401 /* SSA */ +#define FCAL_UPDATE 103 /* Update the Fcode on Sbus soc card */ +#define FCODE_UPDATE 117 /* Update the Fcode on all cards */ +#define QLGC_UPDATE 116 /* Update the Fcode on PCI card(s) */ +#define INQUIRY 105 +#define LED 107 +#define LED_ON 108 +#define LED_OFF 109 +#define LED_BLINK 110 +#define NVRAM_DATA 402 /* SSA */ +#define POWER_OFF 403 /* SSA */ +#define POWER_ON 111 +#define PASSWORD 112 +#define PURGE 404 /* SSA */ +#define PERF_STATISTICS 405 /* SSA */ +#define PROBE 113 +#define RELEASE 210 +#define RESERVE 211 +#define START 213 +#define STOP 214 +#define SYNC_CACHE 406 /* SSA */ +#define SET_BOOT_DEV 115 /* Set the boot-device variable in nvram */ +#define INSERT_DEVICE 106 /* Hot plug */ +#define REMOVE_DEVICE 114 /* hot plug */ + +/* Device hotplugging */ +#define REPLACE_DEVICE 150 +#define DEV_ONLINE 155 +#define DEV_OFFLINE 156 +#define DEV_GETSTATE 157 +#define DEV_RESET 158 +#define BUS_QUIESCE 160 +#define BUS_UNQUIESCE 161 +#define BUS_GETSTATE 162 +#define BUS_RESET 163 +#define BUS_RESETALL 164 + +#define SKIP 111 +#define QUIT 222 + +#define L_LED_STATUS 0x00 +#define L_LED_RQST_IDENTIFY 0x01 +#define L_LED_ON 0x02 +#define L_LED_OFF 0x04 + + +/* Enclosure Specific */ +#define ALARM 407 /* SSA */ +#define ALARM_OFF 408 /* SSA */ +#define ALARM_ON 409 /* SSA */ +#define ALARM_SET 410 /* SSA */ +#define ENV_DISPLAY 411 /* SSA */ + +/* Expert commands */ +#define RDLS 215 +#define P_BYPASS 218 +#define P_ENABLE 219 +#define BYPASS 220 +#define ENABLE 221 +#define FORCELIP 222 +#define LUX_P_OFFLINE 223 +#define LUX_P_ONLINE 224 +#define EXT_LOOPBACK 225 +#define INT_LOOPBACK 226 +#define NO_LOOPBACK 227 +#define CREATE_FAB 228 + +/* Undocumented commands */ +#define DUMP 300 +#define CHECK_FILE 301 /* Undocumented - Check download file */ +#define DUMP_MAP 302 /* Dump map of loop */ +#define VERSION 303 /* undocumented */ +#define AU 304 /* undocumented */ +#define PORT 305 /* undocumented */ + +/* Undocumented diagnostic subcommands */ +#define SYSDUMP 350 + + +/* SSA - for adm_download */ +/* #define SSAFIRMWARE_FILE "/usr/lib/firmware/ssa/ssafirmware" */ + +/* Global variables */ +extern char *whoami; +extern int Options; +extern const int OPTION_A; +extern const int OPTION_B; +extern const int OPTION_C; +extern const int OPTION_D; +extern const int OPTION_E; +extern const int OPTION_F; +extern const int OPTION_L; +extern const int OPTION_P; +extern const int OPTION_R; +extern const int OPTION_T; +extern const int OPTION_V; +extern const int OPTION_Z; +extern const int OPTION_Y; +extern const int OPTION_CAPF; +extern const int PVERBOSE; +extern const int SAVE; +extern const int EXPERT; + +#define TARGET_ID(box_id, f_r, slot) \ + ((box_id | ((f_r == 'f' ? 0 : 1) << 4)) | (slot + 2)) + +#define NEWER(time1, time2) (time1.tv_sec > time2.tv_sec) + +/* used to set the behavior of get_slash_devices_from_osDevName. */ +#define STANDARD_DEVNAME_HANDLING 1 +#define NOT_IGNORE_DANGLING_LINK 2 + +#include <hbaapi.h> +#ifndef __x86 +#include <sys/scsi/generic/mode.h> +#include <sys/scsi/generic/sense.h> +#include <sys/scsi/impl/uscsi.h> +#include <g_state.h> +#include <stgcom.h> +#include <l_common.h> +#else +typedef struct l_inquiry_inq_2 { +#if defined(_BIT_FIELDS_HTOL) + uchar_t inq_2_reladdr : 1, /* relative addressing */ + inq_wbus32 : 1, /* 32 bit wide data xfers */ + inq_wbus16 : 1, /* 16 bit wide data xfers */ + inq_sync : 1, /* synchronous data xfers */ + inq_linked : 1, /* linked commands */ + inq_res1 : 1, /* reserved */ + inq_cmdque : 1, /* command queueing */ + inq_sftre : 1; /* Soft Reset option */ +#else + uchar_t inq_sftre : 1, /* Soft Reset option */ + inq_cmdque : 1, /* command queueing */ + inq_res1 : 1, /* reserved */ + inq_linked : 1, /* linked commands */ + inq_sync : 1, /* synchronous data xfers */ + inq_wbus16 : 1, /* 16 bit wide data xfers */ + inq_wbus32 : 1, /* 32 bit wide data xfers */ + inq_2_reladdr : 1; /* relative addressing */ +#endif /* _BIT_FIELDS_HTOL */ +} L_inq_2; + +typedef struct l_inquiry_inq_3 { +#if defined(_BIT_FIELDS_HTOL) + uchar_t inq_3_reladdr : 1, /* relative addressing */ + inq_SIP_2 : 3, /* Interlocked Protocol */ + inq_3_linked : 1, /* linked commands */ + inq_trandis : 1, /* Transfer Disable */ + inq_3_cmdque : 1, /* command queueing */ + inq_SIP_3 : 1; /* Interlocked Protocol */ +#else + uchar_t inq_SIP_3 : 1, /* Interlocked Protocol */ + inq_3_cmdque : 1, /* command queueing */ + inq_trandis : 1, /* Transfer Disable */ + inq_3_linked : 1, /* linked commands */ + inq_SIP_2 : 3, /* Interlocked Protocol */ + inq_3_reladdr : 1; /* relative addressing */ +#endif /* _BIT_FIELDS_HTOL */ +} L_inq_3; + +typedef struct l_inquiry_struct { + /* + * byte 0 + * + * Bits 7-5 are the Peripheral Device Qualifier + * Bits 4-0 are the Peripheral Device Type + * + */ + uchar_t inq_dtype; + /* byte 1 */ +#if defined(_BIT_FIELDS_HTOL) + uchar_t inq_rmb : 1, /* removable media */ + inq_qual : 7; /* device type qualifier */ +#else + uchar_t inq_qual : 7, /* device type qualifier */ + inq_rmb : 1; /* removable media */ +#endif /* _BIT_FIELDS_HTOL */ + + /* byte 2 */ +#if defined(_BIT_FIELDS_HTOL) + uchar_t inq_iso : 2, /* ISO version */ + inq_ecma : 3, /* ECMA version */ + inq_ansi : 3; /* ANSI version */ +#else + uchar_t inq_ansi : 3, /* ANSI version */ + inq_ecma : 3, /* ECMA version */ + inq_iso : 2; /* ISO version */ +#endif /* _BIT_FIELDS_HTOL */ + + /* byte 3 */ +#define inq_aerc inq_aenc /* SCSI-3 */ +#if defined(_BIT_FIELDS_HTOL) + uchar_t inq_aenc : 1, /* async event notification cap. */ + inq_trmiop : 1, /* supports TERMINATE I/O PROC msg */ + inq_normaca : 1, /* Normal ACA Supported */ + : 1, /* reserved */ + inq_rdf : 4; /* response data format */ +#else + uchar_t inq_rdf : 4, /* response data format */ + : 1, /* reserved */ + inq_normaca : 1, /* Normal ACA Supported */ + inq_trmiop : 1, /* supports TERMINATE I/O PROC msg */ + inq_aenc : 1; /* async event notification cap. */ +#endif /* _BIT_FIELDS_HTOL */ + + /* bytes 4-7 */ + uchar_t inq_len; /* additional length */ + uchar_t : 8; /* reserved */ +#if defined(_BIT_FIELDS_HTOL) + uchar_t : 2, /* reserved */ + inq_port : 1, /* Only defined when dual_p set */ + inq_dual_p : 1, /* Dual Port */ + inq_mchngr : 1, /* Medium Changer */ + inq_SIP_1 : 3; /* Interlocked Protocol */ +#else + uchar_t inq_SIP_1 : 3, /* Interlocked Protocol */ + inq_mchngr : 1, /* Medium Changer */ + inq_dual_p : 1, /* Dual Port */ + inq_port : 1, /* Only defined when dual_p set */ + : 2; /* reserved */ +#endif /* _BIT_FIELDS_HTOL */ + + union { + L_inq_2 inq_2; + L_inq_3 inq_3; + } ui; + + + /* bytes 8-35 */ + + uchar_t inq_vid[8]; /* vendor ID */ + + uchar_t inq_pid[16]; /* product ID */ + + uchar_t inq_revision[4]; /* product revision level */ + + /* + * Bytes 36-55 are vendor-specific parameter bytes + */ + + /* SSA specific definitions */ + /* bytes 36 - 39 */ +#define inq_ven_specific_1 inq_firmware_rev + uchar_t inq_firmware_rev[4]; /* firmware revision level */ + + /* bytes 40 - 51 */ + uchar_t inq_serial[12]; /* serial number, not used any more */ + + /* bytes 52-53 */ + uchar_t inq_res2[2]; + + /* byte 54, 55 */ + uchar_t inq_ssa_ports; /* number of ports */ + uchar_t inq_ssa_tgts; /* number of targets */ + + /* + * Bytes 56-95 are reserved. + */ + uchar_t inq_res3[40]; + /* + * 96 to 'n' are vendor-specific parameter bytes + */ + uchar_t inq_box_name[32]; + uchar_t inq_avu[256]; +} L_inquiry; +#define HEX_ONLY 0 /* Print Hex only */ +#define HEX_ASCII 1 /* Print Hex and Ascii */ +#define WWN_SIZE 8 /* # of bytes to dump per line */ + +/* NOTE: These command op codes are not defined in commands.h */ +#define SCMD_SYNC_CACHE 0x35 +#define SCMD_LOG_SENSE 0x4d +#define SCMD_PERS_RESERV_IN 0x5e +#define SCMD_PERS_RESERV_OUT 0x5f + +typedef struct rls_payload { + uint_t rls_portno; + uint_t rls_linkfail; + uint_t rls_syncfail; + uint_t rls_sigfail; + uint_t rls_primitiverr; + uint_t rls_invalidword; + uint_t rls_invalidcrc; +} rls_payload_t; + +typedef struct l_inquiry00_struct { +#if defined(_BIT_FIELDS_LTOH) +uchar_t qual :3, + dtype :5; +#else +uchar_t dtype :5, + qual :3; +#endif /* _BIT_FIELDS_LTOH */ +uchar_t page_code; +uchar_t reserved; +uchar_t len; +uchar_t page_list[251]; +} L_inquiry00; + +#define MIN(a, b) (a < b ? a : b) +#define ER_DPRINTF if (getenv("_LUX_ER_DEBUG") != NULL) (void) printf +#define O_DPRINTF if (getenv("_LUX_O_DEBUG") != NULL) (void) printf +#define P_DPRINTF if (getenv("_LUX_P_DEBUG") != NULL) (void) printf +#define R_DPRINTF if (getenv("_LUX_R_DEBUG") != NULL) (void) printf +#define I_DPRINTF if (getenv("_LUX_I_DEBUG") != NULL) (void) printf +#define S_DPRINTF if (getenv("_LUX_S_DEBUG") != NULL) (void) printf +#define RETRY_FCIO_IOCTL 360 +#define WAIT_FCIO_IOCTL 250000 /* 1/4 of a second */ + +#endif /* __x86 */ + + +int adm_display_config(char **argv); +void adm_download(char **argv, char *file_name); +void up_encl_name(char **argv, int argc); +void adm_failover(char **argv); +void pho_probe(); +void non_encl_probe(); +void adm_led(char **argv, int led_action); +void up_password(char **argv); +int adm_start(char **argv); +int adm_stop(char **argv); +int adm_power_off(char **argv, int off_flag); +int adm_forcelip(char **argv); +void adm_bypass_enable(char **argv, int bypass_flag); +int adm_port_offline_online(char *argv[], int flag); +void display_link_status(char **argv); +int read_repos_file(char *repos_filename); +int adm_check_file(char **argv, int flag); +void dump(char **argv); +void dump_map(char **argv); +int adm_port_loopback(char *portpath, int flag); +int adm_inquiry(char **argv); +int adm_display_port(int verbose); + +int adm_reserve(char *path); +int adm_release(char *path); +void i18n_catopen(); +void dump_hex_data(char *, uchar_t *, int, int); +void print_errString(int, char *); +void print_chars(uchar_t *, int, int); +void print_inq_data(char *, char *, L_inquiry, uchar_t *, size_t); +void print_fabric_dtype_prop(uchar_t *hba_port_wwn, uchar_t *port_wwn, + uchar_t dtype_prop); +void print_private_loop_dtype_prop(uchar_t *hba_port_wwn, uchar_t *port_wwn, + uchar_t dtype_prop); +char *get_errString(int errornum); +int cmp_raw_wwn(uchar_t *wwn_1, uchar_t *wwn_2); + +/* routines in fchba*.c files */ +int fchba_display_port(int verbose); +int fchba_display_config(char **argv, int option_t_input, int argc); +char *get_slash_devices_from_osDevName(char *osDevName, int flag); +int get_scsi_vhci_pathinfo(char *dev_path, sv_iocdata_t *ioc, + int *path_count); +int get_mode_page(char *path, uchar_t **pg_buf); +int scsi_mode_sense_cmd(int fd, uchar_t *buf_ptr, int buf_len, uchar_t pc, + uchar_t page_code); +int scsi_release(char *path); +int scsi_reserve(char *path); +int is_path(char *arg); +int is_wwn(char *arg); +int loadLibrary(); +uint32_t getNumberOfAdapters(); +int getAdapterAttrs(HBA_HANDLE handle, + char *name, HBA_ADAPTERATTRIBUTES *attrs); +int getAdapterPortAttrs(HBA_HANDLE handle, char *name, int portIndex, + HBA_PORTATTRIBUTES *attrs); +HBA_STATUS fetch_mappings(HBA_HANDLE handle, HBA_WWN pwwn, + HBA_FCPTARGETMAPPINGV2 **map); +int match_mappings(char *compare, HBA_FCPTARGETMAPPINGV2 *map); +uint64_t wwnConversion(uchar_t *wwn); + + +#ifdef __cplusplus +} +#endif + +#endif /* _COMMON_H */ diff --git a/usr/src/cmd/luxadm/diag.c b/usr/src/cmd/luxadm/diag.c new file mode 100644 index 0000000000..6336b5530d --- /dev/null +++ b/usr/src/cmd/luxadm/diag.c @@ -0,0 +1,262 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + + +/*LINTLIBRARY*/ + + +/* + * This module is part of the photon library + */ + +/* + * I18N message number ranges + * This file: 3500 - 3999 + * Shared common messages: 1 - 1999 + */ + +/* Includes */ +#include <stdlib.h> +#include <stdio.h> +#include <sys/file.h> +#include <sys/errno.h> +#include <sys/types.h> +#include <sys/param.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <sys/scsi/scsi.h> +#include <nl_types.h> +#include <strings.h> +#include <sys/ddi.h> /* for max */ +#include <libdevice.h> +#include <sys/fibre-channel/fcio.h> +#define _SYS_FC4_FCAL_LINKAPP_H +#include <sys/fc4/fcio.h> +#include <sys/devctl.h> +#include <sys/scsi/targets/sesio.h> +#include <l_common.h> +#include <l_error.h> +#include <a_state.h> +#include <a5k.h> +#include <stgcom.h> +#include "luxadm.h" + + +/* Defines */ +#define VERBPRINT if (verbose) (void) printf + + + +static int +print_ssd_in_box(char *ses_path, uchar_t *box_name, int verbose) +{ +L_state l_state; +int err, i; +struct dlist *ml; +WWN_list *wwn_list, *wwn_list_ptr; +char *s; +L_inquiry inq; +int enc_type = 0; + + wwn_list = wwn_list_ptr = NULL; + if (err = l_get_status(ses_path, &l_state, verbose)) { + return (err); + } + + if (err = g_get_wwn_list(&wwn_list, verbose)) { + return (err); + + } + + /* Need to find out whether this device is a daktari */ + if (g_get_inquiry(ses_path, &inq)) { + return (L_SCSI_ERROR); + } + if ((strncmp((char *)&inq.inq_pid[0], DAK_OFF_NAME, + strlen(DAK_OFF_NAME)) == 0) || + (strncmp((char *)&inq.inq_pid[0], DAK_PROD_STR, + strlen(DAK_PROD_STR)) == 0)) { + enc_type = DAK_ENC_TYPE; + } + for (i = 0; i < (int)l_state.total_num_drv/2; i++) { + if (l_state.drv_front[i].ib_status.code != S_NOT_INSTALLED) { + + ml = l_state.drv_front[i].g_disk_state.multipath_list; + while (ml) { + for (wwn_list_ptr = wwn_list; wwn_list_ptr != NULL; + wwn_list_ptr = wwn_list_ptr->wwn_next) { + s = wwn_list_ptr->physical_path; + if (strcmp((char *)s, + ml->dev_path) == 0) { + (void) fprintf(stdout, MSGSTR(3500, + "%-80.80s %-17.17s %-17.17s %-22.22s "), + wwn_list_ptr->physical_path, + wwn_list_ptr->node_wwn_s, + wwn_list_ptr->port_wwn_s, + wwn_list_ptr->logical_path); + if (enc_type == DAK_ENC_TYPE) { + (void) fprintf(stdout, + MSGSTR(3513, "%s,s%d\n"), + box_name, i); + } else { + (void) fprintf(stdout, + MSGSTR(3501, "%s,f%d\n"), + box_name, i); + } + } + } + ml = ml->next; + } + + } + } + for (i = 0; i < (int)l_state.total_num_drv/2; i++) { + if (l_state.drv_rear[i].ib_status.code != S_NOT_INSTALLED) { + + ml = l_state.drv_rear[i].g_disk_state.multipath_list; + while (ml) { + wwn_list_ptr = wwn_list; + for (wwn_list_ptr = wwn_list; wwn_list_ptr != NULL; + wwn_list_ptr = wwn_list_ptr->wwn_next) { + s = wwn_list_ptr->physical_path; + if (strcmp((char *)s, + ml->dev_path) == 0) { + (void) fprintf(stdout, MSGSTR(3502, + "%-80.80s %-17.17s %-17.17s %-22.22s "), + wwn_list_ptr->physical_path, + wwn_list_ptr->node_wwn_s, + wwn_list_ptr->port_wwn_s, + wwn_list_ptr->logical_path); + if (enc_type == DAK_ENC_TYPE) { + (void) fprintf(stdout, + MSGSTR(3513, "%s,s%d\n"), + box_name, + (int)l_state.total_num_drv/2+i); + } else { + (void) fprintf(stdout, + MSGSTR(3503, "%s,r%d\n"), + box_name, i); + } + } + } + ml = ml->next; + } + + } + } + g_free_wwn_list(&wwn_list); + return (0); +} + + + +int +sysdump(int verbose) +{ +int err; + +Box_list *b_list = NULL; +Box_list *o_list = NULL; +Box_list *c_list = NULL; +int multi_print_flag; + + if (err = l_get_box_list(&b_list, verbose)) { + return (err); + } + if (b_list == NULL) { + (void) fprintf(stdout, + MSGSTR(93, "No %s enclosures found " + "in /dev/es\n"), ENCLOSURE_PROD_NAME); + } else { + o_list = b_list; + while (b_list != NULL) { + /* Don't re-print multiple paths */ + c_list = o_list; + multi_print_flag = 0; + while (c_list != b_list) { + if (strcmp(c_list->b_node_wwn_s, + b_list->b_node_wwn_s) == 0) { + multi_print_flag = 1; + break; + } + c_list = c_list->box_next; + } + if (multi_print_flag) { + b_list = b_list->box_next; + continue; + } + /* Found enclosure */ + + (void) fprintf(stdout, + MSGSTR(3504, "Enclosure name:%s Node WWN:%s\n"), + b_list->b_name, b_list->b_node_wwn_s); + + (void) fprintf(stdout, MSGSTR(3505, + "%-80.80s %-17.17s %-17.17s %-22.22s %-20.20s \n"), + MSGSTR(3506, "Physical"), + MSGSTR(3507, "Node_WWN"), + MSGSTR(3508, "Port_WWN"), + MSGSTR(3509, "Logical"), + MSGSTR(3510, "Name")); + + (void) fprintf(stdout, MSGSTR(3511, + "%-80.80s %-17.17s %-17.17s %-22.22s %-20.20s\n"), + b_list->b_physical_path, + b_list->b_node_wwn_s, + b_list->b_port_wwn_s, + b_list->logical_path, + b_list->b_name); + + c_list = o_list; + while (c_list != NULL) { + if ((c_list != b_list) && + (strcmp(c_list->b_node_wwn_s, + b_list->b_node_wwn_s) == 0)) { + (void) fprintf(stdout, MSGSTR(3512, + "%-80.80s %-17.17s %-17.17s %-22.22s %-20.20s\n"), + c_list->b_physical_path, + c_list->b_node_wwn_s, + c_list->b_port_wwn_s, + c_list->logical_path, + c_list->b_name); + } + c_list = c_list->box_next; + } + /* + * Print the individual disk information for each box. + */ + if (err = print_ssd_in_box(b_list->b_physical_path, + b_list->b_name, verbose)) { + return (err); + } + b_list = b_list->box_next; + } + } + (void) l_free_box_list(&b_list); + return (0); +} diff --git a/usr/src/cmd/luxadm/errorcodes.h b/usr/src/cmd/luxadm/errorcodes.h new file mode 100644 index 0000000000..09f80696e0 --- /dev/null +++ b/usr/src/cmd/luxadm/errorcodes.h @@ -0,0 +1,536 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _ERRORCODES_H +#define _ERRORCODES_H + + + + +/* + * Include any headers you depend on. + */ + +#ifdef __cplusplus +extern "C" { +#endif + + +/* On the sparc platform, error codes come from stgcom.h */ +#ifdef __x86 + +/* + * All error numbers below this base value + * are assumed to be UNIX error codes. + */ + +#define L_BASE L_SCSI_ERROR + +/* + * SCSI Errors + * + */ +/* SCSI error */ +#define L_SCSI_ERROR 0x10000 + +/* Receive Diagnostics: Transfer length is not word aligned */ +#define L_RD_INVLD_TRNSFR_LEN 0x11002 + +/* No disk element found in Receive diag. page */ +#define L_RD_NO_DISK_ELEM 0x11003 + +/* Illegal mode sense page length */ +#define L_ILLEGAL_MODE_SENSE_PAGE 0x11004 + +/* Invalid number of ENV. SENSE pages */ +#define L_INVALID_NO_OF_ENVSEN_PAGES 0x11005 + +/* Buffer is *too* small to hold more than 8 pages */ +#define L_INVALID_BUF_LEN 0x11006 + +/* Scsi_vhci errors */ +#define L_SCSI_VHCI_ERROR 0x11007 +#define L_SCSI_VHCI_ALREADY_ACTIVE 0x11008 +#define L_SCSI_VHCI_NO_STANDBY 0x11009 +#define L_SCSI_VHCI_FAILOVER_NOTSUP 0x1100a +#define L_SCSI_VHCI_FAILOVER_BUSY 0x1100b + + +/* + * Error definitions + * for Format Errors. + */ +#define L_INVALID_PATH 0x20200 + +/* Failed to open a given path */ +#define L_OPEN_PATH_FAIL 0x20001 + +/* Invalid password length. */ +#define L_INVALID_PASSWORD_LEN 0x20002 + +/* Given disk physical path is not valid. */ +#define L_INVLD_PHYS_PATH_TO_DISK 0x20004 + +/* Invalid name id found in the physical path */ +#define L_INVLD_ID_FOUND 0x20005 + +/* Invalid WWN format found */ +#define L_INVLD_WWN_FORMAT 0x20006 + +/* No WWN found in the disk's physical path */ +#define L_NO_WWN_FOUND_IN_PATH 0x20007 + +/* No Loop address found in the phys path */ +#define L_NO_LOOP_ADDRS_FOUND 0x20008 + +/* Invalid port number found in the phys path */ +#define L_INVLD_PORT_IN_PATH 0x20009 + +/* Invalid LED request */ +#define L_INVALID_LED_RQST 0x20010 + +/* Invalid path format */ +#define L_INVALID_PATH_FORMAT 0x20011 + +/* failed to get the physical path */ +#define L_NO_PHYS_PATH 0x20012 + +/* failed to get the ses path */ +#define L_NO_SES_PATH 0x20015 + +/* No "/" found in the physical path */ +#define L_INVLD_PATH_NO_SLASH_FND 0x20100 + +/* No "@" found in the physical path */ +#define L_INVLD_PATH_NO_ATSIGN_FND 0x20101 + +/* Invalid slot (slot < 0 or slot > 10). */ +#define L_INVALID_SLOT 0x20102 + +/* No valid path to a device */ +#define L_NO_VALID_PATH 0x20103 + +/* No disk devices found in /dev/rdsk directory */ +#define L_NO_DISK_DEV_FOUND 0x20104 + +/* No tape devices found in /dev/rmt directory */ +#define L_NO_TAPE_DEV_FOUND 0x20105 + +/* Device's Node WWN not found in the WWN list. */ +#define L_NO_NODE_WWN_IN_WWNLIST 0x20106 + +/* Device's Node WWN not found in the Box list. */ +#define L_NO_NODE_WWN_IN_BOXLIST 0x20107 + +/* Null WWN list found. */ +#define L_NULL_WWN_LIST 0x20108 + +/* No devices found. */ +#define L_NO_DEVICES_FOUND 0x20109 + +/* function arg error in wwn_list process */ +#define L_PROC_WWN_ARG_ERROR 0x20110 + +/* WWN property not found */ +#define L_NO_WWN_PROP_FOUND 0x20111 + +/* No driver nodes found for requested driver */ +#define L_NO_DRIVER_NODES_FOUND 0x20112 + +/* ULP error on device(s) */ +#define L_GET_DEV_LIST_ULP_FAILURE 0x20150 + +/* + * Error definitions + * for FC Loop (FC4 devices). + */ +/* Invalid loop map found */ +#define L_INVALID_LOOP_MAP 0x20202 + +/* SFIOCGMAP ioctl failed */ +#define L_SFIOCGMAP_IOCTL_FAIL 0x20203 + +/* FCIO_GETMAP ioctl failed */ +#define L_FCIO_GETMAP_IOCTL_FAIL 0x20204 + +/* FCIO_LINKSTATUS ioctl failed */ +#define L_FCIO_LINKSTATUS_FAILED 0x20205 + +/* FCIO_GETMAP: Invalid # of entries */ +#define L_FCIOGETMAP_INVLD_LEN 0x20206 + +/* FCIO_FORCE_LIP ioctl failed. */ +#define L_FCIO_FORCE_LIP_FAIL 0x20207 + +/* Error definitions for FC devices */ +/* FCIO_RESET_LINK ioctl failed */ +#define L_FCIO_RESET_LINK_FAIL 0x20208 + +/* FCIO_GET_FCODE_REV_FAIL ioctl failed */ +#define L_FCIO_GET_FCODE_REV_FAIL 0x20209 + +/* FCIO_GET_FW_REV_FAIL ioctl failed */ +#define L_FCIO_GET_FW_REV_FAIL 0x20210 + +/* FCIO_GET_DEV_LIST returns invalid dev. counts */ +#define L_INVALID_DEVICE_COUNT 0x20211 + +/* L_FCIO_GET_NUM_DEVS_FAIL ioctl failed */ +#define L_FCIO_GET_NUM_DEVS_FAIL 0x20212 + +/* L_FCIO_GET_DEV_LIST_FAIL ioctl failed */ +#define L_FCIO_GET_DEV_LIST_FAIL 0x20213 + +/* L_FCIO_GET_LINK_STATUS ioctl failed */ +#define L_FCIO_GET_LINK_STATUS_FAIL 0x20214 + +/* L_FCIO_LOOPBACK_INTERNAL or FCIO_CMD/FCIO_LASER_OFF ioctl failed */ +#define L_PORT_OFFLINE_FAIL 0x20215 + +/* Internal Loopback or laser off ioctls not supported */ +#define L_PORT_OFFLINE_UNSUPPORTED 0x20216 + +/* L_FCIO_NO_LOOPBACK or FCIO_CMD/FCIO_LASER_ON ioctl failed */ +#define L_PORT_ONLINE_FAIL 0x20217 + +/* No-Loopback or laser on ioctls not supported */ +#define L_PORT_ONLINE_UNSUPPORTED 0x20218 + +/* L_FCIO_GET_HOST_PARAMS ioctl failed */ +#define L_FCIO_GET_HOST_PARAMS_FAIL 0x20219 + +/* Loopback mode failure */ +#define L_LOOPBACK_FAILED 0x20220 + +/* Loopback unsupported */ +#define L_LOOPBACK_UNSUPPORTED 0x20221 + +/* FCIO_FORCE_LIP ioctl failed on one of the paths, say, of an MPXIO device */ +#define L_FCIO_FORCE_LIP_PARTIAL_FAIL 0x20222 + +/* + * Error definitions + * for Fabric FC driver ioctls + */ +/* FCP_TGT_INQUIRY ioctl failed */ +#define L_FCP_TGT_INQUIRY_FAIL 0x20250 + +/* + * Error definitions + * for 24-bit address handling + */ +/* Private loop address > 0xFF found */ +#define L_INVALID_PRIVATE_LOOP_ADDRESS 0x20401 + +/* Encountered an unexpected fibre channel topology value */ +#define L_UNEXPECTED_FC_TOPOLOGY 0x20402 + +/* Fabric address was not found */ +#define L_NO_FABRIC_ADDR_FOUND 0x20403 + +/* The FCIO_GET_TOPOLOGY ioctl failed */ +#define L_FCIO_GET_TOPOLOGY_FAIL 0x20404 + +/* Invalid fabric or public loop address */ +#define L_INVALID_FABRIC_ADDRESS 0x20405 + +/* Point to Point fibre channel topology not supported */ +#define L_PT_PT_FC_TOP_NOT_SUPPORTED 0x20406 + +/* + * Error definitions for Tapestry SAN support. + */ +/* The FCIO_DEV_LOGIN ioctl failed */ +#define L_FCIO_DEV_LOGIN_FAIL 0x20407 + +/* The FCIO_DEV_LOGOUT ioctl failed */ +#define L_FCIO_DEV_LOGOUT_FAIL 0x20408 + +/* Operation not supported on connected topology */ +#define L_OPNOSUPP_ON_TOPOLOGY 0x20409 + +/* Operation not supported on the path */ +#define L_INVALID_PATH_TYPE 0x20410 + +/* FCIO_GET_STATE ioctl failed */ +#define L_FCIO_GET_STATE_FAIL 0x20411 + +/* input WWN not found in dev list */ +#define L_WWN_NOT_FOUND_IN_DEV_LIST 0x20412 + +/* + * Error definitions for + * g_dev_map_init related routines. + */ +/* input addr invalid */ +#define L_INVALID_MAP_DEV_ADDR 0x20430 + +/* input property invalid */ +#define L_INVALID_MAP_DEV_PROP_NAME 0x20431 + +/* input property invalid */ +#define L_INVALID_MAP_DEV_PROP_TYPE 0x20432 + +/* input property name invalid */ +#define L_INVALID_MAP_DEV_PROP 0x20433 + +/* device not found */ +#define L_NO_SUCH_DEV_FOUND 0x20434 + +/* prop not found */ +#define L_NO_SUCH_PROP_FOUND 0x20435 + +/* invalid arg found */ +#define L_INVALID_ARG 0x20436 + +/* + * Error definitions + * for Downloading IB FW. + */ +/* Invalid download file checksum */ +#define L_DWNLD_CHKSUM_FAILED 0x20301 + +/* Unable to read download exec header */ +#define L_DWNLD_READ_HEADER_FAIL 0x20302 + +/* Number of bytes read from download file is not correct */ +#define L_DWNLD_READ_INCORRECT_BYTES 0x20303 + +/* Wrong text segment size */ +#define L_DWNLD_INVALID_TEXT_SIZE 0x20304 + +/* Error reading the download file */ +#define L_DWNLD_READ_ERROR 0x20305 + +/* Bad firmware magic found in the download file */ +#define L_DWNLD_BAD_FRMWARE 0x20306 + +/* Timeout message for the IB to be available */ +#define L_DWNLD_TIMED_OUT 0x20307 + +/* Error with Rec Diag page 1 */ +#define L_REC_DIAG_PG1 0x20600 + +/* Invalid transfer Length */ +#define L_TRANSFER_LEN 0x20601 + +/* A firmware file must be specified on the command line */ +#define L_REQUIRE_FILE 0x20602 + + +/* + * Error definitions + * for System Errors + */ +#define L_MALLOC_FAILED 0x30000 + +#define L_MEMCPY_FAILED 0x30001 + +/* Cannot get status for the given path */ +#define L_LSTAT_ERROR 0x30020 + +/* Error reading the symbolic link */ +#define L_SYMLINK_ERROR 0x30021 + +/* Could not convert std. time to hrs/min/sec */ +#define L_LOCALTIME_ERROR 0x30022 + +/* select() system call failed to wait for specified time */ +#define L_SELECT_ERROR 0x30023 + +/* uname() system call failed to get the system info. */ +#define L_UNAME_FAILED 0x30024 + +/* Cannot get status for the given path */ +#define L_FSTAT_ERROR 0x30025 + +/* Cannot get status for the given path */ +#define L_STAT_ERROR 0x30026 + +/* di_init() failed to return snapshot of device tree */ +#define L_DEV_SNAPSHOT_FAILED 0x30027 + +/* di_drv_first_node() failed to find a valid driver */ +#define L_PORT_DRIVER_NOT_FOUND 0x30029 + +/* failed to find any device paths */ +#define L_PHYS_PATH_NOT_FOUND 0x30030 + +/* No device identifier found */ +#define L_NO_DEVID 0x30031 + +/* Driver not supported */ +#define L_DRIVER_NOTSUPP 0x30032 + +/* di_prom_init failure */ +#define L_PROM_INIT_FAILED 0x30033 + +/* + * Error definitions + * for individual + * devices. + */ +/* Device busy */ +#define L_DEV_BUSY 0x40000 + +/* Disk reserved */ +#define L_DEVICE_RESERVED 0x40001 + +/* One or more disks in enclosure are reserved */ +#define L_DISKS_RESERVED 0x40002 + +/* Exclusive open to a device failed. May be busy */ +#define L_EXCL_OPEN_FAILED 0x40003 + +/* Empty slot: Device not installed */ +#define L_SLOT_EMPTY 0x40100 + + +/* + * Error definitions + * for Devctl functions. + */ +/* Devctl acquire fails */ +#define L_ACQUIRE_FAIL 0x40200 + + +/* Power off fails. Device may be busy */ +#define L_POWER_OFF_FAIL_BUSY 0x40300 + + +/* + * Error definitions + * specific to Enclosure. + */ +/* Failed to change the enclosure name */ +#define L_ENCL_NAME_CHANGE_FAIL 0x40400 + +/* Duplicate enclosure names found */ +#define L_DUPLICATE_ENCLOSURES 0x40401 + +/* Invalid no. of dsks in SENA enclosure */ +#define L_INVALID_NUM_DISKS_ENCL 0x40402 + +/* Path is not to a SENA ecnlosure. */ +#define L_ENCL_INVALID_PATH 0x40403 + +/* Cannot get the box list */ +#define L_NO_ENCL_LIST_FOUND 0x40404 + + +/* + * Error definitions + * specific to IB. + */ +/* No element returned from the enclosure */ +#define L_IB_NO_ELEM_FOUND 0x40500 + +/* Invalid page code found in Receive Diag. page. */ +#define L_RD_PG_INVLD_CODE 0x40501 + +/* Reading Receive Diag. page failed: small buffer. */ +#define L_RD_PG_MIN_BUFF 0x40502 + +/* Get status failed */ +#define L_GET_STATUS_FAILED 0x40600 + +/* Warning define. */ +#define L_WARNING 0x90000 + +/* Persistant Rservation: Invalid transfer length */ +#define L_PR_INVLD_TRNSFR_LEN 0x10001 + +/* + * Error definitions + * for Format Errors. + */ +/* box name conflicts with the SSA name */ +#define L_SSA_CONFLICT 0x20013 + + +/* + * Error definitions + * for System Errors + */ +/* drvconfig fail */ +#define L_DRVCONFIG_ERROR 0x31001 + +/* disks program failed */ +#define L_DISKS_ERROR 0x31002 + +/* devlinks program failed */ +#define L_DEVLINKS_ERROR 0x31003 + +/* fail to read /dev/rdsk directory. */ +#define L_READ_DEV_DIR_ERROR 0x31004 + +/* Failed to open /dev/es/ directory. */ +#define L_OPEN_ES_DIR_FAILED 0x31005 + +/* fail to get status from /dev/es directory. */ +#define L_LSTAT_ES_DIR_ERROR 0x31006 + +/* disks program failed */ +#define L_TAPES_ERROR 0x31007 + +/* fail to get status from /dev/rmt/directory. */ +#define L_STAT_RMT_DIR_ERROR 0x31008 + +/* fail to get status from /dev/rmt/directory. */ +#define L_STAT_DEV_DIR_ERROR 0x31009 + + +/* + * Error definitions + * specific to Back plane. + */ +/* Backplane: Busy or reserved disks found */ +#define L_BP_BUSY_RESERVED 0x50000 + +/* Backplane: one or more busy disks found */ +#define L_BP_BUSY 0x50001 + +/* Backplane: one or more reserved disks found */ +#define L_BP_RESERVED 0x50002 + +/* No BP element found in the enclosure */ +#define L_NO_BP_ELEM_FOUND 0x50003 + +/* + * Thread errors. + */ +#define L_TH_CREATE 0x60000 +#define L_TH_JOIN 0x60001 + + + +#endif /* __x86 */ + + +#ifdef __cplusplus +} +#endif + +#endif /* _ERRORCODES_H */ diff --git a/usr/src/cmd/luxadm/errormsgs.c b/usr/src/cmd/luxadm/errormsgs.c new file mode 100644 index 0000000000..542e0b5799 --- /dev/null +++ b/usr/src/cmd/luxadm/errormsgs.c @@ -0,0 +1,1108 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + + + +/* + * I18N message number ranges + * This file: 10000 - 10499 + * Shared common messages: 1 - 1999 + */ + +/* #define _POSIX_SOURCE 1 */ + + +/* Includes */ +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <nl_types.h> +#include <sys/scsi/scsi.h> +#include <string.h> +#include <errno.h> +#include "common.h" +#include "errorcodes.h" + + + +/* Defines */ +#define MAXLEN 1000 + +/* + * Allocate space for and return a pointer to a string + * on the stack. If the string is null, create + * an empty string. + */ +char * +alloc_string(char *s) +{ + char *ns; + + if (s == (char *)NULL) { + ns = (char *)calloc(1, 1); + } else { + ns = (char *)calloc(1, strlen(s) + 1); + if (ns != NULL) { + (void) strncpy(ns, s, (strlen(s) + 1)); + } + } + return (ns); +} + + +/* + * Decodes the SCSI sense byte to a string. + * + * RETURNS: + * character string + */ +static char * +decode_sense_byte(uchar_t status) +{ + switch (status & STATUS_MASK) { + case STATUS_GOOD: + return (MSGSTR(10000, "Good status")); + + case STATUS_CHECK: + return (MSGSTR(128, "Check condition")); + + case STATUS_MET: + return (MSGSTR(124, "Condition met")); + + case STATUS_BUSY: + return (MSGSTR(37, "Busy")); + + case STATUS_INTERMEDIATE: + return (MSGSTR(10001, "Intermediate")); + + case STATUS_INTERMEDIATE_MET: + return (MSGSTR(10002, "Intermediate - condition met")); + + case STATUS_RESERVATION_CONFLICT: + return (MSGSTR(10003, "Reservation_conflict")); + + case STATUS_TERMINATED: + return (MSGSTR(126, "Command terminated")); + + case STATUS_QFULL: + return (MSGSTR(83, "Queue full")); + + default: + return (MSGSTR(4, "Unknown status")); + } +} + + +/* + * This function finds a predefined error string to a given + * error number (errornum), allocates memory for the string + * and returns the corresponding error message to the caller. + * + * RETURNS + * error string if O.K. + * NULL otherwise + */ +char +*get_errString(int errornum) +{ +char err_msg[MAXLEN], *errStrg; + + err_msg[0] = '\0'; /* Just in case */ + if (errornum < L_BASE) { + /* Some sort of random system error most likely */ + errStrg = strerror(errno); + if (errStrg != NULL) { + (void) strcpy(err_msg, errStrg); + } else { /* Something's _really_ messed up */ + (void) sprintf(err_msg, + MSGSTR(10081, + " Error: could not decode the" + " error message.\n" + " The given error message is not" + " defined in the library.\n" + " Message number: %d.\n"), errornum); + } + + /* Make sure ALL CASES set err_msg to something */ + } else switch (errornum) { + case L_SCSI_ERROR: + (void) sprintf(err_msg, + MSGSTR(10096, + " Error: SCSI failure.")); + break; + + case L_PR_INVLD_TRNSFR_LEN: + (void) sprintf(err_msg, + MSGSTR(10005, + " Error: Persistant Reserve command" + " transfer length not word aligned.")); + break; + + case L_RD_NO_DISK_ELEM: + (void) sprintf(err_msg, + MSGSTR(10006, + " Error: Could not find the disk elements" + " in the Receive Diagnostic pages.")); + break; + + case L_RD_INVLD_TRNSFR_LEN: + (void) sprintf(err_msg, + MSGSTR(10007, + " Error: Receive Diagnostic command" + " transfer length not word aligned.")); + break; + + case L_ILLEGAL_MODE_SENSE_PAGE: + (void) sprintf(err_msg, + MSGSTR(10008, + " Error: Programming error - " + "illegal Mode Sense parameter.")); + break; + + case L_INVALID_NO_OF_ENVSEN_PAGES: + (void) sprintf(err_msg, + MSGSTR(10009, + " Error: Invalid no. of sense pages.\n" + " Could not get valid sense page" + " information from the device.")); + break; + + case L_INVALID_BUF_LEN: + (void) sprintf(err_msg, + MSGSTR(10010, + " Error: Invalid buffer length.\n" + " Could not get diagnostic " + " information from the device.")); + break; + + case L_INVALID_PATH: + (void) sprintf(err_msg, + MSGSTR(113, + " Error: Invalid pathname")); + break; + + case L_NO_PHYS_PATH: + (void) sprintf(err_msg, + MSGSTR(10011, + " Error: Could not get" + " physical path to the device.")); + break; + + case L_NO_SES_PATH: + (void) sprintf(err_msg, + MSGSTR(10098, + " Error: No SES found" + " for the device path.")); + break; + + case L_INVLD_PATH_NO_SLASH_FND: + (void) sprintf(err_msg, + MSGSTR(10012, + "Error in the device physical path.")); + break; + + case L_INVLD_PATH_NO_ATSIGN_FND: + (void) sprintf(err_msg, + MSGSTR(10013, + " Error in the device physical path:" + " no @ found.")); + break; + + case L_INVALID_SLOT: + (void) sprintf(err_msg, + MSGSTR(10014, + " Error: Invalid path format." + " Invalid slot.")); + break; + + case L_INVALID_LED_RQST: + (void) sprintf(err_msg, + MSGSTR(10015, + " Error: Invalid LED request.")); + break; + + case L_INVALID_PATH_FORMAT: + (void) sprintf(err_msg, + MSGSTR(10016, + " Error: Invalid path format.")); + break; + + case L_OPEN_PATH_FAIL: + (void) sprintf(err_msg, + MSGSTR(10017, + " Error opening the path.")); + break; + + case L_INVALID_PASSWORD_LEN: + (void) sprintf(err_msg, + MSGSTR(10018, + "Error: Invalid password length.")); + break; + + case L_INVLD_PHYS_PATH_TO_DISK: + (void) sprintf(err_msg, + MSGSTR(10019, + " Error: Physical path not of a disk.")); + break; + + case L_INVLD_ID_FOUND: + (void) sprintf(err_msg, + MSGSTR(10020, + " Error in the device physical path:" + " Invalid ID found in the path.")); + break; + + case L_INVLD_WWN_FORMAT: + (void) sprintf(err_msg, + MSGSTR(10021, + " Error in the device physical path:" + " Invalid wwn format.")); + + break; + + case L_NO_VALID_PATH: + (void) sprintf(err_msg, + MSGSTR(10022, + " Error: Could not find valid path to" + " the device.")); + break; + + case L_NO_WWN_FOUND_IN_PATH: + (void) sprintf(err_msg, + MSGSTR(10023, + " Error in the device physical path:" + " No WWN found.")); + + break; + + case L_NO_NODE_WWN_IN_WWNLIST: + (void) sprintf(err_msg, + MSGSTR(10024, + " Error: Device's Node WWN is not" + " found in the WWN list.\n")); + break; + + case L_NO_NODE_WWN_IN_BOXLIST: + (void) sprintf(err_msg, + MSGSTR(10025, + " Error: Device's Node WWN is not" + " found in the Box list.\n")); + break; + + case L_NULL_WWN_LIST: + (void) sprintf(err_msg, + MSGSTR(10026, + " Error: Null WWN list found.")); + break; + + case L_NO_LOOP_ADDRS_FOUND: + (void) sprintf(err_msg, + MSGSTR(10027, + " Error: Could not find the loop address for " + " the device at physical path.")); + + break; + + case L_INVLD_PORT_IN_PATH: + (void) sprintf(err_msg, + MSGSTR(10028, + "Error in the device physical path:" + " Invalid port number found." + " (Should be 0 or 1).")); + + break; + + case L_INVALID_LOOP_MAP: + (void) sprintf(err_msg, + MSGSTR(10029, + "Error: Invalid loop map found.")); + break; + + case L_SFIOCGMAP_IOCTL_FAIL: + (void) sprintf(err_msg, + MSGSTR(10030, + " Error: SFIOCGMAP ioctl failed." + " Cannot read loop map.")); + break; + + case L_FCIO_GETMAP_IOCTL_FAIL: + (void) sprintf(err_msg, + MSGSTR(10031, + " Error: FCIO_GETMAP ioctl failed." + " Cannot read loop map.")); + break; + + case L_FCIO_LINKSTATUS_FAILED: + (void) sprintf(err_msg, + MSGSTR(10032, + " Error: FCIO_LINKSTATUS ioctl failed." + " Cannot read loop map.")); + break; + + case L_FCIOGETMAP_INVLD_LEN: + (void) sprintf(err_msg, + MSGSTR(10033, + " Error: FCIO_GETMAP ioctl returned" + " an invalid parameter:" + " # entries to large.")); + break; + + case L_FCIO_FORCE_LIP_FAIL: + (void) sprintf(err_msg, + MSGSTR(10034, + " Error: FCIO_FORCE_LIP ioctl failed.")); + break; + + case L_FCIO_FORCE_LIP_PARTIAL_FAIL: + (void) sprintf(err_msg, + MSGSTR(10115, + " Error: FCIO_FORCE_LIP ioctl failed on one" + " or more (but not all) of the paths.")); + break; + + case L_DWNLD_CHKSUM_FAILED: + (void) sprintf(err_msg, + MSGSTR(10035, + "Error: Download file checksum failed.")); + + break; + + case L_DWNLD_READ_HEADER_FAIL: + (void) sprintf(err_msg, + MSGSTR(10036, + " Error: Reading download file exec" + " header failed.")); + break; + + case L_DWNLD_READ_INCORRECT_BYTES: + (void) sprintf(err_msg, + MSGSTR(10037, + " Error: Incorrect number of bytes read.")); + break; + + case L_DWNLD_INVALID_TEXT_SIZE: + (void) sprintf(err_msg, + MSGSTR(10038, + " Error: Reading text segment: " + " Found wrong size.")); + break; + + case L_DWNLD_READ_ERROR: + (void) sprintf(err_msg, + MSGSTR(10039, + " Error: Failed to read download file.")); + break; + + case L_DWNLD_BAD_FRMWARE: + (void) sprintf(err_msg, + MSGSTR(10040, + " Error: Bad Firmware MAGIC.")); + break; + + case L_DWNLD_TIMED_OUT: + (void) sprintf(err_msg, + MSGSTR(10041, + " Error: Timed out in 5 minutes" + " waiting for the" + " IB to become available.")); + break; + + case L_REC_DIAG_PG1: + (void) sprintf(err_msg, + MSGSTR(10042, + " Error parsing the Receive" + " diagnostic page.")); + break; + + case L_TRANSFER_LEN: + (void) sprintf(err_msg, + MSGSTR(10043, " ")); + break; + + case L_REQUIRE_FILE: + (void) sprintf(err_msg, + MSGSTR(10109, + " Error: No default file. You must specify" + " the filename path.")); + break; + + case L_MALLOC_FAILED: + (void) sprintf(err_msg, + MSGSTR(10, + " Error: Unable to allocate memory.")); + break; + + case L_LOCALTIME_ERROR: + (void) sprintf(err_msg, + MSGSTR(10044, + " Error: Could not convert time" + " to broken-down time: Hrs/Mins/Secs.")); + break; + + case L_SELECT_ERROR: + (void) sprintf(err_msg, + MSGSTR(10045, + " select() error during retry:" + " Could not wait for" + " specified time.")); + break; + + case L_NO_DISK_DEV_FOUND: + (void) sprintf(err_msg, + MSGSTR(10046, + " Error: No disk devices found" + " in the /dev/rdsk" + " directory.")); + break; + + case L_NO_TAPE_DEV_FOUND: + (void) sprintf(err_msg, + MSGSTR(10047, + " Error: No tape devices found" + " in the /dev/rmt" + " directory.")); + break; + + case L_LSTAT_ERROR: + (void) sprintf(err_msg, + MSGSTR(10048, + " lstat() error: Cannot obtain status" + " for the device.")); + break; + + case L_SYMLINK_ERROR: + (void) sprintf(err_msg, + MSGSTR(10049, + " Error: Could not read the symbolic link.")); + break; + + case L_UNAME_FAILED: + (void) sprintf(err_msg, + MSGSTR(10050, + " uname() error: Could not obtain the" + " architeture of the host machine.")); + break; + + case L_DRVCONFIG_ERROR: + (void) sprintf(err_msg, + MSGSTR(10051, + " Error: Could not run drvconfig.")); + break; + + case L_DISKS_ERROR: + (void) sprintf(err_msg, + MSGSTR(10052, + " Error: Could not run disks.")); + break; + + case L_DEVLINKS_ERROR: + (void) sprintf(err_msg, + MSGSTR(10053, + " Error: Could not run devlinks.")); + break; + + case L_READ_DEV_DIR_ERROR: + (void) sprintf(err_msg, + MSGSTR(10054, + " Error: Could not read /dev/rdsk" + " directory.")); + break; + + case L_OPEN_ES_DIR_FAILED: + (void) sprintf(err_msg, + MSGSTR(10055, + " Error: Could not open /dev/es" + " directory.")); + break; + + case L_LSTAT_ES_DIR_ERROR: + (void) sprintf(err_msg, + MSGSTR(10056, + " lstat() error: Could not get status" + " for /dev/es directory.")); + break; + + case L_DEV_BUSY: + (void) sprintf(err_msg, + MSGSTR(10057, + " Error: Could not offline the device\n" + " May be Busy.")); + break; + + case L_EXCL_OPEN_FAILED: + (void) sprintf(err_msg, + MSGSTR(10058, + " Error: Could not open device in" + " exclusive mode." + " May already be open.")); + break; + + case L_DEVICE_RESERVED: + (void) sprintf(err_msg, + MSGSTR(10059, + " Error: Disk is reserved.")); + break; + + case L_DISKS_RESERVED: + (void) sprintf(err_msg, + MSGSTR(10060, + " Error: One or more disks in" + " SENA are reserved.")); + break; + + case L_SLOT_EMPTY: + (void) sprintf(err_msg, + MSGSTR(10061, + " Error: Slot is empty.")); + break; + + case L_ACQUIRE_FAIL: + (void) sprintf(err_msg, + MSGSTR(10062, + " Error: Could not acquire" + " the device.")); + break; + + case L_POWER_OFF_FAIL_BUSY: + (void) sprintf(err_msg, + MSGSTR(10063, + " Error: Could not power off the device.\n" + " May be Busy.")); + break; + + case L_ENCL_NAME_CHANGE_FAIL: + (void) sprintf(err_msg, + MSGSTR(10064, + " Error: The Enclosure name change failed.")); + break; + + case L_DUPLICATE_ENCLOSURES: + (void) sprintf(err_msg, + MSGSTR(10065, + " Error: There are two or more enclosures" + " with the same name." + " Please use a logical or physical" + " pathname.")); + break; + + case L_INVALID_NUM_DISKS_ENCL: + (void) sprintf(err_msg, + MSGSTR(10066, + " Error: The number of disks in the" + " front & rear of the enclosure are" + " different." + " This is not a supported configuration.")); + break; + + case L_ENCL_INVALID_PATH: + (void) sprintf(err_msg, + MSGSTR(10067, + " Error: Invalid path." + " Device is not a SENA subsystem.")); + break; + + case L_NO_ENCL_LIST_FOUND: + (void) sprintf(err_msg, + MSGSTR(10068, + " Error: Cannot get the Box list.")); + break; + + case L_IB_NO_ELEM_FOUND: + (void) sprintf(err_msg, + MSGSTR(10069, + " Error: No elements returned from" + " enclosure (IB).")); + break; + + case L_GET_STATUS_FAILED: + (void) sprintf(err_msg, + MSGSTR(10070, + " Error: Get status failed.")); + break; + + case L_RD_PG_MIN_BUFF: + (void) sprintf(err_msg, + MSGSTR(10071, + " Error: Reading page from IB.\n" + " Buffer size too small.")); + break; + + case L_RD_PG_INVLD_CODE: + (void) sprintf(err_msg, + MSGSTR(10072, + " Error: Reading page from IB\n" + " Invalid page code or page len found.")); + break; + + case L_BP_BUSY_RESERVED: + (void) sprintf(err_msg, + MSGSTR(10073, + " Error: There is a busy or reserved disk" + " attached to this backplane.\n" + " You must close the disk,\n" + " or release the disk,\n" + " or resubmit the command using" + " the Force option.")); + break; + + case L_BP_BUSY: + (void) sprintf(err_msg, + MSGSTR(10074, + " Error: There is a busy disk" + " attached to this backplane.\n" + " You must close the disk,\n" + " or resubmit the command using" + " the Force option.")); + break; + + case L_BP_RESERVED: + (void) sprintf(err_msg, + MSGSTR(10075, + " Error: There is a reserved disk" + " attached to this backplane.\n" + " You must release the disk,\n" + " or resubmit the subcommand using" + " the Force option.")); + break; + + case L_NO_BP_ELEM_FOUND: + (void) sprintf(err_msg, + MSGSTR(10076, + " Error: No Back plane elements found" + " in the enclosure.")); + break; + + case L_SSA_CONFLICT: + (void) sprintf(err_msg, + MSGSTR(10077, + " There is a conflict between the " + "enclosure name and an SSA name of " + "same form, cN.\n" + " Please use a logical or physical " + "pathname.")); + break; + + case L_WARNING: + (void) sprintf(err_msg, + MSGSTR(10078, " Warning:")); + + break; + + case L_TH_JOIN: + (void) sprintf(err_msg, + MSGSTR(10079, + " Error: Thread join failed.")); + break; + + case L_FCIO_RESET_LINK_FAIL: + (void) sprintf(err_msg, + MSGSTR(10082, + " Error: FCIO_RESET_LINK ioctl failed.\n" + " Could not reset the loop.")); + break; + + case L_FCIO_GET_FCODE_REV_FAIL: + (void) sprintf(err_msg, + MSGSTR(10083, + " Error: FCIO_GET_FCODE_REV ioctl failed.\n" + " Could not get the fcode version.")); + break; + + case L_FCIO_GET_FW_REV_FAIL: + (void) sprintf(err_msg, + MSGSTR(10084, + " Error: FCIO_GET_FW_REV ioctl failed.\n" + " Could not get the firmware revision.")); + break; + + case L_NO_DEVICES_FOUND: + (void) sprintf(err_msg, + MSGSTR(10085, + " No FC devices found.")); + break; + + case L_INVALID_DEVICE_COUNT: + (void) sprintf(err_msg, + MSGSTR(10086, + " Error: FCIO_GET_DEV_LIST ioctl returned" + " an invalid device count.")); + break; + + case L_FCIO_GET_NUM_DEVS_FAIL: + (void) sprintf(err_msg, + MSGSTR(10087, + " Error: FCIO_GET_NUM_DEVS ioctl failed.\n" + " Could not get the number of devices.")); + break; + + case L_FCIO_GET_DEV_LIST_FAIL: + (void) sprintf(err_msg, + MSGSTR(10088, + " Error: FCIO_GET_DEV_LIST ioctl failed.\n" + " Could not get the device list.")); + break; + + case L_FCIO_GET_LINK_STATUS_FAIL: + (void) sprintf(err_msg, + MSGSTR(10089, + " Error: FCIO_GET_LINK_STATUS ioctl failed.\n" + " Could not get the link status.")); + break; + + case L_PORT_OFFLINE_FAIL: + (void) sprintf(err_msg, + MSGSTR(10090, + " Error: ioctl to offline the port failed.")); + break; + + case L_PORT_OFFLINE_UNSUPPORTED: + (void) sprintf(err_msg, + MSGSTR(10091, + " Error: The driver does not support ioctl to" + " disable the FCA port.")); + break; + + case L_PORT_ONLINE_FAIL: + (void) sprintf(err_msg, + MSGSTR(10092, + " Error: ioctl to online the port failed.")); + break; + + case L_PORT_ONLINE_UNSUPPORTED: + (void) sprintf(err_msg, + MSGSTR(10093, + " Error: The driver does not support ioctl to" + " enable the FCA port.")); + break; + + case L_FCP_TGT_INQUIRY_FAIL: + (void) sprintf(err_msg, + MSGSTR(10094, + " Error: FCP_TGT_INQUIRY ioctl failed.\n" + " Could not get the target inquiry data" + " from FCP.")); + break; + + case L_FSTAT_ERROR: + (void) sprintf(err_msg, + MSGSTR(10095, + " fstat() error: Cannot obtain status" + " for the device.")); + break; + + case L_FCIO_GET_HOST_PARAMS_FAIL: + (void) sprintf(err_msg, + MSGSTR(10097, + " Error: FCIO_GET_HOST_PARAMS ioctl failed.\n" + " Could not get the host parameters.")); + break; + + case L_STAT_ERROR: + (void) sprintf(err_msg, + MSGSTR(10099, + " stat() error: Cannot obtain status" + " for the device.")); + break; + + case L_DEV_SNAPSHOT_FAILED: + (void) sprintf(err_msg, + MSGSTR(10100, + " Error: Could not retrieve device tree" + " snapshot.")); + break; + + case L_LOOPBACK_UNSUPPORTED: + (void) sprintf(err_msg, + MSGSTR(10101, + " Error: Loopback mode is unsupported for this" + " device.")); + break; + + case L_LOOPBACK_FAILED: + (void) sprintf(err_msg, + MSGSTR(10102, + " Error: Error occurred during loopback mode" + " set.")); + break; + + case L_FCIO_GET_TOPOLOGY_FAIL: + (void) sprintf(err_msg, + MSGSTR(10103, + " Error: FCIO_GET_TOPOLOGY ioctl failed.\n" + " Could not get the fca port topology.")); + break; + + case L_UNEXPECTED_FC_TOPOLOGY: + (void) sprintf(err_msg, + MSGSTR(10104, + " Error: Unexpected Fibre Channel topology" + " found.")); + break; + + case L_INVALID_PRIVATE_LOOP_ADDRESS: + (void) sprintf(err_msg, + MSGSTR(10105, + " Error: AL_PA is not a valid private loop" + " address.")); + break; + + case L_NO_FABRIC_ADDR_FOUND: + (void) sprintf(err_msg, + MSGSTR(10106, + " Error: Could not find the fabric address" + " for the device at physical path.")); + break; + + case L_INVALID_FABRIC_ADDRESS: + (void) sprintf(err_msg, + MSGSTR(10107, + " Error: Device port address on the Fabric" + " topology is not valid.")); + break; + + case L_PT_PT_FC_TOP_NOT_SUPPORTED: + (void) sprintf(err_msg, + MSGSTR(10108, + " Error: Point to Point Fibre Channel " + "topology is currently not supported.")); + break; + + case L_FCIO_DEV_LOGIN_FAIL: + (void) sprintf(err_msg, + MSGSTR(10310, + " Error: FCIO_DEV_LOGIN ioctl failed.")); + break; + + case L_FCIO_DEV_LOGOUT_FAIL: + (void) sprintf(err_msg, + MSGSTR(10311, + " Error: FCIO_DEV_LOGOUT ioctl failed.")); + break; + + case L_OPNOSUPP_ON_TOPOLOGY: + (void) sprintf(err_msg, + MSGSTR(10312, + " Error: operation not supported " + "on connected topology.")); + break; + + case L_INVALID_PATH_TYPE: + (void) sprintf(err_msg, + MSGSTR(10313, + " Error: operation not supported " + "on the path.")); + break; + + case L_FCIO_GET_STATE_FAIL: + (void) sprintf(err_msg, + MSGSTR(10314, + " Error: FCIO_GET_STATE ioctl failed.")); + break; + + case L_WWN_NOT_FOUND_IN_DEV_LIST: + (void) sprintf(err_msg, + MSGSTR(10315, + " Error: device WWN not found in " + "device list.")); + break; + + case L_STAT_RMT_DIR_ERROR: + (void) sprintf(err_msg, + MSGSTR(10110, + " stat() error: Could not get status" + " for /dev/rmt directory.")); + break; + + case L_STAT_DEV_DIR_ERROR: + (void) sprintf(err_msg, + MSGSTR(10111, + " stat() error: Could not get status" + " for /dev/dsk directory.")); + break; + + case L_PROM_INIT_FAILED: + (void) sprintf(err_msg, + MSGSTR(10234, + " Error: di_prom_init failure")); + break; + + case L_PORT_DRIVER_NOT_FOUND: + (void) sprintf(err_msg, + MSGSTR(10113, + " Error: requested port driver" + " does not exist")); + break; + + case L_PHYS_PATH_NOT_FOUND: + (void) sprintf(err_msg, + MSGSTR(10114, + " Error: requested phys path does not exist")); + break; + + case L_GET_DEV_LIST_ULP_FAILURE: + (void) sprintf(err_msg, + MSGSTR(10150, + " Error: g_get_dev_list failed on ULP " + "processing of target device(s)")); + break; + + case L_SCSI_VHCI_ERROR: + (void) sprintf(err_msg, + MSGSTR(10230, + " Error: Unable to perform failover")); + break; + + case L_SCSI_VHCI_ALREADY_ACTIVE: + (void) sprintf(err_msg, + MSGSTR(10231, + " Error: Pathclass already active")); + break; + + case L_NO_DEVID: + (void) sprintf(err_msg, + MSGSTR(10232, + " Error: No device identifier found")); + break; + + case L_DRIVER_NOTSUPP: + (void) sprintf(err_msg, + MSGSTR(10233, + " Error: Driver not supported")); + break; + + case L_PROC_WWN_ARG_ERROR: + (void) sprintf(err_msg, + MSGSTR(10235, + " Error: process WWN argument")); + break; + + case L_NO_WWN_PROP_FOUND: + (void) sprintf(err_msg, + MSGSTR(10236, + " Error: WWN prop not found")); + break; + + case L_NO_DRIVER_NODES_FOUND: + (void) sprintf(err_msg, + MSGSTR(10237, + " Error: Requested driver nodes not found")); + break; + + case L_INVALID_MAP_DEV_ADDR: + (void) sprintf(err_msg, + MSGSTR(10330, + " Error: Invalid map device handle found")); + break; + + case L_INVALID_MAP_DEV_PROP_TYPE: + (void) sprintf(err_msg, + MSGSTR(10331, + " Error: Invalid device property type found")); + break; + + case L_INVALID_MAP_DEV_PROP_NAME: + (void) sprintf(err_msg, + MSGSTR(10332, + " Error: Invalid device property name found")); + break; + + case L_INVALID_MAP_DEV_PROP: + (void) sprintf(err_msg, + MSGSTR(10333, + " Error: Invalid device property handle " + "found")); + break; + + case L_SCSI_VHCI_NO_STANDBY: + (void) sprintf(err_msg, + MSGSTR(10334, + " Error: Unable to perform failover, " + "standby path unavailable")); + break; + + case L_SCSI_VHCI_FAILOVER_NOTSUP: + (void) sprintf(err_msg, + MSGSTR(10335, + " Error: Device does not support failover")); + break; + + case L_SCSI_VHCI_FAILOVER_BUSY: + (void) sprintf(err_msg, + MSGSTR(10336, + " Error: Failover currently in progress")); + break; + + case L_NO_SUCH_DEV_FOUND: + (void) sprintf(err_msg, + MSGSTR(10337, + " Error: No such device found")); + break; + + case L_NO_SUCH_PROP_FOUND: + (void) sprintf(err_msg, + MSGSTR(10338, + " Error: No such property found")); + break; + + case L_INVALID_ARG: + (void) sprintf(err_msg, + MSGSTR(10339, + " Error: Invalid argument found")); + break; + + default: + + if (((L_SCSI_ERROR ^ errornum) == STATUS_GOOD) || + ((L_SCSI_ERROR ^ errornum) == STATUS_BUSY) || + ((L_SCSI_ERROR ^ errornum) == STATUS_CHECK) || + ((L_SCSI_ERROR ^ errornum) == STATUS_MET) || + ((L_SCSI_ERROR ^ errornum) == STATUS_INTERMEDIATE) || + ((L_SCSI_ERROR ^ errornum) == STATUS_INTERMEDIATE_MET) || + ((L_SCSI_ERROR ^ errornum) == STATUS_RESERVATION_CONFLICT) || + ((L_SCSI_ERROR ^ errornum) == STATUS_TERMINATED) || + ((L_SCSI_ERROR ^ errornum) == STATUS_QFULL)) { + (void) sprintf(err_msg, + MSGSTR(10080, + " SCSI Error - Sense Byte:(0x%x) %s \n" + " Error: Retry failed."), + (L_SCSI_ERROR ^ errornum) & STATUS_MASK, + decode_sense_byte((uchar_t)L_SCSI_ERROR ^ errornum)); + } else { + (void) sprintf(err_msg, + MSGSTR(10081, + " Error: could not decode the" + " error message.\n" + " The given error message is not" + " defined in the library.\n" + " Message number: %d.\n"), errornum); + } + + } /* end of switch */ + + errStrg = alloc_string(err_msg); + + return (errStrg); +} diff --git a/usr/src/cmd/luxadm/fabric_conf.c b/usr/src/cmd/luxadm/fabric_conf.c new file mode 100644 index 0000000000..2a7611f319 --- /dev/null +++ b/usr/src/cmd/luxadm/fabric_conf.c @@ -0,0 +1,335 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + + + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> +#include <stdarg.h> +#include <syslog.h> +#include <libdevice.h> +#include <sys/fibre-channel/fcio.h> +#include "common.h" + +static int parse_line(char *line, char *path, char *wwn, char *filename); +static int create_ap_instance(char *ap_id, char *wwn_string, + char *filename, char *line); +static void log_error(char *msg_id, char *input_tmplt, ...); +static char ctoi(char c); + +/* + * Simple wrapper for syslog error messages. + * Allows easy addition of syserr output if desired. + */ +static void +log_error(char *msg_id, char *input_tmplt, ...) +{ + va_list ap; + char input_merged_msg[200]; + char *msg_template = "ID[luxadm.create_fabric_device.%s] %s"; + /* + * First %s for msg_id in merged msg. + * Second %s is for input merged_msg + */ + char *merged_msg; + + va_start(ap, input_tmplt); + /* insert caller's args */ + (void) vsprintf(input_merged_msg, input_tmplt, ap); + va_end(ap); + + merged_msg = (char *)malloc(strlen(msg_template) + + strlen(input_merged_msg) + + strlen(msg_id) + 1); + if (merged_msg == NULL) { + syslog(LOG_ERR, + "ID[luxadm.create_fabric_device.2317] " + "malloc failure, %s", strerror(errno)); + } else { + sprintf(merged_msg, msg_template, msg_id, input_merged_msg); + /* first insert msg_id */ + syslog(LOG_ERR, merged_msg, ""); + (void) puts(merged_msg); /* also print message */ + free(merged_msg); + } +} + +/* + * Routines for reading tapestry repository file + */ + +#define COMMENT_CHAR '#' +int +read_repos_file(char *repos_filename) +{ + int fd; + char *line; + char *tmp_ptr, *mmap_ptr; + char path[MAXPATHLEN]; + int ret; + char wwn[FC_WWN_SIZE*2+1]; + struct stat stbuf; + unsigned int filesize; + unsigned int bytes_read; + + if (repos_filename == NULL || *repos_filename == NULL) { + log_error("2310", + "filename missing for -f option of " + "luxadm -e create_fabric_device"); + return (-1); + } + + fd = open(repos_filename, O_RDONLY); + + if (fd == -1) { + log_error("2311", + "fopen failed: cannot open repository file %s. %d", + repos_filename, strerror(errno)); + return (-1); + } + + if (fstat(fd, &stbuf) == -1) { + close(fd); + log_error("2312", + "stat failed on file %s. %s", + repos_filename, strerror(errno)); + return (-1); + } + filesize = stbuf.st_size; + tmp_ptr = mmap_ptr = mmap((caddr_t)0, filesize, + (PROT_READ | PROT_WRITE), MAP_PRIVATE, fd, 0); + + if (mmap_ptr == MAP_FAILED) { + log_error("2315", + "Failed to mmap file %s. %s", + repos_filename, strerror(errno)); + return (-1); + } + + bytes_read = 0; + while (bytes_read < filesize) { + line = tmp_ptr; + while (bytes_read < filesize && *tmp_ptr != '\n') { + bytes_read++; + tmp_ptr++; + } + if (*tmp_ptr == '\n') { + *tmp_ptr = NULL; + tmp_ptr++; + bytes_read++; + } + + /* If the line is a comment, read another line */ + if (*line == COMMENT_CHAR) { + continue; + } + ret = parse_line(line, path, wwn, repos_filename); + if (ret == 0) { + ret = create_ap_instance(path, + wwn, repos_filename, line); + } + } + + ret = close(fd); + ret = munmap(mmap_ptr, filesize); + return (ret); +} + +/* + * Input is paramater 1 - a line from repository + * Output is other parameters, the path to the attachment point, + * and the port wwn are parsed from the repository + * Format is + * "/devices/pci..../fp@1,0:fc::wwn" + * If controller name is missing, that's okay. Other fields + * must be present + * + * Return 0 on success or -1 on failure; all failures logged to syslog. + */ +#define WWN_DELIM "::" +static int +parse_line(char *line, char *path, char *wwn, char *filename) +{ + char *p_path, *p_wwn, *p_delim; + char *line_copy; + + line_copy = strdup(line); + if (line_copy == NULL) { + log_error("2317", + "malloc failure, %s", strerror(errno)); + } + p_path = line_copy; + p_delim = strstr(p_path, WWN_DELIM); + if (p_delim == NULL) { + log_error("2313", + "Invalid line (%s) in file %s.", line, filename); + free(line_copy); + return (-1); + } + *p_delim = NULL; /* NULL terminate path */ + + if (strlcpy(path, p_path, MAXPATHLEN) >= MAXPATHLEN) { + log_error("2318", + "Path too long (%s) in file %s.", p_path, filename); + free(line_copy); + return (-1); + } + + p_wwn = p_delim + strlen(WWN_DELIM); + /* + * Now look for the blank delimiter before the controller + * + * This is just the case when there may be a controller # + * after the attachment point and WWN. For example - + * /devices/pci@b,2000/pci@2/SUNW,qlc@4/fp@0,0:fc::220000203707f4f1 c4 + */ + p_delim = strchr(p_wwn, ' '); + if (p_delim != NULL) { + /* now p_delim points to blank */ + *p_delim = NULL; /* terminate wwn at delim */ + } else { + char *p_last_char; + p_last_char = p_wwn+strlen(p_wwn)-1; + if (*p_last_char == '\n') { + *p_last_char = NULL; + } + } + strcpy(wwn, p_wwn); + free(line_copy); + return (0); +} + +static char +ctoi(char c) +{ + if ((c >= '0') && (c <= '9')) + c -= '0'; + else if ((c >= 'A') && (c <= 'F')) + c = c - 'A' + 10; + else if ((c >= 'a') && (c <= 'f')) + c = c - 'a' + 10; + else + c = -1; + return (c); +} + +/* + * "string" is Input and "port_wwn" has the output + * + * This function converts a string to WWN. + * For example a string like + * "220000203707F4F1" gets converted to 0x220000203707F4F1 ... + * where + * port_wwn[0] = 0x22, + * port_wwn[1] = 0x00, + * port_wwn[2] = 0x00, + * port_wwn[3] = 0x20, + * port_wwn[4] = 0x37, + * port_wwn[5] = 0x07, + * port_wwn[6] = 0xF4, and + * port_wwn[7] = 0xF1 + */ +static int +string_to_wwn(const uchar_t *string, uchar_t *port_wwn) +{ + int i; + char c, c1; + uchar_t *wwnp; + + wwnp = port_wwn; + for (i = 0; i < WWN_SIZE; i++, wwnp++) { + + c = ctoi(*string++); + c1 = ctoi(*string++); + if (c == -1 || c1 == -1) + return (-1); + *wwnp = ((c << 4) + c1); + } + + return (0); +} + +static int +create_ap_instance(char *ap_id, char *wwn_string, + char *filename, char *line) +{ + devctl_hdl_t bus_handle, dev_handle; + devctl_ddef_t ddef_handle; + int ret; + uchar_t wwn_array[FC_WWN_SIZE]; + + ddef_handle = devctl_ddef_alloc("dummy", 0); + if (ddef_handle == NULL) { + log_error("2314", + "Internal error to process line (%s) " + "in file: %s. %s", + line, filename, strerror(errno)); + return (-1); + } + /* + * g_string_to_wwn() has not been used here because it + * prepends 2 NULLs. + */ + if (string_to_wwn((uchar_t *)wwn_string, wwn_array) != 0) { + log_error("2314", + "Internal error to process line (%s) " + "in file: %s. %s", + line, filename, strerror(errno)); + devctl_ddef_free(ddef_handle); + return (-1); + } + (void) devctl_ddef_byte_array(ddef_handle, + "port-wwn", FC_WWN_SIZE, wwn_array); + + if ((bus_handle = devctl_bus_acquire(ap_id, 0)) == NULL) { + devctl_ddef_free(ddef_handle); + log_error("2314", + "Internal error to process line (%s) " + "in file: %s. %s", + line, filename, strerror(errno)); + return (-1); + } + if (ret = + devctl_bus_dev_create(bus_handle, ddef_handle, 0, &dev_handle)) { + devctl_ddef_free(ddef_handle); + devctl_release(bus_handle); + log_error("2316", + "configuration failed for line (%s) " + "in file: %s. %s", + line, filename, strerror(errno)); + return (-1); + } + devctl_release(dev_handle); + devctl_ddef_free(ddef_handle); + devctl_release(bus_handle); + return (ret); +} diff --git a/usr/src/cmd/luxadm/fcalupdate.c b/usr/src/cmd/luxadm/fcalupdate.c new file mode 100644 index 0000000000..c9a8e158fa --- /dev/null +++ b/usr/src/cmd/luxadm/fcalupdate.c @@ -0,0 +1,948 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +/* + * I18N message number ranges + * This file: 4500 - 4999 + * Shared common messages: 1 - 1999 + */ + +#include <fcntl.h> +#include <limits.h> +#include <setjmp.h> +#include <signal.h> +#include <siginfo.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <unistd.h> +#include <ctype.h> +#include <dirent.h> +#include <sys/exec.h> +#include <sys/exechdr.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/fibre-channel/fcio.h> +#include <sys/socalreg.h> +/* + * The following define is not to + * include sys/fc4/fcal_linkapp.h + * file from sys/socalio.h, since it + * has same structure defines as in + * sys/fibre-channel/fcio.h. + */ +#define _SYS_FC4_FCAL_LINKAPP_H +#include <sys/socalio.h> +#include <sys/time.h> +#include <nl_types.h> +#include <errno.h> +#include <stgcom.h> +#include <gfc.h> +#include <l_common.h> +#include "luxadm.h" + +/* Defines */ +#define FEPROM_SIZE 256*1024 +#define FEPROM_MAX_PROGRAM 25 +#define FEPROM_MAX_ERASE 1000 +#define FEPROM_READ_MEMORY 0x00 +#define FEPROM_ERASE 0x20 +#define FEPROM_ERASE_VERIFY 0xa0 +#define FEPROM_PROGRAM 0x40 +#define FEPROM_PROGRAM_VERIFY 0xc0 +#define FEPROM_RESET 0xff +#define HBA_MAX 128 + +#define FOUND 0 +#define NOT_FOUND 1 +#define PROM_SIZ 0x20010 + +#define MAX_RETRIES 3 +#define MAX_WAIT_TIME 30 + +/* + * The next define is to work around a problem with sbusmem driver not + * able to round up mmap() requests that are not around page boundaries. + */ +#define PROM_SIZ_ROUNDED 0x22000 +#define SAMPLE_SIZ 0x100 + +#define REG_OFFSET 0x20000 + +#define FEPROM_WWN_OFFSET 0x3fe00 + +#define FEPROM_SUN_WWN 0x50200200 + +/* + * We'll leave this on leadville, as the onboard + * isn't allowed to be zapped by luxadm + */ + +#define ONBOARD_SOCAL "SUNW,socal@d" + +#define SOCAL_STR "SUNW,socal" +#define SOCAL_STR_LEN 10 + + +static uchar_t buffer[FEPROM_SIZE]; + +static char sbus_list[HBA_MAX][PATH_MAX]; +static char sbussoc_list[HBA_MAX][PATH_MAX]; +static char bootpath[PATH_MAX]; +static char version[MAXNAMELEN]; + +static uint_t getsbuslist(void); +static int load_file(char *, caddr_t, volatile socal_reg_t *); +static void usec_delay(int); +static void getbootdev(unsigned int); +static void getsocpath(char *, int *); +static int loadsocpath(const char *, int *); +static int warn(void); +static int findversion(int, uchar_t *); +static int write_feprom(uchar_t *, uchar_t *, volatile socal_reg_t *); +static int feprom_erase(volatile uchar_t *, volatile socal_reg_t *); + +static struct exec exec; + +int +fcal_update(unsigned int verbose, char *file) +/*ARGSUSED*/ +{ +int fd, strfound = 0, retval = 0; +int fbuf_idx, fd1, bytes_read; +caddr_t addr; +uint_t i; +uint_t fflag = 0; +uint_t vflag = 0; +uint_t numslots; +volatile socal_reg_t *regs; +char *slotname, socal[MAXNAMELEN]; +char fbuf[BUFSIZ]; + + if (!file) + vflag++; + else { + fflag++; + if ((fd1 = open(file, O_RDONLY)) == -1) { + (void) fprintf(stderr, + MSGSTR(4500, + "Error: open() failed on file " + "%s\n"), file); + return (1); + } + /* + * We will just make a check to see if it the file + * has the "SUNW,socal" strings in it + * We cannot use strstr() here because we are operating on + * binary data and so is very likely to have embedded NULLs + */ + while (!strfound && ((bytes_read = read(fd1, + fbuf, BUFSIZ)) > 0)) { + for (fbuf_idx = 0; fbuf_idx < bytes_read; + fbuf_idx++) { + /* First check for the SUNW,socal string */ + if (strncmp((fbuf + fbuf_idx), SOCAL_STR, + SOCAL_STR_LEN) == 0) { + strfound = 1; + break; + } + + } + } + (void) close(fd1); + + if (!strfound) { + (void) fprintf(stderr, + MSGSTR(4501, + "Error: %s is not a " + "FC100/S Fcode file\n"), file); + return (1); + } + } + + /* + * Get count of, and names of SBus slots using the SBus memory + * interface. + */ + (void) getbootdev(verbose); + if (getenv("_LUX_D_DEBUG") != NULL) { + (void) fprintf(stdout, " Bootpath: %s\n", bootpath); + } + + numslots = getsbuslist(); + (void) fprintf(stdout, + MSGSTR(4503, "\n Found Path to %d FC100/S Cards\n"), numslots); + + for (i = 0; i < numslots; i++) { + + /* + * Open SBus memory for this slot. + */ + slotname = &sbus_list[i][0]; + if (fflag && (strcmp(slotname, bootpath) == 0)) { + (void) fprintf(stderr, + MSGSTR(4504, " Ignoring %s (bootpath)\n"), slotname); + continue; + } + + (void) sprintf(socal, "%s:0", &sbussoc_list[i][0]); + + if ((fd = open(socal, O_RDWR)) < 0) { + (void) sprintf(socal, "%s:1", &sbussoc_list[i][0]); + if ((fd = open(socal, O_RDWR)) < 0) { + (void) fprintf(stderr, + MSGSTR(4505, "Could not open %s\n"), + &sbussoc_list[i][0]); + (void) fprintf(stderr, + MSGSTR(4506, "Ignoring %s\n"), + &sbussoc_list[i][0]); + retval++; + continue; + } + } + + (void) close(fd); + + if (verbose) { + (void) fprintf(stdout, "\n "); + (void) fprintf(stdout, + MSGSTR(85, "Opening %s\n"), slotname); + } + + fd = open(slotname, O_RDWR); + + if (fd < 0) { + perror(MSGSTR(4507, "open of slotname")); + retval++; + continue; + } + + /* + * Mmap that SBus memory into my memory space. + */ + addr = mmap((caddr_t)0, PROM_SIZ_ROUNDED, PROT_READ|PROT_WRITE, + MAP_SHARED, fd, 0); + + if (addr == MAP_FAILED) { + perror(MSGSTR(46, "mmap")); + (void) close(fd); + retval++; + continue; + } + + if ((int)addr == -1) { + perror(MSGSTR(46, "mmap")); + (void) close(fd); + retval++; + continue; + } + + regs = (socal_reg_t *)((int)addr + REG_OFFSET); + + (void) fprintf(stdout, + MSGSTR(4508, "\n Device: %s\n"), + &sbussoc_list[i][0]); + /* + * Load the New FCode + */ + if (fflag) { + if (!warn()) + retval += load_file(file, addr, regs); + } else if (vflag) { + if (findversion(i, (uchar_t *)&version[0]) == FOUND) { + (void) fprintf(stdout, + MSGSTR(4509, + " Detected FC100/S Version: %s\n"), version); + } + } + + if (munmap(addr, PROM_SIZ) == -1) { + perror(MSGSTR(4510, "munmap")); + retval++; + } + + (void) close(fd); + + } + (void) fprintf(stdout, " "); + (void) fprintf(stdout, MSGSTR(125, "Complete\n")); + return (retval); +} + +static int +findversion(int index, uchar_t *version) +/*ARGSUSED*/ +{ +int fd, ntries; +struct socal_fm_version *buffer; +char socal[MAXNAMELEN]; +char fp[MAXNAMELEN]; +char prom_ver[100]; +char mcode_ver[100]; +uint_t dev_type; +fcio_t fcio; +char fw_rev[FC_FW_REV_SIZE + 1]; + + + if ((dev_type = g_get_path_type(&sbussoc_list[index][0])) == 0) { + return (L_INVALID_PATH); + } + + + if (dev_type & FC4_FCA_MASK) { + P_DPRINTF("findversion: found an FC4 path\n"); + (void) sprintf(socal, "%s:0", &sbussoc_list[index][0]); + if ((fd = open(socal, O_RDWR)) < 0) { + (void) sprintf(socal, "%s:1", &sbussoc_list[index][0]); + if ((fd = open(socal, O_RDWR)) < 0) { + (void) fprintf(stderr, + MSGSTR(4511, "Could not open %s\n"), + &sbussoc_list[index][0]); + (void) close(fd); + return (NOT_FOUND); + } + } + if ((buffer = (struct socal_fm_version *)malloc( + sizeof (struct socal_fm_version))) == NULL) { + (void) fprintf(stderr, MSGSTR(10, + " Error: Unable to allocate memory.")); + (void) fprintf(stderr, "\n"); + (void) close(fd); + return (NOT_FOUND); + } + + buffer->fcode_ver = (char *)version; + buffer->mcode_ver = mcode_ver; + buffer->prom_ver = prom_ver; + buffer->fcode_ver_len = MAXNAMELEN - 1; + buffer->mcode_ver_len = 100; + buffer->prom_ver_len = 100; + + if (ioctl(fd, FCIO_FCODE_MCODE_VERSION, buffer) < 0) { + (void) fprintf(stderr, MSGSTR(4512, + "fcal_s_download: could not get" + " fcode version.\n")); + (void) close(fd); + (void) free(buffer); + return (NOT_FOUND); + } + version[buffer->fcode_ver_len] = '\0'; + free(buffer); + + } else if (dev_type & FC_FCA_MASK) { + /* + * Get the fcode and prom's fw version + * using new ioctls. Currently, we pass + * only the fcode version to the calling function + * and ignore the FW version (using the existing + * implementation). The function definition + * might be changed in future to pass both the + * fcode and FW revisions to the calling function, if + * needed by the calling function. + */ + P_DPRINTF("findversion: found an FC path\n"); + (void) sprintf(fp, "%s/fp@0,0:devctl", + &sbussoc_list[index][0]); + if ((fd = open(fp, O_RDWR)) < 0) { + (void) sprintf(fp, "%s/fp@1,0:devctl", + &sbussoc_list[index][0]); + if ((fd = open(fp, O_RDWR)) < 0) { + (void) fprintf(stderr, + MSGSTR(4511, "Could not open %s\n"), + &sbussoc_list[index][0]); + (void) close(fd); + return (NOT_FOUND); + } + } + /* Get the fcode version */ + bzero(version, sizeof (version)); + fcio.fcio_cmd = FCIO_GET_FCODE_REV; + /* Information read operation */ + fcio.fcio_xfer = FCIO_XFER_READ; + fcio.fcio_obuf = (caddr_t)version; + fcio.fcio_olen = MAXNAMELEN; + + for (ntries = 0; ntries < MAX_RETRIES; ntries++) { + if (ioctl(fd, FCIO_CMD, &fcio) != 0) { + if ((errno == EAGAIN) && + (ntries+1 < MAX_RETRIES)) { + /* wait 30 secs */ + (void) sleep(MAX_WAIT_TIME); + continue; + } + I_DPRINTF("ioctl FCIO_GET_FCODE_REV failed.\n" + "Error: %s\n", strerror(errno)); + (void) close(fd); + return (L_FCIO_GET_FCODE_REV_FAIL); + } + break; + } + version[MAXNAMELEN-1] = '\0'; + + /* Get the FW revision */ + bzero(fw_rev, sizeof (fw_rev)); + fcio.fcio_cmd = FCIO_GET_FW_REV; + /* Information read operation */ + fcio.fcio_xfer = FCIO_XFER_READ; + fcio.fcio_obuf = (caddr_t)fw_rev; + fcio.fcio_olen = FC_FW_REV_SIZE; + for (ntries = 0; ntries < MAX_RETRIES; ntries++) { + if (ioctl(fd, FCIO_CMD, &fcio) != 0) { + if ((errno == EAGAIN) && + (ntries+1 < MAX_RETRIES)) { + /* wait 30 secs */ + (void) sleep(MAX_WAIT_TIME); + continue; + } + I_DPRINTF(" FCIO_GET_FW_REV ioctl failed.\n" + " Error: %s\n", strerror(errno)); + (void) close(fd); + return (L_FCIO_GET_FW_REV_FAIL); + } + break; + } + } + + (void) close(fd); + return (FOUND); +} +/* + * program an FEprom with data from 'source_address'. + * program the FEprom with zeroes, + * erase it, + * program it with the real data. + */ +static int +feprom_program(uchar_t *source_address, uchar_t *dest_address, + volatile socal_reg_t *regs) +{ + int i; + + (void) fprintf(stdout, MSGSTR(4513, "Filling with zeroes...\n")); + if (!write_feprom((uchar_t *)0, dest_address, regs)) { + (void) fprintf(stderr, + MSGSTR(4514, "FEprom at 0x%x: zero fill failed\n"), + (int)dest_address); + return (0); + } + + (void) fprintf(stdout, MSGSTR(4515, "Erasing...\n")); + for (i = 0; i < FEPROM_MAX_ERASE; i++) { + if (feprom_erase(dest_address, regs)) + break; + } + + if (i >= FEPROM_MAX_ERASE) { + (void) fprintf(stderr, + MSGSTR(4516, "FEprom at 0x%x: failed to erase\n"), + (int)dest_address); + return (0); + } else if (i > 0) { + if (i == 1) { + (void) fprintf(stderr, MSGSTR(4517, + "FEprom erased after %d attempt\n"), i); + } else { + (void) fprintf(stderr, MSGSTR(4518, + "FEprom erased after %d attempts\n"), i); + } + } + + (void) fprintf(stdout, MSGSTR(4519, "Programming...\n")); + if (!(write_feprom(source_address, dest_address, regs))) { + (void) fprintf(stderr, + MSGSTR(4520, "FEprom at 0x%x: write failed\n"), + (int)dest_address); + return (0); + } + + /* select the zeroth bank at end so we can read it */ + regs->socal_cr.w &= ~(0x30000); + (void) fprintf(stdout, MSGSTR(4521, "Programming done\n")); + return (1); +} + +/* + * program an FEprom one byte at a time using hot electron injection. + */ +static int +write_feprom(uchar_t *source_address, uchar_t *dest_address, + volatile socal_reg_t *regs) +{ + int pulse, i; + uchar_t *s = source_address; + volatile uchar_t *d; + + for (i = 0; i < FEPROM_SIZE; i++, s++) { + + if ((i & 0xffff) == 0) { + (void) fprintf(stdout, + MSGSTR(4522, "selecting bank %d\n"), i>>16); + regs->socal_cr.w &= ~(0x30000); + regs->socal_cr.w |= i & 0x30000; + } + + d = dest_address + (i & 0xffff); + + for (pulse = 0; pulse < FEPROM_MAX_PROGRAM; pulse++) { + *d = FEPROM_PROGRAM; + *d = source_address ? *s : 0; + usec_delay(50); + *d = FEPROM_PROGRAM_VERIFY; + usec_delay(30); + if (*d == (source_address ? *s : 0)) + break; + } + + if (pulse >= FEPROM_MAX_PROGRAM) { + *dest_address = FEPROM_RESET; + return (0); + } + } + + *dest_address = FEPROM_RESET; + return (1); +} + +/* + * erase an FEprom using Fowler-Nordheim tunneling. + */ +static int +feprom_erase(volatile uchar_t *dest_address, volatile socal_reg_t *regs) +{ + int i; + volatile uchar_t *d = dest_address; + + *d = FEPROM_ERASE; + usec_delay(50); + *d = FEPROM_ERASE; + + usec_delay(10000); /* wait 10ms while FEprom erases */ + + for (i = 0; i < FEPROM_SIZE; i++) { + + if ((i & 0xffff) == 0) { + regs->socal_cr.w &= ~(0x30000); + regs->socal_cr.w |= i & 0x30000; + } + + d = dest_address + (i & 0xffff); + + *d = FEPROM_ERASE_VERIFY; + usec_delay(50); + if (*d != 0xff) { + *dest_address = FEPROM_RESET; + return (0); + } + } + *dest_address = FEPROM_RESET; + return (1); +} + +static void +usec_delay(int s) +{ + hrtime_t now, then; + + now = gethrtime(); + then = now + s*1000; + do { + now = gethrtime(); + } while (now < then); +} + +static uint_t +getsbuslist(void) +{ + int devcnt = 0; + char devpath[PATH_MAX]; + + /* We're searching the /devices directory, so... */ + (void) strcpy(devpath, "/devices"); + + /* get the directory entries under /devices for socal sbusmem */ + (void) getsocpath(devpath, (int *)&devcnt); + + return (devcnt); +} + +static void +getbootdev(unsigned int verbose) +{ + char *df = "df /"; + FILE *ptr; + char *p, *p1; + char bootdev[PATH_MAX]; + char buf[BUFSIZ]; + int foundroot = 0; + + + if ((ptr = popen(df, "r")) != NULL) { + while (fgets(buf, BUFSIZ, ptr) != NULL) { + if (p = strstr(buf, "/dev/dsk/")) { + (void) memset((char *)&bootdev[0], 0, + PATH_MAX); + p1 = p; + while (*p1 != '\0') { + if (!isalnum(*p1) && (*p1 != '/')) + *p1 = ' '; + p1++; + } + (void) sscanf(p, "%s", bootdev); + foundroot = 1; + } + } + if (!foundroot) { + if (verbose) + (void) fprintf(stderr, MSGSTR(44, + "root is not on a local disk!\n")); + (void) memset((char *)&bootpath[0], 0, PATH_MAX); + return; + } + (void) pclose(ptr); + if (bootdev[0]) { + char *ls; + char *p1; + char *p2 = NULL; + char *sbusmem = "/sbusmem@"; + char *slot = ",0:slot"; + + ls = (char *)malloc(PATH_MAX); + (void) memset((char *)ls, NULL, PATH_MAX); + (void) strcpy(ls, "ls -l "); + (void) strcat(ls, bootdev); + if ((ptr = popen(ls, "r")) != NULL) { + while (fgets(buf, BUFSIZ, ptr) != NULL) { + if (p = strstr(buf, "/devices")) { + if (p1 = strstr(buf, "sbus")) { + while (*p1 != '/') + p1++; + p2 = strstr(p1, "@"); + ++p2; + *p1 = '\0'; + } else { + if (p1 = strstr(buf, + SOCAL_STR)) { + p2 = strstr(p1, "@"); + ++p2; + --p1; + *p1 = '\0'; + } + } + } + } + (void) pclose(ptr); + } + (void) memset((char *)&bootdev[0], 0, PATH_MAX); + (void) sscanf(p, "%s", bootdev); + (void) memset((char *)&bootpath[0], 0, PATH_MAX); + (void) strcat(bootpath, bootdev); + (void) strcat(bootpath, sbusmem); + if (p2) { + (void) strncat(bootpath, p2, 1); + (void) strcat(bootpath, slot); + (void) strncat(bootpath, p2, 1); + } + } + } +} + +/* + * This function reads "size" bytes from the FC100/S PROM. + * source_address: PROM address + * dest_address: local memeory + * offset: Location in PROM to start reading from. + */ +static void +feprom_read(uchar_t *source_address, uchar_t *dest_address, + int offset, int size, volatile socal_reg_t *regs) +{ +uchar_t *s = source_address; +uchar_t *d = dest_address; +int i = offset; + + if (getenv("_LUX_D_DEBUG") != NULL) { + (void) fprintf(stdout, + " feprom_read: selecting bank %d\n", + (i&0xf0000)>>16); + if (size <= 8) { + (void) fprintf(stdout, " Data read: "); + } + } + regs->socal_cr.w = i & 0xf0000; + s = source_address + (i & 0xffff); + *s = FEPROM_READ_MEMORY; + usec_delay(6); + for (; s < source_address + (i & 0xffff) + size; d++, s++) { + *d = *s; + if ((getenv("_LUX_D_DEBUG") != NULL) && + (size <= 8)) { + (void) fprintf(stdout, "0x%x ", *d); + } + } + if ((getenv("_LUX_D_DEBUG") != NULL) && + (size <= 8)) { + (void) fprintf(stdout, "\n From offset: 0x%x\n", + offset); + } +} + + +static int +load_file(char *file, caddr_t prom, volatile socal_reg_t *regs) +{ +uint_t wwn_d8, wwn_lo; +uint_t wwn_hi; +int ffd = open(file, 0); + + wwn_hi = FEPROM_SUN_WWN; + + if (ffd < 0) { + perror(MSGSTR(4524, "open of file")); + exit(1); + } + (void) fprintf(stdout, MSGSTR(4525, "Loading FCode: %s\n"), file); + + if (read(ffd, &exec, sizeof (exec)) != sizeof (exec)) { + perror(MSGSTR(4526, "read exec")); + exit(1); + } + + if (exec.a_trsize || exec.a_drsize) { + (void) fprintf(stderr, + MSGSTR(4527, "%s: is relocatable\n"), file); + exit(1); + } + + if (exec.a_data || exec.a_bss) { + (void) fprintf(stderr, + MSGSTR(4528, "%s: has data or bss\n"), file); + exit(1); + } + + if (exec.a_machtype != M_SPARC) { + (void) fprintf(stderr, + MSGSTR(4529, "%s: not for SPARC\n"), file); + exit(1); + } + + (void) fprintf(stdout, MSGSTR(4530, + "Loading 0x%x bytes from %s at offset 0x%x\n"), + (int)exec.a_text, file, 0); + + if (read(ffd, &buffer, exec.a_text) != exec.a_text) { + perror(MSGSTR(4531, "read")); + exit(1); + } + + (void) close(ffd); + + feprom_read((uchar_t *)prom, (uchar_t *)&wwn_d8, + FEPROM_WWN_OFFSET, 4, regs); + feprom_read((uchar_t *)prom, (uchar_t *)&wwn_lo, + FEPROM_WWN_OFFSET + 4, 4, regs); + wwn_hi |= wwn_d8 & 0x0f; /* only last digit is interesting */ + if (getenv("_LUX_D_DEBUG") != NULL) { + (void) fprintf(stdout, + " load_file: Writing WWN hi:0x%x lo:0x%x " + "to the FC100/S PROM\n", wwn_hi, wwn_lo); + } + /* put wwn into buffer location */ + bcopy((const void *)&wwn_hi, + (void *)&buffer[FEPROM_WWN_OFFSET], + sizeof (wwn_hi)); + bcopy((const void *)&wwn_lo, + (void *)&buffer[FEPROM_WWN_OFFSET + 4], + sizeof (wwn_lo)); + bcopy((const void *)&wwn_hi, + (void *)&buffer[FEPROM_WWN_OFFSET + 8], + sizeof (wwn_hi)); + bcopy((const void *)&wwn_lo, + (void *)&buffer[FEPROM_WWN_OFFSET + 0xc], + sizeof (wwn_lo)); + + if (feprom_program((uchar_t *)buffer, (uchar_t *)prom, regs) == 0) { + /* here 0 means failure */ + return (1); + } + + return (0); +} + +static int +warn(void) +{ + char input[1024]; + + input[0] = '\0'; + + (void) fprintf(stderr, MSGSTR(4532, +"\nWARNING!! This program will update the FCode in this FC100/S Sbus Card.\n")); + (void) fprintf(stderr, MSGSTR(4533, +"This may take a few (5) minutes. Please be patient.\n")); + +loop1: + (void) fprintf(stderr, MSGSTR(4534, + "Do you wish to continue ? (y/n) ")); + + (void) gets(input); + + if ((strcmp(input, MSGSTR(4535, "y")) == 0) || + (strcmp(input, MSGSTR(40, "yes")) == 0)) { + return (FOUND); + } else if ((strcmp(input, MSGSTR(4536, "n")) == 0) || + (strcmp(input, MSGSTR(45, "no")) == 0)) { + (void) fprintf(stderr, MSGSTR(4537, "Not Downloading FCode\n")); + return (NOT_FOUND); + } else { + (void) fprintf(stderr, MSGSTR(4538, "Invalid input\n")); + goto loop1; + } +} + + +/* + * getsocpath(): + * Searches the /devices directory recursively returning all soc_name + * entries in sbussoc_list (global). This excludes port entries and + * onboard socal (which leaves only directory entries with + * soc_name included). devcnt is updated to reflect number of soc_name + * devices found. + */ + +static void +getsocpath(char *devpath, int *devcnt) +{ + struct stat statbuf; + struct dirent *dirp; + DIR *dp; + char *ptr; + + if (lstat(devpath, &statbuf) < 0) { + (void) fprintf(stderr, + MSGSTR(4539, "Error: %s lstat() error\n"), devpath); + exit(1); + } + + if (S_ISDIR(statbuf.st_mode) == 0) + /* + * not a directory so + * we don't care about it - return + */ + return; + + else { + if (strstr(devpath, ONBOARD_SOCAL)) + return; + + if (strstr(devpath, SOCAL_STR)) { + /* It's a keeper - call the load function */ + if ((loadsocpath(devpath, devcnt)) < 0) { + (void) fprintf(stderr, + MSGSTR(4540, "Error: Cannot set device list\n"), + devpath); + exit(1); + } + /* + * if socal directory - return, + * nothing else to see here + */ + return; + } + } + + /* + * It's a directory. Call ourself to + * traverse the path(s) + */ + + ptr = devpath + strlen(devpath); + *ptr++ = '/'; + *ptr = 0; + + /* Forget the /devices/pseudo/ directory */ + if (strcmp(devpath, "/devices/pseudo/") == 0) + return; + + if ((dp = opendir(devpath)) == NULL) { + (void) fprintf(stderr, + MSGSTR(4541, "Error: %s Can't read directory\n"), devpath); + exit(1); + } + + while ((dirp = readdir(dp)) != NULL) { + + if (strcmp(dirp->d_name, ".") == 0 || + strcmp(dirp->d_name, "..") == 0) + continue; + + (void) strcpy(ptr, dirp->d_name); /* append name */ + getsocpath(devpath, devcnt); + } + + if (closedir(dp) < 0) { + (void) fprintf(stderr, + MSGSTR(4542, "Error: %s Can't close directory\n"), devpath); + exit(1); + } +} + +static int +loadsocpath(const char *pathname, int *devcnt) +{ + int ret = 0; + int len; + int len_tmp; + char *sp; + char *sp_tmp; + char buffer[PATH_MAX]; + + + /* + * Okay we found a device, now let's load it in to sbussoc_list + * and load the sbusmem file into sbus_list + */ + + if (pathname != NULL && *devcnt < HBA_MAX) { + (void) strcpy(sbussoc_list[*devcnt], pathname); + if (sp_tmp = strstr(sbussoc_list[*devcnt], SOCAL_STR)) { + sp = sp_tmp; + /* len_tmp will be len of "SUNW,socal@" */ + len_tmp = SOCAL_STR_LEN + 1; + } + len = strlen(sbussoc_list[*devcnt]) - strlen(sp); + (void) strncpy(buffer, sbussoc_list[*devcnt], len); + buffer[len] = '\0'; + sp += len_tmp; + (void) sprintf(sbus_list[*devcnt], "%ssbusmem@%c,0:slot%c", + buffer, sp[0], sp[0]); + *devcnt += 1; + } + else + ret = -1; + return (ret); +} diff --git a/usr/src/cmd/luxadm/fchba.c b/usr/src/cmd/luxadm/fchba.c new file mode 100644 index 0000000000..b353532d10 --- /dev/null +++ b/usr/src/cmd/luxadm/fchba.c @@ -0,0 +1,2151 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + + +#include <hbaapi.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/param.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <string.h> +#include <strings.h> +#include <ctype.h> +#include <sys/scsi/generic/sense.h> +#include <sys/scsi/generic/mode.h> +#include <sys/scsi/generic/inquiry.h> +#include <errno.h> +#include <libdevice.h> +#include <config_admin.h> +#include <sys/byteorder.h> +#include <sys/fibre-channel/fcio.h> +#include "common.h" +#include "sun_fc_version.h" + +#define DEFAULT_LUN_COUNT 1024 +#define LUN_SIZE 8 +#define LUN_HEADER_SIZE 8 +#define DEFAULT_LUN_LENGTH DEFAULT_LUN_COUNT * \ + LUN_SIZE + \ + LUN_HEADER_SIZE +struct lun_val { + uchar_t val[8]; +}; +struct rep_luns_rsp { + uint32_t length; + uint32_t rsrvd; + struct lun_val lun[1]; +}; + +/* Extracted from the old scsi.h file */ +struct capacity_data_struct { + uint_t last_block_addr; + uint_t block_size; +}; + + +/* Structure to handle the inq. page 0x80 serial number */ +struct page80 { + uchar_t inq_dtype; + uchar_t inq_page_code; + uchar_t reserved; + uchar_t inq_page_len; + uchar_t inq_serial[251]; +}; + +extern char *dtype[]; +extern int Options; +extern const int OPTION_P; + +int skip_hba(int i); +int find_supported_inq_page(HBA_HANDLE handle, HBA_WWN hwwn, HBA_WWN pwwn, + uint64_t lun, int page_num); +/* + * The routines within this file operate against the T11 + * HBA API interface. In some cases, proprietary Sun driver + * interface are also called to add additional information + * above what the standard library supports. + */ + +uint64_t +wwnConversion(uchar_t *wwn) { + uint64_t tmp; + (void) memcpy(&tmp, wwn, sizeof (uint64_t)); + return (ntohll(tmp)); +} + +void printStatus(HBA_STATUS status) { + switch (status) { + case HBA_STATUS_OK: + printf(MSGSTR(2410, "OK")); + return; + case HBA_STATUS_ERROR: + printf(MSGSTR(2411, "ERROR")); + return; + case HBA_STATUS_ERROR_NOT_SUPPORTED: + printf(MSGSTR(2412, "NOT SUPPORTED")); + return; + case HBA_STATUS_ERROR_INVALID_HANDLE: + printf(MSGSTR(2413, "INVALID HANDLE")); + return; + case HBA_STATUS_ERROR_ARG: + printf(MSGSTR(2414, "ERROR ARG")); + return; + case HBA_STATUS_ERROR_ILLEGAL_WWN: + printf(MSGSTR(2415, "ILLEGAL WWN")); + return; + case HBA_STATUS_ERROR_ILLEGAL_INDEX: + printf(MSGSTR(2416, "ILLEGAL INDEX")); + return; + case HBA_STATUS_ERROR_MORE_DATA: + printf(MSGSTR(2417, "MORE DATA")); + return; + case HBA_STATUS_ERROR_STALE_DATA: + printf(MSGSTR(2418, "STALE DATA")); + return; + case HBA_STATUS_SCSI_CHECK_CONDITION: + printf(MSGSTR(2419, "SCSI CHECK CONDITION")); + return; + case HBA_STATUS_ERROR_BUSY: + printf(MSGSTR(2420, "BUSY")); + return; + case HBA_STATUS_ERROR_TRY_AGAIN: + printf(MSGSTR(2421, "TRY AGAIN")); + return; + case HBA_STATUS_ERROR_UNAVAILABLE: + printf(MSGSTR(2422, "UNAVAILABLE")); + return; + default: + printf(MSGSTR(2423, "UNKNOWN ERROR TYPE %d"), status); + return; + } +} + +uint32_t +getNumberOfAdapters() { + uint32_t count = HBA_GetNumberOfAdapters(); + if (count == 0) { + fprintf(stderr, MSGSTR(2405, + "\nERROR: No Fibre Channel Adapters found.\n")); + } + return (count); +} + +#define MAX_RETRIES 10 + +/* + * Returns non-zero on failure (aka, HBA_STATUS_ERROR_* + * Will handle retries if applicable. + */ +int +getAdapterAttrs(HBA_HANDLE handle, char *name, HBA_ADAPTERATTRIBUTES *attrs) { + int count = 0; + HBA_STATUS status = HBA_STATUS_ERROR_TRY_AGAIN; /* force first pass */ + + /* Loop as long as we have a retryable error */ + while ((status == HBA_STATUS_ERROR_TRY_AGAIN || + status == HBA_STATUS_ERROR_BUSY) && count++ < MAX_RETRIES) { + status = HBA_GetAdapterAttributes(handle, attrs); + if (status == HBA_STATUS_OK) { + break; + } + (void) sleep(1); + } + if (status != HBA_STATUS_OK) { + /* We encountered a non-retryable error */ + fprintf(stderr, MSGSTR(2501, + "\nERROR: Unable to retrieve adapter port details (%s)"), + name); + printStatus(status); + fprintf(stderr, "\n"); + } + return (status); +} + +/* + * Returns non-zero on failure (aka, HBA_STATUS_ERROR_* + * Will handle retries if applicable. + */ +int +getAdapterPortAttrs(HBA_HANDLE handle, char *name, int portIndex, + HBA_PORTATTRIBUTES *attrs) { + int count = 0; + HBA_STATUS status = HBA_STATUS_ERROR_TRY_AGAIN; /* force first pass */ + + /* Loop as long as we have a retryable error */ + while ((status == HBA_STATUS_ERROR_TRY_AGAIN || + status == HBA_STATUS_ERROR_BUSY) && count++ < MAX_RETRIES) { + status = HBA_GetAdapterPortAttributes(handle, portIndex, attrs); + if (status == HBA_STATUS_OK) { + break; + } + + /* The odds of this occuring are very slim, but possible. */ + if (status == HBA_STATUS_ERROR_STALE_DATA) { + /* + * If we hit a stale data scenario, + * we'll just tell the user to try again. + */ + status = HBA_STATUS_ERROR_TRY_AGAIN; + break; + } + sleep(1); + } + if (status != HBA_STATUS_OK) { + /* We encountered a non-retryable error */ + fprintf(stderr, MSGSTR(2501, + "\nERROR: Unable to retrieve adapter port details (%s)"), + name); + printStatus(status); + fprintf(stderr, "\n"); + } + return (status); +} + +/* + * Returns non-zero on failure (aka, HBA_STATUS_ERROR_* + * Will handle retries if applicable. + */ +int +getDiscPortAttrs(HBA_HANDLE handle, char *name, int portIndex, int discIndex, + HBA_PORTATTRIBUTES *attrs) { + int count = 0; + HBA_STATUS status = HBA_STATUS_ERROR_TRY_AGAIN; /* force first pass */ + + /* Loop as long as we have a retryable error */ + while ((status == HBA_STATUS_ERROR_TRY_AGAIN || + status == HBA_STATUS_ERROR_BUSY) && count++ < MAX_RETRIES) { + status = HBA_GetDiscoveredPortAttributes(handle, portIndex, + discIndex, attrs); + if (status == HBA_STATUS_OK) { + break; + } + + /* The odds of this occuring are very slim, but possible. */ + if (status == HBA_STATUS_ERROR_STALE_DATA) { + /* + * If we hit a stale data scenario, we'll just tell the + * user to try again. + */ + status = HBA_STATUS_ERROR_TRY_AGAIN; + break; + } + sleep(1); + } + if (status != HBA_STATUS_OK) { + /* We encountered a non-retryable error */ + fprintf(stderr, MSGSTR(2504, + "\nERROR: Unable to retrieve target port details (%s)"), + name); + printStatus(status); + fprintf(stderr, "\n"); + } + return (status); +} + + +/*ARGSUSED*/ +int +fchba_display_port(int verbose) +{ + int retval = 0; + HBA_HANDLE handle; + HBA_ADAPTERATTRIBUTES hbaAttrs; + HBA_PORTATTRIBUTES portAttrs; + HBA_STATUS status; + int count, adapterIndex, portIndex; + char name[256]; + char *physical = NULL; + char path[MAXPATHLEN]; + + if ((retval = loadLibrary())) { + return (retval); + } + + count = getNumberOfAdapters(); + + for (adapterIndex = 0; adapterIndex < count; adapterIndex ++) { + if (skip_hba(adapterIndex)) { + continue; + } + status = HBA_GetAdapterName(adapterIndex, (char *)&name); + if (status != HBA_STATUS_OK) { + /* Just skip it, maybe it was DR'd */ + continue; + } + handle = HBA_OpenAdapter(name); + if (handle == 0) { + /* Just skip it, maybe it was DR'd */ + continue; + } + + if (getAdapterAttrs(handle, name, &hbaAttrs)) { + /* This should never happen, we'll just skip the adapter */ + HBA_CloseAdapter(handle); + continue; + } + + for (portIndex = 0; portIndex < hbaAttrs.NumberOfPorts; + portIndex++) { + if (getAdapterPortAttrs(handle, name, portIndex, + &portAttrs)) { + continue; + } + physical = get_slash_devices_from_osDevName( + portAttrs.OSDeviceName, + STANDARD_DEVNAME_HANDLING); + if (physical) { + char *tmp = strstr(physical, ":fc"); + if (tmp) { + *tmp = '\0'; + (void) snprintf(path, MAXPATHLEN, "%s:devctl", + physical); + } else { + (void) snprintf(path, MAXPATHLEN, "%s", + physical); + } + free(physical); + physical = NULL; + (void) printf("%-65s ", path); + } else { + (void) printf("%-65s ", portAttrs.OSDeviceName); + } + if (portAttrs.NumberofDiscoveredPorts > 0) { + printf(MSGSTR(2233, "CONNECTED\n")); + } else { + printf(MSGSTR(2234, "NOT CONNECTED\n")); + } + } + } + (void) HBA_FreeLibrary(); + return (retval); +} + +/* + * Internal routines/structure to deal with a path list + * so we can ensure uniqueness + */ +struct path_entry { + char path[MAXPATHLEN]; + HBA_UINT8 wwn[8]; + uchar_t dtype; + struct path_entry *next; +}; +void add_path(struct path_entry *head, struct path_entry *cur) { + struct path_entry *tmp; + for (tmp = head; tmp->next != NULL; tmp = tmp->next) { } + tmp->next = cur; +} +struct path_entry *is_duplicate_path(struct path_entry *head, char *path) { + struct path_entry *tmp; + for (tmp = head; tmp != NULL; tmp = tmp->next) { + if (strncmp(tmp->path, path, sizeof (tmp->path)) == 0) { + return (tmp); + } + } + return (NULL); +} +void free_path_list(struct path_entry *head) { + struct path_entry *tmp; + struct path_entry *tmp2; + for (tmp = head; tmp != NULL; ) { + tmp2 = tmp->next; + free(tmp); + tmp = tmp2; + } +} + + +int +is_wwn(char *arg) { + int i; + if (strlen(arg) == 16) { + for (i = 0; i < 16; i++) { + if (!isxdigit(arg[i])) { + return (0); + } + } + return (1); + } + return (0); +} + +int +is_path(char *arg) { + struct stat buf; + if (stat(arg, &buf)) { + return (0); + } + return (1); +} + +/* We take a wild guess for our first get target mappings call */ +#define MAP_GUESS 50 + +HBA_STATUS +fetch_mappings(HBA_HANDLE handle, HBA_WWN pwwn, HBA_FCPTARGETMAPPINGV2 **map) { + int loop = 0; + int count = 0; + HBA_STATUS status = HBA_STATUS_ERROR_TRY_AGAIN; /* force first pass */ + *map = (HBA_FCPTARGETMAPPINGV2 *) calloc(1, + (sizeof (HBA_FCPSCSIENTRYV2)* (MAP_GUESS-1)) + + sizeof (HBA_FCPTARGETMAPPINGV2)); + + /* Loop as long as we have a retryable error */ + while ((status == HBA_STATUS_ERROR_TRY_AGAIN || + status == HBA_STATUS_ERROR_BUSY || + status == HBA_STATUS_ERROR_MORE_DATA) && loop++ < MAX_RETRIES) { + status = HBA_GetFcpTargetMappingV2(handle, pwwn, *map); + if (status == HBA_STATUS_OK) { + break; + } else if (status == HBA_STATUS_ERROR_MORE_DATA) { + count = (*map)->NumberOfEntries; + free(*map); + *map = (HBA_FCPTARGETMAPPINGV2 *) calloc(1, + (sizeof (HBA_FCPSCSIENTRYV2)* (count-1)) + + sizeof (HBA_FCPTARGETMAPPINGV2)); + (*map)->NumberOfEntries = count; + continue; + } + sleep(1); + } + if (status != HBA_STATUS_OK) { + /* We encountered a non-retryable error */ + fprintf(stderr, MSGSTR(2502, + "\nERROR: Unable to retrieve SCSI device paths " + "(HBA Port WWN %016llx)"), + wwnConversion(pwwn.wwn)); + printStatus(status); + fprintf(stderr, "\n"); + } + return (status); +} + +/* + * Returns the index of the first match, or -1 if no match + */ +int +match_mappings(char *compare, HBA_FCPTARGETMAPPINGV2 *map) { + int mapIndex; + char *physical = NULL; + char *tmp; + int wwnCompare = 0; + uint64_t wwn; + + if (map == NULL || compare == NULL) { + return (-1); + } + + if (is_wwn(compare)) { + wwnCompare = 1; + (void) sscanf(compare, "%016llx", &wwn); + } else { + /* Convert the paths to phsyical paths */ + physical = get_slash_devices_from_osDevName(compare, + STANDARD_DEVNAME_HANDLING); + } + + for (mapIndex = 0; mapIndex < map->NumberOfEntries; mapIndex ++) { + if (wwnCompare) { + if (wwn == wwnConversion( + map->entry[mapIndex].FcpId.NodeWWN.wwn) || + wwn == wwnConversion( + map->entry[mapIndex].FcpId.PortWWN.wwn)) { + return (mapIndex); + } + } else { + if (physical != NULL) { + tmp = get_slash_devices_from_osDevName( + map->entry[mapIndex].ScsiId.OSDeviceName, + STANDARD_DEVNAME_HANDLING); + if ((tmp != NULL) && + strncmp(physical, tmp, MAXPATHLEN) == 0) { + free(physical); + return (mapIndex); + } + } + } + } + if (physical) { + free(physical); + } + return (-1); +} + + +/* + * returns non-zero on failure (aka HBA_STATUS_ERROR_* + */ +int +loadLibrary() { + int status = HBA_LoadLibrary(); + if (status != HBA_STATUS_OK) { + fprintf(stderr, MSGSTR(2505, + "ERROR: Unable to load HBA API library: ")); + printStatus(status); + fprintf(stderr, "\n"); + } + return (status); +} + +int +fchba_non_encl_probe() { + HBA_HANDLE handle; + HBA_ADAPTERATTRIBUTES hbaAttrs; + HBA_PORTATTRIBUTES portAttrs; + HBA_FCPTARGETMAPPINGV2 *map; + HBA_STATUS status; + int count, adapterIndex, portIndex, mapIndex; + char name[256]; + struct path_entry *head = NULL; + uint64_t lun = 0; + L_inquiry inq; + struct scsi_extended_sense sense; + HBA_UINT8 scsiStatus; + uint32_t inquirySize = sizeof (inq), senseSize = sizeof (sense); + + if (loadLibrary()) { + return (-1); + } + + count = getNumberOfAdapters(); + + /* Loop over all HBAs */ + for (adapterIndex = 0; adapterIndex < count; adapterIndex ++) { + if (skip_hba(adapterIndex)) { + continue; + } + status = HBA_GetAdapterName(adapterIndex, (char *)&name); + if (status != HBA_STATUS_OK) { + /* May have been DR'd */ + continue; + } + handle = HBA_OpenAdapter(name); + if (handle == 0) { + /* May have been DR'd */ + continue; + } + + if (getAdapterAttrs(handle, name, &hbaAttrs)) { + /* Should not happen, just skip it */ + HBA_CloseAdapter(handle); + continue; + } + + + /* Loop over all HBA Ports */ + for (portIndex = 0; portIndex < hbaAttrs.NumberOfPorts; + portIndex++) { + if (getAdapterPortAttrs(handle, name, portIndex, + &portAttrs)) { + continue; + } + + if (fetch_mappings(handle, portAttrs.PortWWN, &map)) { + continue; + } + + /* Loop over all target Mapping entries */ + for (mapIndex = 0; mapIndex < map->NumberOfEntries; + mapIndex ++) { + struct path_entry *tmpPath = NULL; + int doInquiry = 0; + if (!head) { + head = (struct path_entry *)calloc(1, + sizeof (struct path_entry)); + tmpPath = head; + strncpy(head->path, + map->entry[mapIndex].ScsiId.OSDeviceName, + sizeof (map->entry[mapIndex].ScsiId.OSDeviceName)); + (void) memcpy(tmpPath->wwn, + map->entry[mapIndex].FcpId.NodeWWN.wwn, + sizeof (HBA_UINT8) * 8); + doInquiry = 1; + } else if (tmpPath = is_duplicate_path(head, + map->entry[mapIndex].ScsiId.OSDeviceName)) { + if (tmpPath->dtype != 0x1f) { + doInquiry = 0; + } else { + doInquiry = 1; + } + } else { + tmpPath = (struct path_entry *) + calloc(1, sizeof (struct path_entry)); + strncpy(tmpPath->path, + map->entry[mapIndex].ScsiId.OSDeviceName, + sizeof (map->entry[mapIndex].ScsiId.OSDeviceName)); + (void) memcpy(tmpPath->wwn, + map->entry[mapIndex].FcpId.NodeWWN.wwn, + sizeof (HBA_UINT8) * 8); + add_path(head, tmpPath); + doInquiry = 1; + } + + if (doInquiry) { + lun = map->entry[mapIndex].FcpId.FcpLun; + memset(&inq, 0, sizeof (inq)); + memset(&sense, 0, sizeof (sense)); + status = HBA_ScsiInquiryV2(handle, + portAttrs.PortWWN, + map->entry[mapIndex].FcpId.PortWWN, + lun, 0, 0, + &inq, &inquirySize, + &scsiStatus, + &sense, &senseSize); + if (status != HBA_STATUS_OK) { + inq.inq_dtype = 0x1f; + } + tmpPath->dtype = inq.inq_dtype; + } + } + } + } + if (head) { + struct path_entry *tmp; + printf(MSGSTR(2098, "\nFound Fibre Channel device(s):\n")); + for (tmp = head; tmp != NULL; tmp = tmp->next) { + printf(" "); + printf(MSGSTR(90, "Node WWN:")); + printf("%016llx ", wwnConversion(tmp->wwn)); + fprintf(stdout, MSGSTR(35, "Device Type:")); + (void) fflush(stdout); + + if ((tmp->dtype & DTYPE_MASK) < 0x10) { + fprintf(stdout, "%s", + dtype[tmp->dtype & DTYPE_MASK]); + } else if ((tmp->dtype & DTYPE_MASK) < 0x1f) { + fprintf(stdout, MSGSTR(2406, + "Reserved")); + } else { + fprintf(stdout, MSGSTR(2407, + "Unknown")); + } + + printf("\n "); + printf(MSGSTR(31, "Logical Path:%s"), tmp->path); + printf("\n"); + + /* We probably shouldn't be using a g_fc interface here */ + if (Options & OPTION_P) { + char *phys_path = + get_slash_devices_from_osDevName( + tmp->path, + STANDARD_DEVNAME_HANDLING); + if (phys_path != NULL) { + fprintf(stdout, " "); + fprintf(stdout, MSGSTR(5, "Physical Path:")); + fprintf(stdout, "\n %s\n", phys_path); + free(phys_path); + } + } + } + free_path_list(head); + } + HBA_FreeLibrary(); + return (0); +} + + +int +fchba_inquiry(char **argv) +{ + int path_index = 0, found = 0; + uint64_t wwn; + uint64_t lun = 0; + HBA_HANDLE handle; + HBA_ADAPTERATTRIBUTES hbaAttrs; + HBA_PORTATTRIBUTES portAttrs; + HBA_FCPTARGETMAPPINGV2 *map; + HBA_STATUS status; + int count, adapterIndex, portIndex, mapIndex; + char name[256]; + L_inquiry inq; + struct page80 serial; + uint32_t serialSize = sizeof (serial); + struct scsi_extended_sense sense; + HBA_UINT8 scsiStatus; + uint32_t inquirySize = sizeof (inq), senseSize = sizeof (sense); + boolean_t goodPath = B_FALSE; + int matched = 0, wwnCompare = 0; + char *tmp, *physical = NULL; + int ret = 0; + + if (loadLibrary()) { + return (-1); + } + for (path_index = 0; argv[path_index] != NULL; path_index++) { + goodPath = B_FALSE; + found = 0; + + if (is_wwn(argv[path_index])) { + (void) sscanf(argv[path_index], "%016llx", &wwn); + wwnCompare = 1; + } else if (!is_path(argv[path_index])) { + fprintf(stderr, MSGSTR(112, "Error: Invalid pathname (%s)"), + argv[path_index]); + fprintf(stderr, "\n"); + ret = -1; + continue; + } + if (!wwnCompare) { + /* Convert the paths to phsyical paths */ + physical = get_slash_devices_from_osDevName(argv[path_index], + STANDARD_DEVNAME_HANDLING); + if (!physical) { + fprintf(stderr, MSGSTR(112, + "Error: Invalid pathname (%s)"), + argv[path_index]); + fprintf(stderr, "\n"); + ret = -1; + continue; + } + } + + count = getNumberOfAdapters(); + + /* Loop over all HBAs */ + for (adapterIndex = 0; adapterIndex < count; adapterIndex ++) { + if (skip_hba(adapterIndex)) { + continue; + } + status = HBA_GetAdapterName(adapterIndex, (char *)&name); + if (status != HBA_STATUS_OK) { + /* May have been DR'd */ + continue; + } + handle = HBA_OpenAdapter(name); + if (handle == 0) { + /* May have been DR'd */ + continue; + } + + if (getAdapterAttrs(handle, name, &hbaAttrs)) { + /* Should never happen */ + HBA_CloseAdapter(handle); + continue; + } + + + /* Loop over all HBA Ports */ + for (portIndex = 0; portIndex < hbaAttrs.NumberOfPorts; + portIndex++) { + if (getAdapterPortAttrs(handle, name, portIndex, + &portAttrs)) { + continue; + } + + if (fetch_mappings(handle, portAttrs.PortWWN, &map)) { + continue; + } + + for (mapIndex = 0; mapIndex < map->NumberOfEntries; + mapIndex ++) { + matched = 0; + if (wwnCompare) { + if (wwn == wwnConversion( + map->entry[mapIndex].FcpId.NodeWWN.wwn) || + wwn == wwnConversion( + map->entry[mapIndex].FcpId.PortWWN.wwn)) { + lun = map->entry[mapIndex].FcpId.FcpLun; + matched = 1; + } + } else { + tmp = get_slash_devices_from_osDevName( + map->entry[mapIndex].ScsiId.OSDeviceName, + STANDARD_DEVNAME_HANDLING); + if ((tmp != NULL) && (strncmp(physical, tmp, + MAXPATHLEN) == 0)) { + lun = map->entry[mapIndex].FcpId.FcpLun; + matched = 1; + free(tmp); + } + } + + if (matched) { + memset(&inq, 0, sizeof (inq)); + memset(&sense, 0, sizeof (sense)); + status = HBA_ScsiInquiryV2(handle, + portAttrs.PortWWN, + map->entry[mapIndex].FcpId.PortWWN, + lun, 0, 0, + &inq, &inquirySize, + &scsiStatus, + &sense, &senseSize); + if (status == HBA_STATUS_OK) { + goodPath = B_TRUE; + /* + * Call the inquiry cmd on page 0x80 only if + * the vendor supports page 0x80 + */ + memset(&serial, 0, sizeof (serial)); + if ((find_supported_inq_page(handle, + portAttrs.PortWWN, + map->entry[mapIndex].FcpId.PortWWN, + lun, 0x80))) { + status = HBA_ScsiInquiryV2(handle, + portAttrs.PortWWN, + map->entry[mapIndex].FcpId.PortWWN, + lun, 1, 0x80, + &serial, &serialSize, + &scsiStatus, + &sense, &senseSize); + if (status != HBA_STATUS_OK) { + strncpy( + (char *)serial.inq_serial, + "Unavailable", + sizeof (serial.inq_serial)); + } + } else { + strncpy((char *)serial.inq_serial, + "Unsupported", + sizeof (serial.inq_serial)); + } + /* + * we are adding serial number information + * from 0x80. If length is less than 39, + * then we want to increase length to 52 to + * reflect the fact that we have serial number + * information + */ + if (inq.inq_len < 39) { + inq.inq_len = 52; + } + print_inq_data(argv[path_index], + map->entry[mapIndex].ScsiId.OSDeviceName, + inq, serial.inq_serial, + sizeof (serial.inq_serial)); + if (! wwnCompare) { + found = 1; + break; + } + } else { + fprintf(stderr, MSGSTR(2430, + "Error: I/O failure communicating with %s "), + map->entry[mapIndex].ScsiId.OSDeviceName); + printStatus(status); + fprintf(stderr, "\n"); + } + } + } + if (found == 1) { + break; + } + } + if (found == 1) { + break; + } + } + + if (physical) { + free(physical); + } + + if (!goodPath) { + fprintf(stderr, MSGSTR(112, "Error: Invalid pathname (%s)"), + argv[path_index]); + fprintf(stderr, "\n"); + ret = -1; + } + } + return (ret); +} + + + +int +fchba_dump_map(char **argv) +{ + int path_index = 0; + uint64_t wwn; + uint64_t lun = 0; + HBA_HANDLE handle; + HBA_ADAPTERATTRIBUTES hbaAttrs; + HBA_PORTATTRIBUTES portAttrs; + HBA_PORTATTRIBUTES discPortAttrs; + HBA_FCPTARGETMAPPINGV2 *map; + HBA_STATUS status; + int count, adapterIndex, portIndex, mapIndex, discIndex; + char name[256], *physical, *comp_phys; + L_inquiry inq; + struct scsi_extended_sense sense; + HBA_UINT8 scsiStatus; + int matched; + int done; + uint32_t inquirySize = sizeof (inq), senseSize = sizeof (sense); + boolean_t goodPath = B_FALSE; + int ret = 0; + uint32_t responseSize = DEFAULT_LUN_LENGTH; + uchar_t raw_luns[DEFAULT_LUN_LENGTH]; + struct rep_luns_rsp *lun_resp; + + + if (loadLibrary()) { + return (-1); + } + for (path_index = 0; argv[path_index] != NULL; path_index++) { + goodPath = B_FALSE; + + if (is_wwn(argv[path_index])) { + (void) sscanf(argv[path_index], "%016llx", &wwn); + } else if (!is_path(argv[path_index])) { + fprintf(stderr, MSGSTR(112, "Error: Invalid pathname (%s)"), + argv[path_index]); + fprintf(stderr, "\n"); + ret = -1; + continue; + } + + count = getNumberOfAdapters(); + + done = 0; + /* Loop over all HBAs */ + for (adapterIndex = 0; adapterIndex < count && !done; + adapterIndex ++) { + if (skip_hba(adapterIndex)) { + continue; + } + status = HBA_GetAdapterName(adapterIndex, (char *)&name); + if (status != HBA_STATUS_OK) { + /* May have been DR'd */ + continue; + } + handle = HBA_OpenAdapter(name); + if (handle == 0) { + /* May have been DR'd */ + continue; + } + + if (getAdapterAttrs(handle, name, &hbaAttrs)) { + /* Should never happen */ + HBA_CloseAdapter(handle); + continue; + } + + + /* Loop over all HBA Ports */ + for (portIndex = 0; portIndex < hbaAttrs.NumberOfPorts && !done; + portIndex++) { + if (getAdapterPortAttrs(handle, name, portIndex, + &portAttrs)) { + continue; + } + + + matched = 0; + if (is_wwn(argv[path_index])) { + if (wwn == wwnConversion( + portAttrs.NodeWWN.wwn) || + wwn == wwnConversion( + portAttrs.PortWWN.wwn)) { + matched = 1; + } + } else { + if (is_path(argv[path_index]) && + ((physical = get_slash_devices_from_osDevName( + argv[path_index], + STANDARD_DEVNAME_HANDLING)) != NULL) && + ((comp_phys = get_slash_devices_from_osDevName( + portAttrs.OSDeviceName, + STANDARD_DEVNAME_HANDLING)) != NULL)) { + char *tmp = strstr(physical, ":devctl"); + if (tmp) { + *tmp = '\0'; + } else { + tmp = strstr(physical, ":fc"); + if (tmp) { + *tmp = '\0'; + } + } + if (strstr(comp_phys, physical)) { + matched = 1; + } + } + if (physical) { + free(physical); + physical = NULL; + } + if (comp_phys) { + free(comp_phys); + comp_phys = NULL; + } + } + + if (!fetch_mappings(handle, portAttrs.PortWWN, &map)) { + mapIndex = match_mappings(argv[path_index], map); + if (mapIndex >= 0) { + matched = 1; + } + } else { + continue; + } + + if (matched) { + goodPath = B_TRUE; + printf(MSGSTR(2095, + "Pos Port_ID Hard_Addr Port WWN" + " Node WWN Type\n")); + for (discIndex = 0; + discIndex < portAttrs.NumberofDiscoveredPorts; + discIndex++) { + if (getDiscPortAttrs(handle, name, portIndex, + discIndex, &discPortAttrs)) { + /* Move on to the next target */ + continue; + } + + printf("%-4d %-6x %-6x %016llx %016llx", + discIndex, + discPortAttrs.PortFcId, 0, + wwnConversion(discPortAttrs.PortWWN.wwn), + wwnConversion(discPortAttrs.NodeWWN.wwn)); + + /* + * devices are not all required to respond to + * Scsi Inquiry calls sent to LUN 0. We must + * fisrt issue a ReportLUN and then send the + * SCSI Inquiry call to the first LUN Returned + * from the ReportLUN call + */ + memset(&sense, 0, sizeof (sense)); + status = HBA_ScsiReportLUNsV2(handle, + portAttrs.PortWWN, + discPortAttrs.PortWWN, + (void *)raw_luns, &responseSize, &scsiStatus, + (void *)&sense, &senseSize); + if (status == HBA_STATUS_OK) { + lun_resp = + (struct rep_luns_rsp *) + (unsigned long)raw_luns; + lun = ntohll( + wwnConversion(lun_resp->lun[0].val)); + } else { + /* + * in case we are unable to retrieve report + * LUN data, we will blindly try sending the + * INQUIRY to lun 0. + */ + lun = 0; + } + memset(&sense, 0, sizeof (sense)); + status = HBA_ScsiInquiryV2(handle, + portAttrs.PortWWN, + discPortAttrs.PortWWN, + lun, 0, 0, + &inq, &inquirySize, + &scsiStatus, + &sense, &senseSize); + if (status != HBA_STATUS_OK) { + inq.inq_dtype = 0x1f; + } + print_fabric_dtype_prop(portAttrs.PortWWN.wwn, + map->entry[mapIndex].FcpId.PortWWN.wwn, + inq.inq_dtype); + } + /* Now dump this HBA's stats */ + printf("%-4d %-6x %-6x %016llx %016llx", + discIndex, + portAttrs.PortFcId, 0, + wwnConversion(portAttrs.PortWWN.wwn), + wwnConversion(portAttrs.NodeWWN.wwn)); + print_fabric_dtype_prop(portAttrs.PortWWN.wwn, + portAttrs.PortWWN.wwn, 0x1f); + done = 1; + } + } + } + if (!goodPath) { + fprintf(stderr, MSGSTR(112, "Error: Invalid pathname (%s)"), + argv[path_index]); + fprintf(stderr, "\n"); + ret = -1; + } + } + return (ret); +} + +int +fchba_display_link_status(char **argv) +{ + int path_index = 0; + uint64_t wwn; + HBA_HANDLE handle; + HBA_ADAPTERATTRIBUTES hbaAttrs; + HBA_PORTATTRIBUTES portAttrs; + HBA_PORTATTRIBUTES discPortAttrs; + HBA_FCPTARGETMAPPINGV2 *map; + HBA_STATUS status; + int count, adapterIndex, portIndex, discIndex; + char name[256], *physical, *comp_phys; + int matched; + struct fc_rls_acc_params rls; + uint32_t rls_size = sizeof (rls); + boolean_t goodPath = B_FALSE; + int ret = 0; + + if (loadLibrary()) { + return (-1); + } + for (path_index = 0; argv[path_index] != NULL; path_index++) { + goodPath = B_FALSE; + + if (is_wwn(argv[path_index])) { + (void) sscanf(argv[path_index], "%016llx", &wwn); + } else if (!is_path(argv[path_index])) { + fprintf(stderr, MSGSTR(112, "Error: Invalid pathname (%s)"), + argv[path_index]); + fprintf(stderr, "\n"); + ret = -1; + continue; + } + + count = getNumberOfAdapters(); + + /* Loop over all HBAs */ + for (adapterIndex = 0; adapterIndex < count; adapterIndex ++) { + if (skip_hba(adapterIndex)) { + continue; + } + status = HBA_GetAdapterName(adapterIndex, (char *)&name); + if (status != HBA_STATUS_OK) { + /* May have been DR'd */ + continue; + } + handle = HBA_OpenAdapter(name); + if (handle == 0) { + /* May have been DR'd */ + continue; + } + + if (getAdapterAttrs(handle, name, &hbaAttrs)) { + /* Should never happen */ + HBA_CloseAdapter(handle); + continue; + } + + + /* Loop over all HBA Ports */ + for (portIndex = 0; portIndex < hbaAttrs.NumberOfPorts; + portIndex++) { + if (getAdapterPortAttrs(handle, name, portIndex, + &portAttrs)) { + continue; + } + + matched = 0; + if (is_wwn(argv[path_index])) { + if (wwn == wwnConversion( + portAttrs.NodeWWN.wwn) || + wwn == wwnConversion( + portAttrs.PortWWN.wwn)) { + matched = 1; + } + } else { + if (is_path(argv[path_index]) && + ((physical = get_slash_devices_from_osDevName( + argv[path_index], + STANDARD_DEVNAME_HANDLING)) != NULL) && + ((comp_phys = get_slash_devices_from_osDevName( + portAttrs.OSDeviceName, + STANDARD_DEVNAME_HANDLING)) != NULL)) { + char *tmp = strstr(physical, ":devctl"); + if (tmp) { + *tmp = '\0'; + } else { + tmp = strstr(physical, ":fc"); + if (tmp) { + *tmp = '\0'; + } + } + if (strstr(comp_phys, physical)) { + matched = 1; + } + } + if (physical) { + free(physical); + physical = NULL; + } + if (comp_phys) { + free(comp_phys); + comp_phys = NULL; + } + } + + if (!matched) { + if (fetch_mappings(handle, portAttrs.PortWWN, &map)) { + continue; + } + } + + if (matched || match_mappings(argv[path_index], map) >= 0) { + goodPath = B_TRUE; + fprintf(stdout, + MSGSTR(2007, "\nLink Error Status " + "information for loop:%s\n"), argv[path_index]); + fprintf(stdout, MSGSTR(2008, "al_pa lnk fail " + " sync loss signal loss sequence err" + " invalid word CRC\n")); + + for (discIndex = 0; + discIndex < portAttrs.NumberofDiscoveredPorts; + discIndex++) { + + + if (getDiscPortAttrs(handle, name, portIndex, + discIndex, &discPortAttrs)) { + continue; + } + + status = HBA_SendRLS(handle, portAttrs.PortWWN, + discPortAttrs.PortWWN, + &rls, &rls_size); + if (status != HBA_STATUS_OK) { + memset(&rls, 0xff, sizeof (rls)); + } + + if ((rls.rls_link_fail == 0xffffffff) && + (rls.rls_sync_loss == 0xffffffff) && + (rls.rls_sig_loss == 0xffffffff) && + (rls.rls_prim_seq_err == 0xffffffff) && + (rls.rls_invalid_word == 0xffffffff) && + (rls.rls_invalid_crc == 0xffffffff)) { + fprintf(stdout, + "%x\t%-12d%-12d%-14d%-15d%-15d%-12d\n", + discPortAttrs.PortFcId, + rls.rls_link_fail, + rls.rls_sync_loss, + rls.rls_sig_loss, + rls.rls_prim_seq_err, + rls.rls_invalid_word, + rls.rls_invalid_crc); + } else { + fprintf(stdout, + "%x\t%-12u%-12u%-14u%-15u%-15u%-12u\n", + discPortAttrs.PortFcId, + rls.rls_link_fail, + rls.rls_sync_loss, + rls.rls_sig_loss, + rls.rls_prim_seq_err, + rls.rls_invalid_word, + rls.rls_invalid_crc); + } + + + } + /* Now dump this HBA's stats */ + status = HBA_SendRLS(handle, portAttrs.PortWWN, + portAttrs.PortWWN, + &rls, &rls_size); + if (status != HBA_STATUS_OK) { + memset(&rls, 0xff, sizeof (rls)); + } + + if ((rls.rls_link_fail == 0xffffffff) && + (rls.rls_sync_loss == 0xffffffff) && + (rls.rls_sig_loss == 0xffffffff) && + (rls.rls_prim_seq_err == 0xffffffff) && + (rls.rls_invalid_word == 0xffffffff) && + (rls.rls_invalid_crc == 0xffffffff)) { + fprintf(stdout, + "%x\t%-12d%-12d%-14d%-15d%-15d%-12d\n", + portAttrs.PortFcId, + rls.rls_link_fail, + rls.rls_sync_loss, + rls.rls_sig_loss, + rls.rls_prim_seq_err, + rls.rls_invalid_word, + rls.rls_invalid_crc); + } else { + fprintf(stdout, + "%x\t%-12u%-12u%-14u%-15u%-15u%-12u\n", + portAttrs.PortFcId, + rls.rls_link_fail, + rls.rls_sync_loss, + rls.rls_sig_loss, + rls.rls_prim_seq_err, + rls.rls_invalid_word, + rls.rls_invalid_crc); + } + } + } + } + if (!goodPath) { + fprintf(stderr, MSGSTR(112, "Error: Invalid pathname (%s)"), + argv[path_index]); + fprintf(stderr, "\n"); + ret = -1; + } + } + (void) fprintf(stdout, + MSGSTR(2009, "NOTE: These LESB counts are not" + " cleared by a reset, only power cycles.\n" + "These counts must be compared" + " to previously read counts.\n")); + return (ret); +} + +typedef struct _PathInformation { + char pathClass[MAXPATHLEN]; + char pathState[MAXPATHLEN]; + int32_t pathInfoState; + int32_t pathInfoExternalState; +} PathInformation; + +struct lun_tracking { + HBA_FCPSCSIENTRYV2 map; + HBA_WWN hba_pwwn; + char hba_path[MAXPATHLEN]; + PathInformation info; + + /* Points to another lun_tracking instance with the same map->LUID */ + struct lun_tracking *next_path; + + /* Points to next lun_tracking with a different map->LUID */ + struct lun_tracking *next_lun; +}; + + +static const char VHCI_COMPONENT[] = "scsi_vhci"; +static void +scsi_vhci_details(struct lun_tracking *lun) +{ + HBA_FCPSCSIENTRYV2 entry = lun->map; + int retval = 0; + int pathcnt, i, count, found = 0; + char temppath[MAXPATHLEN]; + char buf[MAXPATHLEN]; + char *path_state[5]; + + char *phys_path = get_slash_devices_from_osDevName( + entry.ScsiId.OSDeviceName, + STANDARD_DEVNAME_HANDLING); + char *devPath = NULL; + char *trailingCruft = NULL; + char devaddr[MAXPATHLEN]; + sv_iocdata_t ioc; + int prop_buf_size = SV_PROP_MAX_BUF_SIZE; + char *path_class_val = NULL; + char client_path[MAXPATHLEN]; + char phci_path[MAXPATHLEN]; + + /* Only proceed if we are an mpxio path */ + if (phys_path == NULL || strstr(phys_path, VHCI_COMPONENT) == NULL) { + return; + } + + path_state[0] = MSGSTR(2400, "INIT"); + path_state[1] = MSGSTR(2401, "ONLINE"); + path_state[2] = MSGSTR(2402, "STANDBY"); + path_state[3] = MSGSTR(2403, "FAULT"); + path_state[4] = MSGSTR(2404, "OFFLINE"); + + sprintf(devaddr, "%016llx,%x", wwnConversion( + entry.FcpId.PortWWN.wwn), + entry.ScsiId.ScsiOSLun); + + /* First get the controller path */ + sprintf(temppath, "/dev/cfg/c%d", entry.ScsiId.ScsiBusNumber); + if ((count = readlink(temppath, buf, sizeof (buf)))) { + buf[count] = '\0'; + /* Now skip over the leading "../.." */ + devPath = strstr(buf, "/devices/"); + if (devPath == NULL) { + strcpy(lun->info.pathClass, "Unavailable"); + strcpy(lun->info.pathState, "Unavailable"); + free(phys_path); + return; + } + + /* Now chop off the trailing ":xxx" portion if present */ + trailingCruft = strrchr(buf, ':'); + if (trailingCruft) { + trailingCruft[0] = '\0'; + } + } else { + strcpy(lun->info.pathClass, "Unavailable"); + strcpy(lun->info.pathState, "Unavailable"); + free(phys_path); + return; + } + + ioc.client = client_path; + ioc.phci = phci_path; + + retval = get_scsi_vhci_pathinfo(phys_path, &ioc, &pathcnt); + if (retval != 0) { + print_errString(retval, NULL); + exit(-1); + } + + for (i = 0; i < pathcnt; i++) { + nvlist_t *nvl; + if (strstr(devPath, ioc.ret_buf[i].device.ret_phci)) { + /* This could break someday if MPxIO changes devaddr */ + if (strstr(ioc.ret_buf[i].ret_addr, devaddr)) { + retval = nvlist_unpack(ioc.ret_buf[i].ret_prop.buf, + prop_buf_size, &nvl, 0); + if (retval != 0) { + strcpy(lun->info.pathClass, + "UNKNOWN PROB"); + } else { + strcpy(lun->info.pathState, + path_state[ioc.ret_buf[i].ret_state]); + lun->info.pathInfoState = ioc.ret_buf[i].ret_state; + lun->info.pathInfoExternalState = + ioc.ret_buf[i].ret_ext_state; + if (nvlist_lookup_string(nvl, "path-class", + &path_class_val) == 0) { + strcpy(lun->info.pathClass, path_class_val); + } else { + strcpy(lun->info.pathClass, "UNKNOWN"); + } + } + nvlist_free(nvl); + found++; + break; + } + } + + } + + if (!found) { + strcpy(lun->info.pathClass, "Unavailable"); + strcpy(lun->info.pathState, "Unavailable"); + } + free(phys_path); + + /* free everything we alloced */ + for (i = 0; i < ioc.buf_elem; i++) { + free(ioc.ret_buf[i].ret_prop.buf); + free(ioc.ret_buf[i].ret_prop.ret_buf_size); + } + free(ioc.ret_buf); + +} + +/* Utility routine to add new entries to the list (ignores dups) */ +static void +add_lun_path(struct lun_tracking *head, HBA_FCPSCSIENTRYV2 *map, + HBA_WWN pwwn, char *path) +{ + struct lun_tracking *tmp = NULL, *cmp = NULL; + + for (tmp = head; tmp != NULL; tmp = tmp->next_lun) { + if (memcmp(&tmp->map.LUID, &map->LUID, + sizeof (HBA_LUID)) == 0) { + + /* Ensure this isn't a duplicate */ + for (cmp = tmp; cmp->next_path != NULL; + cmp = cmp->next_path) { + if (memcmp(&cmp->map, map, sizeof (cmp->map)) == 0) { + return; + } + } + if (memcmp(&cmp->map, map, sizeof (cmp->map)) == 0) { + return; + } + + /* We have a new entry to add */ + cmp->next_path = (struct lun_tracking *)calloc(1, + sizeof (struct lun_tracking)); + cmp = cmp->next_path; + (void) memcpy(&cmp->map, map, + sizeof (cmp->map)); + (void) memcpy(&cmp->hba_pwwn, &pwwn, + sizeof (cmp->hba_pwwn)); + (void) snprintf(cmp->hba_path, MAXPATHLEN, + path); + scsi_vhci_details(cmp); + return; + } + } + /* Append a new LUN at the end of the list */ + for (tmp = head; tmp->next_lun != NULL; tmp = tmp->next_lun) {} + tmp->next_lun = (struct lun_tracking *)calloc(1, + sizeof (struct lun_tracking)); + tmp = tmp->next_lun; + (void) memcpy(&tmp->map, map, + sizeof (tmp->map)); + (void) memcpy(&tmp->hba_pwwn, &pwwn, + sizeof (tmp->hba_pwwn)); + (void) snprintf(tmp->hba_path, MAXPATHLEN, + path); + scsi_vhci_details(tmp); +} + +/*ARGSUSED*/ +int +fchba_display_config(char **argv, int option_t_input, int argc) +{ + int path_index = 0; + uint64_t wwn; + uint64_t lun = 0; + HBA_HANDLE handle; + HBA_ADAPTERATTRIBUTES hbaAttrs; + HBA_PORTATTRIBUTES portAttrs; + HBA_FCPTARGETMAPPINGV2 *map; + HBA_STATUS status; + int count, adapterIndex, portIndex; + char name[256]; + L_inquiry inq; + struct scsi_extended_sense sense; + struct page80 serial; + HBA_UINT8 scsiStatus; + uint32_t inquirySize = sizeof (inq), senseSize = sizeof (sense); + uint32_t serialSize = sizeof (serial); + struct mode_page *pg_hdr; + uchar_t *pg_buf; + float lunMbytes; + struct capacity_data_struct cap_data; + uint32_t cap_data_size = sizeof (cap_data); + struct mode_header_g1 *mode_header_ptr; + int offset; + char *phys_path = NULL; + int mpxio = 0; + int wwnCompare = 0; + char *physical = NULL; + struct lun_tracking *head = NULL; + boolean_t goodPath = B_FALSE; + int ret = 0; + + + + if ((status = loadLibrary())) { + return (-1); + } + for (path_index = 0; argv[path_index] != NULL; path_index++) { + goodPath = B_FALSE; + + if (is_wwn(argv[path_index])) { + (void) sscanf(argv[path_index], "%016llx", &wwn); + wwnCompare = 1; + } else if (!is_path(argv[path_index])) { + fprintf(stderr, MSGSTR(112, "Error: Invalid pathname (%s)"), + argv[path_index]); + fprintf(stderr, "\n"); + ret = -1; + continue; + } + if (!wwnCompare) { + /* Convert the paths to phsyical paths */ + physical = get_slash_devices_from_osDevName(argv[path_index], + STANDARD_DEVNAME_HANDLING); + if (!physical) { + fprintf(stderr, MSGSTR(112, + "Error: Invalid pathname (%s)"), + argv[path_index]); + fprintf(stderr, "\n"); + ret = -1; + continue; + } + } + + count = getNumberOfAdapters(); + + + /* + * We have to loop twice to ensure we don't miss any + * extra paths for other targets in a multi-target device + */ + + /* First check WWN/path comparisons */ + for (adapterIndex = 0; adapterIndex < count; adapterIndex ++) { + if (skip_hba(adapterIndex)) { + continue; + } + status = HBA_GetAdapterName(adapterIndex, (char *)&name); + if (status != HBA_STATUS_OK) { + /* May have been DR'd */ + continue; + } + handle = HBA_OpenAdapter(name); + if (handle == 0) { + /* May have been DR'd */ + continue; + } + if (getAdapterAttrs(handle, name, &hbaAttrs)) { + /* Should never happen */ + HBA_CloseAdapter(handle); + continue; + } + + /* Loop over all HBA Ports */ + for (portIndex = 0; portIndex < hbaAttrs.NumberOfPorts; + portIndex++) { + int matched = 0; + int mapIndex; + char *tmp; + if (getAdapterPortAttrs(handle, name, portIndex, + &portAttrs)) { + continue; + } + + if (fetch_mappings(handle, portAttrs.PortWWN, &map)) { + continue; + } + + + + for (mapIndex = 0; mapIndex < map->NumberOfEntries; + mapIndex ++) { + matched = 0; + if (wwnCompare) { + if (wwn == wwnConversion( + map->entry[mapIndex].FcpId.NodeWWN.wwn) || + wwn == wwnConversion( + map->entry[mapIndex].FcpId.PortWWN.wwn)) { + matched = 1; + } + } else { + tmp = get_slash_devices_from_osDevName( + map->entry[mapIndex].ScsiId.OSDeviceName, + STANDARD_DEVNAME_HANDLING); + if ((tmp != NULL) && (strncmp(physical, tmp, + MAXPATHLEN) == 0)) { + matched = 1; + free(tmp); + } + } + if (matched && head == NULL) { + goodPath = B_TRUE; + head = (struct lun_tracking *)calloc(1, + sizeof (struct lun_tracking)); + (void) memcpy(&head->map, &map->entry[mapIndex], + sizeof (head->map)); + (void) memcpy(&head->hba_pwwn, &portAttrs.PortWWN, + sizeof (head->hba_pwwn)); + (void) snprintf(head->hba_path, MAXPATHLEN, + portAttrs.OSDeviceName); + scsi_vhci_details(head); + } else if (matched) { + goodPath = B_TRUE; + add_lun_path(head, &map->entry[mapIndex], + portAttrs.PortWWN, portAttrs.OSDeviceName); + } + } + } + } + + if (physical) { + free(physical); + } + + /* Now do it again and look for matching LUIDs (aka GUIDs) */ + for (adapterIndex = 0; adapterIndex < count; adapterIndex ++) { + if (skip_hba(adapterIndex)) { + continue; + } + status = HBA_GetAdapterName(adapterIndex, (char *)&name); + if (status != HBA_STATUS_OK) { + /* May have been DR'd */ + continue; + } + handle = HBA_OpenAdapter(name); + if (handle == 0) { + /* May have been DR'd */ + continue; + } + + if (getAdapterAttrs(handle, name, &hbaAttrs)) { + /* Should never happen */ + HBA_CloseAdapter(handle); + continue; + } + + + /* Loop over all HBA Ports */ + for (portIndex = 0; portIndex < hbaAttrs.NumberOfPorts; + portIndex++) { + int matched = 0; + int mapIndex; + if (getAdapterPortAttrs(handle, name, portIndex, + &portAttrs)) { + continue; + } + + if (fetch_mappings(handle, portAttrs.PortWWN, &map)) { + continue; + } + + + for (mapIndex = 0; mapIndex < map->NumberOfEntries; + mapIndex ++) { + struct lun_tracking *outer; + matched = 0; + for (outer = head; outer != NULL; + outer = outer->next_lun) { + struct lun_tracking *inner; + for (inner = outer; inner != NULL; + inner = inner->next_path) { + if (memcmp(&inner->map.LUID, + &map->entry[mapIndex].LUID, + sizeof (HBA_LUID)) == 0) { + matched = 1; + break; + } + } + if (matched) { + break; + } + } + if (matched && head == NULL) { + goodPath = B_TRUE; + head = (struct lun_tracking *)calloc(1, + sizeof (struct lun_tracking)); + (void) memcpy(&head->map, &map->entry[mapIndex], + sizeof (head->map)); + (void) memcpy(&head->hba_pwwn, &portAttrs.PortWWN, + sizeof (head->hba_pwwn)); + (void) snprintf(head->hba_path, MAXPATHLEN, + portAttrs.OSDeviceName); + scsi_vhci_details(head); + } else if (matched) { + goodPath = B_TRUE; + add_lun_path(head, &map->entry[mapIndex], + portAttrs.PortWWN, portAttrs.OSDeviceName); + } + } + } + } + if (!goodPath) { + fprintf(stderr, MSGSTR(112, "Error: Invalid pathname (%s)"), + argv[path_index]); + fprintf(stderr, "\n"); + ret = -1; + /* Just bomb out instead of going on */ + return (ret); + } + } + + /* Now display all the LUNs that we found that matched */ + { + struct lun_tracking *first_time; + struct lun_tracking *tmp_path; + for (first_time = head; first_time != NULL; + first_time = first_time->next_lun) { + struct lun_tracking *path; + phys_path = get_slash_devices_from_osDevName( + first_time->map.ScsiId.OSDeviceName, + STANDARD_DEVNAME_HANDLING); + /* Change behavior if this is an MPxIO device */ + if (phys_path != NULL) { + if (strstr(phys_path, VHCI_COMPONENT) != NULL) { + mpxio = 1; + } + } + + for (tmp_path = first_time; tmp_path != NULL; + tmp_path = tmp_path->next_path) { + if (mpxio && (strncmp(tmp_path->info.pathState, + "ONLINE", strlen(tmp_path->info.pathState)))) { + /* continue to next online path */ + continue; + } + status = HBA_OpenAdapterByWWN(&handle, + tmp_path->hba_pwwn); + if (status != HBA_STATUS_OK) { + fprintf(stderr, MSGSTR(2431, + "Error: Failed to get handle for %s "), + tmp_path->hba_path); + printStatus(status); + fprintf(stderr, "\n"); + /* continue to next path */ + continue; + } + + lun = tmp_path->map.FcpId.FcpLun; + memset(&inq, 0, sizeof (inq)); + memset(&sense, 0, sizeof (sense)); + + status = HBA_ScsiInquiryV2(handle, + tmp_path->hba_pwwn, + tmp_path->map.FcpId.PortWWN, + lun, 0, 0, + &inq, &inquirySize, + &scsiStatus, + &sense, &senseSize); + + if (status == HBA_STATUS_OK) { + break; + } + HBA_CloseAdapter(handle); + } + + if (tmp_path == NULL) { + fprintf(stderr, MSGSTR(2430, + "Error: I/O failure communicating with %s "), + first_time->map.ScsiId.OSDeviceName); + printStatus(status); + fprintf(stderr, "\n"); + continue; + } + + switch ((inq.inq_dtype & DTYPE_MASK)) { + case DTYPE_DIRECT: + fprintf(stdout, MSGSTR(121, + "DEVICE PROPERTIES for disk: %s\n"), + first_time->map.ScsiId.OSDeviceName); + break; + case DTYPE_SEQUENTIAL: /* Tape */ + fprintf(stdout, MSGSTR(2249, + "DEVICE PROPERTIES for tape: %s\n"), + first_time->map.ScsiId.OSDeviceName); + break; + default: + fprintf(stdout, MSGSTR(2250, + "DEVICE PROPERTIES for: %s\n"), + first_time->map.ScsiId.OSDeviceName); + break; + } + fprintf(stdout, " "); + fprintf(stdout, MSGSTR(3, "Vendor:")); + fprintf(stdout, "\t\t"); + print_chars(inq.inq_vid, sizeof (inq.inq_vid), 0); + fprintf(stdout, MSGSTR(2115, "\n Product ID:\t\t")); + print_chars(inq.inq_pid, sizeof (inq.inq_pid), 0); + + fprintf(stdout, "\n "); + fprintf(stdout, MSGSTR(2119, "Revision:")); + fprintf(stdout, "\t\t"); + print_chars(inq.inq_revision, sizeof (inq.inq_revision), 0); + + fprintf(stdout, "\n "); + fprintf(stdout, MSGSTR(17, "Serial Num:")); + fprintf(stdout, "\t\t"); + (void) fflush(stdout); + /* + * Call the inquiry cmd on page 0x80 only if the vendor + * supports page 0x80. + */ + if ((find_supported_inq_page(handle, first_time->hba_pwwn, + first_time->map.FcpId.PortWWN, lun, 0x80))) { + memset(&serial, 0, sizeof (serial)); + status = HBA_ScsiInquiryV2(handle, + first_time->hba_pwwn, + first_time->map.FcpId.PortWWN, + lun, 1, 0x80, + &serial, &serialSize, + &scsiStatus, + &sense, &senseSize); + if (status == HBA_STATUS_OK) { + print_chars(serial.inq_serial, + sizeof (serial.inq_serial), 0); + } else { + fprintf(stdout, MSGSTR(2506, "Unsupported")); + } + } else { + fprintf(stdout, MSGSTR(2506, "Unsupported")); + } + HBA_CloseAdapter(handle); + if ((inq.inq_dtype & DTYPE_MASK) == DTYPE_DIRECT) { + /* Read capacity wont work on standby paths, so try till OK */ + for (tmp_path = first_time; tmp_path != NULL; + tmp_path = tmp_path->next_path) { + if (mpxio && (strncmp(tmp_path->info.pathState, + "ONLINE", strlen(tmp_path->info.pathState)))) { + /* continue to next online path */ + continue; + } + status = HBA_OpenAdapterByWWN(&handle, + tmp_path->hba_pwwn); + if (status != HBA_STATUS_OK) { + /* continue to next path */ + continue; + } + + status = HBA_ScsiReadCapacityV2(handle, + tmp_path->hba_pwwn, + tmp_path->map.FcpId.PortWWN, + tmp_path->map.FcpId.FcpLun, + &cap_data, &cap_data_size, + &scsiStatus, + &sense, &senseSize); + if (status == HBA_STATUS_OK) { + break; + } else if (status == HBA_STATUS_SCSI_CHECK_CONDITION && + sense.es_key == KEY_UNIT_ATTENTION) { + /* + * retry for check-condition state when unit attention + * condition has been established + */ + status = HBA_ScsiReadCapacityV2(handle, + tmp_path->hba_pwwn, + tmp_path->map.FcpId.PortWWN, + tmp_path->map.FcpId.FcpLun, + &cap_data, &cap_data_size, + &scsiStatus, + &sense, &senseSize); + if (status == HBA_STATUS_OK) { + break; + } + } + HBA_CloseAdapter(handle); + } + } + if (handle != HBA_HANDLE_INVALID) { + HBA_CloseAdapter(handle); + } + if (status != HBA_STATUS_OK) { + /* Make sure we don't display garbage */ + cap_data.block_size = 0; + cap_data.last_block_addr = 0; + } + + if (cap_data.block_size > 0 && + cap_data.last_block_addr > 0) { + lunMbytes = ntohl(cap_data.last_block_addr) + 1; + lunMbytes *= ntohl(cap_data.block_size); + lunMbytes /= (float)(1024*1024); + fprintf(stdout, "\n "); + fprintf(stdout, MSGSTR(60, + "Unformatted capacity:\t%6.3f MBytes"), lunMbytes); + } + fprintf(stdout, "\n"); + + /* + * get mode page information for FC device. + * do not do mode sense if this is a tape device. + * mode sense will rewind the tape + */ + if ((inq.inq_dtype & DTYPE_MASK) != DTYPE_SEQUENTIAL) { + if (get_mode_page(first_time->map.ScsiId.OSDeviceName, + &pg_buf) == 0) { + mode_header_ptr = (struct mode_header_g1 *) + (void *)pg_buf; + offset = sizeof (struct mode_header_g1) + + ntohs(mode_header_ptr->bdesc_length); + pg_hdr = (struct mode_page *)&pg_buf[offset]; + + while (offset < (ntohs(mode_header_ptr->length) + + sizeof (mode_header_ptr->length))) { + if (pg_hdr->code == MODEPAGE_CACHING) { + struct mode_caching *pg8_buf; + pg8_buf = (struct mode_caching *) + (void *)pg_hdr; + if (pg8_buf->wce) { + fprintf(stdout, MSGSTR(2122, + " Write Cache:\t\t" + "Enabled\n")); + } + if (pg8_buf->rcd == 0) { + fprintf(stdout, MSGSTR(2123, + " Read Cache:\t\t" + "Enabled\n")); + fprintf(stdout, MSGSTR(2509, + " Minimum prefetch:\t0x%x\n" + " Maximum prefetch:\t0x%x\n"), + pg8_buf->min_prefetch, + pg8_buf->max_prefetch); + } + break; + } + offset += pg_hdr->length + + sizeof (struct mode_page); + pg_hdr = (struct mode_page *)&pg_buf[offset]; + } + } + } + + fprintf(stdout, " %s\t\t", MSGSTR(35, "Device Type:")); + if ((inq.inq_dtype & DTYPE_MASK) < 0x10) { + fprintf(stdout, "%s\n", + dtype[inq.inq_dtype & DTYPE_MASK]); + } else if ((inq.inq_dtype & DTYPE_MASK) < 0x1f) { + fprintf(stdout, MSGSTR(2432, "Reserved")); + } else { + /* dtype of 0x1f is returned */ + fprintf(stdout, MSGSTR(2433, "Unknown")); + } + + fprintf(stdout, MSGSTR(2128, " Path(s):\n")); + fprintf(stdout, "\n"); + fprintf(stdout, " %s\n", + first_time->map.ScsiId.OSDeviceName); + if (phys_path != NULL) { + fprintf(stdout, " %s\n", phys_path); + } + + /* Now display all paths to this LUN */ + for (path = first_time; path != NULL; + path = path->next_path) { + /* Display the controller information */ + fprintf(stdout, MSGSTR(2303, " Controller \t%s\n"), + path->hba_path); + + fprintf(stdout, MSGSTR(2507, + " Device Address\t\t%016llx,%x\n"), + wwnConversion( + path->map.FcpId.PortWWN.wwn), + path->map.ScsiId.ScsiOSLun); + + fprintf(stdout, MSGSTR(2508, + " Host controller port WWN\t%016llx\n"), + wwnConversion(path->hba_pwwn.wwn)); + + if (mpxio) { + fprintf(stdout, MSGSTR(2305, + " Class\t\t\t%s\n"), path->info.pathClass); + fprintf(stdout, MSGSTR(2306, + " State\t\t\t%s\n"), path->info.pathState); + } + if (phys_path != NULL) { + free(phys_path); + phys_path = NULL; + } + } + printf("\n"); + } + } + return (ret); +} + +/* + * handle expert-mode hotplug commands + * + * return 0 iff all is okay + */ +int +fchba_hotplug_e(int todo, char **argv, int verbose_flag, int force_flag) +{ +char *path_phys = NULL; +int exit_code; +devctl_hdl_t dcp; + + if (todo != DEV_ONLINE && + todo != DEV_OFFLINE) { + fprintf(stderr, "%s\n", strerror(ENOTSUP)); + return (-1); + } + + /* Convert the paths to phsyical paths */ + path_phys = get_slash_devices_from_osDevName(argv[0], + NOT_IGNORE_DANGLING_LINK); + if (!path_phys) { + fprintf(stderr, MSGSTR(112, + "Error: Invalid pathname (%s)"), + argv[0]); + fprintf(stderr, "\n"); + return (-1); + } + if (verbose_flag) { + (void) fprintf(stdout, + MSGSTR(5516, + "phys path = \"%s\"\n"), + path_phys); + } + /* acquire rights to hack on device */ + if ((dcp = devctl_device_acquire(path_phys, + force_flag ? 0 : DC_EXCL)) == NULL) { + + (void) fprintf(stderr, MSGSTR(5517, + "Error: can't acquire \"%s\": %s\n"), + path_phys, strerror(errno)); + return (1); + } + + switch (todo) { + case DEV_ONLINE: + exit_code = devctl_device_online(dcp); + break; + case DEV_OFFLINE: + exit_code = devctl_device_offline(dcp); + break; + } + + if (exit_code != 0) { + perror(MSGSTR(5518, "devctl")); + } + + /* all done now -- release device */ + devctl_release(dcp); + + if (path_phys) { + free(path_phys); + } + + return (exit_code); +} + +/* + * Returns non zero if we should use FC-HBA. + * For x86, luxadm uses FC-HBA. + */ +int +use_fchba() +{ + +#ifdef __x86 + return (1); +#else + return (0); +#endif + +} + +/* + * Returns non-zero if we should skip the HBA at index "i" + */ +int +skip_hba(int i) { + HBA_LIBRARYATTRIBUTES lib_attrs; + (void) HBA_GetVendorLibraryAttributes(i, &lib_attrs); + if (strncmp(lib_attrs.VName, VSL_NAME, + sizeof (lib_attrs.VName)) == 0) { + return (0); + } + return (1); +} + +/* + * Function to determine if the given page is supported by vendor. + */ +int +find_supported_inq_page(HBA_HANDLE handle, HBA_WWN hwwn, HBA_WWN pwwn, + uint64_t lun, int page_num) +{ + struct scsi_extended_sense sense; + L_inquiry00 inq00; + uchar_t *data; + HBA_STATUS status = HBA_STATUS_ERROR; + int index; + HBA_UINT8 scsiStatus; + uint32_t inqSize = sizeof (inq00); + uint32_t senseSize = sizeof (sense); + + status = HBA_ScsiInquiryV2(handle, hwwn, pwwn, lun, 1, 0x00, + &inq00, &inqSize, &scsiStatus, &sense, &senseSize); + + if (status == HBA_STATUS_OK) { + data = (uchar_t *)&inq00; + for (index = 4; (index <= inq00.len+3)&& + (data[index] <= page_num); index ++) { + if (data[index] == page_num) { + return (1); + } + } + } + return (0); +} diff --git a/usr/src/cmd/luxadm/g_adm.c b/usr/src/cmd/luxadm/g_adm.c new file mode 100644 index 0000000000..b6377b00da --- /dev/null +++ b/usr/src/cmd/luxadm/g_adm.c @@ -0,0 +1,6673 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + + +#define LUX_SF_INST_SHIFT4MINOR 6 +#define LUX_SF_MINOR2INST(x) (x >> LUX_SF_INST_SHIFT4MINOR) + +#include <stdlib.h> +#include <stdio.h> +#include <sys/file.h> +#include <sys/errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/param.h> +#include <kstat.h> +#include <sys/mkdev.h> +#include <locale.h> +#include <nl_types.h> +#include <fcntl.h> +#include <unistd.h> +#include <strings.h> +#include <ctype.h> +#include <dirent.h> +#include <limits.h> +#include <stdarg.h> +#include <termio.h> /* For password */ +#include <signal.h> +#include <sys/scsi/scsi.h> +#include <sys/scsi/generic/commands.h> +#include <l_common.h> +#include <l_error.h> +#include <stgcom.h> +#include <a_state.h> +#include <devid.h> +#include <g_state.h> +#include "common.h" + +extern char *dtype[]; +extern char *whoami; +extern int Options; +extern const int OPTION_A; +extern const int OPTION_B; +extern const int OPTION_C; +extern const int OPTION_D; +extern const int OPTION_E; +extern const int OPTION_F; +extern const int OPTION_L; +extern const int OPTION_P; +extern const int OPTION_R; +extern const int OPTION_T; +extern const int OPTION_V; +extern const int OPTION_Z; +extern const int OPTION_Y; +extern const int OPTION_CAPF; +extern const int PVERBOSE; +extern const int SAVE; +extern const int EXPERT; + +static struct termios termios; +static int termio_fd; +static void pho_display_config(char *); +static void dpm_display_config(char *); +static void n_rem_list_entry(uchar_t, struct gfc_map *, + WWN_list **); +static void n_rem_list_entry_fabric(int, struct gfc_map *, + WWN_list **); +static void n_rem_wwn_entry(uchar_t *, WWN_list **); +static void display_disk_info(L_inquiry, L_disk_state, + Path_struct *, struct mode_page *, int, char *, int); +static void display_lun_info(L_disk_state, Path_struct *, + struct mode_page *, int, WWN_list *, char *); +static void display_fc_disk(struct path_struct *, char *, gfc_map_t *, + L_inquiry, int); +static void adm_display_err(char *, int); +static void temperature_messages(struct l_state_struct *, int); +static void ctlr_messages(struct l_state_struct *, int, int); +static void fan_messages(struct l_state_struct *, int, int); +static void ps_messages(struct l_state_struct *, int, int); +static void abnormal_condition_display(struct l_state_struct *); +static void loop_messages(struct l_state_struct *, int, int); +static void revision_msg(struct l_state_struct *, int); +static void mb_messages(struct l_state_struct *, int, int); +static void back_plane_messages(struct l_state_struct *, int, int); +static void dpm_SSC100_messages(struct l_state_struct *, int, int); +static void mb_messages(struct l_state_struct *, int, int); +static void back_plane_messages(struct l_state_struct *, int, int); +static void dpm_SSC100_messages(struct l_state_struct *, int, int); +static void trans_decode(Trans_elem_st *trans); +static void trans_messages(struct l_state_struct *, int); +static void adm_print_pathlist(char *); +static void display_path_info(char *, char *, WWN_list *); +static void copy_wwn_data_to_str(char *, const uchar_t *); +static void adm_mplist_free(struct mplist_struct *); +static int lun_display(Path_struct *path_struct, L_inquiry inq_struct, + int verbose); +static int non_encl_fc_disk_display(Path_struct *path_struct, + L_inquiry inq_struct, int verbose); +static int get_enclStatus(char *phys_path, char *encl_name, int off_flag); +static int get_host_controller_pwwn(char *hba_path, uchar_t *pwwn); +static int get_lun_capacity(char *devpath, + struct scsi_capacity_16 *cap_data); +static int get_path_status(char *devpath, int *status); +static int get_FC4_host_controller_pwwn(char *hba_path, uchar_t *pwwn); + +/* + * Gets the device's state from the SENA IB and + * checks whether device is offlined, bypassed + * or if the slot is empty and prints it to the + * stdout. + * + * RETURNS: + * 0 O.K. + * non-zero otherwise + */ +int +print_devState(char *devname, char *ppath, int fr_flag, int slot, + int verbose_flag) +{ +L_state l_state; +int err; +int i, elem_index = 0; +uchar_t device_off, ib_status_code, bypass_a_en, bypass_b_en; +Bp_elem_st bpf, bpr; + + + if ((err = l_get_status(ppath, &l_state, verbose_flag)) != 0) { + (void) print_errString(err, ppath); + return (err); + } + + for (i = 0; i < (int)l_state.ib_tbl.config.enc_num_elem; i++) { + elem_index++; + if (l_state.ib_tbl.config.type_hdr[i].type == ELM_TYP_BP) { + break; + } + elem_index += l_state.ib_tbl.config.type_hdr[i].num; + } + (void) bcopy((const void *) + &(l_state.ib_tbl.p2_s.element[elem_index]), + (void *)&bpf, sizeof (bpf)); + (void) bcopy((const void *) + &(l_state.ib_tbl.p2_s.element[elem_index + 1]), + (void *)&bpr, sizeof (bpr)); + + if (fr_flag) { + device_off = l_state.drv_front[slot].ib_status.dev_off; + bypass_a_en = l_state.drv_front[slot].ib_status.bypass_a_en; + bypass_b_en = l_state.drv_front[slot].ib_status.bypass_b_en; + ib_status_code = l_state.drv_front[slot].ib_status.code; + } else { + device_off = l_state.drv_rear[slot].ib_status.dev_off; + bypass_a_en = l_state.drv_rear[slot].ib_status.bypass_a_en; + bypass_b_en = l_state.drv_rear[slot].ib_status.bypass_b_en; + ib_status_code = l_state.drv_rear[slot].ib_status.code; + } + if (device_off) { + (void) fprintf(stdout, + MSGSTR(2000, + "%s is offlined and bypassed.\n" + " Could not get device specific" + " information.\n\n"), + devname); + } else if (bypass_a_en && bypass_b_en) { + (void) fprintf(stdout, + MSGSTR(2001, + "%s is bypassed (Port:AB).\n" + " Could not get device specific" + " information.\n\n"), + devname); + } else if (ib_status_code == S_NOT_INSTALLED) { + (void) fprintf(stdout, + MSGSTR(2002, + "Slot %s is empty.\n\n"), + devname); + } else if (((bpf.code != S_NOT_INSTALLED) && + ((bpf.byp_a_enabled || bpf.en_bypass_a) && + (bpf.byp_b_enabled || bpf.en_bypass_b))) || + ((bpr.code != S_NOT_INSTALLED) && + ((bpr.byp_a_enabled || bpr.en_bypass_a) && + (bpr.byp_b_enabled || bpr.en_bypass_b)))) { + (void) fprintf(stdout, + MSGSTR(2003, + "Backplane(Port:AB) is bypassed.\n" + " Could not get device specific" + " information for" + " %s.\n\n"), devname); + } else { + (void) fprintf(stderr, + MSGSTR(33, + " Error: converting" + " %s to physical path.\n" + " Invalid pathname.\n"), + devname); + } + return (-1); +} + +/* + * Given an error number, this functions + * calls the get_errString() to print a + * corresponding error message to the stderr. + * get_errString() always returns an error + * message, even in case of undefined error number. + * So, there is no need to check for a NULL pointer + * while printing the error message to the stdout. + * + * RETURNS: N/A + * + */ +void +print_errString(int errnum, char *devpath) +{ + +char *errStr; + + errStr = g_get_errString(errnum); + + if (devpath == NULL) { + (void) fprintf(stderr, + "%s \n\n", errStr); + } else { + (void) fprintf(stderr, + "%s - %s.\n\n", errStr, devpath); + } + + /* free the allocated memory for error string */ + if (errStr != NULL) + (void) free(errStr); +} + +/* + * adm_inquiry() Display the inquiry information for + * a SENA enclosure(s) or disk(s). + * + * RETURNS: + * none. + */ +int +adm_inquiry(char **argv) +{ +L_inquiry inq; +L_inquiry80 inq80; +size_t serial_len; +int path_index = 0, retval = 0; +int slot, f_r, err = 0, argpwwn, argnwwn; +char inq_path[MAXNAMELEN]; +char *path_phys = NULL, *ptr; +Path_struct *path_struct; +WWN_list *wwn_list, *wwn_list_ptr, *list_start; +char last_logical_path[MAXPATHLEN]; + + while (argv[path_index] != NULL) { + if ((err = l_convert_name(argv[path_index], &path_phys, + &path_struct, Options & PVERBOSE)) != 0) { + (void) strcpy(inq_path, argv[path_index]); + if (((ptr = strstr(inq_path, ",")) != NULL) && + ((*(ptr + 1) == 'f') || (*(ptr + 1) == 'r') || + (*(ptr +1) == 's'))) { + if (err != -1) { + (void) print_errString(err, argv[path_index]); + path_index++; + retval++; + continue; + } + *ptr = NULL; + slot = path_struct->slot; + f_r = path_struct->f_flag; + path_phys = NULL; + if ((err = l_convert_name(inq_path, &path_phys, + &path_struct, Options & PVERBOSE)) != 0) { + (void) fprintf(stderr, + MSGSTR(33, + " Error: converting" + " %s to physical path.\n" + " Invalid pathname.\n"), + argv[path_index]); + if (err != -1) { + (void) print_errString(err, + argv[path_index]); + } + path_index++; + retval++; + continue; + } + if ((err = print_devState(argv[path_index], + path_struct->p_physical_path, + f_r, slot, Options & PVERBOSE)) != 0) { + path_index++; + retval++; + continue; + } + } else { + if (err != -1) { + (void) print_errString(err, argv[path_index]); + } else { + (void) fprintf(stderr, "\n "); + (void) fprintf(stderr, + MSGSTR(112, "Error: Invalid pathname (%s)"), + argv[path_index]); + (void) fprintf(stderr, "\n"); + } + } + path_index++; + retval++; + continue; + } + + if (strstr(argv[path_index], "/") != NULL) { + if (err = g_get_inquiry(path_phys, &inq)) { + (void) fprintf(stderr, "\n"); + (void) print_errString(err, argv[path_index]); + (void) fprintf(stderr, "\n"); + path_index++; + retval++; + continue; + } + + serial_len = sizeof (inq80.inq_serial); + if (err = g_get_serial_number(path_phys, inq80.inq_serial, + &serial_len)) { + (void) fprintf(stderr, "\n"); + (void) print_errString(err, argv[path_index]); + (void) fprintf(stderr, "\n"); + path_index++; + retval++; + continue; + } + print_inq_data(argv[path_index], path_phys, inq, + inq80.inq_serial, serial_len); + path_index++; + continue; + } + if ((err = g_get_wwn_list(&wwn_list, 0)) != 0) { + return (err); + } + g_sort_wwn_list(&wwn_list); + list_start = wwn_list; + argpwwn = argnwwn = 0; + (void) strcpy(last_logical_path, path_phys); + for (wwn_list_ptr = wwn_list; wwn_list_ptr != NULL; + wwn_list_ptr = wwn_list_ptr->wwn_next) { + if (strcasecmp(wwn_list_ptr->port_wwn_s, path_struct->argv) == + 0) { + list_start = wwn_list_ptr; + argpwwn = 1; + break; + } else if (strcasecmp(wwn_list_ptr->node_wwn_s, + path_struct->argv) == 0) { + list_start = wwn_list_ptr; + argnwwn = 1; + break; + } + } + + if (!(argpwwn || argnwwn)) { + /* + * if the wwn list is null or the arg device not found + * from the wwn list, still go ahead to issue inquiry. + */ + if (err = g_get_inquiry(path_phys, &inq)) { + (void) fprintf(stderr, "\n"); + (void) print_errString(err, argv[path_index]); + (void) fprintf(stderr, "\n"); + path_index++; + retval++; + continue; + } + + serial_len = sizeof (inq80.inq_serial); + if (err = g_get_serial_number(path_phys, inq80.inq_serial, + &serial_len)) { + (void) fprintf(stderr, "\n"); + (void) print_errString(err, argv[path_index]); + (void) fprintf(stderr, "\n"); + path_index++; + retval++; + continue; + } + print_inq_data(argv[path_index], path_phys, inq, + inq80.inq_serial, serial_len); + (void) g_free_wwn_list(&wwn_list); + path_index++; + continue; + } + + for (wwn_list_ptr = list_start; wwn_list_ptr != NULL; + wwn_list_ptr = wwn_list_ptr->wwn_next) { + if (argpwwn) { + if (strcasecmp(wwn_list_ptr->port_wwn_s, + path_struct->argv) != 0) { + continue; + } + (void) strcpy(path_phys, + wwn_list_ptr->physical_path); + } else if (argnwwn) { + if (strcasecmp(wwn_list_ptr->node_wwn_s, + path_struct->argv) != 0) { + continue; + } + if (strstr(wwn_list_ptr->logical_path, + last_logical_path) != NULL) { + continue; + } + (void) strcpy(path_phys, + wwn_list_ptr->physical_path); + (void) strcpy(last_logical_path, + wwn_list_ptr->logical_path); + } + + if (err = g_get_inquiry(path_phys, &inq)) { + (void) fprintf(stderr, "\n"); + (void) print_errString(err, argv[path_index]); + (void) fprintf(stderr, "\n"); + retval++; + break; + } + + serial_len = sizeof (inq80.inq_serial); + if (err = g_get_serial_number(path_phys, inq80.inq_serial, + &serial_len)) { + (void) fprintf(stderr, "\n"); + (void) print_errString(err, argv[path_index]); + (void) fprintf(stderr, "\n"); + retval++; + break; + } + print_inq_data(argv[path_index], path_phys, inq, + inq80.inq_serial, serial_len); + + } + + (void) g_free_wwn_list(&wwn_list); + path_index++; + } + return (retval); +} + +/* + * FORCELIP expert function + */ +int +adm_forcelip(char **argv) +{ +int slot, f_r, path_index = 0, err = 0, retval = 0; +Path_struct *path_struct = NULL; +char *path_phys = NULL, *ptr; +char err_path[MAXNAMELEN]; + + while (argv[path_index] != NULL) { + if ((err = l_convert_name(argv[path_index], &path_phys, + &path_struct, Options & PVERBOSE)) != 0) { + (void) strcpy(err_path, argv[path_index]); + if (err != -1) { + (void) print_errString(err, argv[path_index]); + path_index++; + retval++; + continue; + } + if (((ptr = strstr(err_path, ", ")) != NULL) && + ((*(ptr + 1) == 'f') || (*(ptr + 1) == 'r') || + (*(ptr +1) == 's'))) { + *ptr = NULL; + slot = path_struct->slot; + f_r = path_struct->f_flag; + path_phys = NULL; + if ((err = l_convert_name(err_path, + &path_phys, &path_struct, + Options & PVERBOSE)) != 0) { + (void) fprintf(stderr, MSGSTR(33, + " Error: converting" + " %s to physical path.\n" + " Invalid pathname.\n"), + argv[path_index]); + if (err != -1) { + (void) print_errString(err, + argv[path_index]); + } + path_index++; + retval++; + continue; + } + if ((err = print_devState(argv[path_index], + path_struct->p_physical_path, + f_r, slot, Options & PVERBOSE)) != 0) { + path_index++; + retval++; + continue; + } + } else { + (void) fprintf(stderr, "\n "); + (void) fprintf(stderr, MSGSTR(112, + "Error: Invalid pathname (%s)"), + argv[path_index]); + (void) fprintf(stderr, "\n"); + } + path_index++; + retval++; + continue; + } + if (err = g_force_lip(path_phys, Options & PVERBOSE)) { + (void) print_errString(err, argv[path_index]); + path_index++; + retval++; + continue; + } + path_index++; + if (path_struct != NULL) { + (void) free(path_struct); + } + } + return (retval); +} + + +/* + * DISPLAY function + * + * RETURNS: + * 0 O.K. + */ +int +adm_display_config(char **argv) +{ +L_inquiry inq, ses_inq; +int i, slot, f_r, path_index = 0, err = 0, opnerr = 0; +int retval = 0; +gfc_map_t map; +Path_struct *path_struct; +char *path_phys = NULL, *ptr; +char ses_path[MAXPATHLEN], inq_path[MAXNAMELEN]; + + + while (argv[path_index] != NULL) { + VERBPRINT(MSGSTR(2108, " Displaying information for: %s\n"), + argv[path_index]); + map.dev_addr = (gfc_port_dev_info_t *)NULL; + if ((err = l_convert_name(argv[path_index], &path_phys, + &path_struct, Options & PVERBOSE)) != 0) { + if (strstr(argv[path_index], SCSI_VHCI) == NULL) { + + (void) strcpy(inq_path, argv[path_index]); + if (((ptr = strstr(inq_path, ",")) != NULL) && + ((*(ptr + 1) == 'f') || (*(ptr + 1) == 'r') || + (*(ptr +1) == 's'))) { + + if (err != -1) { + (void) print_errString(err, + argv[path_index]); + path_index++; + retval++; + continue; + } + *ptr = NULL; + slot = path_struct->slot; + f_r = path_struct->f_flag; + if ((err = l_convert_name(inq_path, &path_phys, + &path_struct, Options & PVERBOSE)) + != 0) { + + (void) fprintf(stderr, + MSGSTR(33, + " Error: converting" + " %s to physical path.\n" + " Invalid pathname.\n"), + argv[path_index]); + if (err != -1) { + (void) print_errString(err, + argv[path_index]); + } + path_index++; + retval++; + continue; + } + + if ((err = print_devState(argv[path_index], + path_struct->p_physical_path, + f_r, slot, Options & PVERBOSE)) != 0) { + path_index++; + retval++; + continue; + } + } else { + if (err != -1) { + (void) print_errString(err, + argv[path_index]); + } else { + (void) fprintf(stderr, "\n "); + (void) fprintf(stderr, + MSGSTR(112, + "Error: Invalid pathname (%s)"), + argv[path_index]); + (void) fprintf(stderr, "\n"); + } + } + + } else { + if (err != -1) { + (void) print_errString(err, + argv[path_index]); + } else { + (void) fprintf(stderr, "\n "); + (void) fprintf(stderr, + MSGSTR(112, + "Error: Invalid pathname (%s)"), + argv[path_index]); + (void) fprintf(stderr, "\n"); + } + } + + path_index++; + retval++; + continue; + } + + /* + * See what kind of device we are talking to. + */ + if ((opnerr = g_get_inquiry(path_phys, &inq)) != 0) { + if (opnerr == L_OPEN_PATH_FAIL) { + /* + * We check only for L_OPEN_PATH_FAIL because + * that is the only error code returned by + * g_get_inquiry() which is not got from the ioctl + * call itself. So, we are dependent, in a way, on the + * implementation of g_get_inquiry(). + * + */ + (void) print_errString(errno, argv[path_index]); + path_index++; + retval++; + continue; + } + } else if (!g_enclDiskChk((char *)inq.inq_vid, + (char *)inq.inq_pid)) { + if ((err = lun_display(path_struct, + inq, Options & PVERBOSE)) != 0) { + (void) print_errString(err, path_phys); + exit(1); + } + } else if (strstr((char *)inq.inq_pid, ENCLOSURE_PROD_ID) != NULL) { + /* + * Display SENA enclosure. + */ + (void) fprintf(stdout, "\n\t\t\t\t "); + print_chars(inq.inq_pid, sizeof (inq.inq_pid), 0); + + (void) fprintf(stdout, "\n"); + if (Options & OPTION_R) { + adm_display_err(path_phys, + (inq.inq_dtype & DTYPE_MASK)); + } else { + pho_display_config(path_phys); + } + } else if ((((inq.inq_dtype & DTYPE_MASK) == DTYPE_ESI)) && + (l_get_enc_type(inq) == DAK_ENC_TYPE)) { + /* + * Display for the Daktari/DPM + */ + (void) fprintf(stdout, "\n\t\t"); + for (i = 0; i < sizeof (inq.inq_pid); i++) { + (void) fprintf(stdout, "%c", inq.inq_pid[i]); + } + (void) fprintf(stdout, "\n"); + if (Options & OPTION_R) { + adm_display_err(path_phys, + (inq.inq_dtype & DTYPE_MASK)); + } else { + dpm_display_config(path_phys); + } + /* + * if device is in SENA enclosure + * + * if the slot is valid, then I know this is a SENA enclosure + * and can continue + * otherwise: + * I first get the ses_path, if this doesn't fail + * I retrieve the inquiry data from the ses node + * and check teh PID to make sure this is a SENA + */ + } else if (((inq.inq_dtype & DTYPE_MASK) == DTYPE_DIRECT) && + ((path_struct->slot_valid == 1) || + ((g_get_dev_map(path_phys, &map, + (Options & PVERBOSE)) == 0) && + (l_get_ses_path(path_phys, ses_path, + &map, Options & PVERBOSE) == 0) && + (g_get_inquiry(ses_path, &ses_inq) == 0) && + ((strstr((char *)ses_inq.inq_pid, ENCLOSURE_PROD_ID) + != NULL))))) { + if (Options & OPTION_R) { + adm_display_err(path_phys, + (inq.inq_dtype & DTYPE_MASK)); + } else { + display_fc_disk(path_struct, ses_path, &map, inq, + Options & PVERBOSE); + } + + } else if (strstr((char *)inq.inq_pid, "SUN_SEN") != 0) { + if (strcmp(argv[path_index], path_phys) != 0) { + (void) fprintf(stdout, " "); + (void) fprintf(stdout, + MSGSTR(5, "Physical Path:")); + (void) fprintf(stdout, "\n %s\n", path_phys); + } + (void) fprintf(stdout, MSGSTR(2109, "DEVICE is a ")); + print_chars(inq.inq_vid, sizeof (inq.inq_vid), 1); + (void) fprintf(stdout, " "); + print_chars(inq.inq_pid, sizeof (inq.inq_pid), 1); + (void) fprintf(stdout, MSGSTR(2110, " card.")); + if (inq.inq_len > 31) { + (void) fprintf(stdout, " "); + (void) fprintf(stdout, MSGSTR(26, "Revision:")); + (void) fprintf(stdout, " "); + print_chars(inq.inq_revision, + sizeof (inq.inq_revision), 0); + } + (void) fprintf(stdout, "\n"); + /* if device is not in SENA or SSA enclosures. */ + } else if ((inq.inq_dtype & DTYPE_MASK) < 0x10) { + switch ((inq.inq_dtype & DTYPE_MASK)) { + case DTYPE_DIRECT: + case DTYPE_SEQUENTIAL: /* Tape */ + if (Options & OPTION_R) { + adm_display_err(path_phys, + (inq.inq_dtype & DTYPE_MASK)); + } else if (non_encl_fc_disk_display(path_struct, + inq, Options & PVERBOSE) != 0) { + (void) fprintf(stderr, + MSGSTR(2111, + "Error: getting the device" + " information.\n")); + retval++; + } + break; + /* case 0x01: same as default */ + default: + (void) fprintf(stdout, " "); + (void) fprintf(stdout, MSGSTR(35, + "Device Type:")); + (void) fprintf(stdout, "%s\n", + dtype[inq.inq_dtype & DTYPE_MASK]); + break; + } + } else if ((inq.inq_dtype & DTYPE_MASK) < 0x1f) { + (void) fprintf(stdout, + MSGSTR(2112, " Device type: Reserved")); + (void) fprintf(stdout, "\n"); + } else { + (void) fprintf(stdout, + MSGSTR(2113, " Device type: Unknown device")); + (void) fprintf(stdout, "\n"); + } + path_index++; + if (map.dev_addr != NULL) { + free((void *)map.dev_addr); + } + (void) free(path_struct); + } + return (retval); +} + + +/* + * Powers off a list of SENA enclosure(s) + * and disk(s) which is provided by the user. + * + * RETURNS: + * none. + */ +int +adm_power_off(char **argv, int off_flag) +{ +int path_index = 0, err = 0, retval = 0; +L_inquiry inq; +char *path_phys = NULL; +Path_struct *path_struct; + + while (argv[path_index] != NULL) { + if ((err = l_convert_name(argv[path_index], &path_phys, + &path_struct, Options & PVERBOSE)) != 0) { + /* + * In case we did not find the device + * in the /devices directory. + * + * Only valid for pathnames like box,f1 + */ + if (path_struct->ib_path_flag) { + path_phys = path_struct->p_physical_path; + } else { + (void) fprintf(stderr, + MSGSTR(33, + " Error: converting" + " %s to physical path.\n" + " Invalid pathname.\n"), + argv[path_index]); + if (err != -1) { + (void) print_errString(err, + argv[path_index]); + } + path_index++; + retval++; + continue; + } + } + if (path_struct->ib_path_flag) { + /* + * We are addressing a disk using a path + * format type box,f1. + */ + if (err = l_dev_pwr_up_down(path_phys, + path_struct, off_flag, Options & PVERBOSE, + Options & OPTION_CAPF)) { + /* + * Is it Bypassed... try to give more + * informtaion. + */ + print_devState(argv[path_index], + path_struct->p_physical_path, + path_struct->f_flag, path_struct->slot, + Options & PVERBOSE); + retval++; + } + path_index++; + continue; + } + + if (err = g_get_inquiry(path_phys, &inq)) { + (void) print_errString(err, argv[path_index]); + path_index++; + retval++; + continue; + } + if ((strstr((char *)inq.inq_pid, ENCLOSURE_PROD_ID) != 0) || + (strncmp((char *)inq.inq_vid, "SUN ", + sizeof (inq.inq_vid)) && + ((inq.inq_dtype & DTYPE_MASK) == DTYPE_ESI))) { + + if (get_enclStatus(path_phys, argv[path_index], + off_flag) != 0) { + path_index++; + retval++; + continue; + } + /* power off SENA enclosure. */ + if (err = l_pho_pwr_up_down(argv[path_index], path_phys, + off_flag, Options & PVERBOSE, + Options & OPTION_CAPF)) { + (void) print_errString(err, argv[path_index]); + retval++; + } + } else if ((inq.inq_dtype & DTYPE_MASK) == DTYPE_DIRECT) { + if (err = l_dev_pwr_up_down(path_phys, + path_struct, off_flag, Options & PVERBOSE, + Options & OPTION_CAPF)) { + (void) print_errString(err, argv[path_index]); + retval++; + } + } else { + /* + * SSA section: + */ + (void) print_errString(L_INVALID_PATH, + argv[path_index]); + } + path_index++; + } + return (retval); +} + + + +void +adm_bypass_enable(char **argv, int bypass_flag) +{ +int path_index = 0, err = 0; +L_inquiry inq; +char *path_phys = NULL; +Path_struct *path_struct; + + if ((err = l_convert_name(argv[path_index], &path_phys, + &path_struct, Options & PVERBOSE)) != 0) { + /* + * In case we did not find the device + * in the /devices directory. + * + * Only valid for pathnames like box,f1 + */ + if (path_struct->ib_path_flag) { + path_phys = path_struct->p_physical_path; + } else { + (void) fprintf(stderr, + MSGSTR(33, + " Error: converting" + " %s to physical path.\n" + " Invalid pathname.\n"), + argv[path_index]); + if (err != -1) { + (void) print_errString(err, argv[path_index]); + } + exit(-1); + } + } + if (path_struct->ib_path_flag) { + if (Options & OPTION_F) { + E_USEAGE(); + exit(-1); + } + /* + * We are addressing a disk using a path + * format type box,f1 and no disk + * path was found. + * So set the Force flag so no reserved/busy + * check is performed. + */ + if (err = l_dev_bypass_enable(path_struct, + bypass_flag, OPTION_CAPF, + Options & OPTION_A, + Options & PVERBOSE)) { + (void) print_errString(err, argv[path_index]); + exit(-1); + } + return; + } + + if (err = g_get_inquiry(path_phys, &inq)) { + (void) print_errString(err, argv[path_index]); + exit(-1); + } + if ((strstr((char *)inq.inq_pid, ENCLOSURE_PROD_ID) != 0) || + (strncmp((char *)inq.inq_vid, "SUN ", + sizeof (inq.inq_vid)) && + ((inq.inq_dtype & DTYPE_MASK) == DTYPE_ESI))) { + if ((!((Options & OPTION_F) || + (Options & OPTION_R))) || + ((Options & OPTION_R) && + (Options & OPTION_F))) { + E_USEAGE(); + exit(-1); + } + if (err = l_bp_bypass_enable(path_phys, bypass_flag, + Options & OPTION_A, + Options & OPTION_F, + Options & OPTION_CAPF, + Options & PVERBOSE)) { + (void) print_errString(err, argv[path_index]); + exit(-1); + } + } else if ((inq.inq_dtype & DTYPE_MASK) == DTYPE_DIRECT) { + if (Options & OPTION_F) { + E_USEAGE(); + exit(-1); + } + if (err = l_dev_bypass_enable(path_struct, + bypass_flag, Options & OPTION_CAPF, + Options & OPTION_A, + Options & PVERBOSE)) { + (void) print_errString(err, argv[path_index]); + exit(-1); + } + } +} + +/* + * adm_download() Download subsystem microcode. + * Path must point to a LUX IB. + * + * RETURNS: + * None. + */ +void +adm_download(char **argv, char *file_name) +{ +int path_index = 0, err = 0; +char *path_phys = NULL; +L_inquiry inq; +Path_struct *path_struct; + + while (argv[path_index] != NULL) { + /* + * See what kind of device we are talking to. + */ + if ((err = l_convert_name(argv[path_index], &path_phys, + &path_struct, Options & PVERBOSE)) != 0) { + (void) fprintf(stderr, + MSGSTR(33, + " Error: converting" + " %s to physical path.\n" + " Invalid pathname.\n"), + argv[path_index]); + if (err != -1) { + (void) print_errString(err, argv[path_index]); + } + exit(-1); + } + if (err = g_get_inquiry(path_phys, &inq)) { + (void) print_errString(err, argv[path_index]); + exit(-1); + } + if ((strstr((char *)inq.inq_pid, ENCLOSURE_PROD_ID) != 0) || + (strncmp((char *)inq.inq_vid, "SUN ", + sizeof (inq.inq_vid)) && + ((inq.inq_dtype & DTYPE_MASK) == DTYPE_ESI))) { + if (err = l_download(path_phys, + file_name, (Options & SAVE), + (Options & PVERBOSE))) { + (void) print_errString(err, + (err == L_OPEN_PATH_FAIL) ? + argv[path_index]: file_name); + exit(-1); + } + } else { + (void) fprintf(stderr, + MSGSTR(112, "Error: Invalid pathname (%s)"), + argv[path_index]); + } + path_index++; + } +} + +/* + * display_link_status() Reads and displays the link status. + * + * RETURNS: + * none. + */ +void +display_link_status(char **argv) +{ +AL_rls *rls = NULL, *n; +int path_index = 0, err = 0; +char *path_phys = NULL; +Path_struct *path_struct; + + + while (argv[path_index] != NULL) { + if ((err = l_convert_name(argv[path_index], &path_phys, + &path_struct, Options & PVERBOSE)) != 0) { + (void) fprintf(stderr, + MSGSTR(33, + " Error: converting" + " %s to physical path.\n" + " Invalid pathname.\n"), + argv[path_index]); + if (err != -1) { + (void) print_errString(err, argv[path_index]); + } + exit(-1); + } + if (err = g_rdls(path_phys, &rls, Options & PVERBOSE)) { + (void) print_errString(err, argv[path_index]); + exit(-1); + } + n = rls; + if (n != NULL) { + (void) fprintf(stdout, + MSGSTR(2007, "\nLink Error Status " + "information for loop:%s\n"), + n->driver_path); + (void) fprintf(stdout, MSGSTR(2008, "al_pa lnk fail " + " sync loss signal loss sequence err" + " invalid word CRC\n")); + } + while (n) { + if ((n->payload.rls_linkfail == 0xffffffff) && + (n->payload.rls_syncfail == 0xffffffff) && + (n->payload.rls_sigfail == 0xffffffff) && + (n->payload.rls_primitiverr == 0xffffffff) && + (n->payload.rls_invalidword == 0xffffffff) && + (n->payload.rls_invalidcrc == 0xffffffff)) { + (void) fprintf(stdout, + "%x\t%-12d%-12d%-14d%-15d%-15d%-12d\n", + n->al_ha, + n->payload.rls_linkfail, + n->payload.rls_syncfail, + n->payload.rls_sigfail, + n->payload.rls_primitiverr, + n->payload.rls_invalidword, + n->payload.rls_invalidcrc); + } else { + (void) fprintf(stdout, + "%x\t%-12u%-12u%-14u%-15u%-15u%-12u\n", + n->al_ha, + n->payload.rls_linkfail, + n->payload.rls_syncfail, + n->payload.rls_sigfail, + n->payload.rls_primitiverr, + n->payload.rls_invalidword, + n->payload.rls_invalidcrc); + } + n = n->next; + } + + path_index++; + } + (void) fprintf(stdout, + MSGSTR(2009, "NOTE: These LESB counts are not" + " cleared by a reset, only power cycles.\n" + "These counts must be compared" + " to previously read counts.\n")); +} + + +/* + * ib_present_chk() Check to see if IB 0 or 1 is present in the box. + * + * RETURN: + * 1 if ib present + * 0 otherwise + */ +int +ib_present_chk(struct l_state_struct *l_state, int which_one) +{ +Ctlr_elem_st ctlr; +int i; +int elem_index = 0; +int result = 1; + + for (i = 0; i < (int)l_state->ib_tbl.config.enc_num_elem; i++) { + elem_index++; /* skip global */ + if (l_state->ib_tbl.config.type_hdr[i].type == ELM_TYP_IB) { + (void) bcopy((const void *) + &l_state->ib_tbl.p2_s.element[elem_index + which_one], + (void *)&ctlr, sizeof (ctlr)); + if (ctlr.code == S_NOT_INSTALLED) { + result = 0; + } + break; + } + elem_index += l_state->ib_tbl.config.type_hdr[i].num; + } + return (result); +} + +/* + * print_individual_state() Print individual disk status. + * + * RETURNS: + * none. + */ +void +print_individual_state(int status, int port) +{ + if (status & L_OPEN_FAIL) { + (void) fprintf(stdout, " ("); + (void) fprintf(stdout, + MSGSTR(28, "Open Failed")); + (void) fprintf(stdout, ") "); + } else if (status & L_NOT_READY) { + (void) fprintf(stdout, " ("); + (void) fprintf(stdout, + MSGSTR(20, "Not Ready")); + (void) fprintf(stdout, ") "); + } else if (status & L_NOT_READABLE) { + (void) fprintf(stdout, "("); + (void) fprintf(stdout, + MSGSTR(88, "Not Readable")); + (void) fprintf(stdout, ") "); + } else if (status & L_SPUN_DWN_D) { + (void) fprintf(stdout, " ("); + (void) fprintf(stdout, + MSGSTR(68, "Spun Down")); + (void) fprintf(stdout, ") "); + } else if (status & L_SCSI_ERR) { + (void) fprintf(stdout, " ("); + (void) fprintf(stdout, + MSGSTR(70, "SCSI Error")); + (void) fprintf(stdout, ") "); + } else if (status & L_RESERVED) { + if (port == PORT_A) { + (void) fprintf(stdout, + MSGSTR(2010, + " (Rsrv cnflt:A) ")); + } else if (port == PORT_B) { + (void) fprintf(stdout, + MSGSTR(2011, + " (Rsrv cnflt:B) ")); + } else { + (void) fprintf(stdout, + MSGSTR(2012, + " (Reserve cnflt)")); + } + } else if (status & L_NO_LABEL) { + (void) fprintf(stdout, "("); + (void) fprintf(stdout, + MSGSTR(92, "No UNIX Label")); + (void) fprintf(stdout, ") "); + } +} + + +/* + * display_disk_msg() Displays status for + * an individual SENA device. + * + * RETURNS: + * none. + */ +void +display_disk_msg(struct l_disk_state_struct *dsk_ptr, + struct l_state_struct *l_state, Bp_elem_st *bp, int front_flag) +{ +int loop_flag = 0; +int a_and_b = 0; +int state_a = 0, state_b = 0; + + if (dsk_ptr->ib_status.code == S_NOT_INSTALLED) { + (void) fprintf(stdout, + MSGSTR(30, "Not Installed")); + (void) fprintf(stdout, " "); + if (dsk_ptr->ib_status.fault || + dsk_ptr->ib_status.fault_req) { + (void) fprintf(stdout, "("); + (void) fprintf(stdout, + MSGSTR(2013, "Faulted")); + (void) fprintf(stdout, + ") "); + } else if (dsk_ptr->ib_status.ident || + dsk_ptr->ib_status.rdy_to_ins || + dsk_ptr->ib_status.rmv) { + (void) fprintf(stdout, + MSGSTR(2014, + "(LED Blinking) ")); + } else { + (void) fprintf(stdout, + " "); + } + } else if (dsk_ptr->ib_status.dev_off) { + (void) fprintf(stdout, MSGSTR(2015, "Off")); + if (dsk_ptr->ib_status.fault || dsk_ptr->ib_status.fault_req) { + (void) fprintf(stdout, "("); + (void) fprintf(stdout, + MSGSTR(2016, "Faulted")); + (void) fprintf(stdout, + ") "); + } else if (dsk_ptr->ib_status.bypass_a_en && + dsk_ptr->ib_status.bypass_b_en) { + (void) fprintf(stdout, + MSGSTR(2017, + "(Bypassed:AB)")); + (void) fprintf(stdout, + " "); + } else if (dsk_ptr->ib_status.bypass_a_en) { + (void) fprintf(stdout, + MSGSTR(2018, + "(Bypassed: A)")); + (void) fprintf(stdout, + " "); + } else if (dsk_ptr->ib_status.bypass_b_en) { + (void) fprintf(stdout, + MSGSTR(2019, + "(Bypassed: B)")); + (void) fprintf(stdout, + " "); + } else { + (void) fprintf(stdout, + " "); + } + } else { + (void) fprintf(stdout, MSGSTR(2020, "On")); + + if (dsk_ptr->ib_status.fault || dsk_ptr->ib_status.fault_req) { + (void) fprintf(stdout, " ("); + (void) fprintf(stdout, + MSGSTR(2021, "Faulted")); + (void) fprintf(stdout, ") "); + } else if (dsk_ptr->ib_status.bypass_a_en && + dsk_ptr->ib_status.bypass_b_en) { + (void) fprintf(stdout, " "); + (void) fprintf(stdout, + MSGSTR(2022, "(Bypassed:AB)")); + (void) fprintf(stdout, " "); + } else if (ib_present_chk(l_state, 0) && + dsk_ptr->ib_status.bypass_a_en) { + /* + * Before printing that the port is bypassed + * verify that there is an IB for this port. + * If not then don't print. + */ + (void) fprintf(stdout, " "); + (void) fprintf(stdout, + MSGSTR(2023, "(Bypassed: A)")); + (void) fprintf(stdout, " "); + } else if (ib_present_chk(l_state, 1) && + dsk_ptr->ib_status.bypass_b_en) { + (void) fprintf(stdout, " "); + (void) fprintf(stdout, + MSGSTR(2024, "(Bypassed: B)")); + (void) fprintf(stdout, " "); + } else if ((bp->code != S_NOT_INSTALLED) && + ((bp->byp_a_enabled || bp->en_bypass_a) && + !(bp->byp_b_enabled || bp->en_bypass_b))) { + (void) fprintf(stdout, + MSGSTR(2025, + " (Bypassed BP: A)")); + } else if ((bp->code != S_NOT_INSTALLED) && + ((bp->byp_b_enabled || bp->en_bypass_b) && + !(bp->byp_a_enabled || bp->en_bypass_a))) { + (void) fprintf(stdout, + MSGSTR(2026, + "(Bypassed BP: B)")); + } else if ((bp->code != S_NOT_INSTALLED) && + ((bp->byp_a_enabled || bp->en_bypass_a) && + (bp->byp_b_enabled || bp->en_bypass_b))) { + (void) fprintf(stdout, + MSGSTR(2027, + "(Bypassed BP:AB)")); + } else { + state_a = dsk_ptr->g_disk_state.d_state_flags[PORT_A]; + state_b = dsk_ptr->g_disk_state.d_state_flags[PORT_B]; + a_and_b = state_a & state_b; + + if (dsk_ptr->l_state_flag & L_NO_LOOP) { + (void) fprintf(stdout, + MSGSTR(2028, + " (Loop not accessible)")); + loop_flag = 1; + } else if (dsk_ptr->l_state_flag & L_INVALID_WWN) { + (void) fprintf(stdout, + MSGSTR(2029, + " (Invalid WWN) ")); + } else if (dsk_ptr->l_state_flag & L_INVALID_MAP) { + (void) fprintf(stdout, + MSGSTR(2030, + " (Login failed) ")); + } else if (dsk_ptr->l_state_flag & L_NO_PATH_FOUND) { + (void) fprintf(stdout, + MSGSTR(2031, + " (No path found)")); + } else if (a_and_b) { + print_individual_state(a_and_b, PORT_A_B); + } else if (state_a && (!state_b)) { + print_individual_state(state_a, PORT_A); + } else if ((!state_a) && state_b) { + print_individual_state(state_b, PORT_B); + } else if (state_a || state_b) { + /* NOTE: Double state - should do 2 lines. */ + print_individual_state(state_a | state_b, + PORT_A_B); + } else { + (void) fprintf(stdout, " ("); + (void) fprintf(stdout, + MSGSTR(29, "O.K.")); + (void) fprintf(stdout, + ") "); + } + } + if (loop_flag) { + (void) fprintf(stdout, " "); + } else if (strlen(dsk_ptr->g_disk_state.node_wwn_s)) { + (void) fprintf(stdout, "%s", + dsk_ptr->g_disk_state.node_wwn_s); + } else { + (void) fprintf(stdout, " "); + } + } + if (front_flag) { + (void) fprintf(stdout, " "); + } +} + + + +/* + * pho_display_config() Displays device status + * information for a SENA enclosure. + * + * RETURNS: + * none. + */ +void +pho_display_config(char *path_phys) +{ +L_state l_state; +Bp_elem_st bpf, bpr; +int i, j, elem_index = 0, err = 0; + + + /* Get global status */ + if (err = l_get_status(path_phys, &l_state, + (Options & PVERBOSE))) { + (void) print_errString(err, path_phys); + exit(-1); + } + + /* + * Look for abnormal status. + */ + if (l_state.ib_tbl.p2_s.ui.ab_cond) { + abnormal_condition_display(&l_state); + } + + (void) fprintf(stdout, + MSGSTR(2032, " DISK STATUS \n" + "SLOT FRONT DISKS (Node WWN) " + " REAR DISKS (Node WWN)\n")); + /* + * Print the status for each disk + */ + for (j = 0; j < (int)l_state.ib_tbl.config.enc_num_elem; j++) { + elem_index++; + if (l_state.ib_tbl.config.type_hdr[j].type == ELM_TYP_BP) + break; + elem_index += l_state.ib_tbl.config.type_hdr[j].num; + } + (void) bcopy((const void *) + &(l_state.ib_tbl.p2_s.element[elem_index]), + (void *)&bpf, sizeof (bpf)); + (void) bcopy((const void *) + &(l_state.ib_tbl.p2_s.element[elem_index + 1]), + (void *)&bpr, sizeof (bpr)); + + for (i = 0; i < (int)l_state.total_num_drv/2; i++) { + (void) fprintf(stdout, "%-2d ", i); + display_disk_msg(&l_state.drv_front[i], &l_state, &bpf, 1); + display_disk_msg(&l_state.drv_rear[i], &l_state, &bpr, 0); + (void) fprintf(stdout, "\n"); + } + + + + /* + * Display the subsystem status. + */ + (void) fprintf(stdout, + MSGSTR(2242, + " SUBSYSTEM STATUS\nFW Revision:")); + print_chars(l_state.ib_tbl.config.prod_revision, + sizeof (l_state.ib_tbl.config.prod_revision), 1); + (void) fprintf(stdout, MSGSTR(2034, " Box ID:%d"), + l_state.ib_tbl.box_id); + (void) fprintf(stdout, " "); + (void) fprintf(stdout, MSGSTR(90, "Node WWN:")); + for (i = 0; i < 8; i++) { + (void) fprintf(stdout, "%1.2x", + l_state.ib_tbl.config.enc_node_wwn[i]); + } + /* Make sure NULL terminated although it is supposed to be */ + if (strlen((const char *)l_state.ib_tbl.enclosure_name) <= + sizeof (l_state.ib_tbl.enclosure_name)) { + (void) fprintf(stdout, MSGSTR(2035, " Enclosure Name:%s\n"), + l_state.ib_tbl.enclosure_name); + } + + /* + * + */ + elem_index = 0; + /* Get and print CONTROLLER messages */ + for (i = 0; i < (int)l_state.ib_tbl.config.enc_num_elem; i++) { + elem_index++; /* skip global */ + switch (l_state.ib_tbl.config.type_hdr[i].type) { + case ELM_TYP_PS: + ps_messages(&l_state, i, elem_index); + break; + case ELM_TYP_FT: + fan_messages(&l_state, i, elem_index); + break; + case ELM_TYP_BP: + back_plane_messages(&l_state, i, elem_index); + break; + case ELM_TYP_IB: + ctlr_messages(&l_state, i, elem_index); + break; + case ELM_TYP_LN: + /* + * NOTE: I just use the Photon's message + * string here and don't look at the + * language code. The string includes + * the language name. + */ + if (l_state.ib_tbl.config.type_hdr[i].text_len != 0) { + (void) fprintf(stdout, "%s\t", + l_state.ib_tbl.config.text[i]); + } + break; + case ELM_TYP_LO: /* Loop configuration */ + loop_messages(&l_state, i, elem_index); + break; + case ELM_TYP_MB: /* Loop configuration */ + mb_messages(&l_state, i, elem_index); + break; + + } + /* + * Calculate the index to each element. + */ + elem_index += l_state.ib_tbl.config.type_hdr[i].num; + } + (void) fprintf(stdout, "\n"); +} + + + + +/* + * dpm_display_config() Displays device status + * information for a DAKTARI enclosure. + * + * RETURNS: + * none. + */ +void +dpm_display_config(char *path_phys) +{ +L_state l_state; +Bp_elem_st bpf, bpr; +int i, j, elem_index = 0, err = 0, count; + + + /* Get global status */ + if (err = l_get_status(path_phys, &l_state, + (Options & PVERBOSE))) { + (void) print_errString(err, path_phys); + exit(-1); + } + + /* + * Look for abnormal status. + */ + if (l_state.ib_tbl.p2_s.ui.ab_cond) { + abnormal_condition_display(&l_state); + } + + (void) fprintf(stdout, + MSGSTR(2247, " DISK STATUS \n" + "SLOT DISKS (Node WWN) \n")); + /* + * Print the status for each disk + */ + for (j = 0; j < (int)l_state.ib_tbl.config.enc_num_elem; j++) { + elem_index++; + if (l_state.ib_tbl.config.type_hdr[j].type == ELM_TYP_BP) + break; + elem_index += l_state.ib_tbl.config.type_hdr[j].num; + } + (void) bcopy((const void *) + &(l_state.ib_tbl.p2_s.element[elem_index]), + (void *)&bpf, sizeof (bpf)); + (void) bcopy((const void *) + &(l_state.ib_tbl.p2_s.element[elem_index + 1]), + (void *)&bpr, sizeof (bpr)); + + for (i = 0, count = 0; + i < (int)l_state.total_num_drv/2; + i++, count++) { + (void) fprintf(stdout, "%-2d ", count); + display_disk_msg(&l_state.drv_front[i], &l_state, &bpf, 1); + (void) fprintf(stdout, "\n"); + } + for (i = 0; i < (int)l_state.total_num_drv/2; i++, count++) { + (void) fprintf(stdout, "%-2d ", count); + display_disk_msg(&l_state.drv_rear[i], &l_state, &bpf, 1); + (void) fprintf(stdout, "\n"); + } + + + /* + * Display the subsystem status. + */ + (void) fprintf(stdout, + MSGSTR(2033, + "\t\tSUBSYSTEM STATUS\nFW Revision:")); + for (i = 0; i < sizeof (l_state.ib_tbl.config.prod_revision); i++) { + (void) fprintf(stdout, "%c", + l_state.ib_tbl.config.prod_revision[i]); + } + (void) fprintf(stdout, MSGSTR(2034, " Box ID:%d"), + l_state.ib_tbl.box_id); + (void) fprintf(stdout, "\n "); + + (void) fprintf(stdout, MSGSTR(90, "Node WWN:")); + + for (i = 0; i < 8; i++) { + (void) fprintf(stdout, "%1.2x", + l_state.ib_tbl.config.enc_node_wwn[i]); + } + /* Make sure NULL terminated although it is supposed to be */ + if (strlen((const char *)l_state.ib_tbl.enclosure_name) <= + sizeof (l_state.ib_tbl.enclosure_name)) { + (void) fprintf(stdout, MSGSTR(2035, " Enclosure Name:%s\n"), + l_state.ib_tbl.enclosure_name); + } + + /* + * + */ + elem_index = 0; + /* Get and print CONTROLLER messages */ + for (i = 0; i < (int)l_state.ib_tbl.config.enc_num_elem; i++) { + elem_index++; /* skip global */ + switch (l_state.ib_tbl.config.type_hdr[i].type) { + case ELM_TYP_PS: + ps_messages(&l_state, i, elem_index); + break; + case ELM_TYP_FT: + fan_messages(&l_state, i, elem_index); + break; + case ELM_TYP_BP: + dpm_SSC100_messages(&l_state, i, elem_index); + break; + case ELM_TYP_IB: + ctlr_messages(&l_state, i, elem_index); + break; + case ELM_TYP_LN: + /* + * NOTE: I just use the Photon's message + * string here and don't look at the + * language code. The string includes + * the language name. + */ + if (l_state.ib_tbl.config.type_hdr[i].text_len != 0) { + (void) fprintf(stdout, "%s\t", + l_state.ib_tbl.config.text[i]); + } + break; + case ELM_TYP_LO: /* Loop configuration */ + loop_messages(&l_state, i, elem_index); + break; + case ELM_TYP_MB: /* Loop configuration */ + mb_messages(&l_state, i, elem_index); + break; + case ELM_TYP_FL: + trans_messages(&l_state, 1); + break; + + } + /* + * Calculate the index to each element. + */ + elem_index += l_state.ib_tbl.config.type_hdr[i].num; + } + (void) fprintf(stdout, "\n"); +} + + + + + + +/* + * Change the FPM (Front Panel Module) password of the + * subsystem associated with the IB addressed by the + * enclosure or pathname to name. + * + */ +void +intfix(void) +{ + if (termio_fd) { + termios.c_lflag |= ECHO; + ioctl(termio_fd, TCSETS, &termios); + } + exit(SIGINT); +} + + +/* + * up_password() Changes the password for SENA enclosure. + * + * RETURNS: + * none. + */ +void +up_password(char **argv) +{ +int path_index = 0, err = 0; +char password[1024]; +char input[1024]; +int i, j, matched, equal; +L_inquiry inq; +void (*sig)(); +char *path_phys = NULL; +Path_struct *path_struct; + + + if ((termio_fd = open("/dev/tty", O_RDONLY)) == -1) { + (void) fprintf(stderr, + MSGSTR(2036, "Error: tty open failed.\n")); + exit(-1); + } + ioctl(termio_fd, TCGETS, &termios); + sig = sigset(SIGINT, (void (*)())intfix); + /* + * Make sure path valid and is to a PHO + * before bothering operator. + */ + if ((err = l_convert_name(argv[path_index], &path_phys, + &path_struct, Options & PVERBOSE)) != 0) { + (void) fprintf(stderr, + MSGSTR(33, + " Error: converting" + " %s to physical path.\n" + " Invalid pathname.\n"), + argv[path_index]); + if (err != -1) { + (void) print_errString(err, argv[path_index]); + } + exit(-1); + } + if (err = g_get_inquiry(path_phys, &inq)) { + (void) print_errString(err, argv[path_index]); + exit(-1); + } + if ((strstr((char *)inq.inq_pid, ENCLOSURE_PROD_ID) == 0) && + (!(strncmp((char *)inq.inq_vid, "SUN ", + sizeof (inq.inq_vid)) && + ((inq.inq_dtype & DTYPE_MASK) == DTYPE_ESI)))) { + /* + * Again this is like the ssaadm code in that the name + * is still not defined before this code must be released. + */ + (void) fprintf(stderr, + MSGSTR(2037, "Error: Enclosure is not a %s\n"), + ENCLOSURE_PROD_ID); + exit(-1); + } + (void) fprintf(stdout, + MSGSTR(2038, + "Changing FPM password for subsystem %s\n"), + argv[path_index]); + + equal = 0; + while (!equal) { + memset(input, 0, sizeof (input)); + memset(password, 0, sizeof (password)); + (void) fprintf(stdout, + MSGSTR(2039, "New password: ")); + + termios.c_lflag &= ~ECHO; + ioctl(termio_fd, TCSETS, &termios); + + (void) gets(input); + (void) fprintf(stdout, + MSGSTR(2040, "\nRe-enter new password: ")); + (void) gets(password); + termios.c_lflag |= ECHO; + ioctl(termio_fd, TCSETS, &termios); + for (i = 0; input[i]; i++) { + if (!isdigit(input[i])) { + (void) fprintf(stderr, + MSGSTR(2041, "\nError: Invalid password." + " The password" + " must be 4 decimal-digit characters.\n")); + exit(-1); + } + } + if (i && (i != 4)) { + (void) fprintf(stderr, + MSGSTR(2042, "\nError: Invalid password." + " The password" + " must be 4 decimal-digit characters.\n")); + exit(-1); + } + for (j = 0; password[j]; j++) { + if (!isdigit(password[j])) { + (void) fprintf(stderr, + MSGSTR(2043, "\nError: Invalid password." + " The password" + " must be 4 decimal-digit characters.\n")); + exit(-1); + } + } + if (i != j) { + matched = -1; + } else for (i = matched = 0; password[i]; i++) { + if (password[i] == input[i]) { + matched++; + } + } + if ((matched != -1) && (matched == i)) { + equal = 1; + } else { + (void) fprintf(stdout, + MSGSTR(2044, "\npassword: They don't match;" + " try again.\n")); + } + } + (void) fprintf(stdout, "\n"); + sscanf(input, "%s", password); + (void) signal(SIGINT, sig); /* restore signal handler */ + + /* Send new password to IB */ + if (l_new_password(path_phys, input)) { + (void) print_errString(err, path_phys); + exit(-1); + } +} + +/* + * Call g_failover to process failover command + */ +void +adm_failover(char **argv) +{ +int path_index = 0, err = 0; +char pathclass[20]; +char *path_phys = NULL; + + (void) memset(pathclass, 0, sizeof (pathclass)); + (void) strcpy(pathclass, argv[path_index++]); + if ((strcmp(pathclass, "primary") != 0) && + (strcmp(pathclass, "secondary") != 0)) { + (void) fprintf(stderr, + MSGSTR(2300, "Incorrect pathclass\n")); + exit(-1); + } + + while (argv[path_index] != NULL) { + path_phys = g_get_physical_name(argv[path_index++]); + if ((path_phys == NULL) || + (strstr(path_phys, SCSI_VHCI) == NULL)) { + (void) fprintf(stderr, + MSGSTR(2301, "Incorrect pathname\n")); + exit(-1); + } + + if (err = g_failover(path_phys, pathclass)) { + (void) print_errString(err, NULL); + exit(-1); + } + } +} + + + +/* + * up_encl_name() Update the enclosures logical name. + * + * RETURNS: + * none. + */ +void +up_encl_name(char **argv, int argc) +{ +int i, rval, al_pa, path_index = 0, err = 0; +L_inquiry inq; +Box_list *b_list = NULL; +uchar_t node_wwn[WWN_SIZE], port_wwn[WWN_SIZE]; +char wwn1[(WWN_SIZE*2)+1], name[1024], *path_phys = NULL; +Path_struct *path_struct; + + (void) memset(name, 0, sizeof (name)); + (void) memset(&inq, 0, sizeof (inq)); + (void) sscanf(argv[path_index++], "%s", name); + for (i = 0; name[i]; i++) { + if ((!isalnum(name[i]) && + ((name[i] != '#') && + (name[i] != '-') && + (name[i] != '_') && + (name[i] != '.'))) || i >= 16) { + (void) fprintf(stderr, + MSGSTR(2045, "Error: Invalid enclosure name.\n")); + (void) fprintf(stderr, MSGSTR(2046, + "Usage: %s [-v] subcommand {a name consisting of" + " 1-16 alphanumeric characters}" + " {enclosure... | pathname...}\n"), whoami); + exit(-1); + } + } + + if (((Options & PVERBOSE) && (argc != 5)) || + (!(Options & PVERBOSE) && (argc != 4))) { + (void) fprintf(stderr, + MSGSTR(114, "Error: Incorrect number of arguments.\n")); + (void) fprintf(stderr, MSGSTR(2047, + "Usage: %s [-v] subcommand {a name consisting of" + " 1-16 alphanumeric characters}" + " {enclosure... | pathname...}\n"), whoami); + exit(-1); + } + + if ((err = l_convert_name(argv[path_index], &path_phys, + &path_struct, Options & PVERBOSE)) != 0) { + (void) fprintf(stderr, + MSGSTR(33, + " Error: converting" + " %s to physical path.\n" + " Invalid pathname.\n"), + argv[path_index]); + if (err != -1) { + (void) print_errString(err, argv[path_index]); + } + exit(-1); + } + /* + * Make sure we are talking to an IB. + */ + if (err = g_get_inquiry(path_phys, &inq)) { + (void) print_errString(err, argv[path_index]); + exit(-1); + } + if ((strstr((char *)inq.inq_pid, ENCLOSURE_PROD_ID) == 0) && + (!(strncmp((char *)inq.inq_vid, "SUN ", + sizeof (inq.inq_vid)) && + ((inq.inq_dtype & DTYPE_MASK) == DTYPE_ESI)))) { + /* + * Again this is like the ssaadm code in that the name + * is still not defined before this code must be released. + */ + (void) fprintf(stderr, + MSGSTR(2048, "Error: Pathname does not point to a %s" + " enclosure\n"), ENCLOSURE_PROD_NAME); + exit(-1); + } + + if (err = g_get_wwn(path_phys, port_wwn, node_wwn, &al_pa, + Options & PVERBOSE)) { + (void) print_errString(err, argv[path_index]); + exit(-1); + } + + for (i = 0; i < WWN_SIZE; i++) { + (void) sprintf(&wwn1[i << 1], "%02x", node_wwn[i]); + } + if ((err = l_get_box_list(&b_list, Options & PVERBOSE)) != 0) { + (void) print_errString(err, argv[path_index]); + exit(-1); + } + if (b_list == NULL) { + (void) fprintf(stdout, + MSGSTR(93, "No %s enclosures found " + "in /dev/es\n"), ENCLOSURE_PROD_NAME); + exit(-1); + } else if (l_duplicate_names(b_list, wwn1, name, + Options & PVERBOSE)) { + (void) fprintf(stderr, + MSGSTR(2049, "Warning: The name you selected, %s," + " is already being used.\n" + "Please choose a unique name.\n" + "You can use the \"probe\" subcommand to" + " see all of the enclosure names.\n"), + name); + (void) l_free_box_list(&b_list); + exit(-1); + } + (void) l_free_box_list(&b_list); + + /* Send new name to IB */ + if (rval = l_new_name(path_phys, name)) { + (void) print_errString(rval, path_phys); + exit(-1); + } + if (Options & PVERBOSE) { + (void) fprintf(stdout, + MSGSTR(2050, "The enclosure has been renamed to %s\n"), + name); + } +} + + +static int +get_enclStatus(char *phys_path, char *encl_name, int off_flag) +{ +int found_pwrOnDrv = 0, slot; +int found_pwrOffDrv = 0, err = 0; +L_state l_state; + + if ((err = l_get_status(phys_path, + &l_state, Options & PVERBOSE)) != 0) { + (void) print_errString(err, encl_name); + return (err); + } + + if (off_flag) { + for (slot = 0; slot < l_state.total_num_drv/2; + slot++) { + if (((l_state.drv_front[slot].ib_status.code != + S_NOT_INSTALLED) && + (!l_state.drv_front[slot].ib_status.dev_off)) || + ((l_state.drv_rear[slot].ib_status.code != + S_NOT_INSTALLED) && + (!l_state.drv_rear[slot].ib_status.dev_off))) { + found_pwrOnDrv++; + break; + } + } + if (!found_pwrOnDrv) { + (void) fprintf(stdout, + MSGSTR(2051, + "Notice: Drives in enclosure" + " \"%s\" have already been" + " powered off.\n\n"), + encl_name); + return (-1); + } + } else { + for (slot = 0; slot < l_state.total_num_drv/2; + slot++) { + if (((l_state.drv_front[slot].ib_status.code != + S_NOT_INSTALLED) && + (l_state.drv_front[slot].ib_status.dev_off)) || + ((l_state.drv_rear[slot].ib_status.code != + S_NOT_INSTALLED) && + (l_state.drv_rear[slot].ib_status.dev_off))) { + found_pwrOffDrv++; + break; + } + } + if (!found_pwrOffDrv) { + (void) fprintf(stdout, + MSGSTR(2052, + "Notice: Drives in enclosure" + " \"%s\" have already been" + " powered on.\n\n"), + encl_name); + return (-1); + } + } + return (0); +} + + + + + +/* + * adm_led() The led_request subcommand requests the subsystem + * to display the current state or turn off, on, or blink + * the yellow LED associated with the disk specified by the + * enclosure or pathname. + * + * RETURNS: + * none. + */ +void +adm_led(char **argv, int led_action) +{ +int path_index = 0, err = 0; +gfc_map_t map; +L_inquiry inq; +Dev_elem_st status; +char *path_phys = NULL; +Path_struct *path_struct; +int enc_t = 0; /* enclosure type */ +char ses_path[MAXPATHLEN]; +L_inquiry ses_inq; + + while (argv[path_index] != NULL) { + if ((err = l_convert_name(argv[path_index], &path_phys, + &path_struct, Options & PVERBOSE)) != 0) { + /* Make sure we have a device path. */ + if (path_struct->ib_path_flag) { + path_phys = path_struct->p_physical_path; + } else { + (void) fprintf(stderr, + MSGSTR(33, + " Error: converting" + " %s to physical path.\n" + " Invalid pathname.\n"), + argv[path_index]); + if (err != -1) { + (void) print_errString(err, + argv[path_index]); + } + exit(-1); + } + } + if (!path_struct->ib_path_flag) { + if (err = g_get_inquiry(path_phys, &inq)) { + (void) print_errString(err, argv[path_index]); + exit(-1); + } + if ((inq.inq_dtype & DTYPE_MASK) != DTYPE_DIRECT) { + (void) fprintf(stderr, + MSGSTR(2053, + "Error: pathname must be to a disk device.\n" + " %s\n"), argv[path_index]); + exit(-1); + } + } + /* + * See if we are in fact talking to a loop or not. + */ + if (err = g_get_dev_map(path_phys, &map, + (Options & PVERBOSE))) { + (void) print_errString(err, argv[path_index]); + + } + if (led_action == L_LED_ON) { + (void) fprintf(stderr, MSGSTR(2054, + "The led_on functionality is not applicable " + "to this subsystem.\n")); + exit(-1); + } + if (err = l_led(path_struct, led_action, &status, + (Options & PVERBOSE))) { + (void) print_errString(err, argv[path_index]); + exit(-1); + } + + /* Check to see if we have a daktari */ + if (l_get_ses_path(path_phys, ses_path, &map, + (Options & PVERBOSE)) == 0) { + if (g_get_inquiry(ses_path, &ses_inq) == 0) { + enc_t = l_get_enc_type(ses_inq); + } + } + switch (led_action) { + case L_LED_STATUS: + if (status.fault || status.fault_req) { + if (!path_struct->slot_valid) { + (void) fprintf(stdout, + MSGSTR(2055, "LED state is ON for " + "device:\n %s\n"), path_phys); + } else { + if (enc_t == DAK_ENC_TYPE) { + if (path_struct->f_flag) { + (void) fprintf(stdout, + MSGSTR(2236, "LED state is ON for " + "device in location: slot %d\n"), + path_struct->slot); + } else { + (void) fprintf(stdout, + MSGSTR(2236, "LED state is ON for " + "device in location: slot %d\n"), + path_struct->slot + + (MAX_DRIVES_DAK/2)); + } + } else { + (void) fprintf(stdout, + (path_struct->f_flag) ? + MSGSTR(2056, "LED state is ON for " + "device in location: front,slot %d\n") + : MSGSTR(2057, "LED state is ON for " + "device in location: rear,slot %d\n"), + path_struct->slot); + } + } + } else if (status.ident || status.rdy_to_ins || + status.rmv) { + if (!path_struct->slot_valid) { + (void) fprintf(stdout, MSGSTR(2058, + "LED state is BLINKING for " + "device:\n %s\n"), path_phys); + } else { + if (enc_t == DAK_ENC_TYPE) { + if (path_struct->f_flag) { + (void) fprintf(stdout, MSGSTR(2237, + "LED state is BLINKING for " + "device in location: slot %d\n"), + path_struct->slot); + } else { + (void) fprintf(stdout, MSGSTR(2237, + "LED state is BLINKING for " + "device in location: slot %d\n"), + path_struct->slot + (MAX_DRIVES_DAK/2)); + } + } else { + (void) fprintf(stdout, + (path_struct->f_flag) ? + MSGSTR(2059, "LED state is BLINKING for " + "device in location: front,slot %d\n") + : MSGSTR(2060, "LED state is BLINKING for " + "device in location: rear,slot %d\n"), + path_struct->slot); + } + } + } else { + if (!path_struct->slot_valid) { + (void) fprintf(stdout, + MSGSTR(2061, "LED state is OFF for " + "device:\n %s\n"), path_phys); + } else { + if (enc_t == DAK_ENC_TYPE) { + if (path_struct->f_flag) { + (void) fprintf(stdout, MSGSTR(2238, + "LED state is OFF for " + "device in location: slot %d\n"), + path_struct->slot); + } else { + (void) fprintf(stdout, MSGSTR(2238, + "LED state is OFF for " + "device in location: slot %d\n"), + path_struct->slot + MAX_DRIVES_DAK/2); + } + } else { + (void) fprintf(stdout, + (path_struct->f_flag) ? + MSGSTR(2062, "LED state is OFF for " + "device in location: front,slot %d\n") + : MSGSTR(2063, "LED state is OFF for " + "device in location: rear,slot %d\n"), + path_struct->slot); + } + } + } + break; + } + free((void *)map.dev_addr); + path_index++; + } +} + + + + + +/* + * dump() Dump information + * + * RETURNS: + * none. + */ +void +dump(char **argv) +{ +uchar_t *buf; +int path_index = 0, err = 0; +L_inquiry inq; +char hdr_buf[MAXNAMELEN]; +Rec_diag_hdr *hdr, *hdr_ptr; +char *path_phys = NULL; +Path_struct *path_struct; + + /* + * get big buffer + */ + if ((hdr = (struct rec_diag_hdr *)calloc(1, MAX_REC_DIAG_LENGTH)) == + NULL) { + (void) print_errString(L_MALLOC_FAILED, NULL); + exit(-1); + } + buf = (uchar_t *)hdr; + + while (argv[path_index] != NULL) { + if ((err = l_convert_name(argv[path_index], &path_phys, + &path_struct, Options & PVERBOSE)) != 0) { + (void) fprintf(stderr, + MSGSTR(33, + " Error: converting" + " %s to physical path.\n" + " Invalid pathname.\n"), + argv[path_index]); + if (err != -1) { + (void) print_errString(err, argv[path_index]); + } + exit(-1); + } + if (err = g_get_inquiry(path_phys, &inq)) { + (void) print_errString(err, argv[path_index]); + } else { + (void) g_dump(MSGSTR(2065, "INQUIRY data: "), + (uchar_t *)&inq, 5 + inq.inq_len, HEX_ASCII); + } + + (void) memset(buf, 0, MAX_REC_DIAG_LENGTH); + if (err = l_get_envsen(path_phys, buf, MAX_REC_DIAG_LENGTH, + (Options & PVERBOSE))) { + (void) print_errString(err, argv[path_index]); + exit(-1); + } + (void) fprintf(stdout, + MSGSTR(2066, "\t\tEnvironmental Sense Information\n")); + + /* + * Dump all pages. + */ + hdr_ptr = hdr; + + while (hdr_ptr->page_len != 0) { + (void) sprintf(hdr_buf, MSGSTR(2067, "Page %d: "), + hdr_ptr->page_code); + (void) g_dump(hdr_buf, (uchar_t *)hdr_ptr, + HEADER_LEN + hdr_ptr->page_len, HEX_ASCII); + hdr_ptr += ((HEADER_LEN + hdr_ptr->page_len) / + sizeof (struct rec_diag_hdr)); + } + path_index++; + } + (void) free(buf); +} + + + +/* + * display_socal_stats() Display socal driver kstat information. + * + * RETURNS: + * none. + */ +void +display_socal_stats(int port, char *socal_path, struct socal_stats *fc_stats) +{ +int i; +int header_flag = 0; +char status_msg_buf[MAXNAMELEN]; +int num_status_entries; + + (void) fprintf(stdout, MSGSTR(2068, + "\tInformation for FC Loop on port %d of" + " FC100/S Host Adapter\n\tat path: %s\n"), + port, socal_path); + if (fc_stats->version > 1) { + (void) fprintf(stdout, "\t"); + (void) fprintf(stdout, MSGSTR(32, + "Information from %s"), fc_stats->drvr_name); + (void) fprintf(stdout, "\n"); + if ((*fc_stats->node_wwn != NULL) && + (*fc_stats->port_wwn[port] != NULL)) { + (void) fprintf(stdout, MSGSTR(104, + " Host Adapter WWN's: Node:%s" + " Port:%s\n"), + fc_stats->node_wwn, + fc_stats->port_wwn[port]); + } + if (*fc_stats->fw_revision != NULL) { + (void) fprintf(stdout, MSGSTR(105, + " Host Adapter Firmware Revision: %s\n"), + fc_stats->fw_revision); + } + if (fc_stats->parity_chk_enabled != 0) { + (void) fprintf(stdout, MSGSTR(2069, + " This Host Adapter checks S-Bus parity.\n")); + } + } + + (void) fprintf(stdout, MSGSTR(2070, + " Version Resets Req_Q_Intrpts Qfulls" + " Unsol_Resps Lips\n")); + + (void) fprintf(stdout, " %4d%8d%11d%13d%10d%7d\n", + fc_stats->version, + fc_stats->resets, + fc_stats->reqq_intrs, + fc_stats->qfulls, + fc_stats->pstats[port].unsol_resps, + fc_stats->pstats[port].lips); + + (void) fprintf(stdout, MSGSTR(2071, + " Els_rcvd Abts" + " Abts_ok Offlines Loop_onlines Onlines\n")); + + (void) fprintf(stdout, " %4d%9d%10d%9d%13d%10d\n", + fc_stats->pstats[port].els_rcvd, + fc_stats->pstats[port].abts, + fc_stats->pstats[port].abts_ok, + fc_stats->pstats[port].offlines, + fc_stats->pstats[port].online_loops, + fc_stats->pstats[port].onlines); + + /* If any status conditions exist then display */ + if (fc_stats->version > 1) { + num_status_entries = FC_STATUS_ENTRIES; + } else { + num_status_entries = 64; + } + + for (i = 0; i < num_status_entries; i++) { + if (fc_stats->pstats[port].resp_status[i] != 0) { + if (header_flag++ == 0) { + (void) fprintf(stdout, MSGSTR(2072, + " Fibre Channel Transport status:\n " + "Status Value" + " Count\n")); + } + (void) l_format_fc_status_msg(status_msg_buf, + fc_stats->pstats[port].resp_status[i], i); + (void) fprintf(stdout, " %s\n", + status_msg_buf); + } + } +} + + + +/* + * display_sf_stats() Display sf driver kstat information. + * + * This routine is called by private loop device only + * + * RETURNS: + * none. + */ +void +display_sf_stats(char *path_phys, int dtype, struct sf_stats *sf_stats) +{ +int i, al_pa, err = 0; +gfc_map_t map; +uchar_t node_wwn[WWN_SIZE]; +uchar_t port_wwn[WWN_SIZE]; +gfc_port_dev_info_t *dev_addr_list; + + if (sf_stats->version > 1) { + (void) fprintf(stdout, "\n\t"); + (void) fprintf(stdout, MSGSTR(32, + "Information from %s"), + sf_stats->drvr_name); + (void) fprintf(stdout, "\n"); + } else { + (void) fprintf(stdout, + MSGSTR(2073, "\n\t\tInformation from sf driver:\n")); + } + + (void) fprintf(stdout, MSGSTR(2074, + " Version Lip_count Lip_fail" + " Alloc_fail #_cmds " + "Throttle_limit Pool_size\n")); + + (void) fprintf(stdout, " %4d%9d%12d%11d%10d%11d%12d\n", + sf_stats->version, + sf_stats->lip_count, + sf_stats->lip_failures, + sf_stats->cralloc_failures, + sf_stats->ncmds, + sf_stats->throttle_limit, + sf_stats->cr_pool_size); + + (void) fprintf(stdout, MSGSTR(2075, + "\n\t\tTARGET ERROR INFORMATION:\n")); + (void) fprintf(stdout, MSGSTR(2076, + "AL_PA Els_fail Timouts Abts_fail" + " Tsk_m_fail " + " Data_ro_mis Dl_len_mis Logouts\n")); + + if (err = g_get_dev_map(path_phys, &map, (Options & PVERBOSE))) { + (void) print_errString(err, path_phys); + exit(-1); + } + + if (dtype == DTYPE_DIRECT) { + if (err = g_get_wwn(path_phys, port_wwn, node_wwn, &al_pa, + Options & PVERBOSE)) { + (void) print_errString(err, path_phys); + exit(-1); + } + /* for san toleration, only need to modify the code */ + /* such that the current sf_al_map structure replaced */ + /* by the new gfc_map structure for private loop device */ + for (i = 0, dev_addr_list = map.dev_addr; i < map.count; + i++, dev_addr_list++) { + if (dev_addr_list->gfc_port_dev.priv_port.sf_al_pa + == al_pa) { + (void) fprintf(stdout, + "%3x%10d%8d%10d%11d%13d%11d%9d\n", + al_pa, + sf_stats->tstats[i].els_failures, + sf_stats->tstats[i].timeouts, + sf_stats->tstats[i].abts_failures, + sf_stats->tstats[i].task_mgmt_failures, + sf_stats->tstats[i].data_ro_mismatches, + sf_stats->tstats[i].dl_len_mismatches, + sf_stats->tstats[i].logouts_recvd); + break; + } + } + if (i >= map.count) { + (void) print_errString(L_INVALID_LOOP_MAP, path_phys); + exit(-1); + } + } else { + for (i = 0, dev_addr_list = map.dev_addr; i < map.count; + i++, dev_addr_list++) { + (void) fprintf(stdout, + "%3x%10d%8d%10d%11d%13d%11d%9d\n", + dev_addr_list->gfc_port_dev.priv_port.sf_al_pa, + sf_stats->tstats[i].els_failures, + sf_stats->tstats[i].timeouts, + sf_stats->tstats[i].abts_failures, + sf_stats->tstats[i].task_mgmt_failures, + sf_stats->tstats[i].data_ro_mismatches, + sf_stats->tstats[i].dl_len_mismatches, + sf_stats->tstats[i].logouts_recvd); + } + } + free((void *)map.dev_addr); +} + + + +/* + * adm_display_err() Displays enclosure specific + * error information. + * + * RETURNS: + * none. + */ +static void +adm_display_err(char *path_phys, int dtype) +{ +int i, drvr_inst, sf_inst, socal_inst, port, al_pa, err = 0; +char *char_ptr, socal_path[MAXPATHLEN], drvr_path[MAXPATHLEN]; +struct stat sbuf; +kstat_ctl_t *kc; +kstat_t *ifp_ks, *sf_ks, *fc_ks; +sf_stats_t sf_stats; +socal_stats_t fc_stats; +ifp_stats_t ifp_stats; +int header_flag = 0, pathcnt = 1; +char status_msg_buf[MAXNAMELEN]; +gfc_map_t map; +uchar_t node_wwn[WWN_SIZE], port_wwn[WWN_SIZE]; +uint_t path_type; +gfc_port_dev_info_t *dev_addr_list; +mp_pathlist_t pathlist; +int p_on = 0, p_st = 0; + + if ((kc = kstat_open()) == (kstat_ctl_t *)NULL) { + (void) fprintf(stderr, + MSGSTR(2077, " Error: can't open kstat\n")); + exit(-1); + } + + if (strstr(path_phys, SCSI_VHCI)) { + (void) strcpy(drvr_path, path_phys); + if (err = g_get_pathlist(drvr_path, &pathlist)) { + (void) print_errString(err, NULL); + exit(-1); + } + pathcnt = pathlist.path_count; + p_on = p_st = 0; + for (i = 0; i < pathcnt; i++) { + if (pathlist.path_info[i].path_state < MAXPATHSTATE) { + if (pathlist.path_info[i].path_state == + MDI_PATHINFO_STATE_ONLINE) { + p_on = i; + break; + } else if (pathlist.path_info[i].path_state == + MDI_PATHINFO_STATE_STANDBY) { + p_st = i; + } + } + } + if (pathlist.path_info[p_on].path_state == + MDI_PATHINFO_STATE_ONLINE) { + /* on_line path */ + (void) strcpy(drvr_path, + pathlist.path_info[p_on].path_hba); + } else { + /* standby or path0 */ + (void) strcpy(drvr_path, + pathlist.path_info[p_st].path_hba); + } + free(pathlist.path_info); + } else { + + (void) strcpy(drvr_path, path_phys); + + if ((char_ptr = strrchr(drvr_path, '/')) == NULL) { + (void) print_errString(L_INVLD_PATH_NO_SLASH_FND, + path_phys); + exit(-1); + } + *char_ptr = '\0'; /* Make into nexus or HBA driver path. */ + } + /* + * Each HBA and driver stack has its own structures + * for this, so we have to handle each one individually. + */ + path_type = g_get_path_type(drvr_path); + + if (path_type) { /* Quick sanity check for valid path */ + if ((err = g_get_nexus_path(drvr_path, &char_ptr)) != 0) { + (void) print_errString(err, path_phys); + exit(-1); + } + (void) strcpy(socal_path, char_ptr); + + } + + /* attach :devctl to get node stat instead of dir stat. */ + (void) strcat(drvr_path, FC_CTLR); + + if (stat(drvr_path, &sbuf) < 0) { + (void) print_errString(L_LSTAT_ERROR, path_phys); + exit(-1); + } + + drvr_inst = minor(sbuf.st_rdev); + + + /* + * first take care of ifp card. + */ + if (path_type & FC4_PCI_FCA) { + if ((ifp_ks = kstat_lookup(kc, "ifp", + drvr_inst, "statistics")) != NULL) { + + if (kstat_read(kc, ifp_ks, &ifp_stats) < 0) { + (void) fprintf(stderr, + MSGSTR(2082, + "Error: could not read ifp%d\n"), drvr_inst); + exit(-1); + } + (void) fprintf(stdout, MSGSTR(2083, + "\tInformation for FC Loop of" + " FC100/P Host Adapter\n\tat path: %s\n"), + drvr_path); + if (ifp_stats.version > 1) { + (void) fprintf(stdout, "\t"); + (void) fprintf(stdout, MSGSTR(32, + "Information from %s"), + ifp_stats.drvr_name); + (void) fprintf(stdout, "\n"); + if ((*ifp_stats.node_wwn != NULL) && + (*ifp_stats.port_wwn != NULL)) { + (void) fprintf(stdout, MSGSTR(104, + " Host Adapter WWN's: Node:%s" + " Port:%s\n"), + ifp_stats.node_wwn, + ifp_stats.port_wwn); + } + if (*ifp_stats.fw_revision != NULL) { + (void) fprintf(stdout, MSGSTR(105, + " Host Adapter Firmware Revision: %s\n"), + ifp_stats.fw_revision); + } + if (ifp_stats.parity_chk_enabled != 0) { + (void) fprintf(stdout, MSGSTR(2084, + " This Host Adapter checks " + "PCI-Bus parity.\n")); + } + } + + (void) fprintf(stdout, MSGSTR(2085, + " Version Lips\n")); + (void) fprintf(stdout, " %10d%7d\n", + ifp_stats.version, + ifp_stats.lip_count); + /* If any status conditions exist then display */ + for (i = 0; i < FC_STATUS_ENTRIES; i++) { + if (ifp_stats.resp_status[i] != 0) { + if (header_flag++ == 0) { + (void) fprintf(stdout, MSGSTR(2086, + " Fibre Channel Transport " + "status:\n " + "Status " + " Value" + " Count\n")); + } + (void) l_format_ifp_status_msg( + status_msg_buf, + ifp_stats.resp_status[i], i); + (void) fprintf(stdout, " %s\n", + status_msg_buf); + } + } + + (void) fprintf(stdout, MSGSTR(2087, + "\n\t\tTARGET ERROR INFORMATION:\n")); + (void) fprintf(stdout, MSGSTR(2088, + "AL_PA logouts_recvd task_mgmt_failures" + " data_ro_mismatches data_len_mismatch\n")); + + if (err = g_get_dev_map(path_phys, &map, + (Options & PVERBOSE))) { + (void) print_errString(err, path_phys); + exit(-1); + } + + + if (dtype == DTYPE_DIRECT) { + if (err = g_get_wwn(path_phys, port_wwn, + node_wwn, &al_pa, + Options & PVERBOSE)) { + (void) print_errString(err, + path_phys); + exit(-1); + } + for (i = 0, dev_addr_list = map.dev_addr; + i < map.count; i++, + dev_addr_list++) { + if (dev_addr_list->gfc_port_dev. + priv_port.sf_al_pa + == al_pa) { + (void) fprintf + (stdout, + "%3x%14d%18d%20d%20d\n", + al_pa, + ifp_stats.tstats[i]. + logouts_recvd, + ifp_stats.tstats[i]. + task_mgmt_failures, + ifp_stats.tstats[i]. + data_ro_mismatches, + ifp_stats.tstats[i]. + dl_len_mismatches); + break; + } + } + if (i >= map.count) { + + (void) print_errString( + L_INVALID_LOOP_MAP, path_phys); + exit(-1); + } + + } else { + for (i = 0, dev_addr_list = map.dev_addr; + i < map.count; i++, + dev_addr_list++) { + (void) fprintf(stdout, + "%3x%14d%18d%20d%20d\n", + dev_addr_list->gfc_port_dev. + priv_port.sf_al_pa, + ifp_stats.tstats[i].logouts_recvd, + ifp_stats.tstats[i].task_mgmt_failures, + ifp_stats.tstats[i].data_ro_mismatches, + ifp_stats.tstats[i].dl_len_mismatches); + } + } + + free((void *)map.dev_addr); + } + } else if (path_type & FC4_SF_XPORT) { + /* + * process cards with sf xport nodes. + */ + if (stat(socal_path, &sbuf) < 0) { + (void) print_errString(L_LSTAT_ERROR, path_phys); + exit(-1); + } + socal_inst = minor(sbuf.st_rdev)/2; + port = socal_inst%2; + + sf_inst = LUX_SF_MINOR2INST(minor(sbuf.st_rdev)); + if (!(sf_ks = kstat_lookup(kc, "sf", sf_inst, + "statistics"))) { + (void) fprintf(stderr, + MSGSTR(2078, + " Error: could not lookup driver stats for sf%d\n"), + sf_inst); + exit(-1); + } + if (!(fc_ks = kstat_lookup(kc, "socal", socal_inst, + "statistics"))) { + (void) fprintf(stderr, + MSGSTR(2079, + " Error: could not lookup driver stats for socal%d\n"), + socal_inst); + exit(-1); + } + if (kstat_read(kc, sf_ks, &sf_stats) < 0) { + (void) fprintf(stderr, + MSGSTR(2080, + " Error: could not read driver stats for sf%d\n"), + sf_inst); + exit(-1); + } + if (kstat_read(kc, fc_ks, &fc_stats) < 0) { + (void) fprintf(stderr, + MSGSTR(2081, + " Error: could not read driver stats for socal%d\n"), + socal_inst); + exit(-1); + } + (void) display_socal_stats(port, socal_path, &fc_stats); + (void) display_sf_stats(path_phys, dtype, &sf_stats); + } else if ((path_type & FC_FCA_MASK) == FC_PCI_FCA) { + fprintf(stderr, MSGSTR(2252, + "\n WARNING!! display -r on qlc is" + " currently not supported.\n")); + } else { + fprintf(stderr, MSGSTR(2253, + "\n WARNING!! display -r is not supported on path\n" + " %s\n"), drvr_path); + } + (void) kstat_close(kc); + +} + + + +/*ARGSUSED*/ +/* + * adm_display_verbose_disk() Gets the mode page information + * for a SENA disk and prints that information. + * + * RETURNS: + * none. + */ +void +adm_display_verbose_disk(char *path, int verbose) +{ +uchar_t *pg_buf; +Mode_header_10 *mode_header_ptr; +Mp_01 *pg1_buf; +Mp_04 *pg4_buf; +struct mode_page *pg_hdr; +int offset, hdr_printed = 0, err = 0; + + if ((err = l_get_mode_pg(path, &pg_buf, verbose)) == 0) { + + mode_header_ptr = (struct mode_header_10_struct *)(int)pg_buf; + pg_hdr = ((struct mode_page *)((int)pg_buf + + (uchar_t)sizeof (struct mode_header_10_struct) + + (uchar_t *)(uintptr_t)(mode_header_ptr->bdesc_length))); + offset = sizeof (struct mode_header_10_struct) + + mode_header_ptr->bdesc_length; + while (offset < (mode_header_ptr->length + + sizeof (mode_header_ptr->length))) { + switch (pg_hdr->code) { + case 0x01: + pg1_buf = (struct mode_page_01_struct *) + (int)pg_hdr; + P_DPRINTF(" adm_display_verbose_disk:" + "Mode Sense page 1 found.\n"); + if (hdr_printed++ == 0) { + (void) fprintf(stdout, + MSGSTR(2089, + " Mode Sense data:\n")); + } + (void) fprintf(stdout, + MSGSTR(2090, + " AWRE:\t\t\t%d\n" + " ARRE:\t\t\t%d\n" + " Read Retry Count:\t\t" + "%d\n" + " Write Retry Count:\t\t" + "%d\n"), + pg1_buf->awre, + pg1_buf->arre, + pg1_buf->read_retry_count, + pg1_buf->write_retry_count); + break; + case MODEPAGE_GEOMETRY: + pg4_buf = (struct mode_page_04_struct *) + (int)pg_hdr; + P_DPRINTF(" adm_display_verbose_disk:" + "Mode Sense page 4 found.\n"); + if (hdr_printed++ == 0) { + (void) fprintf(stdout, + MSGSTR(2091, + " Mode Sense data:\n")); + } + if (pg4_buf->rpm) { + (void) fprintf(stdout, + MSGSTR(2092, + " Medium rotation rate:\t" + "%d RPM\n"), pg4_buf->rpm); + } + break; + } + offset += pg_hdr->length + sizeof (struct mode_page); + pg_hdr = ((struct mode_page *)((int)pg_buf + + (uchar_t)offset)); + } + + + + + + } else if (getenv("_LUX_P_DEBUG") != NULL) { + (void) print_errString(err, path); + } +} + +/* + * Print out the port_wwn or node_wwn + */ +void +print_wwn(FILE *fd, uchar_t *pn_wwn) +{ + + (void) fprintf(fd, + " %1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x", + pn_wwn[0], pn_wwn[1], pn_wwn[2], pn_wwn[3], + pn_wwn[4], pn_wwn[5], pn_wwn[6], pn_wwn[7]); +} + +/* + * Print out the fabric dev port_id, hard_addr, port_wwn and node_wwn + */ +void +print_fabric_prop(int pos, uchar_t *port_wwn, uchar_t *node_wwn, int port_addr, + int hard_addr) +{ + (void) fprintf(stdout, "%-4d %-6x %-6x ", + pos, port_addr, hard_addr); + print_wwn(stdout, port_wwn); + print_wwn(stdout, node_wwn); +} + +/* + * Print out the private loop dev port_id, hard_addr, port_wwn and node_wwn + */ +void +print_private_loop_prop(int pos, uchar_t *port_wwn, uchar_t *node_wwn, + int port_addr, int hard_addr) +{ + (void) fprintf(stdout, "%-3d %-2x %-2x %-2x ", + pos, port_addr, g_sf_alpa_to_switch[port_addr], hard_addr); + print_wwn(stdout, port_wwn); + print_wwn(stdout, node_wwn); +} + +/* + * Get the device map from + * fc nexus driver and prints the map. + * + * RETURNS: + * none. + */ +void +dump_map(char **argv) +{ +int i = 0, path_index = 0, pathcnt = 1; +int limited_map_flag = 0, err = 0; +char *path_phys = NULL; +Path_struct *path_struct; +struct lilpmap limited_map; +uint_t dev_type; +char temp2path[MAXPATHLEN]; +mp_pathlist_t pathlist; +int p_pw = 0, p_on = 0, p_st = 0; +gfc_dev_t map_root, map_dev; +int *port_addr, *hard_addr, pos = 0, count; +uchar_t *hba_port_wwn, *port_wwn, *node_wwn, *dtype_prop; +uint_t map_topo; + + while (argv[path_index] != NULL) { + if ((err = l_convert_name(argv[path_index], &path_phys, + &path_struct, Options & PVERBOSE)) != 0) { + (void) fprintf(stderr, + MSGSTR(33, + " Error: converting" + " %s to physical path.\n" + " Invalid pathname.\n"), + argv[path_index]); + if (err != -1) { + (void) print_errString(err, argv[path_index]); + } + exit(-1); + } + + if (strstr(path_phys, SCSI_VHCI) != NULL) { + /* obtain phci */ + (void) strcpy(temp2path, path_phys); + if (err = g_get_pathlist(temp2path, &pathlist)) { + (void) print_errString(err, NULL); + exit(-1); + } + pathcnt = pathlist.path_count; + p_pw = p_on = p_st = 0; + for (i = 0; i < pathcnt; i++) { + if (pathlist.path_info[i].path_state < + MAXPATHSTATE) { + if (strstr(pathlist.path_info[i]. + path_addr, + path_struct->argv) != NULL) { + p_pw = i; + break; + } + if (pathlist.path_info[i].path_state == + MDI_PATHINFO_STATE_ONLINE) { + p_on = i; + } + if (pathlist.path_info[i].path_state == + MDI_PATHINFO_STATE_STANDBY) { + p_st = i; + } + } + } + if (strstr(pathlist.path_info[p_pw].path_addr, + path_struct->argv) != NULL) { + /* matching input pwwn */ + (void) strcpy(temp2path, + pathlist.path_info[p_pw].path_hba); + } else if (pathlist.path_info[p_on].path_state == + MDI_PATHINFO_STATE_ONLINE) { + /* on_line path */ + (void) strcpy(temp2path, + pathlist.path_info[p_on].path_hba); + } else { + /* standby or path0 */ + (void) strcpy(temp2path, + pathlist.path_info[p_st].path_hba); + } + free(pathlist.path_info); + (void) strcat(temp2path, FC_CTLR); + } else { + (void) strcpy(temp2path, path_phys); + } + + if ((dev_type = g_get_path_type(temp2path)) == 0) { + (void) print_errString(L_INVALID_PATH, + argv[path_index]); + exit(-1); + } + + if ((map_root = g_dev_map_init(temp2path, &err, + MAP_FORMAT_LILP)) == NULL) { + if (dev_type & FC_FCA_MASK) { + (void) print_errString(err, argv[path_index]); + exit(-1); + } else { + /* + * This did not work so try the FCIO_GETMAP + * type ioctl. + */ + if (err = g_get_limited_map(path_phys, + &limited_map, (Options & PVERBOSE))) { + (void) print_errString(err, + argv[path_index]); + exit(-1); + } + limited_map_flag++; + } + + } + + if (limited_map_flag) { + (void) fprintf(stdout, + MSGSTR(2093, + "Host Adapter AL_PA: %x\n"), + limited_map.lilp_myalpa); + + (void) fprintf(stdout, + MSGSTR(2094, + "Pos AL_PA\n")); + for (i = 0; i < (uint_t)limited_map.lilp_length; i++) { + (void) fprintf(stdout, "%-3d %-2x\n", + i, limited_map.lilp_list[i]); + } + } else { + if ((err = g_dev_prop_lookup_bytes(map_root, + PORT_WWN_PROP, &count, &hba_port_wwn)) != 0) { + g_dev_map_fini(map_root); + (void) print_errString(err, argv[path_index]); + exit(-1); + } + if ((err = g_get_map_topology( + map_root, &map_topo)) != 0) { + (void) print_errString(err, argv[path_index]); + exit(-1); + } + + if ((map_dev = g_get_first_dev(map_root, &err)) == NULL) { + if (err == L_NO_SUCH_DEV_FOUND) { + g_dev_map_fini(map_root); + (void) fprintf(stderr, + MSGSTR(2308, " No devices are found on %s.\n"), + argv[path_index]); + exit(-1); + } else { + g_dev_map_fini(map_root); + (void) print_errString(err, argv[path_index]); + exit(-1); + } + } + + switch (map_topo) { + case FC_TOP_FABRIC: + case FC_TOP_PUBLIC_LOOP: + case FC_TOP_PT_PT: + (void) fprintf(stdout, + MSGSTR(2095, "Pos Port_ID Hard_Addr Port WWN" + " Node WWN Type\n")); + while (map_dev) { + if ((err = g_dev_prop_lookup_ints( + map_dev, PORT_ADDR_PROP, &port_addr)) != 0) { + g_dev_map_fini(map_root); + (void) print_errString(err, argv[path_index]); + exit(-1); + } + if ((err = g_dev_prop_lookup_bytes(map_dev, + PORT_WWN_PROP, &count, &port_wwn)) != 0) { + g_dev_map_fini(map_root); + (void) print_errString(err, argv[path_index]); + exit(-1); + } + if ((err = g_dev_prop_lookup_bytes(map_dev, + NODE_WWN_PROP, &count, &node_wwn)) != 0) { + g_dev_map_fini(map_root); + (void) print_errString(err, argv[path_index]); + exit(-1); + } + if ((err = g_dev_prop_lookup_ints( + map_dev, HARD_ADDR_PROP, &hard_addr)) != 0) { + g_dev_map_fini(map_root); + (void) print_errString(err, argv[path_index]); + exit(-1); + } + print_fabric_prop(pos++, port_wwn, + node_wwn, *port_addr, *hard_addr); + if ((err = g_dev_prop_lookup_bytes(map_dev, + INQ_DTYPE_PROP, &count, &dtype_prop)) != 0) { + (void) fprintf(stdout, + MSGSTR(2307, " Failed to get the type.\n")); + } else { + print_fabric_dtype_prop(hba_port_wwn, port_wwn, + *dtype_prop); + } + + if (((map_dev = g_get_next_dev( + map_dev, &err)) == NULL) && + (err != L_NO_SUCH_DEV_FOUND)) { + g_dev_map_fini(map_root); + (void) print_errString(err, argv[path_index]); + exit(-1); + } + } + break; + case FC_TOP_PRIVATE_LOOP: + (void) fprintf(stdout, + MSGSTR(2295, + "Pos AL_PA ID Hard_Addr " + "Port WWN Node WWN Type\n")); + + while (map_dev) { + if ((err = g_dev_prop_lookup_ints( + map_dev, PORT_ADDR_PROP, &port_addr)) != 0) { + g_dev_map_fini(map_root); + (void) print_errString(err, argv[path_index]); + exit(-1); + } + if ((err = g_dev_prop_lookup_bytes(map_dev, + PORT_WWN_PROP, &count, &port_wwn)) != 0) { + g_dev_map_fini(map_root); + (void) print_errString(err, argv[path_index]); + exit(-1); + } + if ((err = g_dev_prop_lookup_bytes(map_dev, + NODE_WWN_PROP, &count, &node_wwn)) != 0) { + g_dev_map_fini(map_root); + (void) print_errString(err, argv[path_index]); + exit(-1); + } + if ((err = g_dev_prop_lookup_ints( + map_dev, HARD_ADDR_PROP, &hard_addr)) != 0) { + g_dev_map_fini(map_root); + (void) print_errString(err, argv[path_index]); + exit(-1); + } + print_private_loop_prop(pos++, port_wwn, + node_wwn, *port_addr, *hard_addr); + if ((err = g_dev_prop_lookup_bytes(map_dev, + INQ_DTYPE_PROP, &count, &dtype_prop)) != 0) { + (void) fprintf(stdout, + MSGSTR(2307, " Failed to get the type.\n")); + } else { + print_private_loop_dtype_prop(hba_port_wwn, + port_wwn, *dtype_prop); + } + + if (((map_dev = g_get_next_dev( + map_dev, &err)) == NULL) && + (err != L_NO_SUCH_DEV_FOUND)) { + g_dev_map_fini(map_root); + (void) print_errString(err, argv[path_index]); + exit(-1); + } + } + break; + default: + (void) print_errString(L_UNEXPECTED_FC_TOPOLOGY, + argv[path_index]); + exit(-1); + } + g_dev_map_fini(map_root); + } + limited_map_flag = 0; + path_index++; + } +} + +/* + * Gets a list of non-SENA fcal devices + * found on the system. + * + * OUTPUT: + * wwn_list pointer + * NULL: No non-enclosure devices found. + * !NULL: Devices found + * wwn_list points to a linked list of wwn's. + * RETURNS: + * 0 O.K. + */ +int +n_get_non_encl_list(WWN_list **wwn_list_ptr, int verbose) +{ +int i, j, k, err, found_ib = 0, pathcnt = 1; +WWN_list *wwn_list; +Box_list *b_list = NULL; +gfc_map_t map; +uchar_t box_id; +gfc_port_dev_info_t *dev_addr_list; +char phci_path[MAXPATHLEN], oldphci_path[MAXPATHLEN]; +mp_pathlist_t pathlist; + + + /* + * Only interested in devices that are not part of + * a Photon enclosure. + */ + if ((err = l_get_box_list(&b_list, verbose)) != 0) { + return (err); /* Failure */ + } + + if (err = g_get_wwn_list(&wwn_list, verbose)) { + (void) l_free_box_list(&b_list); + return (err); + } + + while (b_list != NULL) { + + pathcnt = 1; + if (strstr(b_list->b_physical_path, SCSI_VHCI) != NULL) { + (void) strcpy(phci_path, b_list->b_physical_path); + if (err = g_get_pathlist(phci_path, &pathlist)) { + (void) print_errString(err, NULL); + exit(-1); + } + pathcnt = pathlist.path_count; + } + + for (k = 0; k < pathcnt; k++) { + + if ((k > 0) && (strstr(oldphci_path, + pathlist.path_info[k].path_hba))) { + continue; + } + + if (strstr(b_list->b_physical_path, SCSI_VHCI) == NULL) { + if ((err = g_get_dev_map(b_list->b_physical_path, + &map, verbose)) != 0) { + (void) g_free_wwn_list(&wwn_list); + (void) l_free_box_list(&b_list); + return (err); + } + } else { + (void) strcpy(phci_path, + pathlist.path_info[k].path_hba); + (void) strcpy(oldphci_path, phci_path); + (void) strcat(phci_path, FC_CTLR); + if (g_get_dev_map(phci_path, &map, verbose)) { + continue; + } + if (pathcnt == 1) { + free(pathlist.path_info); + } + } + + + switch (map.hba_addr.port_topology) { + case FC_TOP_FABRIC: + case FC_TOP_PUBLIC_LOOP: + + for (i = 0, dev_addr_list = map.dev_addr; + i < map.count; i++, dev_addr_list++) { + for (found_ib = 1, j = 0; j < WWN_SIZE; + j++) { + if (b_list->b_node_wwn[j] != + dev_addr_list->gfc_port_dev. + pub_port.dev_nwwn.raw_wwn[j]) { + found_ib = 0; + } + } + if (found_ib) { + (void) n_rem_list_entry_fabric( + dev_addr_list->gfc_port_dev. + pub_port.dev_did.port_id, &map, + &wwn_list); + } + } + break; + + case FC_TOP_PRIVATE_LOOP: + + for (i = 0, dev_addr_list = map.dev_addr; + i < map.count; i++, dev_addr_list++) { + for (found_ib = 1, j = 0; j < WWN_SIZE; + j++) { + if (b_list->b_node_wwn[j] != + dev_addr_list->gfc_port_dev. + priv_port.sf_node_wwn[j]) { + found_ib = 0; + } + } + if (found_ib) { + box_id = g_sf_alpa_to_switch + [dev_addr_list->gfc_port_dev. + priv_port.sf_al_pa] & + BOX_ID_MASK; + /* This function has been added */ + /* here only to keep from having */ + /* to tab over farther */ + (void) n_rem_list_entry(box_id, &map, + &wwn_list); + if (wwn_list == NULL) { + /* Return the list */ + *wwn_list_ptr = NULL; + break; + } + } + } + break; + case FC_TOP_PT_PT: + (void) free((void *)map.dev_addr); + return (L_PT_PT_FC_TOP_NOT_SUPPORTED); + default: + (void) free((void *)map.dev_addr); + return (L_UNEXPECTED_FC_TOPOLOGY); + } + free((void *)map.dev_addr); + + } + if (pathcnt > 1) { + free(pathlist.path_info); + } + + b_list = b_list->box_next; + } + /* Return the list */ + *wwn_list_ptr = wwn_list; + (void) l_free_box_list(&b_list); + return (0); +} + + + +/* + * n_rem_list_entry() We found an IB so remove disks that + * are in the Photon from the individual device list. + * + * OUTPUT: + * wwn_list - removes the fcal disks that are in SENA enclosure + * + * RETURNS: + * none + */ +void +n_rem_list_entry(uchar_t box_id, struct gfc_map *map, + struct wwn_list_struct **wwn_list) +{ +int k; +gfc_port_dev_info_t *dev_addr_list; + + N_DPRINTF(" n_rem_list_entry: Removing devices" + " with box_id=0x%x from device list.\n", box_id); + + + for (k = 0, dev_addr_list = map->dev_addr; k < map->count; + k++, dev_addr_list++) { + if ((g_sf_alpa_to_switch[dev_addr_list->gfc_port_dev. + priv_port.sf_hard_address] & BOX_ID_MASK) + == box_id) { + n_rem_wwn_entry(dev_addr_list->gfc_port_dev. + priv_port.sf_node_wwn, wwn_list); + } + } + +} + + + +/* + * n_rem_list_entry_fabric() We found an IB so remove disks that + * are in the Photon from the individual device list. + * + * OUTPUT: + * wwn_list - removes the fcal disks that are in SENA enclosure + * + * RETURNS: + * none + */ +void +n_rem_list_entry_fabric(int pa, struct gfc_map *map, + struct wwn_list_struct **wwn_list) +{ +int k; +gfc_port_dev_info_t *dev_addr_ptr; + + N_DPRINTF(" n_rem_list_entry: Removing devices" + " with the same domain and area ID as" + " 0x%x PA from device list.\n", pa); + for (k = 0, dev_addr_ptr = map->dev_addr; k < map->count; + k++, dev_addr_ptr++) { + + /* matching the domain and area id with input alpa, */ + /* ignoring last 8 bits. */ + if ((dev_addr_ptr->gfc_port_dev.pub_port.dev_did.port_id | + 0xff) == (pa | 0xff)) { + n_rem_wwn_entry(dev_addr_ptr-> + gfc_port_dev.pub_port.dev_nwwn.raw_wwn, + wwn_list); + } + } +} + + +/* + * n_rem_wwn_entry() removes input wwn from wwn_list. + * + * OUTPUT: + * wwn_list - removes the input wwn from wwn_list if found. + * + * RETURNS: + * none + */ +void +n_rem_wwn_entry(uchar_t node_wwn[], struct wwn_list_struct **wwn_list) +{ +int l, found_dev; +WWN_list *inner, *l1; + + inner = *wwn_list; + while (inner != NULL) { + for (found_dev = 1, l = 0; l < WWN_SIZE; l++) { + if (inner->w_node_wwn[l] != node_wwn[l]) { + found_dev = 0; + } + } + if (found_dev) { + /* Remove this entry from the list */ + if (inner->wwn_prev != NULL) { + inner->wwn_prev->wwn_next = + inner->wwn_next; + } else { + *wwn_list = inner->wwn_next; + } + if (inner->wwn_next != NULL) { + inner->wwn_next->wwn_prev = + inner->wwn_prev; + } + l1 = inner; + N_DPRINTF(" n_rem_wwn_entry: " + "Removing Logical=%s " + "Current=0x%x, " + "Prev=0x%x, Next=0x%x\n", + l1->logical_path, + l1, + l1->wwn_prev, + l1->wwn_next); + inner = inner->wwn_next; + if ((l1->wwn_prev == NULL) && + (l1->wwn_next) == NULL) { + (void) free(l1->physical_path); + (void) free(l1->logical_path); + (void) free(l1); + *wwn_list = NULL; + N_DPRINTF(" n_rem_list_entry: " + "No non-Photon " + "devices left" + " in the list.\n"); + return; + } + (void) free(l1->physical_path); + (void) free(l1->logical_path); + (void) free(l1); + } else { + inner = inner->wwn_next; + } + } +} + + +/* + * non_encl_probe() Finds and displays a list of + * non-SENA fcal devices which is found on the + * system. + * + * RETURNS: + * none. + */ +void +non_encl_probe() +{ +WWN_list *wwn_list, *wwn_listh, *inner, *l1; +int err = 0; +char lun_a[MAXPATHLEN], lun_b[MAXPATHLEN], temppath[MAXPATHLEN]; +char *tempptra, *tempptrb, *tempptr; +mp_pathlist_t pathlist; +int compare_result, retr_outer = 0; +ddi_devid_t devid1 = NULL, devid2 = NULL; +di_node_t root = DI_NODE_NIL; + + if (err = n_get_non_encl_list(&wwn_list, (Options & PVERBOSE))) { + (void) print_errString(err, NULL); + exit(-1); + } + + g_sort_wwn_list(&wwn_list); + + wwn_listh = wwn_list; + + if (wwn_list != NULL) { + if (wwn_list->wwn_next != NULL) { + (void) fprintf(stdout, + MSGSTR(2098, "\nFound Fibre Channel device(s):\n")); + } else { + (void) fprintf(stdout, + MSGSTR(2099, "\nFound Fibre Channel device:\n")); + } + } else { + return; + } + + while (wwn_list != NULL) { + if (strstr(wwn_list->physical_path, SCSI_VHCI) != NULL) { + (void) strcpy(temppath, wwn_list->physical_path); + if ((!g_get_pathlist(temppath, + &pathlist)) && + ((tempptra = strchr(pathlist.path_info[0]. + path_addr, ','))) != NULL) { + tempptra++; + (void) strcpy(lun_a, tempptra); + free(pathlist.path_info); + } + } else { + if ((((tempptr = strstr(wwn_list->physical_path, + SLSH_DRV_NAME_ST)) != NULL) || + ((tempptr = strstr(wwn_list->physical_path, + SLSH_DRV_NAME_SSD)) != NULL)) && + ((tempptra = strchr(tempptr, ',')) != NULL)) { + tempptra++; + (void) strcpy(lun_a, tempptra); + } + } + (void) fprintf(stdout, " "); + (void) fprintf(stdout, MSGSTR(90, "Node WWN:")); + (void) fprintf(stdout, "%s ", wwn_list->node_wwn_s); + + if (wwn_list->device_type < 0x10) { + (void) fprintf(stdout, MSGSTR(35, "Device Type:")); + (void) fprintf(stdout, "%s", + dtype[wwn_list->device_type]); + } else if (wwn_list->device_type < 0x1f) { + (void) fprintf(stdout, MSGSTR(2100, + "Type:Reserved")); + } else { + (void) fprintf(stdout, MSGSTR(2101, + "Type:Unknown")); + } + (void) fprintf(stdout, "\n "); + (void) fprintf(stdout, MSGSTR(31, "Logical Path:%s"), + wwn_list->logical_path); + (void) fprintf(stdout, "\n"); + + if (Options & OPTION_P) { + (void) fprintf(stdout, " "); + (void) fprintf(stdout, + MSGSTR(5, "Physical Path:")); + (void) fprintf(stdout, "\n %s\n", wwn_list->physical_path); + } + inner = wwn_list->wwn_next; + + while (inner != NULL) { + if (strcmp(inner->node_wwn_s, wwn_list->node_wwn_s) == 0) { + + if (tempptra != NULL) { + if (strstr(inner->physical_path, + SCSI_VHCI) != NULL) { + (void) strcpy(temppath, + inner->physical_path); + + if ((!g_get_pathlist(temppath, &pathlist)) && + ((tempptrb = strchr( + pathlist.path_info[0].path_addr, ','))) != + NULL) { + tempptrb++; + (void) strcpy(lun_b, tempptrb); + free(pathlist.path_info); + } + } else { + if ((((tempptr = strstr(inner->physical_path, + SLSH_DRV_NAME_ST)) != NULL) || + ((tempptr = strstr(inner->physical_path, + SLSH_DRV_NAME_SSD)) != NULL)) && + ((tempptrb = strchr(tempptr, ',')) != NULL)) { + tempptrb++; + (void) strcpy(lun_b, tempptrb); + } + } + } + + if (((tempptra == NULL) || (strcmp(lun_a, lun_b)) == 0)) { + + /* + * Have we retrieved a snapshot yet? + */ + if (root == DI_NODE_NIL) { + if ((root = di_init("/", DINFOCPYALL)) == + DI_NODE_NIL) { + (void) fprintf(stdout, + MSGSTR(2319, + "\nFailed to get device tree snapshot:\n")); + exit(1); + } + } + + /* Apply devid to ssd devices only */ + if (!retr_outer && strstr(wwn_list->physical_path, + SLSH_DRV_NAME_SSD) != NULL) { + if ((err = g_devid_get(wwn_list->physical_path, + &devid1, root, SSD_DRVR_NAME)) != 0) { + (void) print_errString(err, + wwn_list->physical_path); + } + /* + * Try retrieve of devid only once. If it fails + * don't try it again but print error, + * There should be a devid prop. + */ + retr_outer = 1; + } + /* + * Apply devid to block devices only. + * Get devid of inner path and compare + * with outer path's devid. + */ + if ((strstr(inner->physical_path, + SLSH_DRV_NAME_SSD) != NULL) && + devid1 != NULL) { + + if ((err = g_devid_get(inner->physical_path, + &devid2, root, SSD_DRVR_NAME)) != 0) { + + (void) print_errString(err, + inner->physical_path); + compare_result = 0; + } else { + compare_result = devid_compare(devid1, devid2); + } + } else { + /* devid isn't applied */ + compare_result = 0; + } + + if (compare_result == 0) { + + if (strcmp(wwn_list->logical_path, + inner->logical_path)) { + (void) fprintf(stdout, " "); + (void) fprintf(stdout, + MSGSTR(31, "Logical Path:%s"), + inner->logical_path); + (void) fprintf(stdout, "\n"); + + if (Options & OPTION_P) { + (void) fprintf(stdout, " "); + (void) fprintf(stdout, MSGSTR(5, + "Physical Path:")); + (void) fprintf(stdout, "\n %s\n", + inner->physical_path); + } + } + + /* Remove this entry from the list */ + if (inner->wwn_prev != NULL) { + inner->wwn_prev->wwn_next = + inner->wwn_next; + } + + if (inner->wwn_next != NULL) { + inner->wwn_next->wwn_prev = + inner->wwn_prev; + } + free(inner->physical_path); + free(inner->logical_path); + l1 = inner; + inner = inner->wwn_next; + (void) free(l1); + + } else { + inner = inner->wwn_next; + } /* End if (compare_result == 0) */ + + } else { + inner = inner->wwn_next; + } + } else { + inner = inner->wwn_next; + } + devid2 = NULL; + } + wwn_list = wwn_list->wwn_next; + retr_outer = 0; + devid1 = NULL; + } /* End while (wwn_list != NULL) */ + + (void) g_free_wwn_list(&wwn_listh); + (void) di_fini(root); +} + +void +pho_probe() +{ + +Box_list *b_list, *o_list, *c_list; +int multi_path_flag, multi_print_flag; +int duplicate_names_found = 0, err = 0; + + b_list = o_list = c_list = NULL; + if ((err = l_get_box_list(&b_list, Options & PVERBOSE)) != 0) { + (void) print_errString(err, NULL); + exit(-1); + } + if (b_list == NULL) { + (void) fprintf(stdout, + MSGSTR(93, "No %s enclosures found " + "in /dev/es\n"), ENCLOSURE_PROD_NAME); + } else { + o_list = b_list; + if (b_list->box_next != NULL) { + (void) fprintf(stdout, MSGSTR(2102, + "Found Enclosure(s)")); + } else { + (void) fprintf(stdout, MSGSTR(2103, "Found Enclosure")); + } + (void) fprintf(stdout, ":\n"); + while (b_list != NULL) { + /* Don't re-print multiple paths */ + c_list = o_list; + multi_print_flag = 0; + while (c_list != b_list) { + if (strcmp(c_list->b_node_wwn_s, + b_list->b_node_wwn_s) == 0) { + multi_print_flag = 1; + break; + } + c_list = c_list->box_next; + } + if (multi_print_flag) { + b_list = b_list->box_next; + continue; + } + (void) fprintf(stdout, + MSGSTR(2104, "%s Name:%s Node WWN:%s "), + b_list->prod_id_s, b_list->b_name, + b_list->b_node_wwn_s); + /* + * Print logical path on same line if not multipathed. + */ + multi_path_flag = 0; + c_list = o_list; + while (c_list != NULL) { + if ((c_list != b_list) && + (strcmp(c_list->b_node_wwn_s, + b_list->b_node_wwn_s) == 0)) { + multi_path_flag = 1; + } + c_list = c_list->box_next; + } + if (multi_path_flag) { + (void) fprintf(stdout, "\n "); + } + (void) fprintf(stdout, + MSGSTR(31, "Logical Path:%s"), + b_list->logical_path); + + if (Options & OPTION_P) { + (void) fprintf(stdout, "\n "); + (void) fprintf(stdout, + MSGSTR(5, "Physical Path:")); + (void) fprintf(stdout, "%s", + b_list->b_physical_path); + } + c_list = o_list; + while (c_list != NULL) { + if ((c_list != b_list) && + (strcmp(c_list->b_node_wwn_s, + b_list->b_node_wwn_s) == 0)) { + (void) fprintf(stdout, "\n "); + (void) fprintf(stdout, + MSGSTR(31, "Logical Path:%s"), + c_list->logical_path); + if (Options & OPTION_P) { + (void) fprintf(stdout, "\n "); + (void) fprintf(stdout, + MSGSTR(5, "Physical Path:")); + (void) fprintf(stdout, "%s", + c_list->b_physical_path); + } + } + c_list = c_list->box_next; + } + (void) fprintf(stdout, "\n"); + /* Check for duplicate names */ + if (l_duplicate_names(o_list, b_list->b_node_wwn_s, + (char *)b_list->b_name, + Options & PVERBOSE)) { + duplicate_names_found++; + } + b_list = b_list->box_next; + } + } + if (duplicate_names_found) { + (void) fprintf(stdout, + MSGSTR(2105, "\nWARNING: There are enclosures with " + "the same names.\n" + "You can not use the \"enclosure\"" + " name to specify these subsystems.\n" + "Please use the \"enclosure_name\"" + " subcommand to select unique names.\n\n")); + } + (void) l_free_box_list(&b_list); +} + +/* + * display_port_status() Prints the device's + * port status. + * + * RETURNS: + * none. + */ +void +display_port_status(int d_state_flag) +{ + + if (d_state_flag & L_OPEN_FAIL) { + (void) fprintf(stdout, MSGSTR(28, "Open Failed")); + } else if (d_state_flag & L_NOT_READY) { + (void) fprintf(stdout, MSGSTR(20, "Not Ready")); + } else if (d_state_flag & L_NOT_READABLE) { + (void) fprintf(stdout, MSGSTR(88, "Not Readable")); + } else if (d_state_flag & L_SPUN_DWN_D) { + (void) fprintf(stdout, MSGSTR(68, "Spun Down")); + } else if (d_state_flag & L_SCSI_ERR) { + (void) fprintf(stdout, MSGSTR(70, "SCSI Error")); + } else if (d_state_flag & L_RESERVED) { + (void) fprintf(stdout, MSGSTR(73, "Reservation conflict")); + } else if (d_state_flag & L_NO_LABEL) { + (void) fprintf(stdout, MSGSTR(92, "No UNIX Label")); + } else { + (void) fprintf(stdout, MSGSTR(29, "O.K.")); + } + (void) fprintf(stdout, "\n"); +} + +/* + * Displays individual SENA + * FC disk information. + * + * Caller to this routine should free the storage due to + * the use of g_get_dev_map + * + * RETURNS: + * none. + */ +void +display_fc_disk(struct path_struct *path_struct, char *ses_path, + gfc_map_t *map, L_inquiry inq, int verbose) +{ +static WWN_list *wwn_list = NULL; +static char path_phys[MAXPATHLEN]; +static L_disk_state l_disk_state; +static L_inquiry local_inq; +static uchar_t node_wwn[WWN_SIZE]; +char same_path_phys = B_FALSE; /* To chk for repeat args */ +uchar_t port_wwn[WWN_SIZE], *pg_buf; +char logical_path[MAXPATHLEN]; +int al_pa, port_a_flag = 0; +int offset, mode_data_avail = 0; +int no_path_flag = 0, err = 0; +L_state l_state; +Mode_header_10 *mode_header_ptr = NULL; +struct mode_page *pg_hdr; + + /* + * Do a quick check to see if its the same path as in last call. + * path_phys is a static array and so dont worry about its + * initialization. + */ + if (strcmp(path_phys, path_struct->p_physical_path) == 0) + same_path_phys = B_TRUE; + + (void) strcpy(path_phys, path_struct->p_physical_path); + (void) memset((char *)logical_path, 0, sizeof (logical_path)); + + /* + * slot_valid is 1 when argument is of the form 'enclosure,[f|r]<n>'. + * If slot_valid != 1, g_get_dev_map and l_get_ses_path would + * already have been called + */ + if (path_struct->slot_valid == 1) { + /* Get the location information. */ + if (err = g_get_dev_map(path_phys, map, (Options & PVERBOSE))) { + (void) print_errString(err, path_phys); + exit(-1); + } + if (err = l_get_ses_path(path_phys, ses_path, map, + (Options & PVERBOSE))) { + (void) print_errString(err, path_phys); + exit(-1); + } + } + + /* + * Get the WWN for our disk if we already haven't or if there was an + * error earlier + */ + if (same_path_phys == B_FALSE) { + if (err = g_get_wwn(path_phys, port_wwn, node_wwn, + &al_pa, (Options & PVERBOSE))) { + (void) print_errString(err, path_phys); + exit(-1); + } + + if (err = g_get_inquiry(ses_path, &local_inq)) { + (void) print_errString(err, ses_path); + exit(-1); + } + } + + /* + * We are interested only a couple of ib_tbl fields and + * those get filled using l_get_ib_status. + * Note that NOT ALL of ib_tbl fields get filled here + */ + if ((err = l_get_ib_status(ses_path, &l_state, + Options & PVERBOSE)) != 0) { + (void) print_errString(err, ses_path); + exit(-1); + } + + /* + * Get path to all the FC disk and tape devices. + * if we haven't already done so in a previous pass + */ + if ((wwn_list == NULL) && (err = g_get_wwn_list(&wwn_list, verbose))) { + (void) print_errString(err, ses_path); + exit(-1); /* Failure */ + } + + /* + * Get the disk status if it is a different path_phys from + * last time. + */ + if (same_path_phys == B_FALSE) { + (void) memset(&l_disk_state, 0, + sizeof (struct l_disk_state_struct)); + if (err = l_get_disk_status(path_phys, &l_disk_state, + wwn_list, (Options & PVERBOSE))) { + (void) print_errString(err, path_phys); + exit(-1); + } + } + + if (l_disk_state.l_state_flag & L_NO_PATH_FOUND) { + (void) fprintf(stderr, MSGSTR(2106, + "\nWARNING: No path found " + "in /dev/rdsk directory\n" + " Please check the logical links in /dev/rdsk\n" + " (It may be necessary to run the \"disks\" " + "program.)\n\n")); + + /* Just call to get the status directly. */ + if (err = l_get_port(ses_path, &port_a_flag, verbose)) { + (void) print_errString(err, ses_path); + exit(-1); + } + if (err = l_get_disk_port_status(path_phys, + &l_disk_state, port_a_flag, + (Options & PVERBOSE))) { + (void) print_errString(err, path_phys); + exit(-1); + } + no_path_flag++; + } + + if (strlen(l_disk_state.g_disk_state.node_wwn_s) == 0) { + (void) sprintf(l_disk_state.g_disk_state.node_wwn_s, + "%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x", + node_wwn[0], node_wwn[1], node_wwn[2], node_wwn[3], + node_wwn[4], node_wwn[5], node_wwn[6], node_wwn[7]); + } + + /* get mode page information for FC device */ + if (l_get_mode_pg(path_phys, &pg_buf, Options & PVERBOSE) == 0) { + mode_header_ptr = (struct mode_header_10_struct *)(int)pg_buf; + pg_hdr = ((struct mode_page *)((int)pg_buf + + (uchar_t)sizeof (struct mode_header_10_struct) + + (uchar_t *)(uintptr_t)(mode_header_ptr->bdesc_length))); + offset = sizeof (struct mode_header_10_struct) + + mode_header_ptr->bdesc_length; + while (offset < (mode_header_ptr->length + + sizeof (mode_header_ptr->length)) && + !mode_data_avail) { + if (pg_hdr->code == MODEPAGE_CACHING) { + mode_data_avail++; + break; + } + offset += pg_hdr->length + sizeof (struct mode_page); + pg_hdr = ((struct mode_page *)((int)pg_buf + + (uchar_t)offset)); + } + } + + switch ((inq.inq_dtype & DTYPE_MASK)) { + case DTYPE_DIRECT: + fprintf(stdout, MSGSTR(121, "DEVICE PROPERTIES for disk: %s\n"), + path_struct->argv); + break; + case DTYPE_SEQUENTIAL: /* Tape */ + fprintf(stdout, MSGSTR(2249, "DEVICE PROPERTIES for tape: %s\n"), + path_struct->argv); + break; + default: + fprintf(stdout, MSGSTR(2250, "DEVICE PROPERTIES for: %s\n"), + path_struct->argv); + break; + } + + if (l_disk_state.g_disk_state.port_a_valid) { + (void) fprintf(stdout, " "); + (void) fprintf(stdout, MSGSTR(141, "Status(Port A):")); + (void) fprintf(stdout, "\t"); + display_port_status( + l_disk_state.g_disk_state.d_state_flags[PORT_A]); + } else { + if (path_struct->f_flag) { + if ((ib_present_chk(&l_state, 0) == 1) && + (l_state.drv_front[path_struct->slot].ib_status.bypass_a_en)) { + (void) fprintf(stdout, + MSGSTR(66, + " Status(Port A):\tBYPASSED\n")); + } + } else { + if ((ib_present_chk(&l_state, 0) == 1) && + (l_state.drv_rear[path_struct->slot].ib_status.bypass_a_en)) { + (void) fprintf(stdout, + MSGSTR(66, + " Status(Port A):\tBYPASSED\n")); + } + } + } + + if (l_disk_state.g_disk_state.port_b_valid) { + (void) fprintf(stdout, " "); + (void) fprintf(stdout, MSGSTR(142, "Status(Port B):")); + (void) fprintf(stdout, "\t"); + display_port_status(l_disk_state.g_disk_state.d_state_flags[PORT_B]); + } else { + if (path_struct->f_flag) { + if ((ib_present_chk(&l_state, 1) == 1) && + (l_state.drv_front[path_struct->slot].ib_status.bypass_b_en)) { + (void) fprintf(stdout, + MSGSTR(65, + " Status(Port B):\tBYPASSED\n")); + } + } else { + if ((ib_present_chk(&l_state, 1) == 1) && + (l_state.drv_rear[path_struct->slot].ib_status.bypass_b_en)) { + (void) fprintf(stdout, + MSGSTR(65, + " Status(Port B):\tBYPASSED\n")); + } + } + } + + if (no_path_flag) { + (void) fprintf(stdout, " "); + if (port_a_flag != NULL) { + (void) fprintf(stdout, MSGSTR(142, "Status(Port B):")); + } else { + (void) fprintf(stdout, MSGSTR(141, "Status(Port A):")); + } + (void) fprintf(stdout, "\t"); + display_port_status( + l_disk_state.g_disk_state.d_state_flags[port_a_flag]); + } else if ((!l_disk_state.g_disk_state.port_a_valid) && + (!l_disk_state.g_disk_state.port_b_valid)) { + (void) fprintf(stdout, MSGSTR(2107, " Status:\t\t" + "No state available.\n")); + } + + (void) display_disk_info(inq, l_disk_state, path_struct, pg_hdr, + mode_data_avail, (char *)local_inq.inq_box_name, verbose); +} + + + + + +/* + * non_encl_fc_disk_display() Prints the device specific + * information for an individual fcal device. + * + * RETURNS: + * none. + */ +static int +non_encl_fc_disk_display(Path_struct *path_struct, + L_inquiry inq_struct, int verbose) +{ + +char phys_path[MAXPATHLEN]; +uchar_t node_wwn[WWN_SIZE], port_wwn[WWN_SIZE], *pg_buf = NULL; +L_disk_state l_disk_state; +struct dlist *mlist; +int i = 0, al_pa, offset, mode_data_avail = 0, err = 0; +int path_a_found = 0, path_b_found = 0, argpwwn = 0, + argnwwn = 0, pathcnt = 1; +L_inquiry local_inq; +Mode_header_10 *mode_header_ptr; +struct mode_page *pg_hdr; +WWN_list *wwn_list, *wwn_list_ptr, *list_start; +char temppath[MAXPATHLEN], last_logical_path[MAXPATHLEN]; +mp_pathlist_t pathlist; + + (void) strcpy(phys_path, path_struct->p_physical_path); + + /* Get path to all the FC disk and tape devices. */ + if (err = g_get_wwn_list(&wwn_list, verbose)) { + return (err); + } + + g_sort_wwn_list(&wwn_list); + + list_start = wwn_list; + (void) strcpy(last_logical_path, phys_path); + + for (wwn_list_ptr = wwn_list; wwn_list_ptr != NULL; + wwn_list_ptr = wwn_list_ptr->wwn_next) { + if (strcasecmp(wwn_list_ptr->port_wwn_s, + path_struct->argv) == 0) { + list_start = wwn_list_ptr; + argpwwn = 1; + break; + } else if (strcasecmp(wwn_list_ptr->node_wwn_s, + path_struct->argv) == 0) { + list_start = wwn_list_ptr; + argnwwn = 1; + break; + } + } + + for (wwn_list_ptr = list_start; wwn_list_ptr != NULL; + wwn_list_ptr = wwn_list_ptr->wwn_next) { + + + if (argpwwn) { + if (strcasecmp(wwn_list_ptr->port_wwn_s, + path_struct->argv) != 0) { + continue; + } + (void) strcpy(phys_path, wwn_list_ptr->physical_path); + path_a_found = 0; + path_b_found = 0; + mode_data_avail = 0; + } else if (argnwwn) { + if (strstr(wwn_list_ptr->logical_path, + last_logical_path) != NULL) { + continue; + } + if (strcasecmp(wwn_list_ptr->node_wwn_s, + path_struct->argv) != 0) { + continue; + } + (void) strcpy(phys_path, wwn_list_ptr->physical_path); + (void) strcpy(last_logical_path, + wwn_list_ptr->logical_path); + path_a_found = 0; + path_b_found = 0; + mode_data_avail = 0; + } + + (void) memset(&l_disk_state, 0, sizeof (struct l_disk_state_struct)); + + if ((err = g_get_multipath(phys_path, + &(l_disk_state.g_disk_state.multipath_list), + wwn_list, verbose)) != 0) { + return (err); + } + mlist = l_disk_state.g_disk_state.multipath_list; + if (mlist == NULL) { + l_disk_state.l_state_flag = L_NO_PATH_FOUND; + N_DPRINTF(" non_encl_fc_disk_display: Error finding" + " multiple paths to the disk.\n"); + (void) g_free_wwn_list(&wwn_list); + return (-1); + } + + /* get mode page information for FC device */ + if (l_get_mode_pg(phys_path, &pg_buf, verbose) == 0) { + mode_header_ptr = (struct mode_header_10_struct *)(int)pg_buf; + pg_hdr = ((struct mode_page *)((int)pg_buf + + (uchar_t)sizeof (struct mode_header_10_struct) + + (uchar_t *)(uintptr_t)(mode_header_ptr->bdesc_length))); + offset = sizeof (struct mode_header_10_struct) + + mode_header_ptr->bdesc_length; + while (offset < (mode_header_ptr->length + + sizeof (mode_header_ptr->length)) && + !mode_data_avail) { + if (pg_hdr->code == MODEPAGE_CACHING) { + mode_data_avail++; + break; + } + offset += pg_hdr->length + sizeof (struct mode_page); + pg_hdr = ((struct mode_page *)((int)pg_buf + + (uchar_t)offset)); + } + } + + switch ((inq_struct.inq_dtype & DTYPE_MASK)) { + case DTYPE_DIRECT: + fprintf(stdout, MSGSTR(121, "DEVICE PROPERTIES for disk: %s\n"), + path_struct->argv); + break; + case DTYPE_SEQUENTIAL: /* Tape */ + fprintf(stdout, MSGSTR(2249, "DEVICE PROPERTIES for tape: %s\n"), + path_struct->argv); + break; + default: + fprintf(stdout, MSGSTR(2250, "DEVICE PROPERTIES for: %s\n"), + path_struct->argv); + break; + } + while ((mlist != NULL) && (!(path_a_found && path_b_found))) { + (void) strcpy(phys_path, mlist->dev_path); + if (err = g_get_inquiry(phys_path, &local_inq)) { + (void) fprintf(stderr, + MSGSTR(2114, + "non_encl_fc_disk_display: Inquiry failed\n")); + (void) print_errString(err, phys_path); + (void) g_free_multipath( + l_disk_state.g_disk_state.multipath_list); + (void) g_free_wwn_list(&wwn_list); + return (-1); + } + if ((err = g_get_wwn(mlist->dev_path, port_wwn, node_wwn, + &al_pa, verbose)) != 0) { + (void) print_errString(err, mlist->dev_path); + (void) g_free_multipath( + l_disk_state.g_disk_state.multipath_list); + (void) g_free_wwn_list(&wwn_list); + return (-1); + } + if (strlen(l_disk_state.g_disk_state.node_wwn_s) == 0) { + (void) sprintf(l_disk_state.g_disk_state.node_wwn_s, + "%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x", + node_wwn[0], node_wwn[1], node_wwn[2], node_wwn[3], + node_wwn[4], node_wwn[5], node_wwn[6], node_wwn[7]); + } + if ((err = l_get_disk_port_status(phys_path, &l_disk_state, + (local_inq.inq_port) ? FC_PORT_B : FC_PORT_A, + verbose)) != 0) { + (void) print_errString(err, phys_path); + (void) g_free_multipath( + l_disk_state.g_disk_state.multipath_list); + exit(-1); + } + + if ((!local_inq.inq_port) && (!path_a_found)) { + (void) sprintf(l_disk_state.g_disk_state.port_a_wwn_s, + "%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x", + port_wwn[0], port_wwn[1], port_wwn[2], port_wwn[3], + port_wwn[4], port_wwn[5], port_wwn[6], port_wwn[7]); + path_a_found = l_disk_state.g_disk_state.port_a_valid = 1; + } + if ((local_inq.inq_port) && (!path_b_found)) { + path_b_found = l_disk_state.g_disk_state.port_b_valid = 1; + (void) sprintf(l_disk_state.g_disk_state.port_b_wwn_s, + "%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x", + port_wwn[0], port_wwn[1], port_wwn[2], port_wwn[3], + port_wwn[4], port_wwn[5], port_wwn[6], port_wwn[7]); + } + + if ((strstr(mlist->dev_path, SCSI_VHCI) != NULL) && + (!l_get_disk_port_status(phys_path, &l_disk_state, + (!local_inq.inq_port) ? FC_PORT_B : FC_PORT_A, + verbose))) { + (void) strcpy(temppath, mlist->dev_path); + if (err = g_get_pathlist(temppath, &pathlist)) { + (void) print_errString(err, NULL); + exit(-1); + } + pathcnt = pathlist.path_count; + if (pathcnt > 1) { + for (i = 0; i < pathcnt; i++) { + if ((!path_a_found) && + (path_b_found) && + (strstr(pathlist.path_info[i]. + path_addr, + l_disk_state.g_disk_state. + port_b_wwn_s) == NULL)) { + + (void) strncpy(l_disk_state. + g_disk_state.port_a_wwn_s, + pathlist.path_info[i]. + path_addr, 16); + path_a_found = l_disk_state. + g_disk_state.port_a_valid = 1; + } + if ((path_a_found) && + (!path_b_found) && + (strstr(pathlist.path_info[i]. + path_addr, + l_disk_state.g_disk_state. + port_a_wwn_s) == NULL)) { + + (void) strncpy(l_disk_state. + g_disk_state.port_b_wwn_s, + pathlist.path_info[i]. + path_addr, 16); + path_b_found = l_disk_state. + g_disk_state.port_b_valid = 1; + } + if ((path_a_found) && (path_b_found)) { + break; + } + } + } + free(pathlist.path_info); + } + + mlist = mlist->next; + } + + if (l_disk_state.g_disk_state.port_a_valid) { + (void) fprintf(stdout, " "); + (void) fprintf(stdout, MSGSTR(141, "Status(Port A):")); + (void) fprintf(stdout, "\t"); + display_port_status(l_disk_state.g_disk_state.d_state_flags[FC_PORT_A]); + } + + if (l_disk_state.g_disk_state.port_b_valid) { + (void) fprintf(stdout, " "); + (void) fprintf(stdout, MSGSTR(142, "Status(Port B):")); + (void) fprintf(stdout, "\t"); + display_port_status(l_disk_state.g_disk_state.d_state_flags[FC_PORT_B]); + } + + (void) display_disk_info(local_inq, l_disk_state, path_struct, + pg_hdr, mode_data_avail, NULL, verbose); + (void) g_free_multipath(l_disk_state.g_disk_state.multipath_list); + + if (!(argpwwn || argnwwn)) { + break; + } + + } + (void) g_free_wwn_list(&wwn_list); + return (0); +} + + + +/* + * display_disk_info() Prints the device specific information + * for any FC_AL disk device. + * + * RETURNS: + * none. + */ +void +display_disk_info(L_inquiry inq, L_disk_state l_disk_state, + Path_struct *path_struct, struct mode_page *pg_hdr, + int mode_data_avail, char *name_buf, int options) +{ +float num_blks; +struct dlist *mlist; +int port_a, port_b; +struct my_mode_caching *pg8_buf; +L_inquiry enc_inq; +char *enc_phys_path; +Path_struct *enc_path_struct; +int enc_type = 0; +L_inquiry80 inq80; +size_t serial_len; +int err; + + serial_len = sizeof (inq80.inq_serial); + err = g_get_serial_number(path_struct->p_physical_path, + inq80.inq_serial, &serial_len); + if (err) { + fprintf(stderr, "\n"); + print_errString(err, path_struct->p_physical_path); + fprintf(stderr, "\n"); + exit(1); + } + (void) fprintf(stdout, " "); + (void) fprintf(stdout, MSGSTR(3, "Vendor:")); + (void) fprintf(stdout, "\t\t"); + print_chars(inq.inq_vid, sizeof (inq.inq_vid), 0); + + (void) fprintf(stdout, MSGSTR(2115, "\n Product ID:\t\t")); + print_chars(inq.inq_pid, sizeof (inq.inq_pid), 0); + + (void) fprintf(stdout, MSGSTR(2116, "\n WWN(Node):\t\t%s"), + l_disk_state.g_disk_state.node_wwn_s); + + if (l_disk_state.g_disk_state.port_a_valid) { + (void) fprintf(stdout, MSGSTR(2117, "\n WWN(Port A):\t\t%s"), + l_disk_state.g_disk_state.port_a_wwn_s); + } + if (l_disk_state.g_disk_state.port_b_valid) { + (void) fprintf(stdout, MSGSTR(2118, "\n WWN(Port B):\t\t%s"), + l_disk_state.g_disk_state.port_b_wwn_s); + } + (void) fprintf(stdout, "\n "); + (void) fprintf(stdout, MSGSTR(2119, "Revision:")); + (void) fprintf(stdout, "\t\t"); + print_chars(inq.inq_revision, sizeof (inq.inq_revision), 0); + + (void) fprintf(stdout, "\n "); + (void) fprintf(stdout, MSGSTR(17, "Serial Num:")); + (void) fprintf(stdout, "\t\t"); + print_chars(inq80.inq_serial, serial_len, 0); + num_blks = l_disk_state.g_disk_state.num_blocks; + if (num_blks) { + num_blks /= 2048; /* get Mbytes */ + (void) fprintf(stdout, "\n "); + (void) fprintf(stdout, + MSGSTR(60, + "Unformatted capacity:\t%6.3f MBytes"), num_blks); + } + (void) fprintf(stdout, "\n"); + + if (l_disk_state.g_disk_state.persistent_reserv_flag) { + (void) fprintf(stdout, + MSGSTR(2120, " Persistent Reserve:\t")); + if (l_disk_state.g_disk_state.persistent_active) { + (void) fprintf(stdout, + MSGSTR(39, "Active")); + (void) fprintf(stdout, "\n"); + } + if (l_disk_state.g_disk_state.persistent_registered) { + (void) fprintf(stdout, + MSGSTR(2121, "Found Registered Keys")); + } else { + (void) fprintf(stdout, + MSGSTR(87, "Not being used")); + } + (void) fprintf(stdout, "\n"); + } + + if ((mode_data_avail) && (pg_hdr->code == MODEPAGE_CACHING)) { + pg8_buf = (struct my_mode_caching *)(int)pg_hdr; + if (pg8_buf->wce) { + (void) fprintf(stdout, + MSGSTR(2122, + " Write Cache:\t\t" + "Enabled\n")); + } + if (pg8_buf->rcd == 0) { + (void) fprintf(stdout, + MSGSTR(2123, + " Read Cache:\t\t" + "Enabled\n")); + (void) fprintf(stdout, + MSGSTR(2320, + " Minimum prefetch:" + "\t0x%x\n" + " Maximum prefetch:" + "\t0x%x\n"), + pg8_buf->min_prefetch, + pg8_buf->max_prefetch); + } + } + + /* + * When /dev/rdsk/cxtxdxsx form of input is specified + * for display command the initial library version didn't + * display Location information. The change is made + * to display the same Location info as the non-library version. + */ + + if (name_buf != NULL) { + fprintf(stdout, MSGSTR(2125, " Location:\t\t")); + if (path_struct->slot_valid) { + /* + * We have to do another inquiry on the enclosure (name_buf) + * to determine if this device is within a daktari, or + * a two sided device. + */ + if (!l_convert_name(name_buf, &enc_phys_path, + &enc_path_struct, 0)) { + if (!g_get_inquiry(enc_phys_path, &enc_inq)) { + enc_type = l_get_enc_type(enc_inq); + } + } + /* If either of the above fail, we just assume the default */ + free(enc_phys_path); + free(enc_path_struct); + if (enc_type == DAK_ENC_TYPE) { + if (path_struct->f_flag) { + (void) fprintf(stdout, MSGSTR(2239, + "In slot %d in the enclosure named: %s\n"), + path_struct->slot, name_buf); + } else { + (void) fprintf(stdout, MSGSTR(2239, + "In slot %d in the enclosure named: %s\n"), + path_struct->slot + (MAX_DRIVES_DAK/2), + name_buf); + } + } else { /* Default enclosure type */ + (void) fprintf(stdout, path_struct->f_flag ? + MSGSTR(2126, + "In slot %d in the Front of the enclosure named: %s\n") + : MSGSTR(2127, + "In slot %d in the Rear of the enclosure named: %s\n"), + path_struct->slot, name_buf); + } + } else { + (void) fprintf(stdout, MSGSTR(2228, + "In the enclosure named: %s\n"), + name_buf); + } + } + + (void) fprintf(stdout, " %s\t\t%s\n", + MSGSTR(35, "Device Type:"), + dtype[inq.inq_dtype & DTYPE_MASK]); + + mlist = l_disk_state.g_disk_state.multipath_list; + (void) fprintf(stdout, MSGSTR(2128, " Path(s):\n")); + if (strstr(mlist->dev_path, SCSI_VHCI) != NULL) { + (void) fprintf(stdout, " %s\n %s\n", + mlist->logical_path, mlist->dev_path); + (void) adm_print_pathlist(mlist->dev_path); + } else { + while (mlist) { + (void) fprintf(stdout, " %s\n %s\n", + mlist->logical_path, mlist->dev_path); + mlist = mlist->next; + } + } + + if (Options & OPTION_V) { + if (path_struct->slot_valid) { + port_a = PORT_A; + port_b = PORT_B; + } else { + port_a = FC_PORT_A; + port_b = FC_PORT_B; + } + /* Only bother if the state is O.K. */ + if ((l_disk_state.g_disk_state.port_a_valid) && + (l_disk_state.g_disk_state.d_state_flags[port_a] == 0)) + adm_display_verbose_disk(path_struct->p_physical_path, options); + else if ((l_disk_state.g_disk_state.port_b_valid) && + (l_disk_state.g_disk_state.d_state_flags[port_b] == 0)) + adm_display_verbose_disk(path_struct->p_physical_path, options); + } + (void) fprintf(stdout, "\n"); + +} + + + +/* + * temp_decode() Display temperature bytes 1-3 state. + * + * RETURNS: + * none. + */ +void +temp_decode(Temp_elem_st *temp) +{ + if (temp->ot_fail) { + (void) fprintf(stdout, MSGSTR(2129, + ": FAILURE - Over Temperature")); + } + if (temp->ut_fail) { + (void) fprintf(stdout, MSGSTR(2130, + ": FAILURE - Under Temperature")); + } + if (temp->ot_warn) { + (void) fprintf(stdout, MSGSTR(2131, + ": WARNING - Over Temperature")); + } + if (temp->ut_warn) { + (void) fprintf(stdout, MSGSTR(2132, + ": WARNING - Under Temperature")); + } +} + + + +/* + * disp_degree() Display temperature in Degrees Celsius. + * + * RETURNS: + * none. + */ +void +disp_degree(Temp_elem_st *temp) +{ +int t; + + t = temp->degrees; + t -= 20; /* re-adjust */ + /* + * NL_Comment + * The %c is the degree symbol. + */ + (void) fprintf(stdout, ":%1.2d%cC ", t, 186); +} + + + +/* + * trans_decode() Display tranceivers state. + * + * RETURNS: + * none. + */ +void +trans_decode(Trans_elem_st *trans) +{ + if (trans->disabled) { + (void) fprintf(stdout, ": "); + (void) fprintf(stdout, MSGSTR(34, + "Disabled")); + } + if (trans->lol) { + (void) fprintf(stdout, MSGSTR(2133, + ": Not receiving a signal")); + } + if (trans->lsr_fail) { + (void) fprintf(stdout, MSGSTR(2134, + ": Laser failed")); + } +} + + + +/* + * trans_messages() Display tranceiver status. + * + * NOTE: The decoding of the status assumes that the elements + * are in order with the first two elements are for the + * "A" IB. It also assumes the tranceivers are numbered + * 0 and 1. + * + * RETURNS: + * none. + */ +void +trans_messages(struct l_state_struct *l_state, int ib_a_flag) +{ +Trans_elem_st trans; +int i, j, k; +int count = 0; +int elem_index = 0; + + /* Get and print messages */ + for (i = 0; i < (int)l_state->ib_tbl.config.enc_num_elem; i++) { + elem_index++; + if (l_state->ib_tbl.config.type_hdr[i].type == ELM_TYP_FL) { + + if (l_state->ib_tbl.config.type_hdr[i].text_len != 0) { + (void) fprintf(stdout, "\n\t\t%s\n", + l_state->ib_tbl.config.text[i]); + } + count = k = 0; + + for (j = 0; j < + (int)l_state->ib_tbl.config.type_hdr[i].num; j++) { + /* + * Only display the status for the selected IB. + */ + if ((count < 2 && ib_a_flag) || + (count >= 2 && !ib_a_flag)) { + (void) bcopy((const void *) + &l_state->ib_tbl.p2_s.element[elem_index + j], + (void *)&trans, sizeof (trans)); + + if (k == 0) { + (void) fprintf(stdout, "\t\t%d ", k); + } else { + (void) fprintf(stdout, "\n\t\t%d ", k); + } + if (trans.code == S_OK) { + (void) fprintf(stdout, + MSGSTR(29, "O.K.")); + revision_msg(l_state, elem_index + j); + } else if ((trans.code == S_CRITICAL) || + (trans.code == S_NONCRITICAL)) { + (void) fprintf(stdout, + MSGSTR(2135, "Failed")); + revision_msg(l_state, elem_index + j); + trans_decode(&trans); + } else if (trans.code == S_NOT_INSTALLED) { + (void) fprintf(stdout, + MSGSTR(30, "Not Installed")); + } else if (trans.code == S_NOT_AVAILABLE) { + (void) fprintf(stdout, + MSGSTR(34, "Disabled")); + revision_msg(l_state, elem_index + j); + } else { + (void) fprintf(stdout, + MSGSTR(4, "Unknown status")); + } + k++; + } + count++; + } + } + /* + * Calculate the index to each element. + */ + elem_index += l_state->ib_tbl.config.type_hdr[i].num; + } + (void) fprintf(stdout, "\n"); +} + + + +/* + * temperature_messages() Display temperature status. + * + * RETURNS: + * none. + */ +void +temperature_messages(struct l_state_struct *l_state, int rear_flag) +{ +Temp_elem_st temp; +int i, j, last_ok = 0; +int all_ok = 1; +int elem_index = 0; + + /* Get and print messages */ + for (i = 0; i < (int)l_state->ib_tbl.config.enc_num_elem; i++) { + elem_index++; /* skip global */ + if (l_state->ib_tbl.config.type_hdr[i].type == ELM_TYP_TS) { + if (!rear_flag) { + rear_flag = 1; /* only do front or rear backplane */ + if (l_state->ib_tbl.config.type_hdr[i].text_len != 0) { + (void) fprintf(stdout, "\t %s", + l_state->ib_tbl.config.text[i]); + } + + /* + * Check global status and if not all O.K. + * then print individually. + */ + (void) bcopy((const void *)&l_state->ib_tbl.p2_s.element[i], + (void *)&temp, sizeof (temp)); + for (j = 0; j < + (int)l_state->ib_tbl.config.type_hdr[i].num; j++) { + (void) bcopy((const void *) + &l_state->ib_tbl.p2_s.element[elem_index + j], + (void *)&temp, sizeof (temp)); + + if ((j == 0) && (temp.code == S_OK) && + (!(temp.ot_fail || temp.ot_warn || + temp.ut_fail || temp.ut_warn))) { + (void) fprintf(stdout, "\n\t %d", j); + } else if ((j == 6) && (temp.code == S_OK) && + all_ok) { + (void) fprintf(stdout, "\n\t %d", j); + } else if (last_ok && (temp.code == S_OK)) { + (void) fprintf(stdout, "%d", j); + } else { + (void) fprintf(stdout, "\n\t\t%d", j); + } + if (temp.code == S_OK) { + disp_degree(&temp); + if (temp.ot_fail || temp.ot_warn || + temp.ut_fail || temp.ut_warn) { + temp_decode(&temp); + all_ok = 0; + last_ok = 0; + } else { + last_ok++; + } + } else if (temp.code == S_CRITICAL) { + (void) fprintf(stdout, + MSGSTR(122, "Critical failure")); + last_ok = 0; + all_ok = 0; + } else if (temp.code == S_NONCRITICAL) { + (void) fprintf(stdout, + MSGSTR(89, "Non-Critical Failure")); + last_ok = 0; + all_ok = 0; + } else if (temp.code == S_NOT_INSTALLED) { + (void) fprintf(stdout, + MSGSTR(30, "Not Installed")); + last_ok = 0; + all_ok = 0; + } else if (temp.code == S_NOT_AVAILABLE) { + (void) fprintf(stdout, + MSGSTR(34, "Disabled")); + last_ok = 0; + all_ok = 0; + } else { + (void) fprintf(stdout, + MSGSTR(4, "Unknown status")); + last_ok = 0; + all_ok = 0; + } + } + if (all_ok) { + (void) fprintf(stdout, + MSGSTR(2136, " (All temperatures are " + "NORMAL.)")); + } + all_ok = 1; + (void) fprintf(stdout, "\n"); + } else { + rear_flag = 0; + } + } + elem_index += l_state->ib_tbl.config.type_hdr[i].num; + } +} + + + +/* + * ib_decode() Display IB byte 3 state. + * + * RETURNS: + * none. + */ +void +ib_decode(Ctlr_elem_st *ctlr) +{ + if (ctlr->overtemp_alart) { + (void) fprintf(stdout, MSGSTR(2137, + " - IB Over Temperature Alert ")); + } + if (ctlr->ib_loop_1_fail) { + (void) fprintf(stdout, MSGSTR(2138, + " - IB Loop 1 has failed ")); + } + if (ctlr->ib_loop_0_fail) { + (void) fprintf(stdout, MSGSTR(2139, + " - IB Loop 0 has failed ")); + } +} + + + +/* + * mb_messages() Display motherboard + * (interconnect assembly) messages. + * + * RETURNS: + * none. + */ +void +mb_messages(struct l_state_struct *l_state, int index, int elem_index) +{ +int j; +Interconnect_st interconnect; + + if (l_state->ib_tbl.config.type_hdr[index].text_len != 0) { + (void) fprintf(stdout, "%s\n", + l_state->ib_tbl.config.text[index]); + } + for (j = 0; j < (int)l_state->ib_tbl.config.type_hdr[index].num; + j++) { + (void) bcopy((const void *) + &l_state->ib_tbl.p2_s.element[elem_index + j], + (void *)&interconnect, sizeof (interconnect)); + (void) fprintf(stdout, "\t"); + + if (interconnect.code == S_OK) { + (void) fprintf(stdout, + MSGSTR(29, "O.K.")); + revision_msg(l_state, elem_index + j); + } else if (interconnect.code == S_NOT_INSTALLED) { + (void) fprintf(stdout, + MSGSTR(30, "Not Installed")); + } else if (interconnect.code == S_CRITICAL) { + if (interconnect.eprom_fail != NULL) { + (void) fprintf(stdout, MSGSTR(2140, + "Critical Failure: EEPROM failure")); + } else { + (void) fprintf(stdout, MSGSTR(2141, + "Critical Failure: Unknown failure")); + } + revision_msg(l_state, elem_index + j); + } else if (interconnect.code == S_NONCRITICAL) { + if (interconnect.eprom_fail != NULL) { + (void) fprintf(stdout, MSGSTR(2142, + "Non-Critical Failure: EEPROM failure")); + } else { + (void) fprintf(stdout, MSGSTR(2143, + "Non-Critical Failure: Unknown failure")); + } + revision_msg(l_state, elem_index + j); + } else if (interconnect.code == S_NOT_AVAILABLE) { + (void) fprintf(stdout, + MSGSTR(34, "Disabled")); + revision_msg(l_state, elem_index + j); + } else { + (void) fprintf(stdout, + MSGSTR(4, "Unknown status")); + } + (void) fprintf(stdout, "\n"); + } + + +} + + + +/* + * back_plane_messages() Display back_plane messages + * including the temperature's. + * + * RETURNS: + * none. + */ +void +back_plane_messages(struct l_state_struct *l_state, int index, int elem_index) +{ +Bp_elem_st bp; +int j; +char status_string[MAXPATHLEN]; + + if (l_state->ib_tbl.config.type_hdr[index].text_len != 0) { + (void) fprintf(stdout, "%s\n", + l_state->ib_tbl.config.text[index]); + } + for (j = 0; j < (int)l_state->ib_tbl.config.type_hdr[index].num; + j++) { + (void) bcopy((const void *) + &l_state->ib_tbl.p2_s.element[elem_index + j], + (void *)&bp, sizeof (bp)); + if (j == 0) { + (void) fprintf(stdout, + MSGSTR(2144, "\tFront Backplane: ")); + } else { + (void) fprintf(stdout, + MSGSTR(2145, "\tRear Backplane: ")); + } + + (void) l_element_msg_string(bp.code, status_string); + (void) fprintf(stdout, "%s", status_string); + + if (bp.code != S_NOT_INSTALLED) { + revision_msg(l_state, elem_index + j); + if ((bp.byp_a_enabled || bp.en_bypass_a) && + !(bp.byp_b_enabled || bp.en_bypass_b)) { + (void) fprintf(stdout, " ("); + (void) fprintf(stdout, + MSGSTR(130, "Bypass A enabled")); + (void) fprintf(stdout, ")"); + } else if ((bp.byp_b_enabled || bp.en_bypass_b) && + !(bp.byp_a_enabled || bp.en_bypass_a)) { + (void) fprintf(stdout, " ("); + (void) fprintf(stdout, + MSGSTR(129, "Bypass B enabled")); + (void) fprintf(stdout, ")"); + /* This case covers where a and b are bypassed */ + } else if (bp.byp_b_enabled || bp.en_bypass_b) { + (void) fprintf(stdout, + MSGSTR(2146, " (Bypass's A & B enabled)")); + } + (void) fprintf(stdout, "\n"); + temperature_messages(l_state, j); + } else { + (void) fprintf(stdout, "\n"); + } + } +} + + +/* + * dpm_SSC100_messages() Display SSC100 messages + * including the temperature's. + * + * RETURNS: + * none. + */ +void +dpm_SSC100_messages(struct l_state_struct *l_state, int index, int elem_index) +{ +Bp_elem_st bp; +int j; +char status_string[MAXPATHLEN]; + + if (l_state->ib_tbl.config.type_hdr[index].text_len != 0) { + (void) fprintf(stdout, "%s\n", + l_state->ib_tbl.config.text[index]); + } + for (j = 0; j < (int)l_state->ib_tbl.config.type_hdr[index].num; + j++) { + (void) bcopy((const void *) + &l_state->ib_tbl.p2_s.element[elem_index + j], + (void *)&bp, sizeof (bp)); + (void) fprintf(stdout, MSGSTR(2246, " SSC100 #%d: "), j); + + (void) l_element_msg_string(bp.code, status_string); + (void) fprintf(stdout, "%s", status_string); + + if (bp.code != S_NOT_INSTALLED) { + revision_msg(l_state, elem_index + j); + if ((bp.byp_a_enabled || bp.en_bypass_a) && + !(bp.byp_b_enabled || bp.en_bypass_b)) { + (void) fprintf(stdout, " ("); + (void) fprintf(stdout, + MSGSTR(130, "Bypass A enabled")); + (void) fprintf(stdout, ")"); + } else if ((bp.byp_b_enabled || bp.en_bypass_b) && + !(bp.byp_a_enabled || bp.en_bypass_a)) { + (void) fprintf(stdout, " ("); + (void) fprintf(stdout, + MSGSTR(129, "Bypass B enabled")); + (void) fprintf(stdout, ")"); + /* This case covers where a and b are bypassed */ + } else if (bp.byp_b_enabled || bp.en_bypass_b) { + (void) fprintf(stdout, + MSGSTR(2146, " (Bypass's A & B enabled)")); + } + (void) fprintf(stdout, "\n"); + } else { + (void) fprintf(stdout, "\n"); + } + } + temperature_messages(l_state, 0); +} + + + + +/* + * loop_messages() Display loop messages. + * + * RETURNS: + * none. + */ +void +loop_messages(struct l_state_struct *l_state, int index, int elem_index) +{ +Loop_elem_st loop; +int j; + + if (l_state->ib_tbl.config.type_hdr[index].text_len != 0) { + (void) fprintf(stdout, "%s\n", + l_state->ib_tbl.config.text[index]); + } + for (j = 0; j < (int)l_state->ib_tbl.config.type_hdr[index].num; + j++) { + (void) bcopy((const void *) + &l_state->ib_tbl.p2_s.element[elem_index + j], + (void *)&loop, sizeof (loop)); + + (void) fprintf(stdout, "\t"); + if (j == 0) { + if (loop.code == S_NOT_INSTALLED) { + (void) fprintf(stdout, + MSGSTR(2147, "Loop A is not installed")); + } else { + if (loop.split) { + (void) fprintf(stdout, MSGSTR(2148, + "Loop A is configured as two separate loops.")); + } else { + (void) fprintf(stdout, MSGSTR(2149, + "Loop A is configured as a single loop.")); + } + } + } else { + if (loop.code == S_NOT_INSTALLED) { + (void) fprintf(stdout, + MSGSTR(2150, "Loop B is not installed")); + } else { + if (loop.split) { + (void) fprintf(stdout, MSGSTR(2151, + "Loop B is configured as two separate loops.")); + } else { + (void) fprintf(stdout, MSGSTR(2152, + "Loop B is configured as a single loop.")); + } + } + } + (void) fprintf(stdout, "\n"); + } +} + + + +/* + * ctlr_messages() Display ESI Controller status. + * + * RETURNS: + * none. + */ +void +ctlr_messages(struct l_state_struct *l_state, int index, int elem_index) +{ +Ctlr_elem_st ctlr; +int j; +int ib_a_flag = 1; + + if (l_state->ib_tbl.config.type_hdr[index].text_len != 0) { + (void) fprintf(stdout, "%s\n", + l_state->ib_tbl.config.text[index]); + } + for (j = 0; j < (int)l_state->ib_tbl.config.type_hdr[index].num; + j++) { + (void) bcopy((const void *) + &l_state->ib_tbl.p2_s.element[elem_index + j], + (void *)&ctlr, sizeof (ctlr)); + if (j == 0) { + (void) fprintf(stdout, MSGSTR(2153, "\tA: ")); + } else { + (void) fprintf(stdout, MSGSTR(2154, "\tB: ")); + ib_a_flag = 0; + } + if (ctlr.code == S_OK) { + (void) fprintf(stdout, MSGSTR(29, "O.K.")); + /* If any byte 3 bits set display */ + ib_decode(&ctlr); + /* Display Version message */ + revision_msg(l_state, elem_index + j); + /* + * Display the tranciver module state for this + * IB. + */ + trans_messages(l_state, ib_a_flag); + } else if (ctlr.code == S_CRITICAL) { + (void) fprintf(stdout, + MSGSTR(122, "Critical failure")); + ib_decode(&ctlr); + (void) fprintf(stdout, "\n"); + } else if (ctlr.code == S_NONCRITICAL) { + (void) fprintf(stdout, + MSGSTR(89, "Non-Critical Failure")); + ib_decode(&ctlr); + (void) fprintf(stdout, "\n"); + } else if (ctlr.code == S_NOT_INSTALLED) { + (void) fprintf(stdout, + MSGSTR(30, "Not Installed")); + (void) fprintf(stdout, "\n"); + } else if (ctlr.code == S_NOT_AVAILABLE) { + (void) fprintf(stdout, + MSGSTR(34, "Disabled")); + (void) fprintf(stdout, "\n"); + } else { + (void) fprintf(stdout, + MSGSTR(4, "Unknown status")); + (void) fprintf(stdout, "\n"); + } + } +} + + + +/* + * fan_decode() Display Fans bytes 1-3 state. + * + * RETURNS: + * none. + */ +void +fan_decode(Fan_elem_st *fan) +{ + if (fan->fail) { + (void) fprintf(stdout, MSGSTR(2155, + ":Yellow LED is on")); + } + if (fan->speed == 0) { + (void) fprintf(stdout, MSGSTR(2156, + ":Fan stopped")); + } else if (fan->speed < S_HI_SPEED) { + (void) fprintf(stdout, MSGSTR(2157, + ":Fan speed Low")); + } else { + (void) fprintf(stdout, MSGSTR(2158, + ":Fan speed Hi")); + } +} + +/* + * fan_messages() Display Fan status. + * + * RETURNS: + * none. + */ +void +fan_messages(struct l_state_struct *l_state, int hdr_index, int elem_index) +{ +Fan_elem_st fan; +int j; + + /* Get and print messages */ + if (l_state->ib_tbl.config.type_hdr[hdr_index].text_len != 0) { + (void) fprintf(stdout, "%s\n", + l_state->ib_tbl.config.text[hdr_index]); + } + for (j = 0; j < (int)l_state->ib_tbl.config.type_hdr[hdr_index].num; + j++) { + (void) bcopy((const void *) + &l_state->ib_tbl.p2_s.element[elem_index + j], + (void *)&fan, sizeof (fan)); + (void) fprintf(stdout, "\t%d ", j); + if (fan.code == S_OK) { + (void) fprintf(stdout, MSGSTR(29, "O.K.")); + revision_msg(l_state, elem_index + j); + } else if (fan.code == S_CRITICAL) { + (void) fprintf(stdout, + MSGSTR(122, "Critical failure")); + fan_decode(&fan); + revision_msg(l_state, elem_index + j); + } else if (fan.code == S_NONCRITICAL) { + (void) fprintf(stdout, + MSGSTR(89, "Non-Critical Failure")); + fan_decode(&fan); + revision_msg(l_state, elem_index + j); + } else if (fan.code == S_NOT_INSTALLED) { + (void) fprintf(stdout, + MSGSTR(30, "Not Installed")); + } else if (fan.code == S_NOT_AVAILABLE) { + (void) fprintf(stdout, + MSGSTR(34, "Disabled")); + revision_msg(l_state, elem_index + j); + } else { + (void) fprintf(stdout, + MSGSTR(4, "Unknown status")); + } + } + (void) fprintf(stdout, "\n"); +} + + + +/* + * ps_decode() Display Power Supply bytes 1-3 state. + * + * RETURNS: + * none. + */ +void +ps_decode(Ps_elem_st *ps) +{ + if (ps->dc_over) { + (void) fprintf(stdout, MSGSTR(2159, + ": DC Voltage too high")); + } + if (ps->dc_under) { + (void) fprintf(stdout, MSGSTR(2160, + ": DC Voltage too low")); + } + if (ps->dc_over_i) { + (void) fprintf(stdout, MSGSTR(2161, + ": DC Current too high")); + } + if (ps->ovrtmp_fail || ps->temp_warn) { + (void) fprintf(stdout, MSGSTR(2162, + ": Temperature too high")); + } + if (ps->ac_fail) { + (void) fprintf(stdout, MSGSTR(2163, + ": AC Failed")); + } + if (ps->dc_fail) { + (void) fprintf(stdout, MSGSTR(2164, + ": DC Failed")); + } +} + + + +/* + * revision_msg() Print the revision message from page 7. + * + * RETURNS: + * none. + */ +void +revision_msg(struct l_state_struct *l_state, int index) +{ + if (strlen((const char *) + l_state->ib_tbl.p7_s.element_desc[index].desc_string)) { + (void) fprintf(stdout, "(%s)", + l_state->ib_tbl.p7_s.element_desc[index].desc_string); + } +} + + + +/* + * ps_messages() Display Power Supply status. + * + * RETURNS: + * none. + */ +void +ps_messages(struct l_state_struct *l_state, int index, int elem_index) +{ +Ps_elem_st ps; +int j; + + /* Get and print Power Supply messages */ + + if (l_state->ib_tbl.config.type_hdr[index].text_len != 0) { + (void) fprintf(stdout, "%s\n", + l_state->ib_tbl.config.text[index]); + } + + for (j = 0; j < (int)l_state->ib_tbl.config.type_hdr[index].num; + j++) { + (void) bcopy((const void *) + &l_state->ib_tbl.p2_s.element[elem_index + j], + (void *)&ps, sizeof (ps)); + (void) fprintf(stdout, "\t%d ", j); + if (ps.code == S_OK) { + (void) fprintf(stdout, MSGSTR(29, "O.K.")); + revision_msg(l_state, elem_index + j); + } else if (ps.code == S_CRITICAL) { + (void) fprintf(stdout, + MSGSTR(122, "Critical failure")); + ps_decode(&ps); + revision_msg(l_state, elem_index + j); + } else if (ps.code == S_NONCRITICAL) { + (void) fprintf(stdout, + MSGSTR(89, "Non-Critical Failure")); + ps_decode(&ps); + revision_msg(l_state, elem_index + j); + } else if (ps.code == S_NOT_INSTALLED) { + (void) fprintf(stdout, + MSGSTR(30, "Not Installed")); + } else if (ps.code == S_NOT_AVAILABLE) { + (void) fprintf(stdout, + MSGSTR(34, "Disabled")); + revision_msg(l_state, elem_index + j); + } else { + (void) fprintf(stdout, + MSGSTR(4, "Unknown status")); + } + + } + (void) fprintf(stdout, "\n"); +} + + + +/* + * abnormal_condition() Display any abnormal condition messages. + * + * RETURNS: + * none. + */ +void +abnormal_condition_display(struct l_state_struct *l_state) +{ + + (void) fprintf(stdout, "\n"); + if (l_state->ib_tbl.p2_s.ui.crit) { + (void) fprintf(stdout, + MSGSTR(2165, " " + "CRITICAL CONDITION DETECTED\n")); + } + if (l_state->ib_tbl.p2_s.ui.non_crit) { + (void) fprintf(stdout, + MSGSTR(2166, " " + "WARNING: NON-CRITICAL CONDITION DETECTED\n")); + } + if (l_state->ib_tbl.p2_s.ui.invop) { + (void) fprintf(stdout, + MSGSTR(2167, " " + "WARNING: Invalid Operation bit set.\n" + "\tThis means an Enclosure Control page" + " or an Array Control page with an invalid\n" + "\tformat has previously been transmitted to the" + " Enclosure Services card by a\n\tSend Diagnostic" + " SCSI command.\n")); + } + (void) fprintf(stdout, "\n"); +} + + + + + +/* + * adm_start() Spin up the given list + * of SENA devices. + * + * RETURNS: + * none. + */ +int +adm_start(char **argv) +{ +char *path_phys = NULL; +Path_struct *path_struct; +int err = 0, retval = 0; + + while (*argv != NULL) { + if ((err = l_convert_name(*argv, &path_phys, + &path_struct, Options & PVERBOSE)) != 0) { + (void) fprintf(stderr, MSGSTR(33, + " Error: converting" + " %s to physical path.\n" + " Invalid pathname.\n"), + *argv); + if (err != -1) { + (void) print_errString(err, *argv); + } + (argv)++; + retval++; + continue; + } + VERBPRINT(MSGSTR(101, "Issuing start to:\n %s\n"), *argv); + if (err = g_start(path_phys)) { + (void) print_errString(err, *argv); + (argv)++; + retval++; + continue; + } + (argv)++; + } + return (retval); +} + + + +/* + * adm_stop() Spin down a + * given list of SENA devices. + * + * RETURNS: + * none. + */ +int +adm_stop(char **argv) +{ +char *path_phys = NULL; +Path_struct *path_struct; +int err = 0, retval = 0; + + while (*argv != NULL) { + if ((err = l_convert_name(*argv, &path_phys, + &path_struct, Options & PVERBOSE)) != 0) { + (void) fprintf(stderr, + MSGSTR(33, + " Error: converting" + " %s to physical path.\n" + " Invalid pathname.\n"), + *argv); + if (err != -1) { + (void) print_errString(err, *argv); + } + (argv)++; + retval++; + continue; + } + + /* + * scsi stop is not supported for tape drives. + * The scsi unload op code for tape is the same as a + * scsi stop for disk so this command will eject the tape. + * If an eject is the desired behavior then remove the + * following if block. ('mt offline' will give you + * the same eject functionality). + */ + if (strstr(path_phys, SLSH_DRV_NAME_ST)) { + errno = ENOTSUP; + (void) print_errString(0, path_phys); + (argv)++; + continue; + } + + VERBPRINT(MSGSTR(100, "Issuing stop to:\n %s\n"), *argv); + if (err = g_stop(path_phys, 1)) { + (void) print_errString(err, *argv); + (argv)++; + retval++; + continue; + } + (argv)++; + } + return (retval); +} + + +/* + * On a SOC+ chip, the port is either put into (offline) or pulled out + * of (online) a loopback mode since the laser cannot be turned on or off. + * As of this writing, this feature is yet to be supported by the ifp + * driver on a QLogic card. + * + * INPUT : + * Command line args and flag - LUX_P_ONLINE or LUX_P_OFFLINE + * The path that is passed has to be the physical path to the port. + * For example : + * /devices/sbus@2,0/SUNW,socal@2,0:0 + * /devices/io-unit@f,e0200000/sbi@0,0/SUNW,socal@2,0:0 + * /devices/pci@1f,4000/SUNW,ifp@2:devctl + * RETURNS : + * Nothing + */ +int +adm_port_offline_online(char *argv[], int flag) +{ + int err, retval = 0; + char *path_phys = NULL; + char *nexus_path_ptr = NULL; + Path_struct *path_struct = NULL; + + while (*argv != NULL) { + if ((err = l_convert_name(*argv, &path_phys, + &path_struct, Options & PVERBOSE)) != 0) { + (void) fprintf(stderr, + MSGSTR(33, + " Error: converting" + " %s to physical path.\n" + " Invalid pathname.\n"), + *argv); + if (err != -1) { + (void) print_errString(err, *argv); + } + argv++; + retval++; + continue; + } + + /* Get the nexus path - need this to print messages */ + if ((err = g_get_nexus_path(path_phys, &nexus_path_ptr)) != 0) { + (void) print_errString(err, *argv); + retval++; + goto cleanup_and_go; + } + + if (flag == LUX_P_OFFLINE) { + if ((err = g_port_offline(nexus_path_ptr))) { + (void) print_errString(err, nexus_path_ptr); + retval++; + goto cleanup_and_go; + } + fprintf(stdout, + MSGSTR(2223, "Port %s has been disabled\n"), + nexus_path_ptr); + } else if (flag == LUX_P_ONLINE) { + if ((err = g_port_online(nexus_path_ptr))) { + (void) print_errString(err, nexus_path_ptr); + retval++; + goto cleanup_and_go; + } + fprintf(stdout, + MSGSTR(2224, "Port %s has been enabled\n"), + nexus_path_ptr); + } else { + (void) fprintf(stderr, + MSGSTR(2225, + "Unknown action requested " + "on port - %d\nIgnoring."), + flag); + retval++; + } +cleanup_and_go: + free(path_phys); + free(path_struct); + free(nexus_path_ptr); + argv++; + } + return (retval); +} + +/* + * Expert level subcommand 'luxadm -e port' + * which displays all FC ports on a host and state information for + * connectivity (CONNECTED or NOT CONNECTED) indicating whether there + * are devices attached to the port. + * + * Sample output for ifp: + * + * /devices/pci@1f,4000/SUNW,ifp@2:devctl CONNECTED + * /devices/pci@1f,2000/SUNW,ifp@1:devctl NOT CONNECTED + * + * Sample output for socal: + * + * /devices/sbus@2,0/SUNW,socal@d,10000:0 CONNECTED + * /devices/sbus@2,0/SUNW,socal@d,10000:1 NOT CONNECTED + * /devices/sbus@2,0/SUNW,socal@2,0:0 NOT CONNECTED + * /devices/sbus@2,0/SUNW,socal@2,0:1 CONNECTED + * + * Note: for socal the path returned is not a devctl path as there is no + * devctl path for socal. + * + * Sample output for fp: + * + * /devices/sbus@2,0/SUNW,qlc@5/fp@0,0:devctl CONNECTED + * /devices/sbus@2,0/SUNW,qlc@4/fp@1,0:devctl CONNECTED + */ +int +adm_display_port(int verbose) +{ + /* + * If another port driver needs to be searched, add it here + */ + static char *portdrvr_list[] = {"socal", + "fp", + "ifp", + NULL}; + portlist_t portlist; + int x = 0, err = 0, retval = 0; + int port_state; + + portlist.hbacnt = 0; + + /* + * Look for all HBA ports as listed in portdrvr_list[] + */ + while (portdrvr_list[x]) { + if (err = g_get_port_path(portdrvr_list[x], &portlist)) { + if (err != L_PORT_DRIVER_NOT_FOUND && + err != L_PHYS_PATH_NOT_FOUND) { + (void) print_errString(err, portdrvr_list[x]); + retval++; + } + } + x++; + } + + + /* + * For each port path found get the connection state. + * If there are devices attached the state is considered connected. + */ + for (x = 0; x < portlist.hbacnt; x++) { + if (err = g_get_port_state(portlist.physpath[x], + &port_state, verbose)) { + (void) print_errString(err, portlist.physpath[x]); + retval++; + } else { + fprintf(stdout, "%-65s ", portlist.physpath[x]); + if (port_state == PORT_CONNECTED) { + (void) fprintf(stdout, + MSGSTR(2233, + "CONNECTED\n")); + } else { + (void) fprintf(stdout, + MSGSTR(2234, + "NOT CONNECTED\n")); + } + } + } + g_free_portlist(&portlist); + return (retval); +} + +/* + * Expert level subcommand 'luxadm -e external_loopback <portpath> + * internal_loopback + * no_loopback + * Does just what you would think. Sets port in designated loopback + * mode. + * INPUT: portpath - path to device on which to set loopback mode + * flag - loopback mode to set. Values are: + * EXT_LOOPBACK + * INT_LOOPBACK + * NO_LOOPBACK + * + * RETURN: 0 on success + * non-zero on failure + */ +int +adm_port_loopback(char *portpath, int flag) +{ + int err; + char *path_phys = NULL; + Path_struct *path_struct = NULL; + int cmd; + + if ((err = l_convert_name(portpath, &path_phys, + &path_struct, Options & PVERBOSE)) != 0) { + (void) fprintf(stderr, + MSGSTR(33, + " Error: converting" + " %s to physical path.\n" + " Invalid pathname.\n"), + portpath); + if (err != -1) { + (void) print_errString(err, portpath); + } + return (-1); + } + + switch (flag) { + case EXT_LOOPBACK: + cmd = EXT_LPBACK; + break; + case INT_LOOPBACK: + cmd = INT_LPBACK; + break; + case NO_LOOPBACK: + cmd = NO_LPBACK; + break; + default: + (void) fprintf(stderr, + MSGSTR(2225, + "Unknown action requested " + "on port - %d\nIgnoring."), + flag); + free(path_phys); + free(path_struct); + return (-1); + } + + + if ((err = g_loopback_mode(path_phys, cmd)) != 0) { + (void) print_errString(err, portpath); + free(path_phys); + free(path_struct); + return (-1); + } else { + switch (flag) { + case EXT_LOOPBACK: + (void) fprintf(stdout, + MSGSTR(2230, + "External loopback mode set " + "on:\n%s\n"), + portpath); + break; + case INT_LOOPBACK: + (void) fprintf(stdout, + MSGSTR(2231, + "Internal loopback mode set " + "on:\n%s\n"), + portpath); + break; + case NO_LOOPBACK: + (void) fprintf(stdout, + MSGSTR(2232, + "Loopback mode unset " + "on:\n%s\n"), + portpath); + break; + default: + fprintf(stderr, + MSGSTR(2248, "Undefined command\n")); + break; + } + } + free(path_phys); + free(path_struct); + return (0); +} + + + +/* + * To print the pathlist and mpxio path attributes + */ +void +adm_print_pathlist(char *dev_path) +{ + int i, pathcnt = 1; + mp_pathlist_t pathlist; + int retval = 0; + char temppath[MAXPATHLEN]; + char wwns[(WWN_SIZE *2) +1]; + uchar_t wwn_data[WWN_SIZE]; + int err; + int state, ext_state = 0; + char *path_state[5]; + + path_state[0] = MSGSTR(2400, "INIT"); + path_state[1] = MSGSTR(2401, "ONLINE"); + path_state[2] = MSGSTR(2402, "STANDBY"); + path_state[3] = MSGSTR(2403, "FAULT"); + path_state[4] = MSGSTR(2404, "OFFLINE"); + + (void) strcpy(temppath, dev_path); + retval = g_get_pathlist(temppath, &pathlist); + if (retval != 0) { + (void) print_errString(retval, NULL); + exit(-1); + } + pathcnt = pathlist.path_count; + for (i = 0; i < pathcnt; i++) { + (void) fprintf(stdout, + MSGSTR(2303, " Controller \t%s\n"), + pathlist.path_info[i].path_hba); + + (void) fprintf(stdout, + MSGSTR(2304, " Device Address\t\t%s\n"), + pathlist.path_info[i].path_addr); + + if ((err = get_host_controller_pwwn( + pathlist.path_info[i].path_hba, + (uchar_t *)&wwn_data)) != 0) { + if (err != ENOTSUP) { + (void) print_errString(err, + pathlist.path_info[i].path_hba); + exit(1); + } + } + + if (!err) { + copy_wwn_data_to_str(wwns, wwn_data); + (void) fprintf(stdout, + MSGSTR(2326, " Host controller port WWN\t%s\n"), + wwns); + } + + (void) fprintf(stdout, + MSGSTR(2305, " Class\t\t\t%s\n"), + pathlist.path_info[i].path_class); + if (pathlist.path_info[i].path_state < MAXPATHSTATE) { + (void) fprintf(stdout, + MSGSTR(2306, " State\t\t\t%s\n"), + path_state[pathlist.path_info[i].path_state]); + } + if ((err = g_stms_get_path_state(dev_path, + pathlist.path_info[i].path_hba, &state, + &ext_state)) != 0) { + (void) print_errString(err, + pathlist.path_info[i].path_hba); + exit(1); + } else { + if ((ext_state & MDI_PATHINFO_STATE_USER_DISABLE) + == MDI_PATHINFO_STATE_USER_DISABLE) { + ext_state = 0; + fprintf(stdout, + MSGSTR(2327, + " I/Os disabled on this %s path\n\n"), + path_state[pathlist.path_info[i].path_state]); + } + } + } + /* Free memory for per path info properties */ + free(pathlist.path_info); +} + +/* + * compare_multipath + * compares path with all paths in pathlist + * If there is a match, 0 is returned, otherwise 1 is returned + */ +int +compare_multipath(char *path, struct mplist_struct *pathlist) +{ + + while (pathlist != NULL) { + if (strncmp(path, pathlist->devpath, MAXPATHLEN) == 0) { + return (0); + } + pathlist = pathlist->next; + } + return (1); +} + +/* + * lun_display() Prints the + * information for an individual lun. + * + * RETURNS: + * none. + */ +static int +lun_display(Path_struct *path_struct, L_inquiry inq_struct, int verbose) +{ + +char phys_path[MAXPATHLEN], last_logical_path[MAXPATHLEN]; +uchar_t *pg_buf = NULL; +L_disk_state l_disk_state; +struct dlist *mlist; +int offset, mode_data_avail, err = 0; +Mode_header_10 *mode_header_ptr; +struct mode_page *pg_hdr; +WWN_list *wwn_list, *list_start, *wwn_list_ptr; +WWN_list *wwn_list_find; +int found = 0; +int argpwwn = 0, argnwwn = 0; +struct mplist_struct *mplistp, *mpl, *mpln; +struct dlist *dlist; + + + + strcpy(phys_path, path_struct->p_physical_path); + strcpy(last_logical_path, phys_path); + + mplistp = mpl = mpln = (struct mplist_struct *)NULL; + /* + * Get path to all the FC disk and tape devices. + * If there is no slash in the argument in this routine, we assume + * it is a wwn argument. + */ + if (strstr(path_struct->argv, "/") != NULL) { + if ((err = g_devices_get_all(&wwn_list)) != 0) { + return (err); + } + } else { + if ((err = g_get_wwn_list(&wwn_list, verbose)) != 0) { + return (err); + } + } + + g_sort_wwn_list(&wwn_list); + + list_start = wwn_list; + + for (wwn_list_ptr = wwn_list; wwn_list_ptr != NULL; + wwn_list_ptr = wwn_list_ptr->wwn_next) { + if (strcasecmp(wwn_list_ptr->port_wwn_s, + path_struct->argv) == 0) { + list_start = wwn_list_ptr; + argpwwn = 1; + break; + } else if (strcasecmp(wwn_list_ptr->node_wwn_s, + path_struct->argv) == 0) { + list_start = wwn_list_ptr; + argnwwn = 1; + break; + } + } + + for (wwn_list_ptr = list_start; wwn_list_ptr != NULL; + wwn_list_ptr = wwn_list_ptr->wwn_next) { + + + if (argpwwn) { + if (strcasecmp(wwn_list_ptr->port_wwn_s, + path_struct->argv) != 0) { + continue; + } + (void) strcpy(phys_path, wwn_list_ptr->physical_path); + } else if (argnwwn) { + if (strstr(wwn_list_ptr->logical_path, + last_logical_path) != NULL) { + continue; + } + if (strcasecmp(wwn_list_ptr->node_wwn_s, + path_struct->argv) != 0) { + continue; + } + (void) strcpy(phys_path, wwn_list_ptr->physical_path); + (void) strcpy(last_logical_path, + wwn_list_ptr->logical_path); + } + + if (argnwwn || argpwwn) { + if (compare_multipath(wwn_list_ptr->logical_path, + mplistp) == 0) { + continue; + } + } + + mode_data_avail = 0; + + (void) memset(&l_disk_state, 0, sizeof (struct l_disk_state_struct)); + + /* + * Don't call g_get_multipath if this is a SCSI_VHCI device + * dlist gets alloc'ed here to retain the free at the end + */ + if (strstr(phys_path, SCSI_VHCI) == NULL) { + if ((err = g_get_multipath(phys_path, + &(l_disk_state.g_disk_state.multipath_list), + wwn_list, verbose)) != 0) { + return (err); + } + + mlist = l_disk_state.g_disk_state.multipath_list; + if (mlist == NULL) { + l_disk_state.l_state_flag = L_NO_PATH_FOUND; + N_DPRINTF(" lun_display: Error finding" + " multiple paths to the disk.\n"); + (void) g_free_wwn_list(&wwn_list); + return (L_NO_VALID_PATH); + } + } else { + /* Search for match on physical path name */ + for (wwn_list_find = list_start; wwn_list_find != NULL; + wwn_list_find = wwn_list_find->wwn_next) { + if (strncmp(wwn_list_find->physical_path, phys_path, + strlen(wwn_list_find->physical_path)) + == 0) { + found++; + break; + } + } + + if (!found) { + return (L_NO_VALID_PATH); + } else { + found = 0; + } + + if ((dlist = (struct dlist *) + calloc(1, sizeof (struct dlist))) == NULL) { + return (L_MALLOC_FAILED); + } + if ((dlist->logical_path = (char *)calloc(1, + strlen(wwn_list_find->logical_path) + 1)) == NULL) { + return (L_MALLOC_FAILED); + } + if ((dlist->dev_path = (char *)calloc(1, + strlen(phys_path) + 1)) == NULL) { + return (L_MALLOC_FAILED); + } + strncpy(dlist->logical_path, wwn_list_find->logical_path, + strlen(wwn_list_find->logical_path)); + strncpy(dlist->dev_path, phys_path, strlen(phys_path)); + l_disk_state.g_disk_state.multipath_list = dlist; + } + + if (argnwwn || argpwwn) { + for (mlist = l_disk_state.g_disk_state.multipath_list; + mlist != NULL; mlist = mlist->next) { + /* add the path to the list for compare */ + if ((mpl = (struct mplist_struct *) + calloc(1, sizeof (struct mplist_struct))) + == NULL) { + adm_mplist_free(mplistp); + return (L_MALLOC_FAILED); + } + + mpl->devpath = (char *)calloc(1, MAXPATHLEN+1); + if (mpl->devpath == NULL) { + adm_mplist_free(mplistp); + return (L_MALLOC_FAILED); + } + strncpy(mpl->devpath, mlist->logical_path, + strlen(mlist->logical_path)); + if (mplistp == NULL) { + mplistp = mpln = mpl; + } else { + mpln->next = mpl; + mpln = mpl; + } + } + } + + /* get mode page information for FC device */ + if (l_get_mode_pg(phys_path, &pg_buf, verbose) == 0) { + mode_header_ptr = (struct mode_header_10_struct *) + (void *)pg_buf; + offset = sizeof (struct mode_header_10_struct) + + mode_header_ptr->bdesc_length; + pg_hdr = (struct mode_page *)&pg_buf[offset]; + + while (offset < (mode_header_ptr->length + + sizeof (mode_header_ptr->length)) && + !mode_data_avail) { + if (pg_hdr->code == MODEPAGE_CACHING) { + mode_data_avail++; + break; + } + offset += pg_hdr->length + sizeof (struct mode_page); + pg_hdr = (struct mode_page *)&pg_buf[offset]; + } + } + + switch ((inq_struct.inq_dtype & DTYPE_MASK)) { + case DTYPE_DIRECT: + fprintf(stdout, MSGSTR(121, "DEVICE PROPERTIES for disk: %s\n"), + path_struct->argv); + break; + case DTYPE_SEQUENTIAL: /* Tape */ + fprintf(stdout, MSGSTR(2249, "DEVICE PROPERTIES for tape: %s\n"), + path_struct->argv); + break; + default: + fprintf(stdout, MSGSTR(2250, "DEVICE PROPERTIES for: %s\n"), + path_struct->argv); + break; + } + + (void) display_lun_info(l_disk_state, path_struct, pg_hdr, + mode_data_avail, wwn_list, phys_path); + + (void) g_free_multipath(l_disk_state.g_disk_state.multipath_list); + + if (!(argpwwn || argnwwn)) { + break; + } + + } /* End for wwn_list_ptr = list_start... */ + + (void) g_free_wwn_list(&wwn_list); + adm_mplist_free(mplistp); + return (0); +} + +/* + * display_lun_info() Prints the device specific information + * for a lun. + * + * RETURNS: + * none. + */ +void +display_lun_info(L_disk_state l_disk_state, Path_struct *path_struct, + struct mode_page *pg_hdr, int mode_data_avail, WWN_list + *wwn_list, char *phys_path) +{ +float lunMbytes; +struct scsi_capacity_16 cap_data; +struct dlist *mlist; +struct my_mode_caching *pg8_buf; +int err; +L_inquiry inq; +hrtime_t start_time, end_time; +char *envdb = NULL; +int peripheral_qual; +L_inquiry80 inq80; +size_t serial_len = sizeof (inq80.inq_serial); + + if ((envdb = getenv("_LUX_T_DEBUG")) != NULL) { + start_time = gethrtime(); + } + + memset(&cap_data, 0, sizeof (cap_data)); + + if (err = g_get_inquiry(phys_path, &inq)) { + fprintf(stderr, "\n"); + print_errString(err, phys_path); + fprintf(stderr, "\n"); + exit(1); + } + + if (err = g_get_serial_number(phys_path, inq80.inq_serial, + &serial_len)) { + fprintf(stderr, "\n"); + print_errString(err, phys_path); + fprintf(stderr, "\n"); + exit(1); + } + /* + * check to see if the peripheral qualifier is zero + * if it is non-zero, we will return with an error. + */ + peripheral_qual = inq.inq_dtype & ~DTYPE_MASK; + if (peripheral_qual != DPQ_POSSIBLE) { + fprintf(stderr, MSGSTR(2254, "\n Error: Logical Unit " + "(%s) is not available.\n"), phys_path); + exit(1); + } + + fprintf(stdout, " "); + fprintf(stdout, MSGSTR(3, "Vendor:")); + fprintf(stdout, "\t\t"); + print_chars(inq.inq_vid, sizeof (inq.inq_vid), 0); + fprintf(stdout, MSGSTR(2115, "\n Product ID:\t\t")); + print_chars(inq.inq_pid, sizeof (inq.inq_pid), 0); + + fprintf(stdout, "\n "); + fprintf(stdout, MSGSTR(2119, "Revision:")); + fprintf(stdout, "\t\t"); + print_chars(inq.inq_revision, sizeof (inq.inq_revision), 0); + + fprintf(stdout, "\n "); + fprintf(stdout, MSGSTR(17, "Serial Num:")); + fprintf(stdout, "\t\t"); + print_chars(inq80.inq_serial, serial_len, 0); + + if ((inq.inq_dtype & DTYPE_MASK) == DTYPE_DIRECT) { + if ((err = get_lun_capacity(phys_path, &cap_data)) != 0) { + print_errString(err, phys_path); + exit(1); + } + + if (cap_data.sc_capacity > 0 && cap_data.sc_lbasize > 0) { + lunMbytes = cap_data.sc_capacity + 1; + lunMbytes *= cap_data.sc_lbasize; + lunMbytes /= (float)(1024*1024); + fprintf(stdout, "\n "); + fprintf(stdout, MSGSTR(60, + "Unformatted capacity:\t%6.3f MBytes"), lunMbytes); + } + } + + fprintf(stdout, "\n"); + + if ((mode_data_avail) && (pg_hdr->code == MODEPAGE_CACHING)) { + pg8_buf = (struct my_mode_caching *)(void *)pg_hdr; + if (pg8_buf->wce) { + fprintf(stdout, MSGSTR(2122, " Write Cache:\t\t" + "Enabled\n")); + } + if (pg8_buf->rcd == 0) { + fprintf(stdout, MSGSTR(2123, " Read Cache:\t\t" + "Enabled\n")); + fprintf(stdout, MSGSTR(2124, " Minimum prefetch:" + "\t0x%x\n Maximum prefetch:\t0x%x\n"), + pg8_buf->min_prefetch, + pg8_buf->max_prefetch); + } + } + + fprintf(stdout, " %s\t\t%s\n", MSGSTR(35, "Device Type:"), + dtype[inq.inq_dtype & DTYPE_MASK]); + + + fprintf(stdout, MSGSTR(2128, " Path(s):\n")); + fprintf(stdout, "\n"); + + if ((mlist = l_disk_state.g_disk_state.multipath_list) == NULL) { + fprintf(stderr, MSGSTR(2323, "Error: No paths found (%s)"), + path_struct->argv); + exit(1); + } + + + if (strstr(mlist->dev_path, SCSI_VHCI) != NULL) { + fprintf(stdout, " %s\n %s\n", + mlist->logical_path, mlist->dev_path); + adm_print_pathlist(mlist->dev_path); + } else { + /* + * first display user's requested path + * This will avoid duplicate inquiries as well + */ + for (mlist = l_disk_state.g_disk_state.multipath_list; + mlist != NULL; mlist = mlist->next) { + if ((strcmp(mlist->dev_path, path_struct->p_physical_path)) + == 0) { + display_path_info(mlist->dev_path, mlist->logical_path, + wwn_list); + break; + } + } + + /* + * Now display rest of paths + * skipping one already displayed + */ + for (mlist = l_disk_state.g_disk_state.multipath_list; + mlist != NULL; mlist = mlist->next) { + if ((strcmp(mlist->dev_path, path_struct->p_physical_path)) + == 0) { + continue; + } + if (err = g_get_inquiry(mlist->dev_path, &inq)) { + fprintf(stderr, "\n"); + print_errString(err, mlist->dev_path); + fprintf(stderr, "\n"); + exit(1); + } + display_path_info(mlist->dev_path, mlist->logical_path, + wwn_list); + } + } + fprintf(stdout, "\n"); + + if (envdb != NULL) { + end_time = gethrtime(); + fprintf(stdout, " display_lun_info: " + "\t\tTime = %lld millisec\n", + (end_time - start_time)/1000000); + } +} + +/* + * display_path_info() Prints the path specific information + * for a lun. + * Note: Only applies to ssd nodes currently + * + * RETURNS: + * none. + */ +static void +display_path_info(char *devpath, char *logicalpath, WWN_list *wwn_list) +{ +WWN_list *wwn_list_walk; +int err; +uchar_t wwn_data[WWN_SIZE]; +char wwns[(WWN_SIZE *2) +1]; +char drvr_path[MAXPATHLEN]; +char *cptr; +int status; + + fprintf(stdout, " %s\n", logicalpath); + fprintf(stdout, " %s\n", devpath); + fprintf(stdout, " %s\t\t", MSGSTR(2321, "LUN path port WWN:")); + + /* + * Walk the wwn list passed in and print the + * port wwn matching the device path + */ + for (wwn_list_walk = wwn_list; wwn_list_walk != NULL; + wwn_list_walk = wwn_list_walk->wwn_next) { + if (strcmp(wwn_list_walk->physical_path, devpath) == 0) { + fprintf(stdout, "%s", wwn_list_walk->port_wwn_s); + break; + } + } + /* + * newline here in case port wwn not found + */ + fprintf(stdout, "\n"); + + drvr_path[0] = '\0'; + (void) strcat(drvr_path, devpath); + if (((cptr = strstr(drvr_path, SLSH_DRV_NAME_SSD)) != NULL) || + ((cptr = strstr(drvr_path, SLSH_DRV_NAME_ST)) != NULL)) {; + *cptr = '\0'; + } else { + fprintf(stderr, MSGSTR(2324, "Error: Incorrect path (%s)\n"), + drvr_path); + exit(1); + } + *cptr = '\0'; + + if ((err = get_host_controller_pwwn(drvr_path, + (uchar_t *)&wwn_data)) != 0) { + print_errString(err, drvr_path); + exit(1); + } + + copy_wwn_data_to_str(wwns, wwn_data); + fprintf(stdout, " %s\t%s\n", + MSGSTR(2322, "Host controller port WWN:"), wwns); + + /* + * Determine path status + */ + if ((err = get_path_status(devpath, &status)) != 0) { + print_errString(err, devpath); + exit(1); + } else { + fprintf(stdout, " %s\t\t", MSGSTR(2329, "Path status:")); + display_port_status(status); + } +} + +/* + * Retrieves the lun capacity + */ +static int +get_lun_capacity(char *devpath, struct scsi_capacity_16 *cap_data) +{ +int fd; + + if (devpath == NULL || cap_data == NULL) { + return (L_INVALID_PATH); + } + + if ((fd = g_object_open(devpath, O_RDONLY | O_NDELAY)) == -1) { + return (L_OPEN_PATH_FAIL); + } else { + (void) g_scsi_read_capacity_1016_cmd(fd, cap_data, + sizeof (struct scsi_capacity_16)); + close(fd); + } + return (0); +} + +/* + * Retrieves the reservation status + */ +static int +get_path_status(char *devpath, int *status) +{ +int fd, mystatus = 0; + + + if (devpath == NULL || status == NULL) { + return (L_INVALID_PATH); + } + + *status = 0; + if ((fd = g_object_open(devpath, O_RDONLY | O_NDELAY)) == -1) { + return (L_OPEN_PATH_FAIL); + } else { + if ((mystatus = g_scsi_tur(fd)) != 0) { + if ((mystatus & L_SCSI_ERROR) && + ((mystatus & ~L_SCSI_ERROR) == STATUS_CHECK)) { + *status = L_NOT_READY; + } else if ((mystatus & L_SCSI_ERROR) && + ((mystatus & ~L_SCSI_ERROR) == + STATUS_RESERVATION_CONFLICT)) { + *status = L_RESERVED; + } else { + *status = L_SCSI_ERR; + } + } + } + close(fd); + return (0); +} + +/* + * Description: + * Retrieves the port wwn associated with the hba node + * + * hba_path: /devices/pci@8,600000/SUNW,qlc@4/fp@0,0 + * pwwn: ptr to a uchar_t array of size WWN_SIZE + */ +static int +get_host_controller_pwwn(char *hba_path, uchar_t *pwwn) +{ +char *cptr, *portptr; +int found = 0, err, devlen; +char my_hba_path[MAXPATHLEN]; +di_node_t node; +di_prom_prop_t promprop; +uchar_t *port_wwn_data = NULL; +int di_ret; +di_prom_handle_t ph; +char *promname; +uchar_t *promdata; +uint_t path_type; +fc_port_dev_t hba_port; + + if (hba_path == NULL || pwwn == NULL) { + return (L_INVALID_PATH); + } + + if ((path_type = g_get_path_type(hba_path)) == 0) { + return (L_INVALID_PATH); + } + + /* + * ifp nodes do not have a port-wwn prom property + * so handle them via FC4 device map + */ + if (path_type & FC4_XPORT_MASK) { + if ((err = get_FC4_host_controller_pwwn(hba_path, pwwn)) != 0) { + return (err); + } else { + return (0); + } + /* For Leadville path get the port wwn through g_get_host param. */ + } else if ((path_type & FC_GEN_XPORT) && + ((path_type & FC_FCA_MASK) == FC_FCA_MASK)) { + /* + * For Leadville path, get the port wwn through + * g_get_host param. This is a general solution + * to support 3rd party vendor Leadville FCA. + */ + my_hba_path[0] = '\0'; + (void) strlcat(my_hba_path, hba_path, sizeof (my_hba_path)); + (void) snprintf(my_hba_path, sizeof (my_hba_path), "%s%s", + hba_path, FC_CTLR); + if ((err = g_get_host_params( + my_hba_path, &hba_port, 0)) != 0) { + return (err); + } else { + (void) memcpy(pwwn, &hba_port.dev_pwwn.raw_wwn[0], + WWN_SIZE); + return (0); + } + } else if ((path_type & FC_FCA_MASK) == FC_PCI_FCA) { + /* + * Get port WWN through prom property + */ + my_hba_path[0] = '\0'; + (void) strlcat(my_hba_path, hba_path, sizeof (my_hba_path)); + /* + * sanity check for /devices mount point + */ + if (strlen(my_hba_path) > (devlen = strlen("/devices"))) { + cptr = &my_hba_path[devlen]; + } else { + return (L_INVALID_PATH); + } + + /* + * Now strip off the trailing "/fp@" + */ + if ((portptr = strstr(cptr, "/fp@")) != NULL) { + *portptr = '\0'; + } + + if ((node = di_init(cptr, DINFOCPYALL)) == DI_NODE_NIL) { + return (L_DEV_SNAPSHOT_FAILED); + } + + if (di_nodeid(node) == DI_SID_NODEID) { + di_ret = di_prop_lookup_bytes(DDI_DEV_T_ANY, node, + "port-wwn", &port_wwn_data); + if (di_ret == -1 || port_wwn_data == NULL) { + di_fini(node); + return (L_NO_WWN_PROP_FOUND); + } else { + (void) memcpy(pwwn, port_wwn_data, WWN_SIZE); + found++; + } + } else if (di_nodeid(node) == DI_PROM_NODEID) { + if ((ph = di_prom_init()) == DI_PROM_HANDLE_NIL) { + di_fini(node); + return (L_PROM_INIT_FAILED); + } + + for (promprop = di_prom_prop_next(ph, node, + DI_PROM_PROP_NIL); + promprop != DI_PROM_PROP_NIL; + promprop = di_prom_prop_next(ph, node, promprop)) { + if (((promname = di_prom_prop_name( + promprop)) != NULL) && + (strcmp(promname, "port-wwn") == 0) && + (di_prom_prop_data(promprop, + &promdata) == WWN_SIZE)) { + /* Found port-wwn */ + (void) memcpy(pwwn, promdata, WWN_SIZE); + found++; + break; + } + } + di_prom_fini(ph); + } + + di_fini(node); + if (found) { + return (0); + } else { + return (L_INVALID_PATH); + } + } else { + return (L_INVALID_PATH_TYPE); + } +} + + +/* + * Description: + * Retrieve pwwn via SFIOCGMAP + */ +static int +get_FC4_host_controller_pwwn(char *hba_path, uchar_t *pwwn) +{ +sf_al_map_t sf_map; +char my_hba_path[MAXPATHLEN]; +int fd; + + if (hba_path == NULL || pwwn == NULL) { + return (L_INVALID_PATH); + } + + (void) snprintf(my_hba_path, sizeof (my_hba_path), "%s%s", + hba_path, FC_CTLR); + + if ((fd = g_object_open(my_hba_path, O_NDELAY | O_RDONLY)) == -1) { + return (errno); + } + + memset(&sf_map, 0, sizeof (sf_al_map_t)); + + if (ioctl(fd, SFIOCGMAP, &sf_map) != 0) { + close(fd); + return (L_SFIOCGMAP_IOCTL_FAIL); + } + + close(fd); + + if (sf_map.sf_count == 0) { + close(fd); + return (L_SFIOCGMAP_IOCTL_FAIL); + } + + (void) memcpy(pwwn, &sf_map.sf_hba_addr.sf_port_wwn[0], WWN_SIZE); + + return (0); +} + +/* + * from_ptr: ptr to uchar_t array of size WWN_SIZE + * to_ptr: char ptr to string of size WWN_SIZE*2+1 + */ +void +copy_wwn_data_to_str(char *to_ptr, const uchar_t *from_ptr) +{ + if ((to_ptr == NULL) || (from_ptr == NULL)) + return; + + sprintf(to_ptr, "%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x", + from_ptr[0], from_ptr[1], from_ptr[2], from_ptr[3], + from_ptr[4], from_ptr[5], from_ptr[6], from_ptr[7]); +} + +/* + * Frees a previously allocated mplist_struct + */ +void +adm_mplist_free(struct mplist_struct *mplistp) +{ +struct mplist_struct *mplistn; + + while (mplistp != NULL) { + mplistn = mplistp->next; + if (mplistp->devpath != NULL) { + free(mplistp->devpath); + mplistp->devpath = NULL; + } + free(mplistp); + mplistp = mplistn; + } +} + +int +adm_reserve(char *path) +{ + char *path_phys = NULL; + int err; + if ((path_phys = + g_get_physical_name(path)) == NULL) { + + (void) fprintf(stderr, "%s: ", whoami); + (void) fprintf(stderr, + MSGSTR(112, "Error: Invalid pathname (%s)"), + path); + (void) fprintf(stderr, "\n"); + return (1); + } + + if ((err = g_reserve(path_phys)) != 0) { + (void) print_errString(err, path); + return (1); + } + return (0); +} + +int +adm_release(char *path) +{ + char *path_phys = NULL; + int err; + if ((path_phys = + g_get_physical_name(path)) == NULL) { + + (void) fprintf(stderr, "%s: ", whoami); + (void) fprintf(stderr, + MSGSTR(112, "Error: Invalid pathname (%s)"), + path); + (void) fprintf(stderr, "\n"); + return (1); + } + + if ((err = g_release(path_phys)) != 0) { + (void) print_errString(err, path); + return (1); + } + return (0); +} + +void +i18n_catopen() { + (void) g_i18n_catopen(); +} + +int adm_check_file(char **path, int flag) { + int err; + if (err = l_check_file(*path, flag)) { + (void) print_errString(err, *path); + return (-1); + } + + (void) fprintf(stdout, MSGSTR(2212, "Download file O.K. \n\n")); + return (0); +} + +/* + * Print out private loop dev dtype + */ +void +print_private_loop_dtype_prop(uchar_t *hba_port_wwn, uchar_t *port_wwn, + uchar_t dtype_prop) +{ + if ((dtype_prop & DTYPE_MASK) < 0x10) { + (void) fprintf(stdout, " 0x%-2x (%s", + (dtype_prop & DTYPE_MASK), dtype[(dtype_prop & DTYPE_MASK)]); + } else if ((dtype_prop & DTYPE_MASK) < 0x1f) { + (void) fprintf(stdout, + MSGSTR(2243, " 0x%-2x (Reserved"), + (dtype_prop & DTYPE_MASK)); + } else { + (void) fprintf(stdout, MSGSTR(2245, + " 0x%-2x (Unknown Type"), (dtype_prop & DTYPE_MASK)); + } + /* Check to see if this is the HBA */ + if (wwnConversion(hba_port_wwn) == wwnConversion(port_wwn)) { + /* MATCH */ + (void) fprintf(stdout, MSGSTR(2244, + ",Host Bus Adapter)\n")); + } else { + (void) fprintf(stdout, ")\n"); + } +} diff --git a/usr/src/cmd/luxadm/hot.h b/usr/src/cmd/luxadm/hot.h new file mode 100644 index 0000000000..66d1e3dd49 --- /dev/null +++ b/usr/src/cmd/luxadm/hot.h @@ -0,0 +1,78 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * PHOTON CONFIGURATION MANAGER + * Common definitions + */ + +/* + * I18N message number ranges + * This file: 13000 - 13499 + * Shared common messages: 1 - 1999 + */ + +#ifndef _HOT_H +#define _HOT_H + + + + +/* + * Include any headers you depend on. + */ + +#ifdef __cplusplus +extern "C" { +#endif + + +#define NODE_CREATION_TIME 60 /* # seconds */ +/* + * Version 0.16 of the SES firmware powers up disks in front/back pairs. + * However, the first disk inserted is usually spun up by itself, so + * we need to calculate a timeout for 22/2 + 1 = 12 disks. + * + * Measured times are about 40s/disk for a total of 40*12=8 min total + * The timeout assumes 10s/iteration or 4*12*10=8 min + */ +#define PHOTON_SPINUP_TIMEOUT (4*12) +#define PHOTON_SPINUP_DELAY 10 + +#define QLC_LIP_DELAY 17 + +#define TARGET_ID(box_id, f_r, slot) \ + ((box_id | ((f_r == 'f' ? 0 : 1) << 4)) | (slot + 2)) + +#define NEWER(time1, time2) (time1.tv_sec > time2.tv_sec) + +extern int Options; + + +#ifdef __cplusplus +} +#endif + +#endif /* _HOT_H */ diff --git a/usr/src/cmd/luxadm/hotplug.c b/usr/src/cmd/luxadm/hotplug.c new file mode 100644 index 0000000000..2efec8ba3c --- /dev/null +++ b/usr/src/cmd/luxadm/hotplug.c @@ -0,0 +1,3037 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + + +/*LINTLIBRARY*/ + + +/* + * Hotplug program for SENA, RSM and SSA + * subsystems and individual FC_AL devices. + */ + +/* #define _POSIX_SOURCE 1 */ + +/* + * I18N message number ranges + * This file: 5500 - 5999 + * Shared common messages: 1 - 1999 + */ + + +/* Includes */ +#include <stdlib.h> +#include <stdio.h> +#include <sys/file.h> +#include <sys/errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/utsname.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <sys/sunddi.h> +#include <sys/ddi.h> /* for min */ +#include <sys/scsi/scsi.h> +#include <nl_types.h> +#include <dirent.h> +#include <sys/wait.h> +#include <l_common.h> +#include <l_error.h> +#include <stgcom.h> +#include <a_state.h> +#include <a5k.h> +#include <rom.h> +#include "hot.h" +#include "common.h" +#include "luxadm.h" + + +/* Internal variables. */ +static char *cmdStrg[][4] = { + { "disks", "-C", 0, 0 }, + { "disks", 0, 0, 0 }, + { "drvconfig", "-i", "ssd", 0 }, + { "drvconfig", 0, 0, 0 }, + { "devlinks", 0, 0, 0 }, + { "tapes", "-C", 0, 0 } +}; + +/* External variables */ +extern char *dtype[]; /* From adm.c */ +extern int Options; +extern const int OPTION_CAPF; + +/* Internal functions */ +/* SENA and Individual FC device Hotplug */ +static int h_pre_insert_encl_dev(timestruc_t *, timestruc_t *, + timestruc_t *); +static int h_post_insert_dev(timestruc_t, timestruc_t); +static int h_pre_remove_dev(Hotplug_Devlist *, + WWN_list *wwn_list, int, int); +static int h_post_remove_dev(Hotplug_Devlist *, int, int); +static int h_pre_hotplug(Hotplug_Devlist **, + WWN_list *, int, int, int); +static int h_post_hotplug(Hotplug_Devlist *, + WWN_list *, int, int, int, int); +static int h_post_insert_encl(timestruc_t); +static int h_pre_hotplug_sena(Hotplug_Devlist *, + WWN_list *, int, int, int); +static int h_post_hotplug_sena(Hotplug_Devlist *, + WWN_list *, int, int, int, int); +static int h_remove_ses_nodes(struct dlist *); +static int h_print_list_warn(Hotplug_Devlist *, int, int); +static int h_display_logical_nodes(struct dlist *); +static void h_print_logical_nodes(struct dlist *); +static int h_remove_nodes(struct dlist *); +static int h_print_list(Hotplug_Devlist *, int *, int); +static int h_get_fcdev_state(char *, char *, int, int *, int *, int); +static int h_chk_dev_busy(Hotplug_Devlist *, + WWN_list *, int *, int, int); +static int h_execCmnd(char **, int); +int hotplug(int, char **, int, int); +int h_insertSena_fcdev(); +static int h_find_new_device_link(char *, timestruc_t); + + + +/* + * Assists the user in hot inserting FC_AL + * individual device(s) and SENA enclosure(s). + * + * RETURNS: + * 0 if OK + * non-zero otherwise + */ +int +h_insertSena_fcdev() +{ +timestruc_t ses_time, dsk_time, rmt_time; +int err; +struct stat ses_stat; + + if ((err = h_pre_insert_encl_dev(&ses_time, &dsk_time, + &rmt_time)) != 0) { + return (err); + } + (void) fprintf(stdout, MSGSTR(5500, + "Please hit <RETURN> when you have finished" + " adding Fibre Channel Enclosure(s)/Device(s): ")); + (void) getchar(); + + if ((err = h_post_insert_dev(dsk_time, rmt_time)) != 0) { + return (err); + } + + if (stat(SES_DIR, &ses_stat) < 0) { + /* + * Non existence of /dev/es dir indicates + * no ses devices inserted. + * No need to call h_post_insert_encl(). + */ + if (errno == ENOENT) { + (void) fprintf(stdout, MSGSTR(5662, + " No new enclosure(s) were added!!\n\n")); + return (0); + } else { + return (L_LSTAT_ES_DIR_ERROR); + } + } + + /* + * if the latest mod time of /dev/es is not newer than + * the original mod time no need to call + * h_post_insert_encl(). + */ + if ((&ses_time != (timestruc_t *)NULL) && + !(NEWER(ses_stat.st_ctim, ses_time))) { + (void) fprintf(stdout, MSGSTR(5662, + " No new enclosure(s) were added!!\n\n")); + return (0); + } + if ((err = h_post_insert_encl(ses_time)) != 0) { + return (err); + } + return (0); +} + + + +/* + * gets the devices state - check for disk's reservations. + * + * RETURNS: + * 0 if OK + * non-zero otherwise + */ +static int +h_get_fcdev_state(char *fc_dev, char *path_phys, int force_flag, + int *busy_flag, int *reserve_flag, int verbose_flag) +{ +int err; +L_inquiry inq; +L_disk_state l_disk_state; + + + if ((err = g_get_inquiry(path_phys, &inq)) != 0) { + (void) fprintf(stderr, + MSGSTR(5501, + "Inquiry failed for %s\n"), + path_phys); + return (err); + } + if (inq.inq_port) { + if ((err = l_get_disk_port_status(path_phys, &l_disk_state, + FC_PORT_B, verbose_flag)) != 0) { + return (err); + } + } else { + if ((err = l_get_disk_port_status(path_phys, &l_disk_state, + FC_PORT_A, verbose_flag)) != 0) { + return (err); + } + } + + /* + * Don't print error msg. if disk is reserved + * and tried to be removed from the same port. + * If force flag is set, remove the disk without + * checking the disk reservations. + */ + if (!force_flag) { + if (((inq.inq_port) && + (l_disk_state.g_disk_state.d_state_flags[FC_PORT_B] & + L_RESERVED)) || + ((!inq.inq_port) && + (l_disk_state.g_disk_state.d_state_flags[FC_PORT_A] & + L_RESERVED))) { + *reserve_flag = 1; + } + } + return (0); +} + + +/* + * Forks a child process and let the child to + * execute a given command string by calling the + * the execvp() function. Then, the parent process + * waits for the child to exit. Once the parent process + * is notified by the kernel with the termination of + * the child, then the parent checks for the exit + * status of the child and return to the caller with -1 in case + * of error and zero otherwise. + * + * RETURNS: + * 0 if OK + * non-zero otherwise + */ +int +h_execCmnd(char *argStr[], int nArg) +{ +pid_t pid; +int ix, status; + + if ((pid = fork()) < 0) { + (void) fprintf(stderr, + MSGSTR(133, + "Error: Failed to fork a process.\n")); + return (-1); + } else if (pid == 0) { + /* child process */ + if (execvp(argStr[0], argStr) < 0) { + (void) fprintf(stderr, + MSGSTR(5502, + " Error: execvp() failed to run " + "the command:")); + for (ix = 0; ix < nArg; ix++) { + (void) fprintf(stderr, + " %s", argStr[ix]); + } + (void) fprintf(stderr, "\n"); + /* let parent know about the error. */ + exit(ENOEXEC); + } + } + + /* parent executes the following. */ + if (waitpid(pid, &status, 0) != pid) { + (void) fprintf(stderr, + MSGSTR(5503, + "Error: waitpid() failed.\n")); + return (-1); + } + if (WIFEXITED(status) && + WEXITSTATUS(status) == ENOEXEC) { + /* child failed to run the command string. */ + return (-1); + } + return (0); + +} + + + + +/* + * frees the hotplug disk list structure. + * + * RETURNS: + * N/A + */ +void +h_free_hotplug_dlist(Hotplug_Devlist **hotplug_dlist) +{ +Hotplug_Devlist *list = NULL; + + while (*hotplug_dlist != NULL) { + list = *hotplug_dlist; + *hotplug_dlist = (*hotplug_dlist)->next; + (void) g_free_multipath(list->seslist); + (void) g_free_multipath(list->dlhead); + (void) free((void *)list); + } +} + + +/* + * finds whether device (SENA or an FCAL device) is busy or not. + * + * OUTPUT: + * busy_flag = 1 (if device busy) + * + * RETURNS: + * 0 if O.K. + * non-zero otherwise + */ +static int +h_chk_dev_busy(Hotplug_Devlist *hotplug_dev, WWN_list *wwn_list, + int *busy_flag, int force_flag, int verbose_flag) +{ +int err; +struct dlist *dlist; + + if (hotplug_dev->dev_type == DTYPE_ESI) { + if ((err = l_offline_photon(hotplug_dev, wwn_list, + force_flag, verbose_flag)) != 0) { + if (err == L_DEV_BUSY) { + *busy_flag = 1; + } else { + return (err); + } + } + for (dlist = hotplug_dev->dlhead; + dlist != NULL; dlist = dlist->next) { + (void) g_online_drive(dlist->multipath, + force_flag); + } + } else { + if ((err = g_offline_drive(hotplug_dev->dlhead, + force_flag)) != 0) { + if (err == L_DEV_BUSY) { + *busy_flag = 1; + } else { + return (err); + } + } + (void) g_online_drive(hotplug_dev->dlhead, force_flag); + } + return (0); +} + + + +/* + * prints the given list to stdout, + * gets the input from user whether + * to skip the busy devices or quit + * and passes that input to the calling + * function. + * + * OUTPUT: + * int *action + * s = Skip + * q = Quit + * + * RETURNS: + * 0 if OK + * non-zero otherwise + */ +static int +h_print_list(Hotplug_Devlist *bsyRsrv_disk_list, int *action, int enc_type) +{ +Hotplug_Devlist *list; +int i = 1; +char choice[2]; + + (void) fprintf(stdout, + MSGSTR(5504, "The list of devices being used" + " (either busy or reserved) by the host:\n")); + for (list = bsyRsrv_disk_list; list != NULL; list = list->next, i++) { + if ((list->dev_type == DTYPE_DIRECT) && + (list->dev_location == SENA)) { + if (list->f_flag != NULL) { + if (enc_type == DAK_ENC_TYPE) { + (void) fprintf(stdout, MSGSTR(5663, + " %d: Box Name: \"%s\" slot %d\n"), + i, list->box_name, list->slot); + } else { + (void) fprintf(stdout, MSGSTR(137, + " %d: Box Name: \"%s\" front slot %d\n"), + i, list->box_name, list->slot); + } + } else { + if (enc_type == DAK_ENC_TYPE) { + (void) fprintf(stdout, MSGSTR(5663, + " %d: Box Name: \"%s\" slot %d\n"), + i, list->box_name, list->slot + (MAX_DRIVES_DAK/2)); + } else { + (void) fprintf(stdout, MSGSTR(136, + " %d: Box Name: \"%s\" rear slot %d\n"), + i, list->box_name, list->slot); + } + } + } else if (((list->dev_type == DTYPE_DIRECT) || + (list->dev_type == DTYPE_SEQUENTIAL)) && + (list->dev_location == NON_SENA)) { + (void) fprintf(stdout, MSGSTR(5505, + " %d: Device %s\n"), + i, list->dev_name); + } else if (list->dev_type == DTYPE_ESI) { + (void) fprintf(stdout, MSGSTR(5506, + " %d: Box: %s\n"), + i, list->box_name); + } + } + + /* Get the user input and continue accordingly. */ + (void) fprintf(stdout, + MSGSTR(5507, + "\n\nPlease enter 's' or <CR> to Skip the \"busy/reserved\"" + " device(s) or\n'q' to Quit and run the" + " subcommand with\n-F (force) option. [Default: s]: ")); + for (;;) { + (void) gets(choice); + if (choice[0] == 'q' || choice[0] == 'Q' || + choice[0] == 's' || choice[0] == 'S' || + choice[0] == '\0') { + break; + } + (void) fprintf(stdout, MSGSTR(5508, + " Enter an appropriate option [s,<CR>,q]: ")); + } + if (choice[0] == 'q' || choice[0] == 'Q') { + *action = QUIT; + } else { + *action = SKIP; + } + (void) fprintf(stdout, "\n\n"); + return (0); +} + + + +/* + * prints the warning message. + * + * RETURNS: + * None. + */ +static void +h_prt_warning() +{ + (void) fprintf(stderr, + MSGSTR(5509, + "\n WARNING!!! Please ensure that no" + " filesystems are mounted on these device(s).\n" + " All data on these devices should have been" + " backed up.\n\n\n")); +} + + + +/* + * handle helper-mode hotplug commands + * + * RETURNS: + * 0 if OK + * non-zero otherwise + */ +int +hotplug(int todo, char **argv, int verbose_flag, int force_flag) +{ +char ses_path[MAXPATHLEN], dev_path[MAXPATHLEN]; +char *path_phys = NULL, code, node_wwn_s[WWN_S_LEN]; +char inq_path[MAXNAMELEN], *ptr = NULL; +uchar_t node_wwn[WWN_SIZE], port_wwn[WWN_SIZE]; +int tid, slot, path_index, dtype, f_r, err = 0; +int al_pa, i, dev_location, found_nullwwn = 0; +int busy_flag = 0, reserve_flag = 0, action = 0; +int pathcnt = 1; +L_state l_state; +gfc_map_t map; +Path_struct *path_struct; +WWN_list *wwn_list = NULL; +Box_list *box_list; +Hotplug_Devlist *disk_list, *disk_list_head, *disk_list_tail; +Hotplug_Devlist *bsyRsrv_dskLst_head, *bsyRsrv_dskLst_tail; +int enc_type; +L_inquiry inq; +char *physpath; +Path_struct *p_pathstruct; +char temp2path[MAXPATHLEN]; +mp_pathlist_t pathlist; +int p_pw = 0, p_on = 0, p_st = 0; + + /* Initialize structures and pointers here */ + disk_list_head = disk_list_tail = (Hotplug_Devlist *)NULL; + bsyRsrv_dskLst_head = (Hotplug_Devlist *)NULL; + bsyRsrv_dskLst_tail = (Hotplug_Devlist *)NULL; + map.dev_addr = NULL; + +#ifdef DEBUG + (void) fprintf(stderr, + "DEBUG: luxadm: hotplug() entering for \"%s\" ...\n", + argv[0] ? argv[0] : "<null ptr>"); +#endif + if ((err = l_get_box_list(&box_list, 0)) != 0) { + return (err); + } + + if (todo == REMOVE_DEVICE) { + (void) h_prt_warning(); + } + + /* + * At this point user want to insert or remove + * one or more pathnames they've specified. + */ + if ((err = g_get_wwn_list(&wwn_list, verbose_flag)) != 0) { + (void) l_free_box_list(&box_list); + return (err); + } + for (path_index = 0; argv[path_index] != NULL; path_index++) { + if ((err = l_convert_name(argv[path_index], &path_phys, + &path_struct, verbose_flag)) != 0) { + /* Make sure we have a device path. */ + (void) strcpy(inq_path, argv[path_index]); + if (((ptr = strstr(inq_path, ",")) != NULL) && + ((*(ptr + 1) == 'f') || (*(ptr + 1) == 'r') || + (*(ptr +1) == 's')) && + todo == REMOVE_DEVICE) { + if (err != -1) { + (void) print_errString(err, + argv[path_index]); + err = 0; + continue; + } + *ptr = NULL; + slot = path_struct->slot; + f_r = path_struct->f_flag; + if ((err = l_convert_name(inq_path, &path_phys, + &path_struct, verbose_flag)) != 0) { + (void) fprintf(stderr, "\n"); + (void) fprintf(stderr, + MSGSTR(33, + " Error: converting" + " %s to physical path.\n" + " Invalid pathname.\n"), + argv[path_index]); + if (err != -1) { + (void) print_errString(err, + argv[path_index]); + } + err = 0; + continue; + } + if ((err = print_devState(argv[path_index], + path_struct->p_physical_path, + f_r, slot, verbose_flag)) != 0) { + err = 0; + continue; + } + } + if (path_struct->ib_path_flag) { + path_phys = path_struct->p_physical_path; + } else { + if (err != -1) { + (void) print_errString(err, + argv[path_index]); + } else { + (void) fprintf(stderr, "\n"); + (void) fprintf(stderr, + MSGSTR(33, + " Error: converting" + " %s to physical path.\n" + " Invalid pathname.\n"), + argv[path_index]); + } + err = 0; + continue; + } + } + if (path_struct->slot_valid || + strstr(path_phys, DRV_NAME_SSD)) { + dtype = DTYPE_DIRECT; + } else if (strstr(path_phys, SLSH_DRV_NAME_ST)) { + dtype = DTYPE_SEQUENTIAL; + } else { + dtype = DTYPE_ESI; + } + + if (strstr(path_phys, SCSI_VHCI) != NULL) { + /* obtain phci */ + (void) strcpy(temp2path, path_phys); + if (err = g_get_pathlist(temp2path, &pathlist)) { + (void) print_errString(err, NULL); + exit(-1); + } + pathcnt = pathlist.path_count; + p_pw = p_on = p_st = 0; + for (i = 0; i < pathcnt; i++) { + if (pathlist.path_info[i].path_state < + MAXPATHSTATE) { + if (strstr(pathlist.path_info[i]. + path_addr, + path_struct->argv) != NULL) { + p_pw = i; + break; + } + if (pathlist.path_info[i].path_state == + MDI_PATHINFO_STATE_ONLINE) { + p_on = i; + } + if (pathlist.path_info[i].path_state == + MDI_PATHINFO_STATE_STANDBY) { + p_st = i; + } + } + } + if (strstr(pathlist.path_info[p_pw].path_addr, + path_struct->argv) != NULL) { + /* matching input pwwn */ + (void) strcpy(temp2path, + pathlist.path_info[p_pw].path_hba); + } else if (pathlist.path_info[p_on].path_state == + MDI_PATHINFO_STATE_ONLINE) { + /* on_line path */ + (void) strcpy(temp2path, + pathlist.path_info[p_on].path_hba); + } else { + /* standby or path0 */ + (void) strcpy(temp2path, + pathlist.path_info[p_st].path_hba); + } + free(pathlist.path_info); + (void) strcat(temp2path, FC_CTLR); + } else { + (void) strcpy(temp2path, path_phys); + } + + if ((err = g_get_dev_map(temp2path, &map, verbose_flag)) + != 0) { + return (err); + } + + if ((map.hba_addr.port_topology == FC_TOP_PUBLIC_LOOP) || + (map.hba_addr.port_topology == FC_TOP_FABRIC)) { + /* public or fabric loop device */ + free((void *)map.dev_addr); + (void) fprintf(stderr, MSGSTR(5540, + "This operation is not " + "supported in this topology.\n")); + exit(-1); + } + + if (todo == REPLACE_DEVICE) { + (void) fprintf(stderr, + MSGSTR(5511, + "Error:" + " replace_device is not supported" + " on this subsystem.\n")); + exit(-1); + } + + if ((todo == REMOVE_DEVICE) && + (dtype == DTYPE_DIRECT || + dtype == DTYPE_SEQUENTIAL || + dtype == DTYPE_UNKNOWN)) { + if (l_chk_null_wwn(path_struct, ses_path, + &l_state, verbose_flag) == 1) { + found_nullwwn = 1; + /* + * set dev_path to NULL, + * if disk has null wwn. + */ + *dev_path = NULL; + dev_location = SENA; + goto getinfo; + } + } + + (void) strcpy(ses_path, path_phys); + + if (strstr(ses_path, "ses") == NULL && + l_get_ses_path(path_phys, ses_path, &map, + verbose_flag) != 0) { + + /* Could be a non-photon disk device */ + if ((todo == REMOVE_DEVICE) && + (dtype == DTYPE_DIRECT || + dtype == DTYPE_SEQUENTIAL)) { + dev_location = NON_SENA; + + if ((err = h_get_fcdev_state(argv[path_index], + path_phys, force_flag, + &busy_flag, &reserve_flag, + verbose_flag)) != 0) { + goto done; + } + (void) strcpy(dev_path, path_phys); + if ((err = g_get_wwn(dev_path, port_wwn, + node_wwn, &al_pa, + verbose_flag)) != 0) { + goto done; + } + (void) sprintf(node_wwn_s, + "%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x", + node_wwn[0], node_wwn[1], node_wwn[2], + node_wwn[3], node_wwn[4], node_wwn[5], + node_wwn[6], node_wwn[7]); + tid = g_sf_alpa_to_switch[al_pa]; + goto loop; + } + continue; + } + + if (strstr(ses_path, "ses") != NULL) { + dev_location = SENA; + if ((err = l_convert_name(ses_path, &physpath, + &p_pathstruct, 0)) != 0) { + free(physpath); + free(p_pathstruct); + goto done; + + } + if ((err = g_get_inquiry(physpath, &inq)) != 0) { + free(physpath); + free(p_pathstruct); + goto done; + } + enc_type = l_get_enc_type(inq); + + } + if ((err = l_get_status(ses_path, + &l_state, verbose_flag)) != 0) { + goto done; + } + if (dtype == DTYPE_ESI) { + /* could be removing a photon */ + if (todo == REMOVE_DEVICE) { + /* + * Need the select ID (tid) for the IB. + */ + if ((err = g_get_wwn(ses_path, port_wwn, + node_wwn, &al_pa, + verbose_flag)) != 0) { + goto done; + } + (void) sprintf(node_wwn_s, + "%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x", + node_wwn[0], node_wwn[1], node_wwn[2], + node_wwn[3], node_wwn[4], node_wwn[5], + node_wwn[6], node_wwn[7]); + tid = g_sf_alpa_to_switch[al_pa]; + *dev_path = '\0'; + /* + * Check if any disk in this photon + * is reserved by another host + */ + if (!force_flag) { + for ( + i = 0; + i < l_state.total_num_drv/2; + i++) { + if ((l_state.drv_front[i].g_disk_state.d_state_flags[PORT_A] & + L_RESERVED) || + (l_state.drv_front[i].g_disk_state.d_state_flags[PORT_B] & + L_RESERVED) || + (l_state.drv_rear[i].g_disk_state.d_state_flags[PORT_A] & + L_RESERVED) || + (l_state.drv_rear[i].g_disk_state.d_state_flags[PORT_B] & + L_RESERVED)) { + reserve_flag = 1; + } + } + } + goto loop; + } + (void) fprintf(stderr, + MSGSTR(5512, + "Error: %s already exists!!\n"), + argv[path_index]); + goto done; + } +getinfo: + if (!path_struct->slot_valid) { + /* We are passing the disks path */ + if ((err = l_get_slot(path_struct, &l_state, + verbose_flag)) != 0) { + goto done; + } + } + + slot = path_struct->slot; + if (path_struct->f_flag) { + tid = l_state.drv_front[slot].ib_status.sel_id; + code = l_state.drv_front[slot].ib_status.code; + (void) strcpy(node_wwn_s, + l_state.drv_front[slot].g_disk_state.node_wwn_s); + } else { + tid = l_state.drv_rear[slot].ib_status.sel_id; + code = l_state.drv_rear[slot].ib_status.code; + (void) strcpy(node_wwn_s, + l_state.drv_rear[slot].g_disk_state.node_wwn_s); + } + + if (found_nullwwn) { + goto loop; + } + + l_make_node(ses_path, tid, dev_path, &map, 0); + + if ((todo == INSERT_DEVICE) && + (g_device_in_map(&map, tid) || + (code != S_NOT_INSTALLED))) { + (void) fprintf(stderr, + MSGSTR(5513, "\nNotice: %s may " + "already be present.\n"), + argv[path_index]); + if (path_struct->f_flag) { + if ((l_state.drv_front[slot].l_state_flag + != L_NO_PATH_FOUND) && + (!l_state.drv_front[slot].ib_status.dev_off)) + continue; + } else { + if ((l_state.drv_rear[slot].l_state_flag + != L_NO_PATH_FOUND) && + (!l_state.drv_rear[slot].ib_status.dev_off)) + continue; + } + } + + /* Check if disk is reserved */ + if ((todo == REMOVE_DEVICE) && (!force_flag)) { + if (path_struct->f_flag) { + if ((l_state.drv_front[slot].g_disk_state.d_state_flags[PORT_A] & + L_RESERVED) || + (l_state.drv_front[slot].g_disk_state.d_state_flags[PORT_B] & + L_RESERVED)) { + reserve_flag = 1; + } + } else { + if ((l_state.drv_rear[slot].g_disk_state.d_state_flags[PORT_A] & + L_RESERVED) || + (l_state.drv_rear[slot].g_disk_state.d_state_flags[PORT_B] & + L_RESERVED)) { + reserve_flag = 1; + } + } + } + +loop: + if ((disk_list = (Hotplug_Devlist *) + calloc(1, sizeof (Hotplug_Devlist))) == NULL) { + (void) print_errString(L_MALLOC_FAILED, NULL); + goto done; + } + + /* + * dev_path is NULL when removing a whole encloser. We + * don't want to call g_get_multipath while removing whole + * enclosure. Its being taken care later in the code path + */ + + if ((todo != INSERT_DEVICE) && (dtype != DTYPE_ESI)) { + if ((err = g_get_multipath(dev_path, + &(disk_list->dlhead), + wwn_list, verbose_flag)) != 0) { + if (disk_list->dlhead != NULL) { + (void) g_free_multipath( + disk_list->dlhead); + } + goto done; + } + } + disk_list->dev_type = dtype; + disk_list->dev_location = dev_location; + (void) strcpy(disk_list->dev_name, + argv[path_index]); + disk_list->tid = tid; + (void) strcpy(disk_list->node_wwn_s, node_wwn_s); + if (dev_location == SENA) { + if ((err = l_get_allses(ses_path, box_list, + &(disk_list->seslist), 0)) != 0) { + if (disk_list->seslist != NULL) { + (void) g_free_multipath(disk_list->seslist); + } + goto done; + } + (void) strcpy(disk_list->box_name, + (char *)l_state.ib_tbl.enclosure_name); + disk_list->slot = slot; + disk_list->f_flag = path_struct->f_flag; + } + if (todo == REMOVE_DEVICE && !force_flag && !reserve_flag) { + if ((err = h_chk_dev_busy(disk_list, wwn_list, + &busy_flag, force_flag, verbose_flag)) != 0) { + goto done; + } + } + + if (reserve_flag || busy_flag) { + if (reserve_flag) + disk_list->reserve_flag = 1; + if (busy_flag) + disk_list->busy_flag = 1; + + if (bsyRsrv_dskLst_head == NULL) { + bsyRsrv_dskLst_head = + bsyRsrv_dskLst_tail = disk_list; + } else { + disk_list->prev = bsyRsrv_dskLst_tail; + bsyRsrv_dskLst_tail->next = disk_list; + bsyRsrv_dskLst_tail = disk_list; + } + reserve_flag = 0; + busy_flag = 0; + + } else if (disk_list_head == NULL) { + disk_list_head = disk_list_tail = disk_list; + } else { + disk_list->prev = disk_list_tail; + disk_list_tail->next = disk_list; + disk_list_tail = disk_list; + } + } + + if (bsyRsrv_dskLst_head != NULL) { + if ((err = h_print_list(bsyRsrv_dskLst_head, + &action, enc_type)) != 0) { + goto done; + } + if (action == SKIP) { + (void) h_free_hotplug_dlist(&bsyRsrv_dskLst_head); + } else if (action == QUIT) { + goto done; + } + } + if (disk_list_head != NULL) { + if ((h_print_list_warn(disk_list_head, todo, enc_type)) != 0) { + goto done; + } + if ((err = h_pre_hotplug(&disk_list_head, wwn_list, todo, verbose_flag, + force_flag)) != 0) { + goto done; + } + if (disk_list_head != NULL) { + if (todo == REMOVE_DEVICE) { + (void) fprintf(stdout, MSGSTR(5514, + "\nHit <Return> after " + "removing the device(s).")); + } else { + (void) fprintf(stdout, MSGSTR(5515, + "\nHit <Return> after " + "inserting the device(s).")); + } + (void) getchar(); + (void) fprintf(stdout, "\n"); + if ((err = h_post_hotplug(disk_list_head, wwn_list, + todo, verbose_flag, force_flag, + enc_type)) != 0) { + goto done; + } + } + } +done: + (void) l_free_box_list(&box_list); + (void) g_free_wwn_list(&wwn_list); + if (err && err != -1) { + return (err); + } + free((void *)map.dev_addr); + return (0); +} + + + + +/* + * Internal routine to clean up ../'s in paths. + * returns 0 if no "../" are left. + * + * Wouldn't it be nice if there was a standard system library + * routine to do this...? + */ +static int +cleanup_dotdot_path(char *path) +{ + char holder[MAXPATHLEN]; + char *dotdot; + char *previous_slash; + + /* Find the first "/../" in the string */ + dotdot = strstr(path, "/../"); + if (dotdot == NULL) { + return (0); + } + + + /* + * If the [0] character is '/' and "../" immediatly + * follows it, then we can strip the ../ + * + * /../../foo/bar == /foo/bar + * + */ + if (dotdot == path) { + strcpy(holder, &path[3]); /* strip "/.." */ + strcpy(path, holder); + return (1); + } + + /* + * Now look for the LAST "/" before the "/../" + * as this is the parent dir we can get rid of. + * We do this by temporarily truncating the string + * at the '/' just before "../" using the dotdot pointer. + */ + *dotdot = '\0'; + previous_slash = strrchr(path, '/'); + if (previous_slash == NULL) { + /* + * hmm, somethings wrong. path looks something + * like "foo/../bar/" so we can't really deal with it. + */ + return (0); + } + /* + * Now truncate the path just after the previous '/' + * and slam everything after the "../" back on + */ + *(previous_slash+1) = '\0'; + (void) strcat(path, dotdot+4); + return (1); /* We may have more "../"s */ +} + + +/* + * Follow symbolic links from the logical device name to + * the /devfs physical device name. To be complete, we + * handle the case of multiple links. This function + * either returns NULL (no links, or some other error), + * or the physical device name, alloc'ed on the heap. + * + * For S10 the physical path may be non-existent. + * + * NOTE: If the path is relative, it will be forced into + * an absolute path by pre-pending the pwd to it. + */ +char * +h_get_physical_name_from_link(char *path) +{ + struct stat stbuf; + char source[MAXPATHLEN]; + char scratch[MAXPATHLEN]; + char pwd[MAXPATHLEN]; + char *tmp; + int cnt; + + /* return NULL if path is NULL */ + if (path == NULL) { + return (NULL); + } + + strcpy(source, path); + for (;;) { + + /* + * First make sure the path is absolute. If not, make it. + * If it's already an absolute path, we have no need + * to determine the cwd, so the program should still + * function within security-by-obscurity directories. + */ + if (source[0] != '/') { + tmp = getcwd(pwd, MAXPATHLEN); + if (tmp == NULL) { + O_DPRINTF("getcwd() failed - %s\n", + strerror(errno)); + return (NULL); + } + /* + * Handle special case of "./foo/bar" + */ + if (source[0] == '.' && source[1] == '/') { + strcpy(scratch, source+2); + } else { /* no "./" so just take everything */ + strcpy(scratch, source); + } + strcpy(source, pwd); + (void) strcat(source, "/"); + (void) strcat(source, scratch); + } + + /* + * Clean up any "../"s that are in the path + */ + while (cleanup_dotdot_path(source)); + + /* + * source is now an absolute path to the link we're + * concerned with + * + * S10: Do NOT ignore dangling links, pointing to devfs nodes. + */ + if (strstr(source, "/devices")) { + return (g_alloc_string(source)); + } + + if (lstat(source, &stbuf) == -1) { + O_DPRINTF("lstat() failed for - %s\n", + source, strerror(errno)); + return (NULL); + } + /* + * If the file is not a link, we're done one + * way or the other. If there were links, + * return the full pathname of the resulting + * file. + * + * Note: All of our temp's are on the stack, + * so we have to copy the final result to the heap. + */ + if (!S_ISLNK(stbuf.st_mode)) { + return (g_alloc_string(source)); + } + cnt = readlink(source, scratch, sizeof (scratch)); + if (cnt < 0) { + O_DPRINTF("readlink() failed - %s\n", + strerror(errno)); + return (NULL); + } + /* + * scratch is on the heap, and for some reason readlink + * doesn't always terminate things properly so we have + * to make certain we're properly terminated + */ + scratch[cnt] = '\0'; + + /* + * Now check to see if the link is relative. If so, + * then we have to append it to the directory + * which the source was in. (This is non trivial) + */ + if (scratch[0] != '/') { + tmp = strrchr(source, '/'); + if (tmp == NULL) { /* Whoa! Something's hosed! */ + O_DPRINTF("Internal error... corrupt path.\n"); + return (NULL); + } + /* Now strip off just the directory path */ + *(tmp+1) = '\0'; /* Keeping the last '/' */ + /* and append the new link */ + (void) strcat(source, scratch); + /* + * Note: At this point, source should have "../"s + * but we'll clean it up in the next pass through + * the loop. + */ + } else { + /* It's an absolute link so no worries */ + strcpy(source, scratch); + } + } + /* Never reach here */ +} + +/* + * Function for getting physical pathnames + * + * For S10 the physical path may not exist at the time devctl calls + * are made. So we should not return error if stat fails on /devices path. + * + * This function can handle 2 different inputs. + * + * 1) Inputs of the form /dev/rdsk/cNtNdNsN + * These are identified by being a link + * The physical path they are linked to is returned. + * + * 2) Inputs of the form /devices/... + * These are actual physical names. + * They are not converted. + */ +char * +h_get_physical_name(char *path) +{ + struct stat stbuf; + char s[MAXPATHLEN]; + char savedir[MAXPATHLEN]; + char *result = NULL; + int status = 0; + + /* return invalid path if path NULL */ + if (path == NULL) { + return (NULL); + } + + (void) strcpy(s, path); + + status = lstat(s, &stbuf); + + /* + * S10: If string is devfs node we allow failed lstat. + */ + if ((status == -1) || !S_ISLNK(stbuf.st_mode)) { + /* Make sure a full path as that is required. */ + if (strstr(s, "/devices")) { + result = g_alloc_string(s); + } else { + if (getcwd(savedir, + sizeof (savedir)) == NULL) { + return (NULL); + } + /* + * Check for this format: + * ./ssd@0,1:g,raw + */ + if (s[0] == '.') { + (void) strcat(savedir, &s[1]); + } else { + (void) strcat(savedir, "/"); + (void) strcat(savedir, s); + } + if ((status != -1) || strstr(s, "/devices")) { + result = g_alloc_string(savedir); + } + } + } else { + /* + * Entry is linked file + * so follow link to physical name + */ + result = h_get_physical_name_from_link(path); + } + +exit: + return (result); +} + + +/* + * handle expert-mode hotplug commands + * + * return 0 iff all is okay + */ +int +hotplug_e(int todo, char **argv, int verbose_flag, int force_flag) +{ +char *path_phys = NULL; +char bus_path[MAXPATHLEN]; +char *ptr; +int exit_code; +devctl_hdl_t dcp; +uint_t devstate; +int i = 0, pathcnt = 1; +mp_pathlist_t pathlist; +int p_pw = 0, p_on = 0, p_st = 0; + + + switch (todo) { + case DEV_ONLINE: + case DEV_OFFLINE: + case DEV_GETSTATE: + case DEV_RESET: + /* get physical name */ + if ((path_phys = h_get_physical_name(argv[0])) == NULL) { + + (void) fprintf(stderr, + MSGSTR(112, "Error: Invalid pathname (%s)"), + argv[0]); + (void) fprintf(stderr, "\n"); + return (1); + } + + if (verbose_flag) { + (void) fprintf(stdout, + MSGSTR(5516, + "phys path = \"%s\"\n"), + path_phys); + } + + /* acquire rights to hack on device */ + if ((dcp = devctl_device_acquire(path_phys, + force_flag ? 0 : DC_EXCL)) == NULL) { + + (void) fprintf(stderr, MSGSTR(5517, + "Error: can't acquire \"%s\": %s\n"), + path_phys, strerror(errno)); + return (1); + } + + switch (todo) { + case DEV_ONLINE: + exit_code = devctl_device_online(dcp); + break; + case DEV_OFFLINE: + exit_code = devctl_device_offline(dcp); + break; + case DEV_GETSTATE: + if ((exit_code = devctl_device_getstate(dcp, + &devstate)) == 0) { + print_dev_state(argv[0], devstate); + } + break; + case DEV_RESET: + exit_code = devctl_device_reset(dcp); + break; + } + + if (exit_code != 0) { + perror(MSGSTR(5518, "devctl")); + } + + /* all done now -- release device */ + devctl_release(dcp); + break; + + /* for hotplugging bus operations */ + case BUS_QUIESCE: + case BUS_UNQUIESCE: + case BUS_GETSTATE: + case BUS_RESET: + case BUS_RESETALL: + /* get physical name */ + if ((path_phys = h_get_physical_name(argv[0])) == + NULL) { + (void) fprintf(stderr, + MSGSTR(112, "Error: Invalid pathname (%s)"), + argv[0]); + (void) fprintf(stderr, "\n"); + return (1); + } + if (verbose_flag) { + printf(MSGSTR(5519, "phys path = \"%s\"\n"), path_phys); + } + + /* acquire rights to hack on device */ + /* delete leaf part from path_phys. */ + if (strstr(path_phys, SCSI_VHCI) != NULL) { + /* obtain phci */ + (void) strcpy(bus_path, path_phys); + if (g_get_pathlist(bus_path, &pathlist)) { + (void) fprintf(stderr, + MSGSTR(112, "Error: Invalid pathname (%s)"), + path_phys); + (void) fprintf(stderr, "\n"); + return (1); + } + pathcnt = pathlist.path_count; + p_pw = p_on = p_st = 0; + for (i = 0; i < pathcnt; i++) { + if (pathlist.path_info[i].path_state < + MAXPATHSTATE) { + if (strstr(pathlist.path_info[i]. + path_addr, + argv[0]) != NULL) { + p_pw = i; + break; + } + if (pathlist.path_info[i].path_state == + MDI_PATHINFO_STATE_ONLINE) { + p_on = i; + } + if (pathlist.path_info[i].path_state == + MDI_PATHINFO_STATE_STANDBY) { + p_st = i; + } + } + } + if (strstr(pathlist.path_info[p_pw].path_addr, + argv[0]) != NULL) { + /* matching input pwwn */ + (void) strcpy(bus_path, + pathlist.path_info[p_pw].path_hba); + } else if (pathlist.path_info[p_on].path_state == + MDI_PATHINFO_STATE_ONLINE) { + /* on_line path */ + (void) strcpy(bus_path, + pathlist.path_info[p_on].path_hba); + } else { + /* standby or path0 */ + (void) strcpy(bus_path, + pathlist.path_info[p_st].path_hba); + } + free(pathlist.path_info); + } else { + + (void) strcpy(bus_path, path_phys); + ptr = strrchr(bus_path, '/'); + if (ptr) { + *ptr = '\0'; + } else { + (void) fprintf(stderr, + MSGSTR(112, "Error: Invalid pathname (%s)"), + path_phys); + (void) fprintf(stderr, "\n"); + return (1); + } + } + + if ((dcp = devctl_bus_acquire(bus_path, + force_flag ? 0 : DC_EXCL)) == NULL) { + (void) fprintf(stderr, + MSGSTR(5521, + " Error: can't acquire bus node from" + " the path \"%s\": %s\n"), + bus_path, strerror(errno)); + return (1); + } + + switch (todo) { + case BUS_QUIESCE: + exit_code = devctl_bus_quiesce(dcp); + break; + case BUS_UNQUIESCE: + exit_code = devctl_bus_unquiesce(dcp); + break; + case BUS_GETSTATE: + if ((exit_code = devctl_bus_getstate(dcp, + &devstate)) == 0) { + print_bus_state(argv[0], devstate); + } + break; + case BUS_RESET: + exit_code = devctl_bus_reset(dcp); + break; + case BUS_RESETALL: + exit_code = devctl_bus_resetall(dcp); + break; + } + + if (exit_code != 0) { + perror(MSGSTR(5522, "devctl")); + } + + /* all done now -- release device */ + devctl_release(dcp); + break; + } + + return (exit_code); +} + + + +/* + * Prepares an individual FC_AL device + * to be removed from the specified + * slot. + * + * RETURNS: + * 0 if OK + * non-zero otherwise. + */ +static int +h_pre_remove_dev(Hotplug_Devlist *hotplug_disk, WWN_list *wwn_list, + int verbose_flag, int force_flag) +{ +char *dev_path, device_name[MAXNAMELEN]; +int err; + + /* Initialize pointers */ + dev_path = NULL; + + if (hotplug_disk->dlhead != NULL) { + dev_path = hotplug_disk->dlhead->dev_path; + (void) strcpy(device_name, (hotplug_disk->dlhead)->logical_path); + } + (void) fprintf(stdout, + MSGSTR(157, + "stopping: %s...."), device_name); + if (!(strstr(dev_path, SLSH_DRV_NAME_ST))) { + if ((err = g_dev_stop(dev_path, wwn_list, verbose_flag)) != 0) + return (err); + } + (void) fprintf(stdout, MSGSTR(156, "Done\n")); + + (void) fprintf(stdout, + MSGSTR(158, "offlining: %s...."), device_name); + if ((err = g_offline_drive(hotplug_disk->dlhead, + force_flag)) != 0) { + (void) fprintf(stdout, + MSGSTR(160, + "\nonlining: %s\n"), device_name); + + (void) g_online_drive(hotplug_disk->dlhead, force_flag); + (void) fprintf(stdout, + MSGSTR(159, "starting: %s...."), + device_name); + if ((err = g_dev_start(dev_path, 0)) != 0) { + return (err); + } + (void) fprintf(stdout, MSGSTR(156, "Done\n")); + return (err); + } + (void) fprintf(stdout, MSGSTR(156, "Done\n")); + return (0); +} + + + +/* + * Prepares a SENA enclosure or SENA FC_AL device + * to be inserted/removed from a specified slot. + * + * RETURNS: + * 0 if OK + * non-zero otherwise. + */ +static int +h_pre_hotplug_sena(Hotplug_Devlist *hotplug_dev, + WWN_list *wwn_list, int todo, + int verbose_flag, int force_flag) +{ +int slot, f_r, i, found_null_wwn = 0, err; +char *ses_path, *dev_path, code; +char node_wwn_s[WWN_SIZE], device_name[MAXNAMELEN]; +struct l_state_struct l_state; +struct dlist *dl; + + + if (hotplug_dev->dev_type == DTYPE_ESI) { + /* entire photon is being removed */ + if ((err = l_offline_photon(hotplug_dev, wwn_list, + force_flag, verbose_flag)) != 0) { + return (err); + } + return (0); + } + + /* if device is an individual sena disk */ + dl = hotplug_dev->seslist; + while (dl) { + ses_path = dl->dev_path; + if ((err = l_get_status(ses_path, &l_state, + verbose_flag)) == 0) + break; + dl = dl->next; + } + if (dl == NULL) { + return (L_GET_STATUS_FAILED); + } + + f_r = hotplug_dev->f_flag; + slot = hotplug_dev->slot; + (void) l_get_drive_name(device_name, slot, f_r, hotplug_dev->box_name); + + /* check if disk has null wwn */ + if (f_r) { + (void) strncpy(node_wwn_s, + l_state.drv_front[slot].g_disk_state.node_wwn_s, WWN_SIZE); + } else { + (void) strncpy(node_wwn_s, + l_state.drv_rear[slot].g_disk_state.node_wwn_s, WWN_SIZE); + } + for (i = 0; i < WWN_SIZE; i++) { + if (node_wwn_s[i] != '0') + break; + found_null_wwn = 1; + } + + switch (todo) { + case INSERT_DEVICE: + if (hotplug_dev->f_flag) { + code = + l_state.drv_front[slot].ib_status.code; + } else { + code = + l_state.drv_rear[slot].ib_status.code; + } + if (code & S_NOT_INSTALLED) { + /* + * At this point we know that the drive is not + * there. Turn on the RQST INSERT bit to make + * the LED blink + */ + if ((err = l_encl_status_page_funcs + (SET_RQST_INSRT, 0, todo, + ses_path, &l_state, f_r, slot, + verbose_flag)) != 0) { + (void) print_errString(err, + device_name); + (void) fprintf(stderr, + MSGSTR(5530, + " %s: could not turn " + "on LED\n"), + device_name); + } + } else { + /* + * Drive is there so start it. + */ + if ((err = l_encl_status_page_funcs + (SET_DRV_ON, 0, todo, + ses_path, &l_state, f_r, slot, + verbose_flag)) != 0) { + (void) print_errString(err, + device_name); + (void) fprintf(stderr, + MSGSTR(5531, + " could not enable" + " %s\n"), + device_name); + } + } + break; + + case REMOVE_DEVICE: + /* + * if disk has null wwn, then + * there is no need to check the + * disk/loop status. + */ + if (found_null_wwn == 1) { + if (getenv("_LUX_W_DEBUG") != NULL) { + (void) fprintf(stdout, + "Device %s has " + "null WWN.\n", + device_name); + } + goto rmv; + } + if (hotplug_dev->f_flag) { + if ( + l_state.drv_front[slot].ib_status.code + == S_NOT_INSTALLED) { + (void) fprintf(stderr, + MSGSTR(86, + " Notice: %s may already" + " be removed.\n"), + device_name); + return (0); + } + } else if ( + l_state.drv_rear[slot].ib_status.code + == S_NOT_INSTALLED) { + (void) fprintf(stderr, + MSGSTR(86, + " Notice: %s may already" + " be removed.\n"), + device_name); + return (0); + } + +rmv: + if (hotplug_dev->dlhead == NULL) { + dev_path = NULL; + } else { + dev_path = hotplug_dev->dlhead->dev_path; + } + + (void) fprintf(stdout, + MSGSTR(157, + "stopping: %s...."), device_name); + if ((err = g_dev_stop(dev_path, wwn_list, 0)) != 0) { + return (err); + } + (void) fprintf(stdout, MSGSTR(156, "Done\n")); + + (void) fprintf(stdout, + MSGSTR(158, "offlining: %s...."), + device_name); + if ((err = g_offline_drive(hotplug_dev->dlhead, + force_flag)) != 0) { + (void) fprintf(stdout, + MSGSTR(160, + "\nonlining: %s\n"), device_name); + (void) g_online_drive(hotplug_dev->dlhead, force_flag); + + (void) fprintf(stdout, + MSGSTR(159, "starting: %s...."), + device_name); + (void) g_dev_start(dev_path, 0); + (void) fprintf(stdout, MSGSTR(156, "Done\n")); + return (err); + } + (void) fprintf(stdout, MSGSTR(156, "Done\n")); + + /* + * Take the drive off the loop + * and blink the LED. + */ + if (hotplug_dev->dev_location == SENA) { + if ((err = l_encl_status_page_funcs(SET_RQST_RMV, 0, + todo, ses_path, &l_state, f_r, + slot, verbose_flag)) != 0) { + (void) print_errString(err, device_name); + (void) fprintf(stderr, + MSGSTR(5539, + " %s: could not blink" + " the yellow LED\n"), + device_name); + } + } + break; + } + return (0); +} + + + +/* + * Performs the post removal operations for + * a SENA enclosure or a SENA FC_AL disk. + * + * RETURNS: + * 0 if OK + * non-zero otherwise + */ +static int +h_post_hotplug_sena(Hotplug_Devlist *hotplug_dev, + WWN_list *wwn_list, int todo, + int verbose_flag, int force_flag, int enc_type) +{ +char *ses_path, *dev_path = NULL, device_name[MAXNAMELEN]; +int tid, slot, f_r, al_pa, timeout = 0; +uchar_t port_wwn[WWN_SIZE], node_wwn[WWN_SIZE]; +char code; +int wait_spinup_flag = 0, wait_map_flag = 0; +int wait_node_flag = 0, err = 0, nArg; +gfc_map_t map; +WWN_list *newWwn_list = NULL; +struct dlist *dl, *dl1; +struct l_state_struct l_state; + + + dl = hotplug_dev->seslist; + slot = hotplug_dev->slot; + f_r = hotplug_dev->f_flag; + tid = hotplug_dev->tid; + + if (hotplug_dev->dev_type == DTYPE_ESI) { + /* + * See if photon has really been removed. If not, + * try onlining the devices if applicable + */ + H_DPRINTF(" post_hotplug_sena: Seeing if enclosure " + "has really been removed:\n" + " tid=0x%x, ses_path %s\n", + tid, dl->dev_path); + + while (dl) { + ses_path = dl->dev_path; + if ((err = g_get_dev_map(ses_path, &map, 0)) == 0) { + if ((map.hba_addr.port_topology == + FC_TOP_PUBLIC_LOOP) || + (map.hba_addr.port_topology == + FC_TOP_FABRIC)) { + /* public or fabric loop device */ + free((void *)map.dev_addr); + (void) fprintf(stdout, MSGSTR(5540, + "This operation is not " + "supported in this topology.\n")); + return (0); + } + if ((err = g_get_wwn(ses_path, port_wwn, + node_wwn, &al_pa, verbose_flag)) == 0) { + tid = g_sf_alpa_to_switch[al_pa]; + if (g_device_in_map(&map, tid)) { + free((void *)map.dev_addr); + break; + } + } + FREE_DEV_ADDR(map.dev_addr); + } + + dl = dl->next; + } + FREE_DEV_ADDR(map.dev_addr); + if (dl) { + (void) fprintf(stdout, MSGSTR(5640, + "Photon \"%s\" not removed." + " Onlining Drives in enclosure.\n"), + hotplug_dev->box_name); + for (dl = hotplug_dev->dlhead; dl; ) { + (void) g_online_drive(dl->multipath, + force_flag); + (void) g_free_multipath(dl->multipath); + dl1 = dl; + dl = dl->next; + (void) free(dl1); + } + hotplug_dev->dlhead = NULL; + return (0); + } + /* + * Remove logical nodes for this + * photon, this includes ses and + * /dev/dsk entries. + * In Solaris7, disks with -C option + * removes the /dev/dsk entries. + * The -C option is available + * only for Solaris7. From Solaris8 + * or higher releases, the "disks" + * program will be replaced by the + * devfsadm program. + */ + /* pass "disks -C" as cmdStrg. */ + nArg = 2; + if (h_execCmnd(cmdStrg[0], nArg) != 0) { + for (dl = hotplug_dev->dlhead; + dl != NULL; dl = dl->next) { + if ((err = h_remove_nodes(dl->multipath)) + != 0) { + return (err); + } + } + } else { + (void) fprintf(stdout, + MSGSTR(5541, + " Logical Nodes being removed" + " under /dev/dsk/ and /dev/rdsk:\n")); + for (dl = hotplug_dev->dlhead; + dl != NULL; dl = dl->next) { + (void) h_print_logical_nodes(dl->multipath); + } + } + + for (dl = hotplug_dev->dlhead; dl != NULL; ) { + (void) g_free_multipath(dl->multipath); + dl1 = dl; + dl = dl->next; + (void) free(dl1); + } + hotplug_dev->dlhead = NULL; + if ((err = h_remove_ses_nodes(hotplug_dev->seslist)) != 0) { + return (err); + } + return (0); + } + + /* post hotplug operations for a SENA disk. */ + if (enc_type == DAK_ENC_TYPE) { + (void) sprintf(device_name, MSGSTR(5664, + " Drive in Box Name \"%s\" slot %d"), + hotplug_dev->box_name, + f_r ? slot : slot + (MAX_DRIVES_DAK/2)); + } else { + if (tid & 0x10) { + (void) sprintf(device_name, MSGSTR(5542, + " Drive in Box Name \"%s\" rear slot %d"), + hotplug_dev->box_name, slot); + } else { + (void) sprintf(device_name, MSGSTR(5543, + " Drive in Box Name \"%s\" front slot %d"), + hotplug_dev->box_name, slot); + } + } + (void) fprintf(stdout, "%s\n", device_name); + + dl = hotplug_dev->seslist; + while (dl) { + ses_path = dl->dev_path; + if ((err = l_get_status(ses_path, &l_state, + verbose_flag)) == 0) + break; + dl = dl->next; + } + if (dl == NULL) { + print_errString(err, ses_path); + return (L_GET_STATUS_FAILED); + } + + code = 0; + while (((err = l_encl_status_page_funcs(OVERALL_STATUS, + &code, todo, ses_path, &l_state, f_r, slot, + verbose_flag)) != 0) || (code != 0)) { + if (err) { + (void) print_errString(err, ses_path); + } else if (todo == REMOVE_DEVICE) { + if (code == S_OK) { + (void) fprintf(stderr, + MSGSTR(5544, + "\n Warning: Device has not been" + " removed from the enclosure\n" + " and is still on the loop.")); + return (0); + } else { + (void) fprintf(stderr, + MSGSTR(5545, + " Notice: Device has not been" + " removed from the enclosure.\n" + " It has been removed from the" + " loop and is ready to be\n" + " removed" + " from the enclosure, and" + " the LED is blinking.\n\n")); + } + goto loop2; + } else if ((todo == INSERT_DEVICE) && + ((code != S_NOT_AVAILABLE) || + (timeout > + PHOTON_SPINUP_TIMEOUT) || + err)) { + (void) fprintf(stderr, + MSGSTR(5546, + "\n Warning: Disk status is" + " Not OK!\n\n")); + return (0); + } + (void) sleep(PHOTON_SPINUP_DELAY); + if (wait_spinup_flag++ == 0) { + (void) fprintf(stdout, MSGSTR(5547, + " Waiting for the disk to spin up:")); + } else { + (void) fprintf(stdout, "."); + } + timeout++; + } + if (wait_spinup_flag) { + (void) fprintf(stdout, "\n"); + } +loop2: + switch (todo) { + case INSERT_DEVICE: + /* check loop map that drive is present */ + for (;;) { + dl = hotplug_dev->seslist; + map.dev_addr = (gfc_port_dev_info_t *)NULL; + while (dl) { + ses_path = dl->dev_path; + if ((err = g_get_dev_map(ses_path, + &map, verbose_flag)) != 0) { + (void) fprintf(stderr, + MSGSTR(5548, + " Error: Could not get" + " map for %s.\n"), + ses_path); + return (err); + } + if (g_device_in_map(&map, tid)) { + goto loop3; + } + FREE_DEV_ADDR(map.dev_addr); + dl = dl->next; + } + if (timeout > PHOTON_SPINUP_TIMEOUT) { + (void) fprintf(stderr, + MSGSTR(5549, + " Warning: Device not in" + " loop map.\n")); + FREE_DEV_ADDR(map.dev_addr); + return (0); + } + if (wait_map_flag++ == 0) { + (void) fprintf(stdout, + MSGSTR(5550, + " Waiting for the device " + "to appear in the loop map:")); + } else { + (void) fprintf(stdout, "."); + } + timeout++; + (void) sleep(PHOTON_SPINUP_DELAY); + } +loop3: + if (wait_map_flag) { + (void) fprintf(stdout, "\n"); + } + + /* + * Run drvconfig and disks to create + * logical nodes + */ + for (;;) { + /* pass "disks" as cmdStrg */ + nArg = 3; + if (h_execCmnd(cmdStrg[2], nArg) != 0) { + (void) fprintf(stderr, + MSGSTR(5551, + " Could not " + "run drvconfig.\n")); + FREE_DEV_ADDR(map.dev_addr); + return (L_DRVCONFIG_ERROR); + } + + if (l_device_present(ses_path, tid, &map, + verbose_flag, &dev_path) == 1) + break; + if (timeout > PHOTON_SPINUP_TIMEOUT) { + (void) fprintf(stderr, + MSGSTR(5552, + " Warning: Could not find " + "any node for inserted " + "device\n")); + FREE_DEV_ADDR(map.dev_addr); + return (0); + } + if (wait_node_flag++ == 0) { + (void) fprintf(stdout, + MSGSTR(5553, + " Waiting for the logical " + "node to be created:")); + } else { + (void) fprintf(stdout, "."); + } + timeout++; + (void) sleep(PHOTON_SPINUP_DELAY); + } + FREE_DEV_ADDR(map.dev_addr); + if (wait_node_flag) { + (void) fprintf(stdout, "\n"); + } + /* + * In Solaris7, disks with -C + * option creates the new links + * and removes any stale links. + * In pre-Solaris7 releases, just + * disks should do it all. + */ + /* pass "disks -C" as cmdStrg */ + nArg = 2; + if (h_execCmnd(cmdStrg[0], nArg) != 0) { + return (L_DISKS_ERROR); + } + /* + * Get a new wwn list here in order to + * get the multiple paths to a newly added + * device. + */ + if ((err = g_get_wwn_list(&newWwn_list, + verbose_flag)) != 0) { + return (err); + } + if ((err = g_get_multipath(dev_path, &dl, + newWwn_list, 0)) != 0) { + return (err); + } + if ((err = h_display_logical_nodes(dl)) != 0) { + return (err); + } + break; + + case REMOVE_DEVICE: +/* + * TBD + * Need to check all loops. + */ + /* check whether device is still in loop map */ + if ((err = g_get_dev_map(ses_path, &map, + verbose_flag)) != 0) { + return (err); + } + + if ((map.hba_addr.port_topology == + FC_TOP_PUBLIC_LOOP) || + (map.hba_addr.port_topology == + FC_TOP_FABRIC)) { + /* public or fabric loop device */ + free((void *)map.dev_addr); + (void) fprintf(stderr, MSGSTR(5540, + "This operation is not " + "supported in this topology.\n")); + /* + * calling routine expects a 0 return code + * or a pre-defined luxadm error code. + * Here we do not have a pre-defined error + * code, a 0 is returned. + */ + return (0); + } + + if (g_device_in_map(&map, tid)) { + (void) fprintf(stderr, MSGSTR(5554, + " Warning: Device still in the loop map.\n")); + FREE_DEV_ADDR(map.dev_addr); + return (0); + } + FREE_DEV_ADDR(map.dev_addr); + /* + * In Solaris7, "disks -C" program + * removes the /dev/{r}dsk entries. + * The -C option is available only + * for Solaris7. From Solaris8 or + * higher releases, the "disks" program + * will be replaced by devfsadm. + */ + /* pass "disks -C" as cmdStrg */ + nArg = 2; + if (h_execCmnd(cmdStrg[0], nArg) != 0) { + return (L_DISKS_ERROR); + } + (void) fprintf(stdout, + MSGSTR(5555, + " Logical Nodes being removed" + " under /dev/dsk/ and /dev/rdsk:\n")); + (void) h_print_logical_nodes( + hotplug_dev->dlhead); + break; + } + return (0); +} + + + + +/* + * Creates new ses entries under /dev/es + * directory for the newly added + * enclosures. + * + * RETURNS: + * 0 if OK + * non-zero otherwise + */ +static int +h_post_insert_encl(timestruc_t ses_lastmtim) +{ +struct stat ses_stat; +char lname[MAXPATHLEN]; +int err, found_newlink = 0; +DIR *dir; +struct dirent *dirent; +Box_list *bl1, *box_list = NULL; + + + if ((dir = opendir(SES_DIR)) == NULL) { + return (L_OPEN_ES_DIR_FAILED); + } + if ((err = l_get_box_list(&box_list, 0)) != 0) { + closedir(dir); + return (err); + } + + /* + * The mod time of /dev/es was newer than the mod time prior to + * insert so dir entry is checked at this time. + */ + while ((dirent = readdir(dir)) != (struct dirent *)NULL) { + if (strcmp(dirent->d_name, ".") == 0 || + strcmp(dirent->d_name, "..") == 0) + continue; + + (void) sprintf(lname, SES_DIR"/%s", dirent->d_name); + if (lstat(lname, &ses_stat) < 0) { + (void) print_errString(L_LSTAT_ES_DIR_ERROR, + lname); + continue; + } + + for (bl1 = box_list; bl1; bl1 = bl1->box_next) { + if (strstr(lname, bl1->b_physical_path)) + break; + } + + if (box_list && bl1) + continue; + + if (NEWER(ses_stat.st_ctim, ses_lastmtim)) { + /* New enclosure was detected. */ + found_newlink++; + if (found_newlink == 1) { + (void) fprintf(stdout, MSGSTR(5556, + " New Logical Nodes under /dev/es:\n")); + } + (void) fprintf(stdout, "\t%s\n", + dirent->d_name); + } + } + if (!found_newlink) { + (void) fprintf(stdout, MSGSTR(5662, + " No new enclosure(s) were added!!\n\n")); + } + + closedir(dir); + + (void) l_free_box_list(&box_list); + return (0); +} + + + +/* + * performs the post removal of individual + * FC_AL disks. + * + * RETURNS: + * 0 if OK + * non-zero otherwise + */ +static int +h_post_remove_dev(Hotplug_Devlist *hotplug_disk, + int todo, int verbose_flag) +{ +char device_name[MAXNAMELEN], *dev_path = NULL; +int tid, err; +gfc_map_t map; +int nArg; + + + tid = hotplug_disk->tid; + (void) sprintf(device_name, + MSGSTR(5557, + "\n Device: %s"), + (hotplug_disk->dlhead)->logical_path); + + (void) fprintf(stdout, "%s\n", device_name); + + dev_path = (hotplug_disk->dlhead)->dev_path; + + /* + * On qlc, after a forcelip on a FC combo box, it sometimes takes 17 + * seconds for the loop to come back online. During this 17 seconds, + * g_get_dev_map * will return L_NO_DEVICES_FOUND. This delay + * has been added to assure that the L_NO_DEVICES_FOUND returned from + * g_get_dev_map is not the result of the 17 second delay on FC combo. + * This only affects qlc. + */ + if ((err = g_get_dev_map(dev_path, &map, verbose_flag)) != 0) { + if ((err == L_NO_DEVICES_FOUND) && + (strstr(dev_path, "SUNW,qlc@") != NULL)) { + sleep(QLC_LIP_DELAY); + if ((err = g_get_dev_map(dev_path, &map, verbose_flag)) + != 0) { + if (err != L_NO_DEVICES_FOUND) + return (err); + } + } else if (err != L_NO_DEVICES_FOUND) + return (err); + } + + /* + * if g_get_dev_map returns L_NO_DEVICES_FOUND, then there are not + * devices attached to the HBA and there is no sense in calling + * g_device_in_map(). + */ + if (err != L_NO_DEVICES_FOUND) { + if ((map.hba_addr.port_topology == FC_TOP_PUBLIC_LOOP) || + (map.hba_addr.port_topology == FC_TOP_FABRIC)) { + /* public or fabric loop device */ + free((void *)map.dev_addr); + (void) fprintf(stderr, MSGSTR(5540, + "This operation is not " + "supported in this topology.\n")); + return (0); + } + + if (g_device_in_map(&map, tid) != 0) { + (void) fprintf(stderr, + MSGSTR(5558, + " Warning: Device has" + " not been removed from\n" + " the slot and is still" + " in the loop map.\n\n")); + free((void *)map.dev_addr); + return (0); + } + free((void *)map.dev_addr); + } + /* + * In Solaris7, "disks -C" program + * removes the /dev/{r}dsk entries. + * The -C option is available only + * for Solaris7. From Solaris8 or + * higher releases, the "disks" program + * will be replaced by devfsadm. + */ + /* pass "disks -C" as cmdStrg. */ + nArg = 2; + if (h_execCmnd(cmdStrg[0], nArg) != 0) { + return (L_DISKS_ERROR); + } + /* pass "tapes -C as cmdStrg. */ + if (h_execCmnd(cmdStrg[5], nArg) != 0) { + return (L_TAPES_ERROR); + } + (void) h_print_logical_nodes(hotplug_disk->dlhead); + + return (0); +} + + + +/* + * Gets the last modification time for + * /dev/es/ and /dev/rdsk directories + * and passes these values to the caller. + * + * RETURNS: + * 0 if OK + * non-zero in case of error + */ +static int +h_pre_insert_encl_dev(timestruc_t *ses_time, timestruc_t *dsk_time, + timestruc_t *rmt_time) +{ +struct stat ses_stat, dsk_stat, rmt_stat; + + if (stat(SES_DIR, &ses_stat) < 0) { + /* + * Even if there exists no /dev/es don't fail it. + * The host doesn't have to have any enclosure device + * configured. + */ + if (errno == ENOENT) { + ses_time = (timestruc_t *)NULL; + } else { + return (L_LSTAT_ES_DIR_ERROR); + } + } else { + *ses_time = ses_stat.st_mtim; + } + + if (stat(DEV_DSK_DIR, &dsk_stat) < 0) { + return (L_STAT_DEV_DIR_ERROR); + } else { + *dsk_time = dsk_stat.st_mtim; + } + if (stat(DEV_TAPE_DIR, &rmt_stat) < 0) { + /* + * Even if there exists no /dev/rmt don't fail it. + * The host doesn't have to have any tape device + * configured. + */ + if (errno == ENOENT) { + rmt_time = (timestruc_t *)NULL; + } else { + return (L_STAT_RMT_DIR_ERROR); + } + } else { + *rmt_time = rmt_stat.st_mtim; + } + + return (0); +} + + + +/* + * Waits for loop intialization to complete + * and runs drvconfig, disks and devlinks to create device nodes + * for devices that are being added and prints the newly created + * /dev/rdsk entries. + * + * RETURNS: + * 0 if OK + * non-zero in case of error + */ + +static int +h_post_insert_dev(timestruc_t dsk_lastmtim, timestruc_t rmt_lastmtim) +{ +int found_newlink = 0, nArg; + + (void) fprintf(stdout, + MSGSTR(5560, + "\nWaiting for Loop Initialization to complete...\n")); + + /* + * We sleep here to let the system create nodes. Not sleeping + * could cause the drvconfig below to run too soon. + */ + + (void) sleep(NODE_CREATION_TIME); + + /* + * Run drvconfig and disks to create + * logical nodes + */ + /* pass "drvconfig" as cmdStrg */ + nArg = 1; + if (h_execCmnd(cmdStrg[3], nArg) != 0) { + return (L_DRVCONFIG_ERROR); + } + + /* + * In 2.7, disks with the -C + * option should be used to + * create new links and remove + * any stale links. + * In pre-2.7 releases, just + * disks should do it all. + */ + + /* pass "disks -C" as cmdStrg */ + nArg = 2; + if (h_execCmnd(cmdStrg[0], nArg) != 0) { + return (L_DISKS_ERROR); + } + /* pass "tapes -C as cmdStrg */ + if (h_execCmnd(cmdStrg[5], nArg) != 0) { + return (L_TAPES_ERROR); + } + + /* pass "devlinks" as cmdStrg */ + nArg = 1; + if (h_execCmnd(cmdStrg[4], nArg) != 0) { + return (L_DEVLINKS_ERROR); + } + + /* check /dev/dsk and /dev/rmt for new links */ + found_newlink = h_find_new_device_link(DEV_DSK_DIR, dsk_lastmtim) + + h_find_new_device_link(DEV_TAPE_DIR, rmt_lastmtim); + + if (!found_newlink) { + (void) fprintf(stdout, MSGSTR(5562, + " No new device(s) were added!!\n\n")); + } + + return (0); +} + + + +/* + * Performs the pre hotplug operations on SENA enclosure(s), + * SENA disk(s) and individual fcal disk(s). + * If the device is failed to remove, then it removes the device from the + * hotplug list and continues with the next device in the list. + * + * RETURNS: + * 0 if OK + * prints an error message to stderr and returns 0 + */ +static int +h_pre_hotplug(Hotplug_Devlist **disk_list_head_ptr, + WWN_list *wwn_list, int todo, + int verbose_flag, int force_flag) +{ +Hotplug_Devlist *list, *disk_list; +int err = 0; + + disk_list = *disk_list_head_ptr; + while (disk_list != NULL) { + if ((disk_list->dev_type == DTYPE_ESI) || + (disk_list->dev_location == SENA)) { + if ((err = h_pre_hotplug_sena(disk_list, wwn_list, + todo, verbose_flag, force_flag)) != 0) { + (void) print_errString(err, + disk_list->dev_name); + goto delete; + } + } else if (disk_list->dev_location == NON_SENA) { + if ((err = h_pre_remove_dev(disk_list, wwn_list, + verbose_flag, force_flag)) != 0) { + (void) print_errString(err, + disk_list->dev_name); + goto delete; + } + } + disk_list = disk_list->next; + continue; +delete: + list = disk_list->prev; + if (list != NULL) { + list->next = disk_list->next; + if (list->next != NULL) + list->next->prev = list; + } + list = disk_list; + disk_list = disk_list->next; + if (list == *disk_list_head_ptr) + *disk_list_head_ptr = disk_list; + (void) g_free_multipath(list->seslist); + (void) g_free_multipath(list->dlhead); + (void) free(list); + } + return (0); +} + + + +/* + * Performs the post removal of a list of SENA enclosure(s), + * SENA disk(s) and individual fcal disk(s). + * + * RETURNS: + * 0 O.K. + * non-zero otherwise + */ +static int +h_post_hotplug(Hotplug_Devlist *hotplug_dlist, + WWN_list *wwn_list, int todo, + int verbose_flag, int force_flag, int enc_type) +{ +Hotplug_Devlist *list; +int err; + + /* Do a lip on every loop so that we get the latest loop maps */ + if (todo != INSERT_DEVICE) { + if ((err = g_forcelip_all(hotplug_dlist)) != 0) { + return (err); + } + } + + while (hotplug_dlist != NULL) { + if ((hotplug_dlist->dev_location == SENA) || + (hotplug_dlist->dev_type == DTYPE_ESI)) { + if ((err = h_post_hotplug_sena(hotplug_dlist, wwn_list, todo, + verbose_flag, force_flag, enc_type)) != 0) + (void) print_errString(err, hotplug_dlist->dev_name); + } else if (hotplug_dlist->dev_location == NON_SENA) { + if ((err = h_post_remove_dev(hotplug_dlist, + todo, verbose_flag)) != 0) + (void) print_errString(err, + hotplug_dlist->dev_name); + } + list = hotplug_dlist; + hotplug_dlist = hotplug_dlist->next; + (void) g_free_multipath(list->seslist); + (void) g_free_multipath(list->dlhead); + (void) free(list); + } + return (0); +} + + +/* + * removes the device's logical paths. + * + * RETURNS: + * 0 if OK + * non-zero otherwise + */ +static int +h_remove_nodes(struct dlist *dl) +{ +char link[MAXPATHLEN], path[MAXPATHLEN]; +char lname[MAXPATHLEN], *ptr; +DIR *dir; +struct dirent *dirent; +struct dlist *dlist; + + if ((dir = opendir(DEV_DSK_DIR)) == NULL) { + return (L_READ_DEV_DIR_ERROR); + } + if (dl == NULL) { + /* pass "disks" as cmdStrg */ + if (h_execCmnd(cmdStrg[1], 1) != 0) { + return (L_DISKS_ERROR); + } + } + + (void) fprintf(stdout, + MSGSTR(5563, + " Removing Logical Nodes: \n")); + + while ((dirent = readdir(dir)) != (struct dirent *)NULL) { + if (strcmp(dirent->d_name, ".") == 0 || + strcmp(dirent->d_name, "..") == 0) { + continue; + } + (void) sprintf(lname, DEV_DSK_DIR"/%s", dirent->d_name); + if (readlink((const char *)lname, (char *)link, + (size_t)MAXPATHLEN) <= 0) { + (void) fprintf(stderr, + MSGSTR(5564, + " Error: Could not read %s\n"), + lname); + continue; + } + for (dlist = dl; dlist != NULL; dlist = dlist->next) { + (void) strcpy(path, dlist->dev_path); + ptr = strrchr(path, ':'); + if (ptr) + *ptr = '\0'; + if (strstr(link, path)) { + (void) unlink(lname); + (void) sprintf(lname, "/dev/rdsk/%s", + dirent->d_name); + (void) fprintf(stdout, + MSGSTR(5565, + "\tRemoving %s\n"), + dirent->d_name); + (void) unlink(lname); + } + } + } + closedir(dir); + return (0); +} + + + +/* + * removes the SENA's ses paths. + * + * RETURNS: + * 0 if OK + * non-zero otherwise + */ +static int +h_remove_ses_nodes(struct dlist *dlist) +{ +char link[MAXPATHLEN], lname[MAXPATHLEN]; +DIR *dir; +struct dirent *dirent; +struct dlist *dl; + + + if ((dir = opendir(SES_DIR)) == NULL) { + return (L_READ_DEV_DIR_ERROR); + } + + (void) fprintf(stdout, MSGSTR(5566, " Removing Ses Nodes:\n")); + + /* + * Remove the ses entries + * of the form ses<#> + * from the /dev/es directory. + */ + + while ((dirent = readdir(dir)) != (struct dirent *)NULL) { + if (strcmp(dirent->d_name, ".") == 0 || + strcmp(dirent->d_name, "..") == 0) + continue; + + (void) sprintf(lname, SES_DIR"/%s", dirent->d_name); + if (readlink((const char *)lname, (char *)link, + (size_t)MAXPATHLEN) <= 0) { + (void) fprintf(stderr, + MSGSTR(5564, + " Error: Could not read %s\n"), + lname); + continue; + } + for (dl = dlist; dl != NULL; dl = dl->next) { + if (strstr(link, dl->dev_path)) { + (void) fprintf(stdout, + MSGSTR(5568, + "\tRemoving %s\n"), + lname); + (void) unlink(lname); + } + } + } + closedir(dir); + (void) g_free_multipath(dlist); + return (0); +} + + +/* + * prints the device's logical + * paths for disks to stdout. + * + * RETURNS: + * 0 if OK + * non-zero otherwise + */ +static void +h_print_logical_nodes(struct dlist *disk_list) +{ +char *lpath, *ptr, *buf_ptr, buf[MAXNAMELEN], dev[MAXNAMELEN]; +struct dlist *dlist; +int i, found_dev = 0; +char *tape_entries[] = { "", "b", "bn", "c", "cb", "cbn", "cn", + "h", "hb", "hbn", "hn", "l", "lb", + "lbn", "ln", "m", "mb", "mbn", "mn", + "n", "u", "ub", "ubn", "un", NULL}; + + for (dlist = disk_list; dlist != NULL; dlist = dlist->next) { + lpath = dlist->logical_path; + if ((ptr = strrchr(lpath, 'c')) == NULL) + continue; + (void) strcpy(buf, ptr); + if ((ptr = strrchr(buf, 's')) == NULL) + continue; + *(++ptr) = NULL; + found_dev++; + if (found_dev == 1) + (void) fprintf(stdout, + MSGSTR(5559, " Logical Nodes being " + "removed under /dev/dsk/ and " + "/dev/rdsk:\n")); + for (i = 0; i <= 7; i++) { + (void) sprintf(dev, "%s%d", buf, i); + (void) fprintf(stdout, "\t%s\n", dev); + } + } + found_dev = 0; + for (dlist = disk_list; dlist != NULL; dlist = dlist->next) { + lpath = dlist->logical_path; + if (strstr(lpath, DEV_TAPE_DIR)) { + if ((ptr = strrchr(lpath, '/')) == NULL) + continue; + found_dev++; + if (found_dev == 1) + (void) fprintf(stdout, "Logical Nodes being " + "removed under /dev/rmt:\n"); + ptr++; + buf_ptr = ptr; + while (*ptr >= '0' && *ptr <= '9') + ptr++; + *ptr = NULL; + for (i = 0, ptr = tape_entries[0]; + ptr != NULL; + i++, ptr = tape_entries[i]) { + (void) sprintf(dev, "%s%s", buf_ptr, ptr); + (void) fprintf(stdout, "\t%s\n", dev); + } + } + } +} + +/* + * displays logical paths to a + * device to stdout. + * + * RETURNS: + * 0 if OK + * non-zero otherwise + */ +static int +h_display_logical_nodes(struct dlist *dlist) +{ +char link[MAXPATHLEN], path[MAXPATHLEN]; +char lname[MAXPATHLEN], *d1; +DIR *dir; +struct dirent *dirent; +struct dlist *dl; + + + if ((dir = opendir(DEV_DSK_DIR)) == NULL) { + return (L_READ_DEV_DIR_ERROR); + } + (void) fprintf(stdout, + MSGSTR(5569, + " Logical Nodes under /dev/dsk and /dev/rdsk :\n")); + + while ((dirent = readdir(dir)) != (struct dirent *)NULL) { + if (strcmp(dirent->d_name, ".") == 0 || + strcmp(dirent->d_name, "..") == 0) { + continue; + } + (void) sprintf(lname, DEV_DSK_DIR"/%s", dirent->d_name); + if (readlink((const char *)lname, (char *)link, + (size_t)MAXPATHLEN) <= 0) { + (void) print_errString(L_SYMLINK_ERROR, lname); + continue; + } + for (dl = dlist; dl; dl = dl->next) { + (void) strcpy(path, dl->dev_path); + d1 = strrchr(path, ':'); + if (d1) + *d1 = '\0'; + if (strstr(link, path)) { + (void) fprintf(stdout, + "\t%s\n", + dirent->d_name); + } + } + } + + closedir(dir); + return (0); +} + + + +/* + * prints a list of devices which + * will be inserted or removed + * to the stdout and asks for + * the user's confirmation. + * + * RETURNS: + * 0 if OK + * non-zero otherwise + */ +static int +h_print_list_warn(Hotplug_Devlist *disk_list_head, int todo, int enc_type) +{ +int i; +char choice[2]; +struct dlist *dl_ses, *dl_multi; +Hotplug_Devlist *disk_list = disk_list_head; + + (void) fprintf(stdout, + MSGSTR(5570, "The list of devices which will be ")); + switch (todo) { + case INSERT_DEVICE: + (void) fprintf(stdout, + MSGSTR(5571, "inserted is:\n")); + break; + case REMOVE_DEVICE: + (void) fprintf(stdout, + MSGSTR(5572, "removed is:\n")); + break; + } + + for (i = 1; disk_list; i++, disk_list = disk_list->next) { + if ((disk_list->dev_type == DTYPE_DIRECT) && + (disk_list->dev_location == SENA)) { + if (disk_list->f_flag != NULL) { + if (enc_type == DAK_ENC_TYPE) { + (void) fprintf(stdout, MSGSTR(5665, + " %d: Box Name: \"%s\" slot %d\n"), + i, disk_list->box_name, disk_list->slot); + } else { + (void) fprintf(stdout, MSGSTR(137, + " %d: Box Name: \"%s\" front slot %d\n"), + i, disk_list->box_name, disk_list->slot); + } + } else { + if (enc_type == DAK_ENC_TYPE) { + (void) fprintf(stdout, MSGSTR(5665, + " %d: Box Name: \"%s\" slot %d\n"), + i, disk_list->box_name, + disk_list->slot + (MAX_DRIVES_DAK/2)); + } else { + (void) fprintf(stdout, MSGSTR(136, + " %d: Box Name: \"%s\" rear slot %d\n"), + i, disk_list->box_name, disk_list->slot); + } + } + } else if (((disk_list->dev_type == DTYPE_DIRECT) || + (disk_list->dev_type == DTYPE_SEQUENTIAL)) && + (disk_list->dev_location == NON_SENA)) { + (void) fprintf(stdout, MSGSTR(5573, + " %d: Device name: %s\n"), + i, disk_list->dev_name); + } else if (disk_list->dev_type == DTYPE_ESI) { + (void) fprintf(stdout, MSGSTR(5574, + " %d: Box name: %s\n"), + i, disk_list->box_name); + } + if (getenv("_LUX_H_DEBUG") != NULL) { + if (disk_list->dev_location == SENA) { + (void) fprintf(stdout, + " Select ID:\t0x%x\n", + disk_list->tid); + if (disk_list->dev_type != DTYPE_ESI) { + if (enc_type == DAK_ENC_TYPE) { + (void) fprintf(stdout, + " Location: \tSlot %d \n", + disk_list->f_flag + ? disk_list->slot + : disk_list->slot + +MAX_DRIVES_DAK/2); + } else { + (void) fprintf(stdout, + " Location: \tSlot %d %s \n", + disk_list->slot, disk_list->f_flag + ? "front" : "rear"); + } + } + } + } + if (todo == REMOVE_DEVICE) { + (void) fprintf(stdout, " "); + (void) fprintf(stdout, MSGSTR(90, "Node WWN:")); + (void) fprintf(stdout, " %s\n", + disk_list->node_wwn_s); + + (void) fprintf(stdout, " "); + (void) fprintf(stdout, MSGSTR(35, "Device Type:")); + if (disk_list->dev_type == DTYPE_ESI) { + (void) fprintf(stdout, MSGSTR(5581, + " SENA (%s)\n"), + dtype[disk_list->dev_type]); + } else { + (void) fprintf(stdout, "%s\n", + dtype[disk_list->dev_type]); + } + + if (disk_list->dev_type == DTYPE_ESI) { + dl_ses = disk_list->seslist; + (void) fprintf(stdout, MSGSTR(5575, + " SES Paths:\n")); + while (dl_ses) { + (void) fprintf(stdout, MSGSTR(5576, + " %s\n"), dl_ses->dev_path); + dl_ses = dl_ses->next; + } + } else { + dl_multi = disk_list->dlhead; + (void) fprintf(stdout, MSGSTR(5577, + " Device Paths:\n")); + while (dl_multi) { + (void) fprintf(stdout, MSGSTR(5578, + " %s\n"), + dl_multi->logical_path); + dl_multi = dl_multi->next; + } + } + } + (void) fprintf(stdout, "\n"); + } + (void) fprintf(stdout, MSGSTR(5579, + "\nPlease verify the above list of devices" + " and\nthen enter 'c' or <CR> to Continue" + " or 'q' to Quit. [Default: c]: ")); + + /* Get the user input and continue accordingly. */ + for (;;) { + (void) gets(choice); + if (choice[0] == 'c' || choice[0] == 'C' || + choice[0] == 'q' || choice[0] == 'Q' || + choice[0] == '\0') { + break; + } + (void) fprintf(stdout, MSGSTR(5580, + " Enter an appropriate option [c,<CR>,q]: ")); + } + + if (choice[0] == 'q' || choice[0] == 'Q') { + return (-1); + } + return (0); +} + + +static int +h_find_new_device_link(char *device_dir, timestruc_t lastmtim) +{ +struct stat dsk_stat; +char lname[MAXPATHLEN], link[MAXPATHLEN]; +char *link_ptr; +DIR *dir; +struct dirent *dirent; +int found_newlink = 0; + + + if ((dir = opendir(device_dir)) == NULL) { + if (errno == ENOENT) { + return (0); + } else { + return (L_READ_DEV_DIR_ERROR); + } + } + + while ((dirent = readdir(dir)) != (struct dirent *)NULL) { + if (strcmp(dirent->d_name, ".") == 0 || + strcmp(dirent->d_name, "..") == 0) { + continue; + } + (void) sprintf(lname, "%s/%s", device_dir, dirent->d_name); + if (lstat(lname, &dsk_stat) < 0) { + (void) print_errString(L_LSTAT_ES_DIR_ERROR, + lname); + continue; + } + if (readlink((const char *)lname, (char *)link, + (size_t)MAXPATHLEN) <= 0) { + (void) print_errString(L_SYMLINK_ERROR, lname); + continue; + } + + /* + * "link" can be a relative pathname. But, since + * g_get_path_type() only accepts absolute paths, we + * will skip to the part where "/devices/" begins and pass a + * pointer from there. Since "link" is got from readlink(), + * it is unlikely that it will not have /devices string, but + * we will check for it anyways. + */ + if (!(link_ptr = strstr(link, "/devices/"))) + continue; + if (!g_get_path_type(link_ptr)) { + continue; + } + if (NEWER(dsk_stat.st_ctim, lastmtim)) { + found_newlink++; + if (found_newlink == 1) { + if (! (strcmp(device_dir, DEV_DSK_DIR))) { + (void) fprintf(stdout, MSGSTR(5561, + " New Logical Nodes under " + "/dev/dsk and /dev/rdsk :\n")); + } else { /* device_dir is /dev/rmt */ + (void) fprintf(stdout, "New Logical " + "Node under /dev/rmt:\n"); + } + } + (void) fprintf(stdout, "\t%s\n", dirent->d_name); + } + } + closedir(dir); + return (found_newlink); +} + + +/* + * prints the device state. + * + * RETURNS: + * None. + */ +void +print_dev_state(char *devname, int state) +{ + (void) printf("\t%s: ", devname); + if (state & DEVICE_ONLINE) { + (void) printf(MSGSTR(3000, "Online")); + if (state & DEVICE_BUSY) { + (void) printf(" "); + (void) printf(MSGSTR(37, "Busy")); + } + if (state & DEVICE_DOWN) { + (void) printf(" "); + (void) printf(MSGSTR(118, "Down")); + } + } else { + if (state & DEVICE_OFFLINE) { + (void) printf(MSGSTR(3001, "Offline")); + if (state & DEVICE_DOWN) { + (void) printf(" "); + (void) printf(MSGSTR(118, "Down")); + } + } + } + (void) printf("\n"); +} + + +/* + * prints the bus state. + * + * RETURNS: + * None. + */ +void +print_bus_state(char *devname, int state) +{ + (void) printf("\t%s: ", devname); + if (state == BUS_QUIESCED) { + (void) printf(MSGSTR(3002, "Quiesced")); + } else if (state == BUS_ACTIVE) { + (void) printf(MSGSTR(39, "Active")); + } else if (state == BUS_SHUTDOWN) { + (void) printf(MSGSTR(3003, "Shutdown")); + } + (void) printf("\n"); +} diff --git a/usr/src/cmd/luxadm/ibfirmware b/usr/src/cmd/luxadm/ibfirmware Binary files differnew file mode 100644 index 0000000000..d3e17f4f42 --- /dev/null +++ b/usr/src/cmd/luxadm/ibfirmware diff --git a/usr/src/cmd/luxadm/lux_util.c b/usr/src/cmd/luxadm/lux_util.c new file mode 100644 index 0000000000..41e8ce7aa2 --- /dev/null +++ b/usr/src/cmd/luxadm/lux_util.c @@ -0,0 +1,1594 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/param.h> +#include <sys/types.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <string.h> +#include <strings.h> +#include <ctype.h> +#include <errno.h> +#include <assert.h> +#include <sys/scsi/impl/uscsi.h> +#include <sys/scsi/generic/commands.h> +#include <sys/scsi/impl/commands.h> +#include <sys/scsi/generic/sense.h> +#include <sys/scsi/generic/mode.h> +#include <sys/scsi/generic/status.h> +#include <sys/scsi/generic/inquiry.h> +#include <sys/scsi/adapters/scsi_vhci.h> +#include <sys/byteorder.h> +#include "common.h" +#include "errorcodes.h" + +#define MAX_MODE_SENSE_LEN 0xffff +#define MAXLEN 1000 + +#define RETRY_PATHLIST 1 +#define BYTES_PER_LINE 16 +#define SCMD_UNKNOWN 0xff + +#define SCSI_VHCI "/devices/scsi_vhci/" +#define SLASH "/" +#define DEV_PREFIX "/devices/" +#define DEV_PREFIX_STRLEN strlen(DEV_PREFIX) +#define DEVICES_DIR "/devices" + +extern char *dtype[]; /* from adm.c */ +extern int rand_r(unsigned int *); + +static int cleanup_dotdot_path(char *path); +static int wait_random_time(void); +static char *scsi_find_command_name(int cmd); +static void scsi_printerr(struct uscsi_cmd *ucmd, + struct scsi_extended_sense *rq, int rqlen, + char msg_string[], char *err_string); +static void string_dump(char *hdr, uchar_t *src, int nbytes, int format, + char msg_string[]); +static int issue_uscsi_cmd(int file, struct uscsi_cmd *command, int flag); + + +static int +wait_random_time(void) +{ +time_t timeval; +struct tm *tmbuf = NULL; +struct timeval tval; +unsigned int seed; +int random; +pid_t pid; + + + /* + * Get the system time and use "system seconds" + * as 'seed' to generate a random number. Then, + * wait between 1/10 - 1/2 seconds before retry. + * Get the current process id and ex-or it with + * the seed so that the random number is always + * different even in case of multiple processes + * generate a random number at the same time. + */ + if ((timeval = time(NULL)) == -1) { + return (errno); + } + if ((tmbuf = localtime(&timeval)) == NULL) { + return (-1); /* L_LOCALTIME_ERROR */ + } + + pid = getpid(); + + /* get a random number. */ + seed = (unsigned int) tmbuf->tm_sec; + seed ^= pid; + random = rand_r(&seed); + + + random = ((random % 500) + 100) * MILLISEC; + tval.tv_sec = random / MICROSEC; + tval.tv_usec = random % MICROSEC; + + if (select(0, NULL, NULL, NULL, &tval) == -1) { + return (-1); /* L_SELECT_ERROR */ + } + return (0); +} + +/* + * Special string dump for error message + */ +static void +string_dump(char *hdr, uchar_t *src, int nbytes, int format, char msg_string[]) +{ + int i; + int n; + char *p; + char s[256]; + + assert(format == HEX_ONLY || format == HEX_ASCII); + + (void) strcpy(s, hdr); + for (p = s; *p; p++) { + *p = ' '; + } + + p = hdr; + while (nbytes > 0) { + (void) sprintf(&msg_string[strlen(msg_string)], + "%s", p); + p = s; + n = MIN(nbytes, BYTES_PER_LINE); + for (i = 0; i < n; i++) { + (void) sprintf(&msg_string[strlen(msg_string)], + "%02x ", + src[i] & 0xff); + } + if (format == HEX_ASCII) { + for (i = BYTES_PER_LINE-n; i > 0; i--) { + (void) sprintf(&msg_string[strlen(msg_string)], + " "); + } + (void) sprintf(&msg_string[strlen(msg_string)], + " "); + for (i = 0; i < n; i++) { + (void) sprintf(&msg_string[strlen(msg_string)], + "%c", + isprint(src[i]) ? src[i] : '.'); + } + } + (void) sprintf(&msg_string[strlen(msg_string)], "\n"); + nbytes -= n; + src += n; + } +} +/* + * Return a pointer to a string telling us the name of the command. + */ +static char * +scsi_find_command_name(int cmd) +{ +/* + * Names of commands. Must have SCMD_UNKNOWN at end of list. + */ +struct scsi_command_name { + int command; + char *name; +} scsi_command_names[29]; + +register struct scsi_command_name *c; + + scsi_command_names[0].command = SCMD_TEST_UNIT_READY; + scsi_command_names[0].name = MSGSTR(61, "Test Unit Ready"); + + scsi_command_names[1].command = SCMD_FORMAT; + scsi_command_names[1].name = MSGSTR(110, "Format"); + + scsi_command_names[2].command = SCMD_REASSIGN_BLOCK; + scsi_command_names[2].name = MSGSTR(77, "Reassign Block"); + + scsi_command_names[3].command = SCMD_READ; + scsi_command_names[3].name = MSGSTR(27, "Read"); + + scsi_command_names[4].command = SCMD_WRITE; + scsi_command_names[4].name = MSGSTR(54, "Write"); + + scsi_command_names[5].command = SCMD_READ_G1; + scsi_command_names[5].name = MSGSTR(79, "Read(10 Byte)"); + + scsi_command_names[6].command = SCMD_WRITE_G1; + scsi_command_names[6].name = MSGSTR(51, "Write(10 Byte)"); + + scsi_command_names[7].command = SCMD_MODE_SELECT; + scsi_command_names[7].name = MSGSTR(97, "Mode Select"); + + scsi_command_names[8].command = SCMD_MODE_SENSE; + scsi_command_names[8].name = MSGSTR(95, "Mode Sense"); + + scsi_command_names[9].command = SCMD_REASSIGN_BLOCK; + scsi_command_names[9].name = MSGSTR(77, "Reassign Block"); + + scsi_command_names[10].command = SCMD_REQUEST_SENSE; + scsi_command_names[10].name = MSGSTR(74, "Request Sense"); + + scsi_command_names[11].command = SCMD_READ_DEFECT_LIST; + scsi_command_names[11].name = MSGSTR(80, "Read Defect List"); + + scsi_command_names[12].command = SCMD_INQUIRY; + scsi_command_names[12].name = MSGSTR(102, "Inquiry"); + + scsi_command_names[13].command = SCMD_WRITE_BUFFER; + scsi_command_names[13].name = MSGSTR(53, "Write Buffer"); + + scsi_command_names[14].command = SCMD_READ_BUFFER; + scsi_command_names[14].name = MSGSTR(82, "Read Buffer"); + + scsi_command_names[15].command = SCMD_START_STOP; + scsi_command_names[15].name = MSGSTR(67, "Start/Stop"); + + scsi_command_names[16].command = SCMD_RESERVE; + scsi_command_names[16].name = MSGSTR(72, "Reserve"); + + scsi_command_names[17].command = SCMD_RELEASE; + scsi_command_names[17].name = MSGSTR(75, "Release"); + + scsi_command_names[18].command = SCMD_MODE_SENSE_G1; + scsi_command_names[18].name = MSGSTR(94, "Mode Sense(10 Byte)"); + + scsi_command_names[19].command = SCMD_MODE_SELECT_G1; + scsi_command_names[19].name = MSGSTR(96, "Mode Select(10 Byte)"); + + scsi_command_names[20].command = SCMD_READ_CAPACITY; + scsi_command_names[20].name = MSGSTR(81, "Read Capacity"); + + scsi_command_names[21].command = SCMD_SYNC_CACHE; + scsi_command_names[21].name = MSGSTR(64, "Synchronize Cache"); + + scsi_command_names[22].command = SCMD_READ_DEFECT_LIST; + scsi_command_names[22].name = MSGSTR(80, "Read Defect List"); + + scsi_command_names[23].command = SCMD_GDIAG; + scsi_command_names[23].name = MSGSTR(108, "Get Diagnostic"); + + scsi_command_names[24].command = SCMD_SDIAG; + scsi_command_names[24].name = MSGSTR(69, "Set Diagnostic"); + + scsi_command_names[25].command = SCMD_PERS_RESERV_IN; + scsi_command_names[25].name = MSGSTR(10500, "Persistent Reserve In"); + + scsi_command_names[26].command = SCMD_PERS_RESERV_OUT; + scsi_command_names[26].name = MSGSTR(10501, "Persistent Reserve out"); + + scsi_command_names[27].command = SCMD_LOG_SENSE; + scsi_command_names[27].name = MSGSTR(10502, "Log Sense"); + + scsi_command_names[28].command = SCMD_UNKNOWN; + scsi_command_names[28].name = MSGSTR(25, "Unknown"); + + + for (c = scsi_command_names; c->command != SCMD_UNKNOWN; c++) + if (c->command == cmd) + break; + return (c->name); +} + + +/* + * Function to create error message containing + * scsi request sense information + */ + +static void +scsi_printerr(struct uscsi_cmd *ucmd, struct scsi_extended_sense *rq, + int rqlen, char msg_string[], char *err_string) +{ + int blkno; + + switch (rq->es_key) { + case KEY_NO_SENSE: + (void) sprintf(msg_string, MSGSTR(91, "No sense error")); + break; + case KEY_RECOVERABLE_ERROR: + (void) sprintf(msg_string, MSGSTR(76, "Recoverable error")); + break; + case KEY_NOT_READY: + (void) sprintf(msg_string, + MSGSTR(10503, + "Device Not ready." + " Error: Random Retry Failed: %s\n."), + err_string); + break; + case KEY_MEDIUM_ERROR: + (void) sprintf(msg_string, MSGSTR(99, "Medium error")); + break; + case KEY_HARDWARE_ERROR: + (void) sprintf(msg_string, MSGSTR(106, "Hardware error")); + break; + case KEY_ILLEGAL_REQUEST: + (void) sprintf(msg_string, MSGSTR(103, "Illegal request")); + break; + case KEY_UNIT_ATTENTION: + (void) sprintf(msg_string, + MSGSTR(10504, + "Unit attention." + "Error: Random Retry Failed.\n")); + break; + case KEY_WRITE_PROTECT: + (void) sprintf(msg_string, MSGSTR(52, "Write protect error")); + break; + case KEY_BLANK_CHECK: + (void) sprintf(msg_string, MSGSTR(131, "Blank check error")); + break; + case KEY_VENDOR_UNIQUE: + (void) sprintf(msg_string, MSGSTR(58, "Vendor unique error")); + break; + case KEY_COPY_ABORTED: + (void) sprintf(msg_string, MSGSTR(123, "Copy aborted error")); + break; + case KEY_ABORTED_COMMAND: + (void) sprintf(msg_string, + MSGSTR(10505, + "Aborted command." + " Error: Random Retry Failed.\n")); + break; + case KEY_EQUAL: + (void) sprintf(msg_string, MSGSTR(117, "Equal error")); + break; + case KEY_VOLUME_OVERFLOW: + (void) sprintf(msg_string, MSGSTR(57, "Volume overflow")); + break; + case KEY_MISCOMPARE: + (void) sprintf(msg_string, MSGSTR(98, "Miscompare error")); + break; + case KEY_RESERVED: + (void) sprintf(msg_string, MSGSTR(10506, + "Reserved value found")); + break; + default: + (void) sprintf(msg_string, MSGSTR(59, "Unknown error")); + break; + } + + (void) sprintf(&msg_string[strlen(msg_string)], + MSGSTR(10507, " during: %s"), + scsi_find_command_name(ucmd->uscsi_cdb[0])); + + if (rq->es_valid) { + blkno = (rq->es_info_1 << 24) | (rq->es_info_2 << 16) | + (rq->es_info_3 << 8) | rq->es_info_4; + (void) sprintf(&msg_string[strlen(msg_string)], + MSGSTR(49, ": block %d (0x%x)"), blkno, blkno); + } + + (void) sprintf(&msg_string[strlen(msg_string)], "\n"); + + if (rq->es_add_len >= 6) { + (void) sprintf(&msg_string[strlen(msg_string)], + MSGSTR(132, " Additional sense: 0x%x ASC Qualifier: 0x%x\n"), + rq->es_add_code, rq->es_qual_code); + /* + * rq->es_add_info[ADD_SENSE_CODE], + * rq->es_add_info[ADD_SENSE_QUAL_CODE]); + */ + } + if (rq->es_key == KEY_ILLEGAL_REQUEST) { + string_dump(MSGSTR(47, " cmd: "), (uchar_t *)ucmd, + sizeof (struct uscsi_cmd), HEX_ONLY, msg_string); + string_dump(MSGSTR(48, " cdb: "), + (uchar_t *)ucmd->uscsi_cdb, + ucmd->uscsi_cdblen, HEX_ONLY, msg_string); + } + string_dump(MSGSTR(43, " sense: "), + (uchar_t *)rq, 8 + rq->es_add_len, HEX_ONLY, + msg_string); + rqlen = rqlen; /* not used */ +} + + +/* + * Execute a command and determine the result. + */ +static int +issue_uscsi_cmd(int file, struct uscsi_cmd *command, int flag) +{ +struct scsi_extended_sense *rqbuf; +int status, i, retry_cnt = 0, err; +char errorMsg[MAXLEN]; + + /* + * Set function flags for driver. + * + * Set Automatic request sense enable + * + */ + command->uscsi_flags = USCSI_RQENABLE; + command->uscsi_flags |= flag; + + /* intialize error message array */ + errorMsg[0] = '\0'; + + /* print command for debug */ + if (getenv("_LUX_S_DEBUG") != NULL) { + if ((command->uscsi_cdb == NULL) || + (flag & USCSI_RESET) || + (flag & USCSI_RESET_ALL)) { + if (flag & USCSI_RESET) { + (void) printf(" Issuing a SCSI Reset.\n"); + } + if (flag & USCSI_RESET_ALL) { + (void) printf(" Issuing a SCSI Reset All.\n"); + } + + } else { + (void) printf(" Issuing the following " + "SCSI command: %s\n", + scsi_find_command_name(command->uscsi_cdb[0])); + (void) printf(" fd=0x%x cdb=", file); + for (i = 0; i < (int)command->uscsi_cdblen; i++) { + (void) printf("%x ", *(command->uscsi_cdb + i)); + } + (void) printf("\n\tlen=0x%x bufaddr=0x%x buflen=0x%x" + " flags=0x%x\n", + command->uscsi_cdblen, + command->uscsi_bufaddr, + command->uscsi_buflen, command->uscsi_flags); + + if ((command->uscsi_buflen > 0) && + ((flag & USCSI_READ) == 0)) { + (void) dump_hex_data(" Buffer data: ", + (uchar_t *)command->uscsi_bufaddr, + MIN(command->uscsi_buflen, 512), HEX_ASCII); + } + } + (void) fflush(stdout); + } + + + /* + * Default command timeout in case command left it 0 + */ + if (command->uscsi_timeout == 0) { + command->uscsi_timeout = 60; + } + /* Issue command - finally */ + +retry: + status = ioctl(file, USCSICMD, command); + if (status == 0 && command->uscsi_status == 0) { + if (getenv("_LUX_S_DEBUG") != NULL) { + if ((command->uscsi_buflen > 0) && + (flag & USCSI_READ)) { + (void) dump_hex_data("\tData read:", + (uchar_t *)command->uscsi_bufaddr, + MIN(command->uscsi_buflen, 512), HEX_ASCII); + } + } + return (status); + } + if ((status != 0) && (command->uscsi_status == 0)) { + if ((getenv("_LUX_S_DEBUG") != NULL) || + (getenv("_LUX_ER_DEBUG") != NULL)) { + (void) printf("Unexpected USCSICMD ioctl error: %s\n", + strerror(errno)); + } + return (status); + } + + /* + * Just a SCSI error, create error message + * Retry once for Unit Attention, + * Not Ready, and Aborted Command + */ + if ((command->uscsi_rqbuf != NULL) && + (((char)command->uscsi_rqlen - (char)command->uscsi_rqresid) > 0)) { + + rqbuf = (struct scsi_extended_sense *)command->uscsi_rqbuf; + + switch (rqbuf->es_key) { + case KEY_NOT_READY: + if (retry_cnt++ < 1) { + ER_DPRINTF("Note: Device Not Ready." + " Retrying...\n"); + + if ((err = wait_random_time()) == 0) { + goto retry; + } else { + return (err); + } + } + break; + + case KEY_UNIT_ATTENTION: + if (retry_cnt++ < 1) { + ER_DPRINTF(" cmd():" + " UNIT_ATTENTION: Retrying...\n"); + + goto retry; + } + break; + + case KEY_ABORTED_COMMAND: + if (retry_cnt++ < 1) { + ER_DPRINTF("Note: Command is aborted." + " Retrying...\n"); + + goto retry; + } + break; + } + if ((getenv("_LUX_S_DEBUG") != NULL) || + (getenv("_LUX_ER_DEBUG") != NULL)) { + scsi_printerr(command, + (struct scsi_extended_sense *)command->uscsi_rqbuf, + (command->uscsi_rqlen - command->uscsi_rqresid), + errorMsg, strerror(errno)); + } + + } else { + + /* + * Retry 5 times in case of BUSY, and only + * once for Reservation-conflict, Command + * Termination and Queue Full. Wait for + * random amount of time (between 1/10 - 1/2 secs.) + * between each retry. This random wait is to avoid + * the multiple threads being executed at the same time + * and also the constraint in Photon IB, where the + * command queue has a depth of one command. + */ + switch ((uchar_t)command->uscsi_status & STATUS_MASK) { + case STATUS_BUSY: + if (retry_cnt++ < 5) { + if ((err = wait_random_time()) == 0) { + R_DPRINTF(" cmd(): No. of retries %d." + " STATUS_BUSY: Retrying...\n", + retry_cnt); + goto retry; + + } else { + return (err); + } + } + break; + + case STATUS_RESERVATION_CONFLICT: + if (retry_cnt++ < 1) { + if ((err = wait_random_time()) == 0) { + R_DPRINTF(" cmd():" + " RESERVATION_CONFLICT:" + " Retrying...\n"); + goto retry; + + } else { + return (err); + } + } + break; + + case STATUS_TERMINATED: + if (retry_cnt++ < 1) { + R_DPRINTF("Note: Command Terminated." + " Retrying...\n"); + + if ((err = wait_random_time()) == 0) { + goto retry; + } else { + return (err); + } + } + break; + + case STATUS_QFULL: + if (retry_cnt++ < 1) { + R_DPRINTF("Note: Command Queue is full." + " Retrying...\n"); + + if ((err = wait_random_time()) == 0) { + goto retry; + } else { + return (err); + } + } + break; + } + + } + if (((getenv("_LUX_S_DEBUG") != NULL) || + (getenv("_LUX_ER_DEBUG") != NULL)) && + (errorMsg[0] != '\0')) { + (void) fprintf(stdout, " %s\n", errorMsg); + } + return (L_SCSI_ERROR | command->uscsi_status); +} + +/* + * MODE SENSE USCSI command + * + * + * pc = page control field + * page_code = Pages to return + */ +int +scsi_mode_sense_cmd(int fd, + uchar_t *buf_ptr, + int buf_len, + uchar_t pc, + uchar_t page_code) +{ +struct uscsi_cmd ucmd; +/* 10 byte Mode Select cmd */ +union scsi_cdb cdb = {SCMD_MODE_SENSE_G1, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +struct scsi_extended_sense sense; +int status; +static int uscsi_count; + + if ((fd < 0) || (buf_ptr == NULL) || (buf_len < 0)) { + return (-1); /* L_INVALID_ARG */ + } + + (void) memset(buf_ptr, 0, buf_len); + (void) memset((char *)&ucmd, 0, sizeof (ucmd)); + /* Just for me - a sanity check */ + if ((page_code > MODEPAGE_ALLPAGES) || (pc > 3) || + (buf_len > MAX_MODE_SENSE_LEN)) { + return (-1); /* L_ILLEGAL_MODE_SENSE_PAGE */ + } + cdb.g1_addr3 = (pc << 6) + page_code; + cdb.g1_count1 = buf_len>>8; + cdb.g1_count0 = buf_len & 0xff; + ucmd.uscsi_cdb = (caddr_t)&cdb; + ucmd.uscsi_cdblen = CDB_GROUP1; + ucmd.uscsi_bufaddr = (caddr_t)buf_ptr; + ucmd.uscsi_buflen = buf_len; + ucmd.uscsi_rqbuf = (caddr_t)&sense; + ucmd.uscsi_rqlen = sizeof (struct scsi_extended_sense); + ucmd.uscsi_timeout = 120; + + status = issue_uscsi_cmd(fd, &ucmd, USCSI_READ); + /* Bytes actually transfered */ + if (status == 0) { + uscsi_count = buf_len - ucmd.uscsi_resid; + S_DPRINTF(" Number of bytes read on " + "Mode Sense 0x%x\n", uscsi_count); + if (getenv("_LUX_D_DEBUG") != NULL) { + (void) dump_hex_data(" Mode Sense data: ", buf_ptr, + uscsi_count, HEX_ASCII); + } + } + return (status); +} + +int +scsi_release(char *path) +{ +struct uscsi_cmd ucmd; +union scsi_cdb cdb = {SCMD_RELEASE, 0, 0, 0, 0, 0}; +struct scsi_extended_sense sense; +int fd, status; + + P_DPRINTF(" scsi_release: Release: Path %s\n", path); + if ((fd = open(path, O_NDELAY | O_RDONLY)) == -1) + return (1); + + (void) memset((char *)&ucmd, 0, sizeof (ucmd)); + + ucmd.uscsi_cdb = (caddr_t)&cdb; + ucmd.uscsi_cdblen = CDB_GROUP0; + ucmd.uscsi_bufaddr = NULL; + ucmd.uscsi_buflen = 0; + ucmd.uscsi_rqbuf = (caddr_t)&sense; + ucmd.uscsi_rqlen = sizeof (struct scsi_extended_sense); + ucmd.uscsi_timeout = 60; + status = (issue_uscsi_cmd(fd, &ucmd, 0)); + + (void) close(fd); + return (status); +} + +int +scsi_reserve(char *path) +{ +struct uscsi_cmd ucmd; +union scsi_cdb cdb = {SCMD_RESERVE, 0, 0, 0, 0, 0}; +struct scsi_extended_sense sense; +int fd, status; + + P_DPRINTF(" scsi_reserve: Reserve: Path %s\n", path); + if ((fd = open(path, O_NDELAY | O_RDONLY)) == -1) + return (1); + + (void) memset((char *)&ucmd, 0, sizeof (ucmd)); + + ucmd.uscsi_cdb = (caddr_t)&cdb; + ucmd.uscsi_cdblen = CDB_GROUP0; + ucmd.uscsi_bufaddr = NULL; + ucmd.uscsi_buflen = 0; + ucmd.uscsi_rqbuf = (caddr_t)&sense; + ucmd.uscsi_rqlen = sizeof (struct scsi_extended_sense); + ucmd.uscsi_timeout = 60; + status = (issue_uscsi_cmd(fd, &ucmd, 0)); + + (void) close(fd); + return (status); +} + +/* + * Print out fabric dev dtype + */ +void +print_fabric_dtype_prop(uchar_t *hba_port_wwn, uchar_t *port_wwn, + uchar_t dtype_prop) +{ + if ((dtype_prop & DTYPE_MASK) < 0x10) { + (void) fprintf(stdout, " 0x%-2x (%s)\n", + (dtype_prop & DTYPE_MASK), dtype[(dtype_prop & DTYPE_MASK)]); + } else if ((dtype_prop & DTYPE_MASK) < 0x1f) { + (void) fprintf(stdout, + MSGSTR(2096, " 0x%-2x (Reserved)\n"), + (dtype_prop & DTYPE_MASK)); + } else { + /* Check to see if this is the HBA */ + if (wwnConversion(hba_port_wwn) != wwnConversion(port_wwn)) { + (void) fprintf(stdout, MSGSTR(2097, + " 0x%-2x (Unknown Type)\n"), (dtype_prop & DTYPE_MASK)); + } else { + /* MATCH */ + (void) fprintf(stdout, MSGSTR(2241, + " 0x%-2x (Unknown Type,Host Bus Adapter)\n"), + (dtype_prop & DTYPE_MASK)); + } + } +} + + +void +print_inq_data(char *arg_path, char *path, L_inquiry inq, uchar_t *serial, + size_t serial_len) +{ +char **p; +uchar_t *v_parm; +int scsi_3, length; +char byte_number[MAXNAMELEN]; +static char *scsi_inquiry_labels_2[21]; +static char *scsi_inquiry_labels_3[22]; +static char *ansi_version[4]; + /* + * Intialize scsi_inquiry_labels_2 with i18n strings + */ + scsi_inquiry_labels_2[0] = MSGSTR(138, "Vendor: "); + scsi_inquiry_labels_2[1] = MSGSTR(149, "Product: "); + scsi_inquiry_labels_2[2] = MSGSTR(139, "Revision: "); + scsi_inquiry_labels_2[3] = MSGSTR(143, "Firmware Revision "); + scsi_inquiry_labels_2[4] = MSGSTR(144, "Serial Number "); + scsi_inquiry_labels_2[5] = MSGSTR(140, "Device type: "); + scsi_inquiry_labels_2[6] = MSGSTR(145, "Removable media: "); + scsi_inquiry_labels_2[7] = MSGSTR(146, "ISO version: "); + scsi_inquiry_labels_2[8] = MSGSTR(147, "ECMA version: "); + scsi_inquiry_labels_2[9] = MSGSTR(148, "ANSI version: "); + scsi_inquiry_labels_2[10] = + MSGSTR(2168, "Async event notification: "); + scsi_inquiry_labels_2[11] = + MSGSTR(2169, "Terminate i/o process msg: "); + scsi_inquiry_labels_2[12] = MSGSTR(150, "Response data format: "); + scsi_inquiry_labels_2[13] = MSGSTR(151, "Additional length: "); + scsi_inquiry_labels_2[14] = MSGSTR(152, "Relative addressing: "); + scsi_inquiry_labels_2[15] = + MSGSTR(2170, "32 bit transfers: "); + scsi_inquiry_labels_2[16] = + MSGSTR(2171, "16 bit transfers: "); + scsi_inquiry_labels_2[17] = + MSGSTR(2172, "Synchronous transfers: "); + scsi_inquiry_labels_2[18] = MSGSTR(153, "Linked commands: "); + scsi_inquiry_labels_2[19] = MSGSTR(154, "Command queueing: "); + scsi_inquiry_labels_2[20] = + MSGSTR(2173, "Soft reset option: "); + + /* + * Intialize scsi_inquiry_labels_3 with i18n strings + */ + scsi_inquiry_labels_3[0] = MSGSTR(138, "Vendor: "); + scsi_inquiry_labels_3[1] = MSGSTR(149, "Product: "); + scsi_inquiry_labels_3[2] = MSGSTR(139, "Revision: "); + scsi_inquiry_labels_3[3] = MSGSTR(143, "Firmware Revision "); + scsi_inquiry_labels_3[4] = MSGSTR(144, "Serial Number "); + scsi_inquiry_labels_3[5] = MSGSTR(140, "Device type: "); + scsi_inquiry_labels_3[6] = MSGSTR(145, "Removable media: "); + scsi_inquiry_labels_3[7] = MSGSTR(2174, "Medium Changer Element: "); + scsi_inquiry_labels_3[8] = MSGSTR(146, "ISO version: "); + scsi_inquiry_labels_3[9] = MSGSTR(147, "ECMA version: "); + scsi_inquiry_labels_3[10] = MSGSTR(148, "ANSI version: "); + scsi_inquiry_labels_3[11] = + MSGSTR(2175, "Async event reporting: "); + scsi_inquiry_labels_3[12] = + MSGSTR(2176, "Terminate task: "); + scsi_inquiry_labels_3[13] = + MSGSTR(2177, "Normal ACA Supported: "); + scsi_inquiry_labels_3[14] = MSGSTR(150, "Response data format: "); + scsi_inquiry_labels_3[15] = MSGSTR(151, "Additional length: "); + scsi_inquiry_labels_3[16] = + MSGSTR(2178, "Cmd received on port: "); + scsi_inquiry_labels_3[17] = + MSGSTR(2179, "SIP Bits: "); + scsi_inquiry_labels_3[18] = MSGSTR(152, "Relative addressing: "); + scsi_inquiry_labels_3[19] = MSGSTR(153, "Linked commands: "); + scsi_inquiry_labels_3[20] = + MSGSTR(2180, "Transfer Disable: "); + scsi_inquiry_labels_3[21] = MSGSTR(154, "Command queueing: "); + + /* + * Intialize scsi_inquiry_labels_3 with i18n strings + */ + ansi_version[0] = MSGSTR(2181, + " (Device might or might not comply to an ANSI version)"); + ansi_version[1] = MSGSTR(2182, + " (This code is reserved for historical uses)"); + ansi_version[2] = MSGSTR(2183, + " (Device complies to ANSI X3.131-1994 (SCSI-2))"); + ansi_version[3] = MSGSTR(2184, + " (Device complies to SCSI-3)"); + + /* print inquiry information */ + + (void) fprintf(stdout, MSGSTR(2185, "\nINQUIRY:\n")); + /* + * arg_path is the path sent to luxadm by the user. if arg_path + * is a /devices path, then we do not need to print out physical + * path info + */ + if (strcmp(arg_path, path) != 0 && + strstr(arg_path, "/devices/") == NULL) { + (void) fprintf(stdout, " "); + (void) fprintf(stdout, + MSGSTR(5, "Physical Path:")); + (void) fprintf(stdout, "\n %s\n", path); + } + if (inq.inq_ansi < 3) { + p = scsi_inquiry_labels_2; + scsi_3 = 0; + } else { + p = scsi_inquiry_labels_3; + scsi_3 = 1; + } + if (inq.inq_len < 11) { + p += 1; + } else { + /* */ + (void) fprintf(stdout, "%s", *p++); + print_chars(inq.inq_vid, sizeof (inq.inq_vid), 0); + (void) fprintf(stdout, "\n"); + } + if (inq.inq_len < 27) { + p += 1; + } else { + (void) fprintf(stdout, "%s", *p++); + print_chars(inq.inq_pid, sizeof (inq.inq_pid), 0); + (void) fprintf(stdout, "\n"); + } + if (inq.inq_len < 31) { + p += 1; + } else { + (void) fprintf(stdout, "%s", *p++); + print_chars(inq.inq_revision, sizeof (inq.inq_revision), 0); + (void) fprintf(stdout, "\n"); + } + if (inq.inq_len < 39) { + p += 2; + } else { + /* + * If Pluto then print + * firmware rev & serial #. + */ + if (strstr((char *)inq.inq_pid, "SSA") != 0) { + (void) fprintf(stdout, "%s", *p++); + print_chars(inq.inq_firmware_rev, + sizeof (inq.inq_firmware_rev), 0); + (void) fprintf(stdout, "\n"); + (void) fprintf(stdout, "%s", *p++); + print_chars(serial, serial_len, 0); + (void) fprintf(stdout, "\n"); + } else if ((inq.inq_dtype & DTYPE_MASK) != DTYPE_ESI) { + p++; + (void) fprintf(stdout, "%s", *p++); + print_chars(serial, serial_len, 0); + (void) fprintf(stdout, "\n"); + } else { + /* if we miss both the above if's */ + p += 2; + } + } + + (void) fprintf(stdout, "%s0x%x (", + *p++, (inq.inq_dtype & DTYPE_MASK)); + if ((inq.inq_dtype & DTYPE_MASK) < 0x10) { + (void) fprintf(stdout, "%s", dtype[inq.inq_dtype & DTYPE_MASK]); + } else if ((inq.inq_dtype & DTYPE_MASK) < 0x1f) { + (void) fprintf(stdout, MSGSTR(71, "Reserved")); + } else { + (void) fprintf(stdout, MSGSTR(2186, "Unknown device")); + } + (void) fprintf(stdout, ")\n"); + + (void) fprintf(stdout, "%s", *p++); + if (inq.inq_rmb != NULL) { + (void) fprintf(stdout, MSGSTR(40, "yes")); + } else { + (void) fprintf(stdout, MSGSTR(45, "no")); + } + (void) fprintf(stdout, "\n"); + + if (scsi_3) { + (void) fprintf(stdout, "%s", *p++); + if (inq.inq_mchngr != NULL) { + (void) fprintf(stdout, MSGSTR(40, "yes")); + } else { + (void) fprintf(stdout, MSGSTR(45, "no")); + } + (void) fprintf(stdout, "\n"); + } + (void) fprintf(stdout, "%s%d\n", *p++, inq.inq_iso); + (void) fprintf(stdout, "%s%d\n", *p++, inq.inq_ecma); + + (void) fprintf(stdout, "%s%d", *p++, inq.inq_ansi); + if (inq.inq_ansi < 0x4) { + (void) fprintf(stdout, "%s", ansi_version[inq.inq_ansi]); + } else + (void) fprintf(stdout, MSGSTR(71, "Reserved")); + (void) fprintf(stdout, "\n"); + + if (inq.inq_aenc) { + (void) fprintf(stdout, "%s", *p++); + (void) fprintf(stdout, MSGSTR(40, "yes")); + (void) fprintf(stdout, "\n"); + } else { + p++; + } + if (scsi_3) { + (void) fprintf(stdout, "%s", *p++); + if (inq.inq_normaca != NULL) { + (void) fprintf(stdout, MSGSTR(40, "yes")); + } else { + (void) fprintf(stdout, MSGSTR(45, "no")); + } + (void) fprintf(stdout, "\n"); + } + if (inq.inq_trmiop) { + (void) fprintf(stdout, "%s", *p++); + (void) fprintf(stdout, MSGSTR(40, "yes")); + (void) fprintf(stdout, "\n"); + } else { + p++; + } + (void) fprintf(stdout, "%s%d\n", *p++, inq.inq_rdf); + (void) fprintf(stdout, "%s0x%x\n", *p++, inq.inq_len); + if (scsi_3) { + if (inq.inq_dual_p) { + if (inq.inq_port != NULL) { + (void) fprintf(stdout, MSGSTR(2187, + "%sa\n"), *p++); + } else { + (void) fprintf(stdout, MSGSTR(2188, + "%sb\n"), *p++); + } + } else { + p++; + } + } + if (scsi_3) { + if (inq.inq_SIP_1 || inq.ui.inq_3.inq_SIP_2 || + inq.ui.inq_3.inq_SIP_3) { + (void) fprintf(stdout, "%s%d, %d, %d\n", *p, + inq.inq_SIP_1, inq.ui.inq_3.inq_SIP_2, + inq.ui.inq_3.inq_SIP_3); + } + p++; + + } + + if (inq.ui.inq_2.inq_2_reladdr) { + (void) fprintf(stdout, "%s", *p); + (void) fprintf(stdout, MSGSTR(40, "yes")); + (void) fprintf(stdout, "\n"); + } + p++; + + if (!scsi_3) { + + if (inq.ui.inq_2.inq_wbus32) { + (void) fprintf(stdout, "%s", *p); + (void) fprintf(stdout, MSGSTR(40, "yes")); + (void) fprintf(stdout, "\n"); + } + p++; + + if (inq.ui.inq_2.inq_wbus16) { + (void) fprintf(stdout, "%s", *p); + (void) fprintf(stdout, MSGSTR(40, "yes")); + (void) fprintf(stdout, "\n"); + } + p++; + + if (inq.ui.inq_2.inq_sync) { + (void) fprintf(stdout, "%s", *p); + (void) fprintf(stdout, MSGSTR(40, "yes")); + (void) fprintf(stdout, "\n"); + } + p++; + + } + if (inq.ui.inq_2.inq_linked) { + (void) fprintf(stdout, "%s", *p); + (void) fprintf(stdout, MSGSTR(40, "yes")); + (void) fprintf(stdout, "\n"); + } + p++; + + if (scsi_3) { + (void) fprintf(stdout, "%s", *p++); + if (inq.ui.inq_3.inq_trandis != NULL) { + (void) fprintf(stdout, MSGSTR(40, "yes")); + } else { + (void) fprintf(stdout, MSGSTR(45, "no")); + } + (void) fprintf(stdout, "\n"); + } + + if (inq.ui.inq_2.inq_cmdque) { + (void) fprintf(stdout, "%s", *p); + (void) fprintf(stdout, MSGSTR(40, "yes")); + (void) fprintf(stdout, "\n"); + } + p++; + + if (!scsi_3) { + if (inq.ui.inq_2.inq_sftre) { + (void) fprintf(stdout, "%s", *p); + (void) fprintf(stdout, MSGSTR(40, "yes")); + (void) fprintf(stdout, "\n"); + } + p++; + + } + + /* + * Now print the vendor-specific data. + */ + v_parm = inq.inq_ven_specific_1; + if (inq.inq_len >= 32) { + length = inq.inq_len - 31; + if (strstr((char *)inq.inq_pid, "SSA") != 0) { + (void) fprintf(stdout, MSGSTR(2189, + "Number of Ports, Targets: %d,%d\n"), + inq.inq_ssa_ports, inq.inq_ssa_tgts); + v_parm += 20; + length -= 20; + } else if ((strstr((char *)inq.inq_pid, "SUN") != 0) || + (strncmp((char *)inq.inq_vid, "SUN ", + sizeof (inq.inq_vid)) == 0)) { + v_parm += 16; + length -= 16; + } + /* + * Do hex Dump of rest of the data. + */ + if (length > 0) { + (void) fprintf(stdout, + MSGSTR(2190, + " VENDOR-SPECIFIC PARAMETERS\n")); + (void) fprintf(stdout, + MSGSTR(2191, + "Byte# Hex Value " + " ASCII\n")); + (void) sprintf(byte_number, + "%d ", inq.inq_len - length + 5); + dump_hex_data(byte_number, v_parm, + MIN(length, inq.inq_res3 - v_parm), HEX_ASCII); + } + /* + * Skip reserved bytes 56-95. + */ + length -= (inq.inq_box_name - v_parm); + if (length > 0) { + (void) sprintf(byte_number, "%d ", + inq.inq_len - length + 5); + dump_hex_data(byte_number, inq.inq_box_name, + MIN(length, sizeof (inq.inq_box_name) + + sizeof (inq.inq_avu)), HEX_ASCII); + } + } + if (getenv("_LUX_D_DEBUG") != NULL) { + dump_hex_data("\nComplete Inquiry: ", + (uchar_t *)&inq, + MIN(inq.inq_len + 5, sizeof (inq)), HEX_ASCII); + } +} + +/* + * Internal routine to clean up ../'s in paths. + * returns 0 if no "../" are left. + * + * Wouldn't it be nice if there was a standard system library + * routine to do this...? + */ +static int +cleanup_dotdot_path(char *path) +{ + char holder[MAXPATHLEN]; + char *dotdot; + char *previous_slash; + + /* Find the first "/../" in the string */ + dotdot = strstr(path, "/../"); + if (dotdot == NULL) { + return (0); + } + + + /* + * If the [0] character is '/' and "../" immediatly + * follows it, then we can strip the ../ + * + * /../../foo/bar == /foo/bar + * + */ + if (dotdot == path) { + strcpy(holder, &path[3]); /* strip "/.." */ + strcpy(path, holder); + return (1); + } + + /* + * Now look for the LAST "/" before the "/../" + * as this is the parent dir we can get rid of. + * We do this by temporarily truncating the string + * at the '/' just before "../" using the dotdot pointer. + */ + *dotdot = '\0'; + previous_slash = strrchr(path, '/'); + if (previous_slash == NULL) { + /* + * hmm, somethings wrong. path looks something + * like "foo/../bar/" so we can't really deal with it. + */ + return (0); + } + /* + * Now truncate the path just after the previous '/' + * and slam everything after the "../" back on + */ + *(previous_slash+1) = '\0'; + (void) strcat(path, dotdot+4); + return (1); /* We may have more "../"s */ +} + +/* + * Follow symbolic links from the logical device name to + * the /devfs physical device name. To be complete, we + * handle the case of multiple links. This function + * either returns NULL (no links, or some other error), + * or the physical device name, alloc'ed on the heap. + * + * NOTE: If the path is relative, it will be forced into + * an absolute path by pre-pending the pwd to it. + */ +char * +get_slash_devices_from_osDevName(char *osDevName, int flag) +{ + struct stat stbuf; + char source[MAXPATHLEN]; + char scratch[MAXPATHLEN]; + char pwd[MAXPATHLEN]; + char *tmp, *phys_path; + int cnt; + boolean_t is_lstat_failed = B_TRUE; + + /* return NULL if path is NULL */ + if (osDevName == NULL) { + return (NULL); + } + + strcpy(source, osDevName); + for (;;) { + + /* + * First make sure the path is absolute. If not, make it. + * If it's already an absolute path, we have no need + * to determine the cwd, so the program should still + * function within security-by-obscurity directories. + */ + if (source[0] != '/') { + tmp = getcwd(pwd, MAXPATHLEN); + if (tmp == NULL) { + return (NULL); + } + /* + * Handle special case of "./foo/bar" + */ + if (source[0] == '.' && source[1] == '/') { + strcpy(scratch, source+2); + } else { /* no "./" so just take everything */ + strcpy(scratch, source); + } + strcpy(source, pwd); + (void) strcat(source, "/"); + (void) strcat(source, scratch); + } + + /* + * Clean up any "../"s that are in the path + */ + while (cleanup_dotdot_path(source)); + + /* + * source is now an absolute path to the link we're + * concerned with + */ + if (flag == NOT_IGNORE_DANGLING_LINK) { + /* + * In order not to ingore dangling links, check + * the lstat. If lstat succeeds, return the path + * from readlink. + * Note: osDevName input with /devices path from + * a dangling /dev link doesn't pass lstat so + * NULL is returned. + */ + if (stat(source, &stbuf) == -1) { + if (!is_lstat_failed && + strstr(source, "/devices")) { + /* + * lstat succeeded previously and source + * contains "/devices" then it is dangling node. + */ + phys_path = (char *)calloc(1, strlen(source) + 1); + if (phys_path != NULL) { + (void) strncpy(phys_path, source, + strlen(source) + 1); + } + return (phys_path); + } else if (is_lstat_failed) { + /* check lstat result. */ + if (lstat(source, &stbuf) == -1) { + return (NULL); + } else { + is_lstat_failed = B_FALSE; /* and coninue */ + } + } else { + /* + * With algorithm that resolves a link and + * then issues readlink(), should not be + * reached here. + */ + return (NULL); + } + } else { + if (lstat(source, &stbuf) == -1) { + /* + * when stat succeeds it is not a dangling node + * so it is not a special case. + */ + return (NULL); + } + } + } else if (flag == STANDARD_DEVNAME_HANDLING) { + /* + * See if there's a real file out there. If not, + * we have a dangling link and we ignore it. + */ + if (stat(source, &stbuf) == -1) { + return (NULL); + } + if (lstat(source, &stbuf) == -1) { + return (NULL); + } + } else { + /* invalid flag */ + return (NULL); + } + + /* + * If the file is not a link, we're done one + * way or the other. If there were links, + * return the full pathname of the resulting + * file. + * + * Note: All of our temp's are on the stack, + * so we have to copy the final result to the heap. + */ + if (!S_ISLNK(stbuf.st_mode)) { + phys_path = (char *)calloc(1, strlen(source) + 1); + if (phys_path != NULL) { + (void) strncpy(phys_path, source, + strlen(source) + 1); + } + return (phys_path); + } + cnt = readlink(source, scratch, sizeof (scratch)); + if (cnt < 0) { + return (NULL); + } + /* + * scratch is on the heap, and for some reason readlink + * doesn't always terminate things properly so we have + * to make certain we're properly terminated + */ + scratch[cnt] = '\0'; + + /* + * Now check to see if the link is relative. If so, + * then we have to append it to the directory + * which the source was in. (This is non trivial) + */ + if (scratch[0] != '/') { + tmp = strrchr(source, '/'); + if (tmp == NULL) { /* Whoa! Something's hosed! */ + O_DPRINTF("Internal error... corrupt path.\n"); + return (NULL); + } + /* Now strip off just the directory path */ + *(tmp+1) = '\0'; /* Keeping the last '/' */ + /* and append the new link */ + (void) strcat(source, scratch); + /* + * Note: At this point, source should have "../"s + * but we'll clean it up in the next pass through + * the loop. + */ + } else { + /* It's an absolute link so no worries */ + strcpy(source, scratch); + } + } + /* Never reach here */ +} + +/* + * Input - Space for client_path, phci_path and paddr fields of ioc structure + * need to be allocated by the caller of this routine. + */ +int +get_scsi_vhci_pathinfo(char *dev_path, sv_iocdata_t *ioc, int *path_count) +{ + char *physical_path, *physical_path_s; + int retval; + int fd; + int initial_path_count; + int current_path_count; + int i; + char *delimiter; + int malloc_error = 0; + int prop_buf_size; + int pathlist_retry_count = 0; + + if (strncmp(dev_path, SCSI_VHCI, + strlen(SCSI_VHCI)) != NULL) { + if ((physical_path = get_slash_devices_from_osDevName( + dev_path, STANDARD_DEVNAME_HANDLING)) == NULL) { + return (L_INVALID_PATH); + } + if (strncmp(physical_path, SCSI_VHCI, + strlen(SCSI_VHCI)) != NULL) { + free(physical_path); + return (L_INVALID_PATH); + } + } else { + if ((physical_path = calloc(1, MAXPATHLEN)) == NULL) { + return (L_MALLOC_FAILED); + } + (void) strcpy(physical_path, dev_path); + } + physical_path_s = physical_path; + + /* move beyond "/devices" prefix */ + physical_path += DEV_PREFIX_STRLEN-1; + /* remove :c,raw suffix */ + delimiter = strrchr(physical_path, ':'); + /* if we didn't find the ':' fine, else truncate */ + if (delimiter != NULL) { + *delimiter = NULL; + } + + /* + * We'll call ioctl SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO + * at least twice. The first time will get the path count + * and the size of the ioctl propoerty buffer. The second + * time will get the path_info for each path. + * + * It's possible that additional paths are added while this + * code is running. If the path count increases between the + * 2 ioctl's above, then we'll retry (and assume all is well). + */ + (void) strcpy(ioc->client, physical_path); + ioc->buf_elem = 1; + ioc->ret_elem = (uint_t *)&(initial_path_count); + ioc->ret_buf = NULL; + + /* free physical path */ + free(physical_path_s); + + /* 0 buf_size asks driver to return actual size needed */ + /* open the ioctl file descriptor */ + if ((fd = open("/devices/scsi_vhci:devctl", O_RDWR)) < 0) { + return (L_OPEN_PATH_FAIL); + } + + retval = ioctl(fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, ioc); + if (retval != 0) { + close(fd); + return (L_SCSI_VHCI_ERROR); + } + prop_buf_size = SV_PROP_MAX_BUF_SIZE; + + + while (pathlist_retry_count <= RETRY_PATHLIST) { + ioc->buf_elem = initial_path_count; + /* Make driver put actual # paths in variable */ + ioc->ret_elem = (uint_t *)&(current_path_count); + + /* + * Allocate space for array of path_info structures. + * Allocate enough space for # paths from get_pathcount + */ + ioc->ret_buf = (sv_path_info_t *) + calloc(initial_path_count, + sizeof (sv_path_info_t)); + if (ioc->ret_buf == NULL) { + close(fd); + return (L_MALLOC_FAILED); + } + + /* + * Allocate space for path properties returned by driver + */ + malloc_error = 0; + for (i = 0; i < initial_path_count; i++) { + ioc->ret_buf[i].ret_prop.buf_size = prop_buf_size; + if ((ioc->ret_buf[i].ret_prop.buf = + (caddr_t)malloc(prop_buf_size)) == NULL) { + malloc_error = 1; + break; + } + if ((ioc->ret_buf[i].ret_prop.ret_buf_size = + (uint_t *)malloc(sizeof (uint_t))) == NULL) { + malloc_error = 1; + break; + } + } + if (malloc_error == 1) { + for (i = 0; i < initial_path_count; i++) { + free(ioc->ret_buf[i].ret_prop.buf); + free(ioc->ret_buf[i].ret_prop.ret_buf_size); + } + free(ioc->ret_buf); + close(fd); + return (L_MALLOC_FAILED); + } + + retval = ioctl(fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, ioc); + if (retval != 0) { + for (i = 0; i < initial_path_count; i++) { + free(ioc->ret_buf[i].ret_prop.buf); + free(ioc->ret_buf[i].ret_prop.ret_buf_size); + } + free(ioc->ret_buf); + close(fd); + return (L_SCSI_VHCI_ERROR); + } + if (initial_path_count < current_path_count) { + /* then a new path was added */ + pathlist_retry_count++; + initial_path_count = current_path_count; + } else { + break; + } + } + /* we are done with ioctl's, lose the fd */ + close(fd); + + /* + * Compare the length num elements from the ioctl response + * and the caller's request - use smaller value. + * + * pathlist_p->path_count now has count returned from ioctl. + * ioc.buf_elem has the value the caller provided. + */ + if (initial_path_count < current_path_count) { + /* More paths exist than we allocated space for */ + *path_count = initial_path_count; + } else { + *path_count = current_path_count; + } + + return (0); +} + +int +get_mode_page(char *path, uchar_t **pg_buf) +{ +struct mode_header_g1 *mode_header_ptr; +int status, size, fd; + + /* open controller */ + if ((fd = open(path, O_NDELAY | O_RDWR)) == -1) + return (-1); /* L_OPEN_PATH_FAIL */ + + /* + * Read the first part of the page to get the page size + */ + size = 20; + if ((*pg_buf = (uchar_t *)calloc(1, size)) == NULL) { + (void) close(fd); + return (L_MALLOC_FAILED); + } + /* read page */ + if (status = scsi_mode_sense_cmd(fd, *pg_buf, size, + 0, MODEPAGE_ALLPAGES)) { + (void) close(fd); + (void) free(*pg_buf); + return (status); + } + /* Now get the size for all pages */ + mode_header_ptr = (struct mode_header_g1 *)(void *)*pg_buf; + size = ntohs(mode_header_ptr->length) + + sizeof (mode_header_ptr->length); + (void) free(*pg_buf); + if ((*pg_buf = (uchar_t *)calloc(1, size)) == NULL) { + (void) close(fd); + return (L_MALLOC_FAILED); + } + /* read all pages */ + if (status = scsi_mode_sense_cmd(fd, *pg_buf, size, + 0, MODEPAGE_ALLPAGES)) { + (void) close(fd); + (void) free(*pg_buf); + return (status); + } + (void) close(fd); + return (0); +} + +/* + * Dump a structure in hexadecimal. + */ +void +dump_hex_data(char *hdr, uchar_t *src, int nbytes, int format) +{ + int i; + int n; + char *p; + char s[256]; + + assert(format == HEX_ONLY || format == HEX_ASCII); + + (void) strcpy(s, hdr); + for (p = s; *p; p++) { + *p = ' '; + } + + p = hdr; + while (nbytes > 0) { + (void) fprintf(stdout, "%s", p); + p = s; + n = MIN(nbytes, BYTES_PER_LINE); + for (i = 0; i < n; i++) { + (void) fprintf(stdout, "%02x ", src[i] & 0xff); + } + if (format == HEX_ASCII) { + for (i = BYTES_PER_LINE-n; i > 0; i--) { + (void) fprintf(stdout, " "); + } + (void) fprintf(stdout, " "); + for (i = 0; i < n; i++) { + (void) fprintf(stdout, "%c", + isprint(src[i]) ? src[i] : '.'); + } + } + (void) fprintf(stdout, "\n"); + nbytes -= n; + src += n; + } +} diff --git a/usr/src/cmd/luxadm/luxadm.h b/usr/src/cmd/luxadm/luxadm.h new file mode 100644 index 0000000000..b428035c03 --- /dev/null +++ b/usr/src/cmd/luxadm/luxadm.h @@ -0,0 +1,110 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * luxadm.h + * + * External functions and global variables needed for PHOTON + */ + +/* + * I18N message number ranges + * This file: 13500 - 13999 + * Shared common messages: 1 - 1999 + */ + +#ifndef _LUXADM_H +#define _LUXADM_H + + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* External functions */ +extern int fc_update(unsigned, unsigned, char *); +extern int fcal_update(unsigned, char *); +extern int q_qlgc_update(unsigned, char *); +extern int emulex_update(char *); +extern int emulex_fcode_reader(int, char *, char *, uint32_t); +extern int setboot(unsigned, unsigned, char *); +extern int sysdump(int); +extern int h_insertSena_fcdev(); +extern int hotplug(int, char **, int, int); +extern int hotplug_e(int, char **, int, int); +extern void print_fabric_dtype_prop(uchar_t *, uchar_t *, uchar_t); +/* SSA and RSM */ +extern int p_download(char *, char *, int, int, uchar_t *); +extern void ssa_fast_write(char *); +extern void ssa_perf_statistics(char *); +extern void ssa_cli_start(char **, int); +extern void ssa_cli_stop(char **, int); +extern void ssa_cli_display_config(char **argv, char *, int, int, int); +extern void cli_display_envsen_data(char **, int); +extern int p_sync_cache(char *); +extern int p_purge(char *); +extern void led(char **, int, int); +extern void alarm_enable(char **, int, int); +extern void alarm_set(char **, int); +extern void power_off(char **, int); +extern char *get_physical_name(char *); + +/* SSA LIB environment sense */ +extern int scsi_get_envsen_data(int, char *, int); +extern int scsi_put_envsen_data(int, char *, int); + +/* hotplug */ +extern void print_errString(int, char *); +extern int print_devState(char *, char *, int, int, int); +extern void print_dev_state(char *, int); +extern void print_bus_state(char *, int); +extern int dev_handle_insert(char *, int); +extern int dev_handle_remove(char *, int); +extern int dev_handle_replace(char *, int); + +/* funct.c */ +extern char ctoi(char); + + +/* Functions for FC-HBA based operations */ +extern int fchba_display_port(int verbose); +extern int fchba_non_encl_probe(); +extern int fchba_inquiry(char **argv); +extern int fchba_dump_map(char **argv); +extern int use_fchba(); +extern int fchba_display_link_status(char **); +extern int fchba_display_config(char **, int, int); +extern int fchba_hotplug_e(int, char **, int, int); + +/* for g_adm.c & hotplug.c */ +int print_devState(char *, char *, int, int, int); + +#ifdef __cplusplus +} +#endif + +#endif /* _LUXADM_H */ diff --git a/usr/src/cmd/luxadm/qlgcupdate.c b/usr/src/cmd/luxadm/qlgcupdate.c new file mode 100644 index 0000000000..653e98e234 --- /dev/null +++ b/usr/src/cmd/luxadm/qlgcupdate.c @@ -0,0 +1,1591 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * I18N message number ranges + * This file: 21000 - 21499 + * Shared common messages: 1 - 1999 + */ + + + +/* + * Functions to support the download of FCode to PCI HBAs + * Qlogic ISP21XX/22XX boards: FC100/P single port, ISP2200 dual port + * and Emulex cards + */ +#include <errno.h> +#include <ctype.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <limits.h> +#include <signal.h> +#include <dirent.h> +#include <nl_types.h> +#include <utmpx.h> +#include <sys/mnttab.h> +#include <sys/file.h> +#include <sys/mtio.h> +#include <sys/scsi/impl/uscsi.h> +#include <sys/fibre-channel/fcio.h> +#include <stgcom.h> +#include <sys/scsi/adapters/ifpio.h> +#include <libdevinfo.h> +#include "luxadm.h" + +/* Error codes - used by the fcode_load_file routine */ +#define FCODE_SUCCESS 0 /* successful completion */ +#define FCODE_LOAD_FAILURE 1 /* general failure */ +#define FCODE_IOCTL_FAILURE 2 /* FCODE ioctl download failure */ + +#define HBA_MAX 128 +#define FCODE_HDR 200 +#define MAX_RETRIES 3 +#define MAX_WAIT_TIME 30 + +/* + * EMULEX Fcode attributes + */ +#define EMULEX_FCODE_VERSION_LENGTH 16 +#define EMULEX_READ_BUFFER_SIZE 128 + +/* Emulex specific error codes */ +#define EMLX_ERRNO_START 0x100 + +/* Diagnostic error codes */ +#define EMLX_TEST_FAILED (EMLX_ERRNO_START + 0) + +/* Download image contains bad data */ +#define EMLX_IMAGE_BAD (EMLX_ERRNO_START + 1) +/* Download image not compatible with current hardware */ +#define EMLX_IMAGE_INCOMPATIBLE (EMLX_ERRNO_START + 2) +/* Unable to take adapter offline */ +#define EMLX_IMAGE_FAILED (EMLX_ERRNO_START + 3) +/* Image download failed */ +#define EMLX_OFFLINE_FAILED (EMLX_ERRNO_START + 4) + + + + +/* + * This is just a random value chosen to identify Sbus Fcodes. Sbus FCode + * for Ivory is based on a 2200 chip but this value does not reflect that. + */ +#define SBUS_CHIP_ID 0x1969 +#define IVORY_BUS "/sbus@" +#define IVORY_DRVR "/SUNW,qlc@" + +/* Global variables */ +static char fc_trans[] = "SUNW,ifp"; /* fibre channel transport */ +static char fp_trans[] = "SUNW,qlc"; /* fca layer driver */ +static char fp_trans_id[] = "fp@"; /* transport layer id */ +static char qlgc2100[] = "FC100/P"; /* product name for 2100 */ +static char qlgc2200[] = "ISP2200"; /* product name for 2200 */ +static char qlgc2300[] = "ISP2300"; /* product name for 2300 */ +static char qlgc2312[] = "ISP2312"; /* product name for 2312 */ +/* + * The variable qlgc2200Sbus represents the string which is always the + * starting string of the version information in an ISP2200 Sbus Fcode. + */ +static char qlgc2200Sbus[] = "ISP2200 Sbus FC-AL Host Adapter Driver"; +static char pcibus_list[HBA_MAX][PATH_MAX]; +/* Internal functions */ +static int q_load_file(int, char *); +static int q_getbootdev(uchar_t *); +static int q_getdevctlpath(char *, int *); +static int q_warn(int); +static int q_findversion(int, int, uchar_t *, uint16_t *); +static int q_findfileversion(char *, uchar_t *, uint16_t *, int, int *); +static int q_findSbusfile(int, int *); +static int memstrstr(char *, char *, int, int); +static int fcode_load_file(int, char *, int *); + +/* + * Functions to support Fcode download for Emulex HBAs + */ +static int emulex_fcodeversion(di_node_t, uchar_t *); +static void handle_emulex_error(int, char *); + +/* + * Searches for and updates the cards. This is the "main" function + * and will give the output to the user by calling the subfunctions. + * args: FCode file; if NULL only the current FCode version is printed + */ +int +q_qlgc_update(unsigned int verbose, char *file) +/*ARGSUSED*/ +{ + int fd, fcode_fd = -1, errnum = 0, devcnt = 0, retval = 0, isSbus = 0; + int sbus_off; + uint_t i, fflag = 0; + uint16_t chip_id = 0, file_id = 0; + uchar_t fcode_buf[FCODE_HDR]; + static uchar_t bootpath[PATH_MAX]; + static uchar_t version[MAXNAMELEN], version_file[MAXNAMELEN]; + char devpath[PATH_MAX], tmppath[PATH_MAX]; + void (*sigint)(); /* to store default SIGTERM setting */ + static struct utmpx *utmpp = NULL; /* pointer for getutxent() */ + char *ptr1, *ptr2; + char phys_path[PATH_MAX]; + /* + * The variables port1 and port2 are used to store the bus id + * e.g. the bus id for this path: + * /devices/sbus@12,0/SUNW,qlc@2,30000/fp@0,0:devctl + * is "sbus@12". They are initialized to a random value and are + * set such that they are not equal initially. + */ + static char port1[MAXNAMELEN] = {NULL}; + static char port2[MAXNAMELEN] = {NULL}; + + if (file) { + fflag++; + + /* check for a valid file */ + if ((fcode_fd = open(file, O_RDONLY)) < 0) { + (void) fprintf(stderr, + MSGSTR(21000, "Error: Could not open %s\n"), file); + return (1); + } + if (read(fcode_fd, fcode_buf, FCODE_HDR) != FCODE_HDR) { + perror(MSGSTR(21001, "read")); + (void) close(fcode_fd); + return (1); + } + + /* + * Check if it's SBUS FCode by calling q_findSbusfile + * if it is then isSbus will be 1, if not it will be 0 + * in case of an error, it will be -1 + */ + isSbus = q_findSbusfile(fcode_fd, &sbus_off); + if (isSbus == -1) { + (void) close(fcode_fd); + return (1); + } + + /* + * FCode header check - make sure it's PCI FCode + * Structure of FCode header (byte# refers to byte numbering + * in FCode spec, not the byte# of our fcode_buf buffer): + * header byte 00 0x55 prom signature byte one + * byte 01 0xaa prom signature byte two + * data byte 00-03 P C I R + * OR + * header byte 32 0x55 + * byte 33 0xaa + * data byte 60-63 P C I R + * The second format with an offset of 32 is used for ifp prom + */ + if (!(((fcode_buf[0x00] == 0x55) && + (fcode_buf[0x01] == 0xaa) && + (fcode_buf[0x1c] == 'P') && + (fcode_buf[0x1d] == 'C') && + (fcode_buf[0x1e] == 'I') && + (fcode_buf[0x1f] == 'R')) || + + ((fcode_buf[0x20] == 0x55) && + (fcode_buf[0x21] == 0xaa) && + (fcode_buf[0x3c] == 'P') && + (fcode_buf[0x3d] == 'C') && + (fcode_buf[0x3e] == 'I') && + (fcode_buf[0x3f] == 'R')) || + + (isSbus))) { + (void) fprintf(stderr, MSGSTR(21002, + "Error: %s is not a valid FC100/P, " + "ISP2200, ISP23xx FCode file.\n"), + file); + (void) close(fcode_fd); + return (1); + } + + /* check for single user mode */ + while ((utmpp = getutxent()) != NULL) { + if (strstr(utmpp->ut_line, "run-level") && + (strcmp(utmpp->ut_line, "run-level S") && + strcmp(utmpp->ut_line, "run-level 1"))) { + if (q_warn(1)) { + (void) endutxent(); + (void) close(fcode_fd); + return (1); + } + break; + } + } + (void) endutxent(); + + /* get bootpath */ + if (!q_getbootdev((uchar_t *)&bootpath[0]) && + getenv("_LUX_D_DEBUG") != NULL) { + (void) fprintf(stdout, " Bootpath: %s\n", bootpath); + } + } + /* + * Get count of, and names of PCI slots with ifp device control + * (devctl) nodes. Search /devices. + */ + (void) strcpy(devpath, "/devices"); + if (q_getdevctlpath(devpath, (int *)&devcnt) == 0) { + (void) fprintf(stdout, MSGSTR(21003, + "\n Found Path to %d FC100/P, ISP2200, ISP23xx Devices\n"), + devcnt); + } else { + (void) fprintf(stderr, MSGSTR(21004, + "Error: Could not get /devices path to FC100/P," + "ISP2200, ISP23xx Cards.\n")); + retval++; + } + + for (i = 0; i < devcnt; i++) { + + (void) strncpy((char *)phys_path, &pcibus_list[i][0], + strlen(&pcibus_list[i][0])); + if (fflag && (strstr((char *)bootpath, + strtok((char *)phys_path, ":")) != NULL)) { + (void) fprintf(stderr, + MSGSTR(21005, "Ignoring %s (bootpath)\n"), + &pcibus_list[i][0]); + continue; + } + + (void) fprintf(stdout, + MSGSTR(21006, "\n Opening Device: %s\n"), &pcibus_list[i][0]); + /* Check if the device is valid */ + if ((fd = open(&pcibus_list[i][0], O_RDWR)) < 0) { + (void) fprintf(stderr, + MSGSTR(21000, "Error: Could not open %s\n"), + &pcibus_list[i][0]); + retval++; + continue; + } + (void) close(fd); + /* + * Check FCode version present on the adapter (at last boot) + */ + if (q_findversion(verbose, i, (uchar_t *)&version[0], + &chip_id) == 0) { + if (strlen((char *)version) == 0) { + (void) fprintf(stdout, MSGSTR(21007, + " Detected FCode Version:\tNo version available for this FCode\n")); + } else { + (void) fprintf(stdout, MSGSTR(21008, + " Detected FCode Version:\t%s\n"), version); + } + } else { + chip_id = 0x0; + } + + if (fflag) { + /* + * For ISP2200, Sbus HBA, do just 1 download + * for both the ports (dual port HBA) + * Here it is assumed that readdir() always + * returns the paths in pcibus_list[] in the + * sorted order. + */ + (void) strcpy(tmppath, pcibus_list[i]); + if (ptr1 = strstr(tmppath, IVORY_BUS)) { + if (ptr2 = strstr(ptr1, IVORY_DRVR)) { + ptr2 = strchr(ptr2, ','); + if (ptr2 = strchr(++ptr2, ',')) { + *ptr2 = '\0'; + } + } + (void) strcpy(port2, ptr1); + if (strcmp(port1, port2) == 0) { + (void) fprintf(stdout, MSGSTR(21037, + "/n New FCode has already been downloaded " + "to this ISP2200 SBus HBA Card.\n" + "It is sufficient to download to one " + "port of the ISP2200 SBus HBA Card. " + "Moving on...\n")); + continue; + } + } + /* + * Check version of the supplied FCode file (once) + */ + if ((file_id != 0 && version_file != NULL) || + (q_findfileversion((char *) + &fcode_buf[0], (uchar_t *)&version_file[0], + &file_id, isSbus, &sbus_off) == 0)) { + (void) fprintf(stdout, MSGSTR(21009, + " New FCode Version:\t\t%s\n"), + version_file); + } else { + (void) close(fcode_fd); + return (1); + } + + /* + * Load the New FCode + * Give warning if file doesn't appear to be correct + * + */ + if (chip_id == 0) { + errnum = 2; /* can't get chip_id */ + retval++; + } else if (chip_id - file_id != 0) { + errnum = 3; /* file/card mismatch */ + retval++; + } else { + errnum = 0; /* everything is ok */ + } + + if (!q_warn(errnum)) { + /* Disable user-interrupt Control-C */ + sigint = + (void (*)(int)) signal(SIGINT, SIG_IGN); + + /* Load FCode */ + (void) fprintf(stdout, MSGSTR(21010, + " Loading FCode: %s\n"), file); + + if (q_load_file(fcode_fd, + &pcibus_list[i][0]) == 0) { + (void) fprintf(stdout, MSGSTR(21011, + " Successful FCode download: %s\n"), + &pcibus_list[i][0]); + (void) strcpy(port1, port2); + } else { + (void) fprintf(stderr, MSGSTR(21012, + "Error: FCode download failed: %s\n"), + &pcibus_list[i][0]); + retval++; + } + /* Restore SIGINT (user interrupt) setting */ + (void) signal(SIGINT, sigint); + } + } + } + (void) fprintf(stdout, " "); + (void) fprintf(stdout, MSGSTR(125, "Complete\n")); + if (fcode_fd != -1) + (void) close(fcode_fd); + return (retval); +} + + +/* + * Retrieve the version banner from the card + * uses ioctl: FCIO_FCODE_MCODE_VERSION FCode revision + */ +static int +q_findversion(int verbose, int index, uchar_t *version, uint16_t *chip_id) +/*ARGSUSED*/ +{ + int fd, ntries; + struct ifp_fm_version *version_buffer = NULL; + char prom_ver[100] = {NULL}; + char mcode_ver[100] = {NULL}; + fcio_t fcio; + + if (strstr(&pcibus_list[index][0], fc_trans)) { + + if ((fd = open(&pcibus_list[index][0], O_RDWR)) < 0) { + (void) fprintf(stderr, + MSGSTR(21000, "Error: Could not open %s\n"), + &pcibus_list[index][0]); + return (1); + } + + if ((version_buffer = (struct ifp_fm_version *)malloc( + sizeof (struct ifp_fm_version))) == NULL) { + (void) fprintf(stderr, + MSGSTR(21013, "Error: Memory allocation failed\n")); + (void) close(fd); + return (1); + } + + version_buffer->fcode_ver = (char *)version; + version_buffer->mcode_ver = mcode_ver; + version_buffer->prom_ver = prom_ver; + version_buffer->fcode_ver_len = MAXNAMELEN - 1; + version_buffer->mcode_ver_len = 100; + version_buffer->prom_ver_len = 100; + + if (ioctl(fd, FCIO_FCODE_MCODE_VERSION, version_buffer) < 0) { + (void) fprintf(stderr, MSGSTR(21014, + "Error: Driver interface FCIO_FCODE_MCODE_VERSION failed\n")); + free(version_buffer); + (void) close(fd); + return (1); + } + version[version_buffer->fcode_ver_len] = '\0'; + + /* Need a way to get card MCODE (firmware) to track certain HW bugs */ + if (getenv("_LUX_D_DEBUG") != NULL) { + (void) fprintf(stdout, " Device %i: QLGC chip_id %x\n", + index+1, *chip_id); + (void) fprintf(stdout, " FCode:%s\n MCODE:%s\n PROM:%s\n", + (char *)version, mcode_ver, prom_ver); + } + free(version_buffer); + + } else if (strstr(&pcibus_list[index][0], fp_trans)) { + /* + * Get the fcode and prom's fw version + * using the fp ioctls. Currently, we pass + * only the fcode version to the calling function + * and ignore the FW version (using the existing + * implementation). + */ + + if ((fd = open(&pcibus_list[index][0], O_RDWR)) < 0) { + (void) fprintf(stderr, + MSGSTR(4511, "Could not open %s\n"), + &pcibus_list[index][0]); + (void) close(fd); + return (1); + } + /* Get the fcode version */ + bzero(version, sizeof (version)); + fcio.fcio_cmd = FCIO_GET_FCODE_REV; + /* Information read operation */ + fcio.fcio_xfer = FCIO_XFER_READ; + fcio.fcio_obuf = (caddr_t)version; + fcio.fcio_olen = MAXNAMELEN; + + for (ntries = 0; ntries < MAX_RETRIES; ntries++) { + if (ioctl(fd, FCIO_CMD, &fcio) != 0) { + if ((errno == EAGAIN) && + (ntries+1 < MAX_RETRIES)) { + /* wait 30 secs */ + (void) sleep(MAX_WAIT_TIME); + continue; + } + (void) close(fd); + return (L_FCIO_GET_FCODE_REV_FAIL); + } + break; + } + version[MAXNAMELEN-1] = '\0'; + } + + /* Get type of card from product name in FCode version banner */ + if (strstr((char *)version, qlgc2100)) { + *chip_id = 0x2100; + } else if (strstr((char *)version, qlgc2200)) { + *chip_id = 0x2200; + if (strstr((char *)version, "Sbus")) { + *chip_id = SBUS_CHIP_ID; + } + } else if (strstr((char *)version, qlgc2300)) { + *chip_id = 0x2300; + } else if (strstr((char *)version, qlgc2312)) { + *chip_id = 0x2312; + } else { + *chip_id = 0x0; + } + + (void) close(fd); + return (0); +} + +/* + * Retrieve the version banner and file type (2100 or 2200) from the file + */ +static int +q_findfileversion(char *dl_fcode, uchar_t *version_file, uint16_t *file_id, + int isSbus, int *sbus_offset) +{ + int mark; + int qlc_offset = 0; + char temp[4] = {NULL}; + + + /* + * Get file version from FCode for 2100 or 2202 + */ + if (isSbus) { + *file_id = SBUS_CHIP_ID; + } else { + if ((dl_fcode[0x23] == 0x22) || + (dl_fcode[0x23] == 0x23)) { + *file_id = dl_fcode[0x22] & 0xff; + *file_id |= (dl_fcode[0x23] << 8) & 0xff00; + } else { + *file_id = dl_fcode[0x42] & 0xff; + *file_id |= (dl_fcode[0x43] << 8) & 0xff00; + } + } + + /* + * Ok, we're just checking for 2200 here. If it is we need + * to offset to find the banner. + */ + if ((*file_id == 0x2200) || + (*file_id == 0x2300) || + (*file_id == 0x2312)) { + qlc_offset = -32; + } + + /* + * If this is an ISP2200 Sbus Fcode file, then search for the string + * "ISP2200 FC-AL Host Adapter Driver" in the whole fcode file + */ + if (isSbus) { + *file_id = SBUS_CHIP_ID; + qlc_offset = *sbus_offset; + /* Subtract 111 from the offset we add below for PCI Fcodes */ + qlc_offset -= 111; + } + + /* Banner length varies; grab banner to end of date marker yr/mo/da */ + version_file[0] = '\0'; + for (mark = (111 + qlc_offset); mark < (191 + qlc_offset); mark++) { + (void) strncpy(temp, (char *)&dl_fcode[mark], 4); + if ((strncmp(&temp[0], "/", 1) == 0) && + (strncmp(&temp[3], "/", 1) == 0)) { + (void) strncat((char *)version_file, + (char *)&dl_fcode[mark], 6); + break; + } + (void) strncat((char *)version_file, temp, 1); + } + return (0); +} + +/* + * Find if the FCode file is a ISP2200 SBUS Fcode file + */ +static int +q_findSbusfile(int fd, int *sbus_offset) +{ + static int file_size; + char *sbus_info; + struct stat statinfo; + + if (lseek(fd, 0, SEEK_SET) == -1) { + perror(MSGSTR(21022, "seek")); + return (-1); + } + if (fstat(fd, &statinfo)) { + perror(MSGSTR(21023, "fstat")); + return (-1); + } + file_size = statinfo.st_size; + + if ((sbus_info = (char *)malloc(file_size)) == NULL) { + (void) fprintf(stderr, + MSGSTR(21013, "Error: Memory allocation failed\n")); + return (-1); + } + + if (read(fd, sbus_info, file_size) < 0) { + perror(MSGSTR(21001, "read")); + free(sbus_info); + return (-1); + } + + /* + * Search for the version string in the whole file + */ + if ((*sbus_offset = memstrstr((char *)sbus_info, qlgc2200Sbus, + file_size, strlen(qlgc2200Sbus))) != -1) { + free(sbus_info); + return (1); + } else { + free(sbus_info); + return (0); + } +} + + +/* + * Build a list of all the devctl entries for all the 2100/2200 based adapters + */ +static int +q_getdevctlpath(char *devpath, int *devcnt) +{ + struct stat statbuf; + struct dirent *dirp = NULL; + DIR *dp = NULL; + char *ptr = NULL; + int err; + int testopen; + + if (lstat(devpath, &statbuf) < 0) { + (void) fprintf(stderr, + MSGSTR(21016, "Error: %s lstat() error\n"), devpath); + return (1); + } + + if ((strstr(devpath, fc_trans) || + (strstr(devpath, fp_trans_id) && strstr(devpath, fp_trans))) && + strstr(devpath, "devctl")) { + /* Verify the path is valid */ + if ((testopen = open(devpath, O_RDONLY)) >= 0) { + (void) close(testopen); + (void) strcpy(pcibus_list[*devcnt], devpath); + *devcnt += 1; + return (0); + } + } + + if (S_ISDIR(statbuf.st_mode) == 0) { + /* + * not a directory so + * we don't care about it - return + */ + return (0); + } + + /* + * It's a directory. Call ourself to + * traverse the path(s) + */ + ptr = devpath + strlen(devpath); + *ptr++ = '/'; + *ptr = 0; + + /* Forget the /devices/pseudo/ directory */ + if (strcmp(devpath, "/devices/pseudo/") == 0) { + return (0); + } + + if ((dp = opendir(devpath)) == NULL) { + (void) fprintf(stderr, + MSGSTR(21017, "Error: %s Can't read directory\n"), devpath); + return (1); + } + + while ((dirp = readdir(dp)) != NULL) { + + if (strcmp(dirp->d_name, ".") == 0 || + strcmp(dirp->d_name, "..") == 0) { + continue; + } + (void) strcpy(ptr, dirp->d_name); /* append name */ + err = q_getdevctlpath(devpath, devcnt); + } + + if (closedir(dp) < 0) { + (void) fprintf(stderr, + MSGSTR(21018, "Error: Can't close directory %s\n"), devpath); + return (1); + } + return (err); +} + +/* + * Get the boot device. Cannot load FCode to current boot device. + * Boot devices under volume management will prompt a warning. + */ +static int +q_getbootdev(uchar_t *bootpath) +{ + struct mnttab mp; + struct mnttab mpref; + FILE *fp = NULL; + static char buf[BUFSIZ]; + char *p = NULL, *p1 = NULL; /* p = full device, p1 = chunk to rm */ + char *slot = ":devctl"; + char *root = "/"; + + if ((fp = fopen(MNTTAB, "r")) == NULL) { + (void) fprintf(stderr, + MSGSTR(21000, "Error: Could not open %s\n"), MNTTAB); + return (1); + } + + mntnull(&mpref); + mpref.mnt_mountp = (char *)root; + + if (getmntany(fp, &mp, &mpref) != 0 || + mpref.mnt_mountp == NULL) { + (void) fprintf(stderr, MSGSTR(21019, + "Error: Cannot get boot device, check %s.\n"), MNTTAB); + (void) fclose(fp); + return (1); + } + (void) fclose(fp); + + /* + * If we can't get a link, we may be dealing with a volume mgr + * so give a warning. If a colon is present, we likely have a + * non-local disk or cd-rom, so no warning is necessary. + * e.g. /devices/pci@1f,4000/scsi@3/sd@6,0:b (cdrom, no link) or + * storage-e4:/blah/blah remote boot server + */ + if (readlink(mp.mnt_special, buf, BUFSIZ) < 0) { + if (strstr(mp.mnt_special, ":") == NULL) { + (void) fprintf(stderr, MSGSTR(21020, + "\nWarning: Cannot read boot device link, check %s.\n"), MNTTAB); + (void) fprintf(stderr, MSGSTR(21021, + "Do not upgrade FCode on adapters controlling the boot device.\n")); + } + return (1); + } + /* + * Copy boot device path to bootpath. First remove leading + * path junk (../../..) then if it's an ifp device, chop off + * the disk and add the devctl to the end of the path. + */ + if (p = strstr(buf, "/devices")) { + if (strstr(buf, fc_trans) != NULL) { + p1 = strrchr(p, '/'); + *p1 = '\0'; + } + } + (void) strcpy((char *)bootpath, (char *)p); + if (p1) { + (void) strcat((char *)bootpath, slot); + } + return (0); +} + +/* + * Load FCode to card. + * uses ioctl: IFPIO_FCODE_DOWNLOAD + */ +static int +q_load_file(int fcode_fd, char *device) +{ + static int dev_fd, fcode_size; + struct stat stat; + ifp_download_t *download_p = NULL; + fcio_t fcio; + uint16_t file_id = 0; + uchar_t *bin; + + if (lseek(fcode_fd, 0, SEEK_SET) == -1) { + perror(MSGSTR(21022, "seek")); + (void) close(fcode_fd); + return (1); + } + if (fstat(fcode_fd, &stat) == -1) { + perror(MSGSTR(21023, "fstat")); + (void) close(fcode_fd); + return (1); + } + + fcode_size = stat.st_size; + + if (strstr(device, fc_trans)) { + if ((download_p = (ifp_download_t *)malloc( + sizeof (ifp_download_t) + fcode_size)) == NULL) { + (void) fprintf(stderr, + MSGSTR(21013, "Error: Memory allocation failed\n")); + (void) close(fcode_fd); + return (1); + } + } else { + if ((bin = (uchar_t *)malloc(fcode_size)) == NULL) { + (void) fprintf(stderr, + MSGSTR(21013, "Error: Memory allocation failed\n")); + (void) close(fcode_fd); + return (1); + } + } + + if (strstr(device, fc_trans)) { + if (read(fcode_fd, download_p->dl_fcode, fcode_size) + != fcode_size) { + perror(MSGSTR(21001, "read")); + free(download_p); + (void) close(fcode_fd); + return (1); + } + } else { + if (read(fcode_fd, bin, fcode_size) + != fcode_size) { + perror(MSGSTR(21001, "read")); + free(bin); + (void) close(fcode_fd); + return (1); + } + } + + + if ((dev_fd = open(device, O_RDWR|O_EXCL)) < 0) { + (void) fprintf(stderr, + MSGSTR(21000, "Error: Could not open %s\n"), device); + free(download_p); + return (1); + } + if (strstr(device, fc_trans)) { + download_p->dl_fcode_len = fcode_size; + file_id = download_p->dl_fcode[0x42] & 0xff; + file_id |= (download_p->dl_fcode[0x43] << 8) & 0xff00; + download_p->dl_chip_id = file_id; + if (ioctl(dev_fd, IFPIO_FCODE_DOWNLOAD, download_p) < 0) { + (void) fprintf(stderr, MSGSTR(21024, + "Error: Driver interface IFPIO_FCODE_DOWNLOAD failed\n")); + free(download_p); + (void) close(dev_fd); + return (1); + } + free(download_p); + } else if (strstr(device, fp_trans)) { + fcio.fcio_cmd = FCIO_DOWNLOAD_FCODE; + /* Information read operation */ + fcio.fcio_xfer = FCIO_XFER_WRITE; + fcio.fcio_ibuf = (caddr_t)bin; + fcio.fcio_ilen = fcode_size; + + if (ioctl(dev_fd, FCIO_CMD, &fcio) != 0) { + (void) fprintf(stderr, MSGSTR(21036, + "Error: Driver interface FCIO_DOWNLOAD_FCODE failed\n")); + free(download_p); + (void) close(dev_fd); + return (1); + } + free(bin); + } + (void) close(dev_fd); + return (0); +} + +/* + * Issue warning strings and loop for Yes/No user interaction + * err# 0 -- we're ok, warn for pending FCode load + * 1 -- not in single user mode + * 2 -- can't get chip_id + * 3 -- card and file do not have same type (2100/2200) + */ +static int +q_warn(int errnum) +{ + char input[1024]; + input[0] = '\0'; + + if (errnum == 1) { + (void) fprintf(stderr, MSGSTR(21025, + "\nWarning: System is not in single-user mode.\n")); + (void) fprintf(stderr, MSGSTR(21026, + "Loading FCode will reset the adapter and terminate I/O activity\n")); + } else { + if (errnum == 2) { + (void) fprintf(stderr, MSGSTR(21027, + " Warning: FCode is missing or existing FCode has" + " unrecognized version.\n")); + return (1); + } else if (errnum == 3) { + (void) fprintf(stderr, MSGSTR(21028, + " Warning: New FCode file version does not match this" + " board type. Skipping...\n")); + return (1); + } + (void) fprintf(stderr, MSGSTR(21029, + "\nWARNING!! This program will update the FCode in this" + " FC100/PCI, ISP2200/PCI, ISP23xx/PCI " + " and Emulex devices.\n")); + (void) fprintf(stderr, MSGSTR(21030, + "This may take a few (5) minutes. Please be patient.\n")); + } + +loop1: + (void) fprintf(stderr, MSGSTR(21031, + "Do you wish to continue ? (y/n) ")); + + (void) gets(input); + + if ((strcmp(input, MSGSTR(21032, "y")) == 0) || + (strcmp(input, MSGSTR(40, "yes")) == 0)) { + return (0); + } else if ((strcmp(input, MSGSTR(21033, "n")) == 0) || + (strcmp(input, MSGSTR(45, "no")) == 0)) { + (void) fprintf(stderr, + MSGSTR(21034, "Not Downloading FCode\n")); + return (1); + } else { + (void) fprintf(stderr, MSGSTR(21035, "Invalid input\n")); + goto loop1; + } +} + +/* + * Name : memstrstr + * Input : pointer to buf1, pointer to buf2, size of buf1, size of buf2 + * Returns : + * Offset of the start of contents-of-buf2 in buf1 if it is found + * -1 if buf1 does not contain contents of buf2 + * Synopsis: + * This function works similar to strstr(). The difference is that null + * characters in the buffer are treated like any other character. So, buf1 + * and buf2 can have embedded null characters in them. + */ +static int +memstrstr(char *s1, char *s2, int size1, int size2) +{ + int count1, count2; + char *s1_ptr, *s2_ptr; + + count1 = size1; count2 = size2; + s1_ptr = s1; s2_ptr = s2; + + if ((size2 == 0)||(size1 == 0)) + return (-1); + + for (count1 = 0; count1 < (size1 - size2 + 1); count1++) { + if (*s1_ptr++ == *s2_ptr++) { + if (--count2 == 0) { + return (count1 - size2 + 1); + } + continue; + } + count2 = size2; + s2_ptr = s2; + } + + return (-1); +} + +/* + * generic fcode load file routine. given a file descriptor to a fcode file + * this routine will issue the FCIO_DOWNLOAD_FCODE ioctl to the given + * device. Any ioctl errors will be returned in fcio_errno + * + * Arguments: + * fcode_fd file descriptor to a fcode file + * device path to the device we will be downloading the fcode onto + * fcio_errno pointer to an int that will be used to return any errors + * back to the caller + * Retrurn Values: + * 0 successful download + * >0 otherwise + */ +static int +fcode_load_file(int fcode_fd, char *device, int *fcio_errno) +{ + + fcio_t fcio; + static int dev_fd, fcode_size; + uchar_t *bin; + struct stat stat; + + if (device == NULL || fcio_errno == NULL) { + return (FCODE_LOAD_FAILURE); + } + + *fcio_errno = 0; + if (lseek(fcode_fd, 0, SEEK_SET) == -1) { + perror(MSGSTR(21022, "seek")); + return (FCODE_LOAD_FAILURE); + } + + if (fstat(fcode_fd, &stat) == -1) { + perror(MSGSTR(21023, "fstat")); + return (FCODE_LOAD_FAILURE); + } + + fcode_size = stat.st_size; + + if ((bin = (uchar_t *)malloc(fcode_size)) == NULL) { + (void) fprintf(stderr, + MSGSTR(21013, "Error: Memory allocation failed\n")); + return (FCODE_LOAD_FAILURE); + } + + if (read(fcode_fd, bin, fcode_size) + != fcode_size) { + perror(MSGSTR(21001, "read")); + free(bin); + return (FCODE_LOAD_FAILURE); + } + + if ((dev_fd = open(device, O_RDWR|O_EXCL)) < 0) { + (void) fprintf(stderr, + MSGSTR(21122, "Error: Could not open %s, failed " + "with errno %d\n"), device, errno); + free(bin); + return (FCODE_LOAD_FAILURE); + } + + fcio.fcio_cmd = FCIO_DOWNLOAD_FCODE; + fcio.fcio_xfer = FCIO_XFER_WRITE; + fcio.fcio_ibuf = (caddr_t)bin; + fcio.fcio_ilen = fcode_size; + + if (ioctl(dev_fd, FCIO_CMD, &fcio) != 0) { + (void) close(dev_fd); + *fcio_errno = fcio.fcio_errno; + free(bin); + return (FCODE_IOCTL_FAILURE); + } + + free(bin); + (void) close(dev_fd); + return (FCODE_SUCCESS); +} + +/* + * Searches for and updates the fcode for Emulex HBA cards + * args: FCode file; if NULL only the current FCode + * version is printed + */ + +int +emulex_update(char *file) +{ + + int fd, retval = 0; + int devcnt = 0; + uint_t state = 0, fflag = 0; + static uchar_t bootpath[PATH_MAX]; + int fcode_fd = -1; + static struct utmpx *utmpp = NULL; + di_node_t root; + di_node_t node, sib_node, count_node; + di_minor_t minor_node; + char phys_path[PATH_MAX], *path; + int errnum = 0, fcio_errno = 0; + static uchar_t prom_ver_data[MAXNAMELEN]; + static char ver_file[EMULEX_FCODE_VERSION_LENGTH]; + void (*sigint)(); + int prop_entries = -1; + int *port_data = NULL; + + if (file) { + /* set the fcode download flag */ + fflag++; + + /* check for a valid file */ + if ((fcode_fd = open(file, O_RDONLY)) < 0) { + (void) fprintf(stderr, + MSGSTR(21118, "Error: Could not open %s, failed " + "with errno %d\n"), file, errno); + return (1); + } + + /* check for single user mode */ + while ((utmpp = getutxent()) != NULL) { + if (strstr(utmpp->ut_line, "run-level") && + (strcmp(utmpp->ut_line, "run-level S") && + strcmp(utmpp->ut_line, "run-level 1"))) { + if (q_warn(1)) { + (void) endutxent(); + (void) close(fcode_fd); + return (1); + } + break; + } + } + (void) endutxent(); + + /* get bootpath */ + if (!q_getbootdev((uchar_t *)&bootpath[0]) && + getenv("_LUX_D_DEBUG") != NULL) { + (void) fprintf(stdout, " Bootpath: %s\n", bootpath); + } + } + + /* + * Download the Fcode to all the emulex cards found + */ + + /* Create a snapshot of the kernel device tree */ + if ((root = di_init("/", DINFOCPYALL)) == DI_NODE_NIL) { + (void) fprintf(stderr, MSGSTR(21114, + "Error: Could not get /devices path to " + "Emulex Devices.\n")); + retval++; + } + + /* point to first node which matches emulex driver */ + node = di_drv_first_node("emlxs", root); + + if (node == DI_NODE_NIL) { + /* + * Could not find any emulex cards + */ + (void) di_fini(root); + (void) fprintf(stderr, MSGSTR(21115, + "\n Found Path to %d Emulex Devices.\n"), devcnt); + retval++; + } else { + + count_node = node; + while (count_node != DI_NODE_NIL) { + state = di_state(count_node); + if ((state & DI_DRIVER_DETACHED) + != DI_DRIVER_DETACHED) { + devcnt++; + } + count_node = di_drv_next_node(count_node); + } + (void) fprintf(stdout, MSGSTR(21116, + "\n Found Path to %d Emulex Devices.\n"), devcnt); + } + + + /* + * Traverse device tree to find all emulex cards + */ + while (node != DI_NODE_NIL) { + + state = di_state(node); + if ((state & DI_DRIVER_DETACHED) == DI_DRIVER_DETACHED) { + node = di_drv_next_node(node); + continue; + } + + sib_node = di_child_node(node); + while (sib_node != DI_NODE_NIL) { + + state = di_state(sib_node); + if ((state & DI_DRIVER_DETACHED) != + DI_DRIVER_DETACHED) { + + /* Found an attached node */ + prop_entries = di_prop_lookup_ints( + DDI_DEV_T_ANY, sib_node, + "port", &port_data); + if (prop_entries != -1) { + + /* Found a node with "port" property */ + minor_node = di_minor_next(sib_node, + DI_MINOR_NIL); + break; + } + } + sib_node = di_sibling_node(sib_node); + } + + if (sib_node == DI_NODE_NIL) { + return (1); + } + path = di_devfs_path(sib_node); + (void) strcpy(phys_path, "/devices"); + (void) strncat(phys_path, path, strlen(path)); + di_devfs_path_free(path); + + if (fflag && (strstr((char *)bootpath, + (char *)phys_path) != NULL)) { + (void) fprintf(stderr, + MSGSTR(21117, "Ignoring %s (bootpath)\n"), + phys_path); + node = di_drv_next_node(node); + continue; + } + + if (minor_node) { + (void) strncat(phys_path, ":", 1); + (void) strncat(phys_path, + di_minor_name(minor_node), + strlen(di_minor_name(minor_node))); + } + + (void) fprintf(stdout, + MSGSTR(21107, "\n Opening Device: %s\n"), + phys_path); + + /* Check if the device is valid */ + if ((fd = open(phys_path, O_RDWR)) < 0) { + (void) fprintf(stderr, + MSGSTR(21121, "Error: Could not open %s, failed " + "with errno %d\n"), phys_path, errno); + retval++; + node = di_drv_next_node(node); + continue; + } + + (void) close(fd); + + /* + * Check FCode version present on the adapter + * (at last boot) + */ + memset(prom_ver_data, 0, sizeof (prom_ver_data)); + if (emulex_fcodeversion(node, (uchar_t *)&prom_ver_data[0]) + == 0) { + errnum = 0; + if (strlen((char *)prom_ver_data) == 0) { + (void) fprintf(stdout, MSGSTR(21108, + " Detected FCode Version:\tNo version available for this FCode\n")); + } else { + (void) fprintf(stdout, MSGSTR(21109, + " Detected FCode Version:\t%s\n"), + prom_ver_data); + } + } else { + errnum = 2; /* can't get prom properties */ + retval++; + } + + if (fflag) { + + memset(ver_file, 0, sizeof (ver_file)); + if (emulex_fcode_reader(fcode_fd, "fcode-version", + ver_file, sizeof (ver_file)) == 0) { + (void) fprintf(stdout, MSGSTR(21110, + " New FCode Version:\t\t%s\n"), + ver_file); + } else { + di_fini(root); + (void) close(fcode_fd); + return (1); + } + + /* + * Load the New FCode + * Give warning if file doesn't appear to be correct + */ + if (!q_warn(errnum)) { + /* Disable user-interrupt Control-C */ + sigint = + (void (*)(int)) signal(SIGINT, SIG_IGN); + /* Load FCode */ + (void) fprintf(stdout, MSGSTR(21111, + " Loading FCode: %s\n"), file); + if (fcode_load_file(fcode_fd, phys_path, + &fcio_errno) == FCODE_SUCCESS) { + (void) fprintf(stdout, MSGSTR(21112, + " Successful FCode download: %s\n"), + phys_path); + } else { + handle_emulex_error(fcio_errno, + phys_path); + retval++; + } + + /* Restore SIGINT (user interrupt) setting */ + (void) signal(SIGINT, sigint); + } + } + + node = di_drv_next_node(node); + } + + di_fini(root); + (void) fprintf(stdout, " "); + (void) fprintf(stdout, MSGSTR(125, "Complete\n")); + if (fcode_fd != -1) + (void) close(fcode_fd); + return (retval); + +} + +/* + * Retrieve the version from the card. + * uses PROM properties + */ +static int +emulex_fcodeversion(di_node_t node, uchar_t *ver) { + di_prom_prop_t promprop; + di_prom_handle_t ph; + char *promname; + uchar_t *ver_data = NULL; + int size, found = 0; + + /* check to make sure ver is not NULL */ + if (ver == NULL) { + return (1); + } + + if ((ph = di_prom_init()) == DI_PROM_HANDLE_NIL) { + return (1); + } + + for (promprop = di_prom_prop_next(ph, node, + DI_PROM_PROP_NIL); + promprop != DI_PROM_PROP_NIL; + promprop = di_prom_prop_next(ph, node, promprop)) { + if (((promname = di_prom_prop_name( + promprop)) != NULL) && + (strcmp(promname, "fcode-version") == 0)) { + size = di_prom_prop_data(promprop, &ver_data); + (void) memset(ver, NULL, size); + (void) memcpy(ver, ver_data, size); + found = 1; + } + } + + if (found) { + return (0); + } else { + return (1); + } +} + +/* + * Retrieves information from the Emulex fcode + * + * Given a pattern, this routine will look for this pattern in the fcode + * file and if found will return the pattern value + * + * possible patterns are manufacturer and fcode-version + */ +int +emulex_fcode_reader(int fcode_fd, char *pattern, char *pattern_value, + uint32_t pattern_value_size) { + int32_t i = 0; + uint32_t n = 0; + uint32_t b = 0; + char byte1; + char byte2; + char byte3; + char byte4; + char buffer1[EMULEX_READ_BUFFER_SIZE]; + char buffer2[EMULEX_READ_BUFFER_SIZE]; + uint32_t plen, image_size; + struct stat stat; + uchar_t *image; + + /* Check the arguments */ + if (!fcode_fd || !pattern_value || pattern_value_size < 8) { + return (1); + } + + if (fstat(fcode_fd, &stat) == -1) { + perror(MSGSTR(21023, "fstat")); + return (1); + } + image_size = stat.st_size; + if (image_size < 2) { + return (1); + } + if ((image = (uchar_t *)calloc(image_size, 1)) == NULL) { + (void) fprintf(stderr, + MSGSTR(21013, "Error: Memory allocation failed\n")); + return (1); + } + + /* Read the fcode image file */ + lseek(fcode_fd, 0, SEEK_SET); + read(fcode_fd, image, image_size); + + /* Initialize */ + bzero(buffer1, sizeof (buffer1)); + bzero(buffer2, sizeof (buffer2)); + /* Default pattern_value string */ + strcpy((char *)pattern_value, "<unknown>"); + plen = strlen(pattern); + n = 0; + b = 0; + i = 0; + + /* Search entire image for pattern string */ + while (i <= (image_size - 2)) { + /* Read next two bytes */ + byte1 = image[i++]; + byte2 = image[i++]; + + /* Check second byte first due to endianness */ + + /* Save byte in circular buffer */ + buffer1[b++] = byte2; + if (b == sizeof (buffer1)) { + b = 0; + } + + /* Check byte for pattern match */ + if (pattern[n++] != byte2) { + /* If no match, then reset pattern */ + n = 0; + } else { + /* + * If complete pattern has been matched then + * exit loop + */ + if (n == plen) { + goto found; + } + } + + + /* Check first byte second due to endianness */ + /* Save byte in circular buffer */ + buffer1[b++] = byte1; + if (b == sizeof (buffer1)) { + b = 0; + } + /* Check byte for pattern match */ + if (pattern[n++] != byte1) { + /* If no match, then reset pattern */ + n = 0; + } else { + /* + * If complete pattern has been matched + * then exit loop + */ + if (n == plen) { + goto found; + } + } + } + + /* Not found. Try again with different endianess */ + + /* Initialize */ + bzero(buffer1, sizeof (buffer1)); + bzero(buffer2, sizeof (buffer2)); + n = 0; + b = 0; + i = 0; + + /* Search entire 32bit endian image for pattern string */ + while (i <= (image_size - 4)) { + /* Read next four bytes */ + byte1 = image[i++]; + byte2 = image[i++]; + byte3 = image[i++]; + byte4 = image[i++]; + + /* Save byte in circular buffer */ + buffer1[b++] = byte4; + if (b == sizeof (buffer1)) { + b = 0; + } + + /* Check byte for pattern match */ + if (pattern[n++] != byte4) { + /* If no match, then reset pattern */ + n = 0; + } else { + /* + * If complete pattern has been matched then exit loop + */ + if (n == plen) { + goto found; + } + } + + /* Save byte in circular buffer */ + buffer1[b++] = byte3; + if (b == sizeof (buffer1)) { + b = 0; + } + + /* Check byte for pattern match */ + if (pattern[n++] != byte3) { + /* If no match, then reset pattern */ + n = 0; + } else { + /* + * If complete pattern has been matched then exit loop + */ + if (n == plen) { + goto found; + } + } + + /* Save byte in circular buffer */ + buffer1[b++] = byte2; + if (b == sizeof (buffer1)) { + b = 0; + } + + /* Check byte for pattern match */ + if (pattern[n++] != byte2) { + /* If no match, then reset pattern */ + n = 0; + } else { + /* + * If complete pattern has been matched then exit loop + */ + if (n == plen) { + goto found; + } + } + + /* Save byte in circular buffer */ + buffer1[b++] = byte1; + if (b == sizeof (buffer1)) { + b = 0; + } + + /* Check byte for pattern match */ + if (pattern[n++] != byte1) { + /* If no match, then reset pattern */ + n = 0; + } else { + /* + * If complete pattern has been matched then exit loop + */ + if (n == plen) { + goto found; + } + } + } + + free(image); + return (1); + +found: + free(image); + + /* Align buffer and eliminate non-printable characters */ + for (i = 0; i < (sizeof (buffer1)-plen); i++) { + byte1 = buffer1[b++]; + if (b == sizeof (buffer1)) { + b = 0; + } + /* Zero any non-printable characters */ + if (byte1 >= 33 && byte1 <= 126) { + buffer2[i] = byte1; + } else { + buffer2[i] = 0; + } + } + + /* + * Scan backwards for first non-zero string. This will be the + * version string + */ + for (i = sizeof (buffer1)-plen-1; i >= 0; i--) { + if (buffer2[i] != 0) { + for (; i >= 0; i--) { + if (buffer2[i] == 0) { + i++; + strncpy((char *)pattern_value, + &buffer2[i], pattern_value_size); + break; + } + } + break; + } + } + return (0); +} + +/* + * error handling routine to handle emulex error conditions + */ +static void +handle_emulex_error(int fcio_errno, char *phys_path) { + if (fcio_errno == EMLX_IMAGE_BAD) { + fprintf(stderr, MSGSTR(21119, + "Error: Fcode download failed. " + "Bad fcode image.\n")); + } else if (fcio_errno == EMLX_IMAGE_INCOMPATIBLE) { + fprintf(stderr, MSGSTR(21120, + "Error: Fcode download failed. Fcode is not " + "compatible with card.\n")); + } else { + (void) fprintf(stderr, MSGSTR(21036, + "Error: Driver interface FCIO_DOWNLOAD_FCODE failed\n")); + (void) fprintf(stderr, + MSGSTR(21113, + "Error: FCode download failed: %s\n"), + phys_path); + } +} diff --git a/usr/src/cmd/luxadm/setboot.c b/usr/src/cmd/luxadm/setboot.c new file mode 100644 index 0000000000..909d312111 --- /dev/null +++ b/usr/src/cmd/luxadm/setboot.c @@ -0,0 +1,257 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * I18N message number ranges + * This file: 6000 - 6499 + * Shared common messages: 1 - 1999 + */ + + + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/param.h> +#include <sys/mnttab.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/openpromio.h> + + +/* + * For i18n + */ +#include <stgcom.h> + + +/* + * 128 is the size of the largest (currently) property name + * 8192 - MAXPROPSIZE - sizeof (int) is the size of the largest + * (currently) property value, viz. nvramrc. + * the sizeof(uint_t) is from struct openpromio + */ +#define MAXPROPSIZE 128 +#define MAXVALSIZE (8192 - MAXPROPSIZE - sizeof (uint_t)) + +#define BOOTDEV_PROP_NAME "boot-device" + +static int getbootdevname(char *, char *); +static int setprom(unsigned, unsigned, char *); +extern int devfs_dev_to_prom_name(char *, char *); + +/* + * Call getbootdevname() to get the absolute pathname of boot device + * and call setprom() to set the boot-device variable. + */ +int +setboot(unsigned int yes, unsigned int verbose, char *fname) +{ + char bdev[MAXPATHLEN]; + + if (!getbootdevname(fname, bdev)) { + (void) fprintf(stderr, MSGSTR(6000, + "Cannot determine device name for %s\n"), + fname); + return (errno); + } + + return (setprom(yes, verbose, bdev)); +} + +/* + * Read the mnttab and resolve the special device of the fs we are + * interested in, into an absolute pathname + */ +static int +getbootdevname(char *bootfs, char *bdev) +{ + FILE *f; + char *fname; + char *devname; + struct mnttab m; + struct stat sbuf; + int mountpt = 0; + int found = 0; + + devname = bootfs; + + if (stat(bootfs, &sbuf) < 0) { + perror(MSGSTR(6001, "stat")); + return (0); + } + + switch (sbuf.st_mode & S_IFMT) { + case S_IFBLK: + break; + default: + mountpt = 1; + break; + } + + if (mountpt) { + fname = MNTTAB; + f = fopen(fname, "r"); + if (f == NULL) { + perror(fname); + return (0); + } + + while (getmntent(f, &m) == 0) { + if (strcmp(m.mnt_mountp, bootfs)) + continue; + else { + found = 1; + break; + } + } + + (void) fclose(f); + + if (!found) { + return (0); + } + devname = m.mnt_special; + } + + if (devfs_dev_to_prom_name(devname, bdev) != 0) { + perror(devname); + return (0); + } + + return (1); +} + +/* + * setprom() - use /dev/openprom to read the "boot_device" variable and set + * it to the new value. + */ +static int +setprom(unsigned yes, unsigned verbose, char *bdev) +{ + struct openpromio *pio; + int fd; + char save_bootdev[MAXVALSIZE]; + + if ((fd = open("/dev/openprom", O_RDWR)) < 0) { + perror(MSGSTR(6002, "Could not open openprom dev")); + return (errno); + } + + pio = (struct openpromio *)malloc(sizeof (struct openpromio) + + MAXVALSIZE + MAXPROPSIZE); + + if (pio == (struct openpromio *)NULL) { + perror(MSGSTR(6003, " Error: Unable to allocate memory.")); + return (errno); + } + + pio->oprom_size = MAXVALSIZE; + (void) strcpy(pio->oprom_array, BOOTDEV_PROP_NAME); + + if (ioctl(fd, OPROMGETOPT, pio) < 0) { + perror(MSGSTR(6004, "openprom getopt ioctl")); + return (errno); + } + + /* + * save the existing boot-device, so we can use it if setting + * to new value fails. + */ + (void) strcpy(save_bootdev, pio->oprom_array); + + if (verbose) { + (void) fprintf(stdout, + MSGSTR(6005, + "Current boot-device = %s\n"), pio->oprom_array); + (void) fprintf(stdout, MSGSTR(6006, + "New boot-device = %s\n"), bdev); + } + + if (!yes) { + (void) fprintf(stdout, MSGSTR(6007, + "Do you want to change boot-device " + "to the new setting? (y/n) ")); + switch (getchar()) { + case 'Y': + case 'y': + break; + default: + return (0); + } + } + + /* set the new value for boot-device */ + + pio->oprom_size = (int)strlen(BOOTDEV_PROP_NAME) + 1 + + (int)strlen(bdev); + + (void) strcpy(pio->oprom_array, BOOTDEV_PROP_NAME); + (void) strcpy(pio->oprom_array + (int)strlen(BOOTDEV_PROP_NAME) + 1, + bdev); + + if (ioctl(fd, OPROMSETOPT, pio) < 0) { + perror(MSGSTR(6008, "openprom setopt ioctl")); + return (errno); + } + + /* read back the value that was set */ + + pio->oprom_size = MAXVALSIZE; + (void) strcpy(pio->oprom_array, BOOTDEV_PROP_NAME); + + if (ioctl(fd, OPROMGETOPT, pio) < 0) { + perror(MSGSTR(6009, "openprom getopt ioctl")); + return (errno); + } + + if (strcmp(bdev, pio->oprom_array)) { + + /* could not set the new device name, set the old one back */ + + perror(MSGSTR(6010, + "Could not set boot-device, reverting to old value")); + pio->oprom_size = (int)strlen(BOOTDEV_PROP_NAME) + 1 + + (int)strlen(save_bootdev); + + (void) strcpy(pio->oprom_array, BOOTDEV_PROP_NAME); + (void) strcpy(pio->oprom_array + + (int)strlen(BOOTDEV_PROP_NAME) + 1, + save_bootdev); + + if (ioctl(fd, OPROMSETOPT, pio) < 0) { + perror(MSGSTR(6011, "openprom setopt ioctl")); + return (errno); + } + + } + + (void) close(fd); + + return (0); +} diff --git a/usr/src/cmd/luxadm/x86_adm.c b/usr/src/cmd/luxadm/x86_adm.c new file mode 100644 index 0000000000..a53b2b92fe --- /dev/null +++ b/usr/src/cmd/luxadm/x86_adm.c @@ -0,0 +1,533 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <hbaapi.h> +#include <errno.h> +#include <fcntl.h> +#include <string.h> +#include <sys/fibre-channel/fcio.h> +#include <sys/fibre-channel/impl/fc_error.h> +#include <sys/scsi/adapters/scsi_vhci.h> +#include "common.h" +#include "errorcodes.h" +#include <locale.h> + +/* The i18n catalog */ +nl_catd l_catd; + +void +i18n_catopen() { + static int fileopen = 0; + + if (setlocale(LC_ALL, "") == NULL) { + (void) fprintf(stderr, + "Cannot operate in the locale requested. " + "Continuing in the default C locale\n"); + } + if (!fileopen) { + l_catd = catopen("a5k_g_fc_i18n_cat", NL_CAT_LOCALE); + if (l_catd == (nl_catd)-1) { + return; + } + fileopen = 1; + } + return; + +} + +/* + * Given an error number, this functions + * calls the get_errString() to print a + * corresponding error message to the stderr. + * get_errString() always returns an error + * message, even in case of undefined error number. + * So, there is no need to check for a NULL pointer + * while printing the error message to the stdout. + * + * RETURNS: N/A + * + */ +void +print_errString(int errnum, char *devpath) +{ + +char *errStr; + + errStr = get_errString(errnum); + + if (devpath == NULL) { + (void) fprintf(stderr, + "%s \n\n", errStr); + } else { + (void) fprintf(stderr, + "%s - %s.\n\n", errStr, devpath); + } + + /* free the allocated memory for error string */ + if (errStr != NULL) + (void) free(errStr); +} + +static void terminate() { + fprintf(stdout, MSGSTR(2506, "Unsupported")); + fprintf(stdout, "\n"); + exit(1); +} + +/*ARGSUSED*/ +int adm_display_config(char **a) { + terminate(); + return (1); +} + +/*ARGSUSED*/ +void adm_download(char **a, char *b) { + terminate(); +} + +/*ARGSUSED*/ +void up_encl_name(char **a, int b) { + terminate(); +} + +void adm_failover(char **argv) { + int path_index = 0, err = 0, fd; + char path_class[MAXNAMELEN]; + char client_path[MAXPATHLEN]; + char *path_phys = NULL, *trailingMinor; + sv_switch_to_cntlr_iocdata_t iocsc; + + (void) memset(path_class, 0, sizeof (path_class)); + (void) strcpy(path_class, argv[path_index++]); + if ((strcmp(path_class, "primary") != 0) && + (strcmp(path_class, "secondary") != 0)) { + (void) fprintf(stderr, + MSGSTR(2300, "Incorrect pathclass\n")); + exit(-1); + } + + if ((fd = open("/devices/scsi_vhci:devctl", O_RDWR)) < 0) { + print_errString(L_OPEN_PATH_FAIL, "/devices/scsi_vhci:devctl"); + exit(-1); + } + + iocsc.client = client_path; + iocsc.class = path_class; + + while (argv[path_index] != NULL) { + path_phys = + get_slash_devices_from_osDevName(argv[path_index++], + STANDARD_DEVNAME_HANDLING); + if ((path_phys == NULL) || + (strstr(path_phys, "/devices/scsi_vhci") == NULL)) { + (void) fprintf(stderr, + MSGSTR(2301, "Incorrect pathname\n")); + close(fd); + exit(-1); + } + + strcpy(iocsc.client, path_phys + strlen("/devices")); + + /* Now chop off the trailing ":xxx" portion if present */ + if ((trailingMinor = strrchr(iocsc.client, ':')) != NULL) { + trailingMinor[0] = '\0'; + } + + if (ioctl(fd, SCSI_VHCI_SWITCH_TO_CNTLR, &iocsc) != 0) { + switch (errno) { + case EALREADY: + err = L_SCSI_VHCI_ALREADY_ACTIVE; + break; + case ENXIO: + err = L_INVALID_PATH; + break; + case EIO: + err = L_SCSI_VHCI_NO_STANDBY; + break; + case ENOTSUP: + err = L_SCSI_VHCI_FAILOVER_NOTSUP; + break; + case EBUSY: + err = L_SCSI_VHCI_FAILOVER_BUSY; + break; + case EFAULT: + default: + err = L_SCSI_VHCI_ERROR; + } + } + + if (err != 0) { + close(fd); + print_errString(err, path_phys); + exit(-1); + } + } + + close(fd); +} + +/*ARGSUSED*/ +int adm_inquiry(char **a) { + terminate(); + return (1); +} + +/*ARGSUSED*/ +void pho_probe() { + terminate(); +} + +/*ARGSUSED*/ +void non_encl_probe() { + terminate(); +} + +/*ARGSUSED*/ +void adm_led(char **a, int b) { + terminate(); +} + +/*ARGSUSED*/ +void up_password(char **a) { + terminate(); +} + +/*ARGSUSED*/ +int adm_reserve(char *path) { + terminate(); + return (1); +} + +/*ARGSUSED*/ +int adm_release(char *path) { + terminate(); + return (1); +} + +/*ARGSUSED*/ +int adm_start(char **a) { + terminate(); + return (1); +} + +/*ARGSUSED*/ +int adm_stop(char **a) { + terminate(); + return (1); +} + +/*ARGSUSED*/ +int adm_power_off(char **a, int b) { + terminate(); + return (1); +} + +int +adm_forcelip(char **argv) +{ + int path_index = 0, fd; + uint64_t wwn; + fcio_t fcio; + HBA_HANDLE handle; + HBA_ADAPTERATTRIBUTES hbaAttrs; + HBA_PORTATTRIBUTES portAttrs; + HBA_FCPTARGETMAPPINGV2 *map; + HBA_STATUS status; + int count, adapterIndex, portIndex, mapIndex; + char name[256]; + int matched, ret = 0, wwnCompare = 0, ntries; + char *physical = NULL, *slash_OSDeviceName = NULL; + + if ((status = loadLibrary())) { + /* loadLibrary print out error msg */ + return (ret++); + } + for (path_index = 0; argv[path_index] != NULL; path_index++) { + + if (is_wwn(argv[path_index])) { + (void) sscanf(argv[path_index], "%016llx", &wwn); + wwnCompare = 1; + } else if (!is_path(argv[path_index])) { + print_errString(L_INVALID_PATH, argv[path_index]); + ret++; + continue; + } + if (!wwnCompare) { + /* Convert the paths to phsyical paths */ + physical = get_slash_devices_from_osDevName(argv[path_index], + STANDARD_DEVNAME_HANDLING); + if (!physical) { + print_errString(L_INVALID_PATH, argv[path_index]); + ret++; + continue; + } + } + + count = getNumberOfAdapters(); + + matched = 0; + for (adapterIndex = 0; adapterIndex < count; adapterIndex ++) { + status = HBA_GetAdapterName(adapterIndex, (char *)&name); + if (status != HBA_STATUS_OK) { + /* May have been DR'd */ + continue; + } + handle = HBA_OpenAdapter(name); + if (handle == 0) { + /* May have been DR'd */ + continue; + } + + if (getAdapterAttrs(handle, name, &hbaAttrs)) { + /* Should never happen */ + HBA_CloseAdapter(handle); + continue; + } + + /* Loop over all HBA Ports */ + for (portIndex = 0; portIndex < hbaAttrs.NumberOfPorts; + portIndex++) { + if (getAdapterPortAttrs(handle, name, portIndex, + &portAttrs)) { + continue; + } + + matched = 0; + if (is_wwn(argv[path_index])) { + if (wwn == wwnConversion( + portAttrs.NodeWWN.wwn) || + wwn == wwnConversion( + portAttrs.PortWWN.wwn)) { + matched = 1; + } + } else { + slash_OSDeviceName = get_slash_devices_from_osDevName( + portAttrs.OSDeviceName, STANDARD_DEVNAME_HANDLING); + if (!slash_OSDeviceName) { + continue; + } else { + if (strncmp(physical, slash_OSDeviceName, + strlen(slash_OSDeviceName) - + strlen(strrchr(slash_OSDeviceName, ':'))) + == 0) { + matched = 1; + } + free(slash_OSDeviceName); + } + } + + if (!matched) { + if (!fetch_mappings(handle, portAttrs.PortWWN, &map)) { + /* + * matchr_mapping checks the arg + * so we pass argv here. + */ + mapIndex = match_mappings(argv[path_index], map); + if (mapIndex >= 0) { + matched = 1; + } + } else { + continue; + } + } + + if (matched) { + if ((fd = open(portAttrs.OSDeviceName, + O_RDONLY | O_EXCL)) == -1) { + print_errString(L_OPEN_PATH_FAIL, + portAttrs.OSDeviceName); + return (ret++); + } + + fcio.fcio_cmd = FCIO_RESET_LINK; + fcio.fcio_xfer = FCIO_XFER_WRITE; + /* + * Reset the local loop here (fcio_ibuf = 0). + * Reset a remote loop on the Fabric by + * passing its node wwn (fcio_len = sizeof(nwwn) + * and fcio_ibuf = (caddr_t)&nwwn) to the port driver. + */ + (void) memset(&wwn, 0, sizeof (wwn)); + fcio.fcio_ilen = sizeof (wwn); + fcio.fcio_ibuf = (caddr_t)&wwn; + + for (ntries = 0; ntries < RETRY_FCIO_IOCTL; ntries++) { + errno = 0; + if (ioctl(fd, FCIO_CMD, &fcio) != 0) { + /* + * When port is offlined, qlc + * returns the FC_OFFLINE error and errno + * is set to EIO. + * We do want to ignore this error, + * especially when an enclosure is + * removed from the loop. + */ + if (fcio.fcio_errno == FC_OFFLINE) + break; + if ((errno == EAGAIN) && + (ntries+1 < RETRY_FCIO_IOCTL)) { + /* wait WAIT_FCIO_IOCTL */ + (void) usleep(WAIT_FCIO_IOCTL); + continue; + } + I_DPRINTF("FCIO ioctl failed.\n" + "Error: %s. fc_error = %d (0x%x)\n", + strerror(errno), fcio.fcio_errno, + fcio.fcio_errno); + close(fd); + print_errString(L_FCIO_FORCE_LIP_FAIL, + portAttrs.OSDeviceName); + return (ret++); + } else { + break; /* ioctl succeeds. */ + } + } + close(fd); + if (ntries == RETRY_FCIO_IOCTL) { + print_errString(L_FCIO_FORCE_LIP_FAIL, + portAttrs.OSDeviceName); + return (ret++); + } + } + if (matched) + break; /* for HBA port for loop */ + } + if (matched) /* HBA adapter for loop */ + break; + } + + if (!matched) { + print_errString(L_INVALID_PATH, argv[path_index]); + ret++; + } + } + HBA_FreeLibrary(); + return (ret); +} + +/*ARGSUSED*/ +void adm_bypass_enable(char **argv, int bypass_flag) { + terminate(); +} + +/*ARGSUSED*/ +int adm_port_offline_online(char **a, int b) { + terminate(); + return (1); +} + +/*ARGSUSED*/ +void display_link_status(char **a) { + terminate(); +} + +/*ARGSUSED*/ +void dump_map(char **argv) { + terminate(); +} + +/*ARGSUSED*/ +int adm_display_port(int a) { + terminate(); + return (1); +} + +/*ARGSUSED*/ +int adm_port_loopback(char *a, int b) { + terminate(); + return (1); +} + +/*ARGSUSED*/ +int hotplug_e(int todo, char **argv, int verbose_flag, int force_flag) { + terminate(); + return (1); +} + +/*ARGSUSED*/ +int +setboot(unsigned int yes, unsigned int verbose, char *fname) +{ + terminate(); + return (1); +} + +/*ARGSUSED*/ +int hotplug(int todo, char **argv, int verbose_flag, int force_flag) { + terminate(); + return (1); +} + +/*ARGSUSED*/ +int adm_check_file(char **argv, int flag) { + terminate(); + return (1); +} + +/*ARGSUSED*/ +int sysdump(int verbose) { + terminate(); + return (1); +} + +/*ARGSUSED*/ +int fcal_update(unsigned int verbose, char *file) { + terminate(); + return (1); +} + +/*ARGSUSED*/ +int q_qlgc_update(unsigned int verbose, char *file) { + terminate(); + return (1); +} + +/*ARGSUSED*/ +int emulex_update(char *file) { + terminate(); + return (1); +} + +/*ARGSUSED*/ +int emulex_fcode_reader(int fcode_fd, char *pattern, char *pattern_value, + uint32_t pattern_value_size) { + terminate(); + return (1); +} + +/*ARGSUSED*/ +void dump(char **argv) { + terminate(); +} + +/*ARGSUSED*/ +int h_insertSena_fcdev() { + terminate(); + return (1); +} |
