/*
* 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
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*LINTLIBRARY*/
/*
* This module is part of the Fibre Channel Interface library.
*
*/
/*
* I18N message number ranges
* This file: 10500 - 10999
* Shared common messages: 1 - 1999
*/
/* Includes */
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <unistd.h>
#include <sys/autoconf.h>
#include <ctype.h> /* for isprint */
#include <dirent.h> /* for DIR */
#include <nl_types.h>
#include <locale.h>
#include <thread.h>
#include <synch.h>
#include <l_common.h>
#include <stgcom.h>
#include <l_error.h>
#include <g_state.h>
#include <libdevinfo.h>
/* Defines */
/* Bus strings - for internal use by g_get_path_type() only */
struct str_type {
char *string;
};
{"pci@", PCI_BUS},
{"sbus@", SBUS},
{"fcoe", FCOE},
{NULL, 0}
};
/*
* Strings below were used before cougar driver(qlc) was proposed.
* {"scsi/", FC_PCI_FCA},
* {"fibre-channel/", FC_PCI_FCA},
*/
{"SUNW,socal@", FC4_SOCAL_FCA},
{NULL, 0}
};
{"/sf@", FC4_SF_XPORT},
{"/fp@", FC_GEN_XPORT},
{NULL, 0}
};
struct _enclDisk {
char *vid;
char *pid;
};
/*
* the non-unique portion of the product identifier sufficient for
* comparison. This table needs to be updated as new drives are supported
* in this table. Currently, the v880 and v890 are the only shipping products
* that utilize the SUNWGS type enclosure. SENA is EOL'd. The risk of new
* devices being added that do not match an entry in this table is small but it
* does exist.
*/
{"SUN", "SENA"},
{"SUN", "SUNWGS"},
{"FUJITSU", "MA"},
{"HITACHI", "DK"},
{"HITACHI", "HU"},
{"SEAGATE", "ST"},
};
/* i18n */
/* Internal Functions */
/*
* Allocate space for and return a pointer to a string
* on the stack. If the string is null, create
* an empty string.
* Use g_destroy_data() to free when no longer used.
*/
char *
g_alloc_string(char *s)
{
char *ns;
if (s == (char *)NULL) {
} else {
}
}
return (ns);
}
/*
* This routine is a wrapper for free.
*/
void
{
A_DPRINTF(" g_destroy_data: Free\'ed buffer at 0x%x\n",
data);
}
/*
* Dump a structure in hexadecimal.
*/
void
{
int i;
int n;
char *p;
char s[256];
for (p = s; *p; p++) {
*p = ' ';
}
p = hdr;
while (nbytes > 0) {
p = s;
for (i = 0; i < n; i++) {
}
for (i = BYTES_PER_LINE-n; i > 0; i--) {
}
for (i = 0; i < n; i++) {
}
}
nbytes -= n;
src += n;
}
}
/*
* 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
{
char *dotdot;
char *previous_slash;
/* Find the first "/../" in the string */
return (0);
}
/*
* If the [0] character is '/' and "../" immediatly
* follows it, then we can strip the ../
*
*
*/
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';
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
*/
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 *
{
char *tmp;
int cnt;
/* return NULL if path is NULL */
return (NULL);
}
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] != '/') {
O_DPRINTF("getcwd() failed - %s\n",
return (NULL);
}
/*
*/
} else { /* no "./" so just take everything */
}
}
/*
* 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
*
* See if there's a real file out there. If not,
* we have a dangling link and we ignore it.
*/
O_DPRINTF("stat() failed for %s- %s\n",
return (NULL);
}
O_DPRINTF("lstat() failed for - %s\n",
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.
*/
return (g_alloc_string(source));
}
if (cnt < 0) {
O_DPRINTF("readlink() failed - %s\n",
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
*/
/*
* 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] != '/') {
O_DPRINTF("Internal error... corrupt path.\n");
return (NULL);
}
/* Now strip off just the directory path */
/* and append the new link */
/*
* 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 */
}
}
/* Never reach here */
}
/*
* Function for getting physical pathnames
*
* This function can handle 3 different inputs.
*
* 1) Inputs of the form cN
* directory for a device that is conected to the
* controller with number 'N' and then getting
* the physical pathname of the controller.
* The format of the controller pathname is
* The physical pathname is returned.
*
* These are identified by being a link
* The physical path they are linked to is returned.
*
* 3) Inputs of the form /devices/...
* These are actual physical names.
* They are not converted.
*/
char *
{
char s[MAXPATHLEN];
int found_flag = 0;
int status = 0;
int i;
/* return invalid path if path NULL */
return (NULL);
}
/*
* See if the form is cN
* Must handle scenaro where there is a file cN in this directory
* Bug ID: 1184633
*
* the form cNdNsN (See man disks).
*/
/*
* Further qualify cN entry
*/
((int)strlen(s) >= 5)) {
goto exit;
}
for (i = 1; i < (int)strlen(s); i++) {
if ((s[i] < '0') || (s[i] > '9')) {
goto exit;
}
}
/*
* path does not point to a file or file is of form cN
*/
P_DPRINTF(" g_get_physical_name: "
"Found entry of the form cN n=%s len=%d\n",
&s[1], strlen(s));
goto exit;
}
continue;
/*
* Silently Ignore for now any names
* not stating with c
*/
continue;
"Warning: Cannot stat %s\n"),
namebuf);
continue;
}
"Warning: %s is not a symbolic link\n"),
namebuf);
continue;
}
/*
* found link to device in /devices
*
* Further qualify to be sure I have
* not found entry of the form c10
* when I am searching for c1
*/
P_DPRINTF(" g_get_physical_name: "
found_flag = 1;
break;
}
}
}
if (found_flag) {
goto exit;
}
/*
* Convert from device name to controller name
*/
}
goto exit;
}
if (status == -1)
goto exit;
"%s: lstat() failed - %s\n"),
goto exit;
}
/*
*/
/*
* Path is not a linked file so must be
* a physical path
*/
/* Make sure a full path as that is required. */
if (strstr(s, "/devices")) {
result = g_alloc_string(s);
} else {
return (NULL);
}
/*
* Check for this format:
* ./ssd@0,1:g,raw
*/
if (s[0] == '.') {
} else {
}
}
}
} else {
/*
* Entry is linked file
* so follow link to physical name
*/
}
exit:
return (result);
}
/*
* Function to open a device
*/
int
{
(void) printf("O_WRONLY,");
(void) printf("O_RDWR,");
} else {
(void) printf("O_RDONLY,");
}
(void) printf("O_NDELAY,");
}
(void) printf("O_APPEND,");
}
(void) printf("O_DSYNC,");
}
(void) printf("O_RSYNC,");
}
(void) printf("O_SYNC,");
}
(void) printf("O_NOCTTY,");
}
(void) printf("O_CREAT,");
}
(void) printf("O_EXCL,");
}
(void) printf("O_TRUNC,");
}
(void) printf("\n");
}
/* Open retries introduced due to bugid 4473337 */
errno = 0;
O_DPRINTF(" Object_open: Retried:%d %d %s\n",
(void) usleep(WAIT_OBJECT_OPEN);
}
if (fd < 0) {
}
return (fd);
}
/*
* Return a pointer to a string telling us the name of the command.
*/
char *
{
/*
* Names of commands. Must have SCMD_UNKNOWN at end of list.
*/
struct scsi_command_name {
int command;
char *name;
register struct scsi_command_name *c;
break;
return (c->name);
}
/*
* Function to create error message containing
* scsi request sense information
*/
void
{
int blkno;
case KEY_NO_SENSE:
break;
case KEY_RECOVERABLE_ERROR:
break;
case KEY_NOT_READY:
(void) sprintf(msg_string,
MSGSTR(10503,
"Device Not ready."
" Error: Random Retry Failed: %s\n."),
break;
case KEY_MEDIUM_ERROR:
break;
case KEY_HARDWARE_ERROR:
break;
case KEY_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:
break;
case KEY_BLANK_CHECK:
break;
case KEY_VENDOR_UNIQUE:
break;
case KEY_COPY_ABORTED:
break;
case KEY_ABORTED_COMMAND:
(void) sprintf(msg_string,
MSGSTR(10505,
"Aborted command."
" Error: Random Retry Failed.\n"));
break;
case KEY_EQUAL:
break;
case KEY_VOLUME_OVERFLOW:
break;
case KEY_MISCOMPARE:
break;
case KEY_RESERVED:
"Reserved value found"));
break;
default:
break;
}
}
/*
* rq->es_add_info[ADD_SENSE_CODE],
* rq->es_add_info[ADD_SENSE_QUAL_CODE]);
*/
}
}
}
/*
* Special string dump for error message
*/
static void
{
int i;
int n;
char *p;
char s[256];
for (p = s; *p; p++) {
*p = ' ';
}
p = hdr;
while (nbytes > 0) {
"%s", p);
p = s;
for (i = 0; i < n; i++) {
"%02x ",
src[i] & 0xff);
}
for (i = BYTES_PER_LINE-n; i > 0; i--) {
" ");
}
" ");
for (i = 0; i < n; i++) {
"%c",
}
}
nbytes -= n;
src += n;
}
}
/*
* This routine is a wrapper for malloc. It allocates pre-zeroed space,
* and checks the return value so the caller doesn't have to.
*/
void *
{
void *ptr;
A_DPRINTF(" g_zalloc: Allocated 0x%x bytes "
return (ptr);
}
/*
* Open up the i18n catalog.
* Returns:
* 0 = O.K.
* -1 = Failed (Will revert to default strings)
*/
int
g_i18n_catopen(void)
{
static int fileopen = 0;
"Cannot operate in the locale requested. "
"Continuing in the default C locale\n");
}
if (mutex_lock(&mp) != 0) {
return (-1);
}
if (!fileopen) {
(void) mutex_unlock(&mp);
return (-1);
}
fileopen = 1;
}
(void) mutex_unlock(&mp);
return (0);
}
/* Macro used by g_get_path_type() */
search_arr_ptr++) {\
found = 1;\
break;\
}\
}
/*
* Input : A NULL terminated string
* This string is checked to be an absolute device path
* Output :
* The FCA type and Xport type if found in the path on success
* 0 on Failure
*
* Examples of valid device strings :
*
* Non Fabric FC driver :
*
* Fabric FC driver (fp) :
* - offical device path for Qlogic 2202 with proper FCODE
* as of 12/99.
*
*/
{
/* Path passed must be an absolute device path */
return (0); /* Invalid path */
}
/* if mpxio device, need to convert from vhci to phci */
return (0);
}
for (i = 0; i < pathcnt; i++) {
p_on = i;
break;
p_st = i;
}
}
}
/* on_line path */
(void) strcpy(drvr_path1,
} else {
/* standby or path0 */
(void) strcpy(drvr_path1,
}
}
if (found == 0) {
/* No valid bus string - so not a valid path */
return (0);
}
if (found != 0) {
}
/*
* continue to check xport even without valid FCA string.
* This is to support 3rd party FCA vendor on Leadville stack.
*/
if (found == 0) {
return (path_type);
} else {
/*
* if leadville tranport is detected and fca is not set yet,
* set fca flag to generic FC_FCA_MASK.
*/
(!(path_type & FC_FCA_MASK))) {
path_type |= FC_FCA_MASK;
}
}
/*
* A quick sanity check to make sure that we dont have
* a combination that is not possible
*/
path_type) ||
path_type)) {
path_type = 0;
}
return (path_type);
}
/*
* g_get_port_path(char *, portlist_t *)
* Purpose: Find all port nexus paths for a particular driver
* Input: portdrvr
* set to name of driver for which to find the paths
* Output: portlist
* allocated structure to hold paths found
* user must call g_free_portlist(portlist_t *) to
* free allocated memory
*/
int
{
char *tmppath;
/* return invalid argument if *portdrvr or *portlist is NULL */
return (L_INVALID_ARG);
}
/* Create a snapshot of the kernel device tree */
if (root == DI_NODE_NIL) {
return (L_DEV_SNAPSHOT_FAILED);
}
/* point to first node which matches portdrvr */
if (node == DI_NODE_NIL) {
/*
* Could not find driver node
*/
return (L_PORT_DRIVER_NOT_FOUND);
else
return (L_PHYS_PATH_NOT_FOUND);
}
while (node) {
/* point to first minor node which matches node */
/* if we have a minor node use it */
while (minor_node) {
/*
* Is this a devctl or pseudo node?
* If not, skip it.
* Soc+ HBA port device paths such as:
* are pseudo nodes as of S9 so we need to
* include those as well.
*/
if (di_minor_nodetype(minor_node) &&
DDI_NT_NEXUS) &&
DDI_PSEUDO))) {
continue;
}
/*
* Prepend '/devices' to path
* Note: The path returned from di_devfs_path
* does NOT begin with '/devices'.
* '/devices' is considered a mount point
*/
/*
* Verify that the path is validly constructed
*/
return (L_STAT_ERROR);
}
/* allocate memory and copy constructed path */
return (L_MALLOC_FAILED);
}
}
}
/*
* Destroy the snapshot and return
*/
return (0);
}
/*
* Free the allocated portlist structure
*/
void
{
int x = 0;
/* return if portlist is NULL */
return;
}
}
}
}
/*
*/
{
int i;
for (i = 0; enclDiskTbl[i].vid; i++) {
return (B_TRUE);
}
}
return (B_FALSE);
}