/*
* 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.
*/
#include "cfga_scsi.h"
#include <libgen.h>
#include <limits.h>
/*
* This file contains helper routines for the SCSI plugin
*/
#if !defined(TEXT_DOMAIN)
#endif
typedef struct strlist {
const char *str;
} strlist_t;
typedef struct {
} errcvt_t;
typedef struct {
int type;
typedef struct {
int type;
/* Function prototypes */
/*
* The string table contains most of the strings used by the scsi cfgadm plugin.
* All strings which are to be internationalized must be in this table.
* Some strings which are not internationalized are also included here.
* Arguments to messages are NOT internationalized.
*/
/*
* The first element (ERR_UNKNOWN) MUST always be present in the array.
*/
/* msg_code num_args, I18N msg_string */
/* ERRORS */
/* Errors with arguments */
/* RCM Errors */
/* Commands */
{CMD_INSERT_DEV, 0, 0, "insert_device"},
{CMD_REMOVE_DEV, 0, 0, "remove_device"},
{CMD_LED_DEV, 0, 0, "led"},
{CMD_LOCATOR_DEV, 0, 0, "locator"},
{CMD_REPLACE_DEV, 0, 0, "replace_device"},
{CMD_RESET_DEV, 0, 0, "reset_device"},
{CMD_RESET_BUS, 0, 0, "reset_bus"},
{CMD_RESET_ALL, 0, 0, "reset_all"},
/* help messages */
{MSG_HELP_USAGE, 0, 0, "\t-x insert_device ap_id [ap_id... ]\n"
"\t-x remove_device ap_id [ap_id... ]\n"
"\t-x replace_device ap_id [ap_id... ]\n"
"\t-x locator[=on|off] ap_id [ap_id... ]\n"
"\t-x led[=LED,mode=on|off|blink] "
"ap_id [ap_id... ]\n"
"\t-x reset_device ap_id [ap_id... ]\n"
"\t-x reset_bus ap_id [ap_id... ]\n"
"\t-x reset_all ap_id [ap_id... ]\n"},
/* hotplug messages */
/* Hotplugging confirmation prompts */
"This operation will suspend activity on SCSI bus: "},
{CONF_UNQUIESCE, 0, 1,
"SCSI bus quiesced successfully.\n"
"It is now safe to proceed with hotplug operation."
"\nEnter y if operation is complete or n to abort"},
{CONF_NO_QUIESCE, 0, 1,
"Proceed with hotplug operation."
"\nEnter y if operation is complete or n to abort"},
/* Misc. */
{WARN_DISCONNECT, 0, 1,
"WARNING: Disconnecting critical partitions may cause system hang."
"\nContinue"},
/* LED messages */
};
char *
led_strs[] = {
"fault",
"power",
"attn",
"active",
"locator",
};
char *
led_mode_strs[] = {
"off",
"on",
"blink",
"faulted",
"unknown",
};
{ SCFGA_LIB_ERR, CFGA_LIB_ERROR },
{ SCFGA_NACK, CFGA_NACK },
{ SCFGA_BUSY, CFGA_BUSY },
{ SCFGA_PRIV, CFGA_PRIV },
{ SCFGA_UNKNOWN_ERR, CFGA_ERROR },
{ SCFGA_ERR, CFGA_ERROR }
};
#define DEV_OP 0
};
};
/*
* SCSI hardware specific commands
*/
/* Command string Command ID Function */
};
{
int i;
for (i = 0; i < N_ERR_CVT_TBL; i++) {
return (err_cvt_tbl[i].cfga_err);
}
}
return (CFGA_ERROR);
}
/*
* Removes duplicate slashes from a pathname and any trailing slashes.
* Returns "/" if input is "/"
*/
static char *
{
int prev_was_slash = 0;
*l_errnop = 0;
return (NULL);
}
return (NULL);
}
prev_was_slash = 0;
if (!prev_was_slash || c != '/') {
*dp++ = c;
}
if (c == '/') {
prev_was_slash = 1;
} else {
prev_was_slash = 0;
}
}
/* Remove trailing slash except if it is the first char */
*(--dp) = '\0';
} else {
*dp = '\0';
}
return (dup);
}
{
int l_errno = 0;
return (SCFGA_LIB_ERR);
}
/* Extract the base(hba) and dynamic(device) component if any */
ret = SCFGA_LIB_ERR;
goto err;
}
/* Remove the dynamic component from the base */
*dyn = '\0';
} else {
}
/* get dyn comp type */
} else {
}
}
/* Create the path */
goto err;
}
return (SCFGA_OK);
err:
return (ret);
}
void
{
return;
}
const char *physpath,
void *arg,
int *l_errnop)
{
int rv;
*l_errnop = 0;
return (SCFGA_LIB_ERR);
}
/* Fix up path for di_init() */
} else if (*root_path != '/') {
*l_errnop = 0;
goto out;
}
/* Remove dynamic component if any */
*cp = '\0';
}
/* Remove minor name if any */
*cp = '\0';
}
/*
* Cached snapshots are always rooted at "/"
*/
init_path = "/";
}
/* Get a snapshot */
ret = SCFGA_LIB_ERR;
goto out;
}
/*
* Lookup the subtree of interest
*/
}
if (walk_root == DI_NODE_NIL) {
ret = SCFGA_LIB_ERR;
goto out;
}
/* Walk the tree */
errno = 0;
if (cmd == SCFGA_WALK_NODE) {
} else if (cmd == SCFGA_WALK_PATH) {
} else {
}
if (rv != 0) {
ret = SCFGA_LIB_ERR;
} else {
*l_errnop = 0;
}
/*FALLTHRU*/
out:
return (ret);
}
const char *func,
char **errstring)
{
int i;
int len;
/*
* Determine if the func has an equal sign; only compare up to
* the equals
*/
};
for (i = 0; i < N_HW_CMDS; i++) {
}
}
return (SCFGA_ERR);
}
int
{
int idx = 0;
/* The string table index and the error id may or may not be same */
} else {
break;
}
}
}
return (idx);
}
/*
* cfga_err() accepts a variable number of message IDs and constructs
* a corresponding error string which is returned via the errstring argument.
* cfga_err() calls dgettext() to internationalize proper messages.
* May be called with a NULL argument.
*/
void
{
int append_newline = 0;
return;
}
/*
* Don't append a newline, the application (for example cfgadm)
* should do that.
*/
append_newline = 0;
}
/*
* This routine accepts a variable number of message IDs and constructs
* a corresponding message string which is printed via the message print
* routine argument.
*/
void
{
char *p = NULL;
return;
}
/* Append a newline after message */
append_newline = 1;
l_errno = 0;
S_FREE(p);
}
/*
* This routine prints the value of an led for a disk.
*/
void
{
return;
}
return;
}
}
/*
* Get internationalized string corresponding to message id
* Caller must free the memory allocated.
*/
char *
{
char *p = NULL;
int l_errno = 0;
return (p);
}
static void
{
int a = 0;
int i = 0, n = 0;
return;
}
n = GET_MSG_NARGS(a); /* 0 implies no additional args */
for (i = 0; i <= n; i++) {
goto out;
}
if (i == 0 && GET_MSG_INTL(a)) {
} else if (i == 0) {
} else {
}
}
}
s = t = NULL;
if (l_errno) {
}
}
if (append_newline) {
len++;
}
goto out;
}
**msgpp = '\0';
}
}
if (append_newline) {
}
/* FALLTHROUGH */
out:
}
}
/*
* Check to see if the given pi_node is the last path to the client device.
*
* Return:
* 0: if there is another path avialable.
* -1: if no other paths available.
*/
static int
{
(pi_state != DI_PATH_STATE_STANDBY)) {
/* it is not last available path */
return (0);
}
/* if anohter pi node is avaialble, return 0 */
pi_state == DI_PATH_STATE_STANDBY)) {
return (0);
}
}
return (-1);
}
char **errstring,
int *l_errnop,
{
*l_errnop = 0;
/* Make sure apid is pathinfo associated apid. */
return (SCFGA_LIB_ERR);
}
return (SCFGA_LIB_ERR);
}
return (SCFGA_LIB_ERR);
}
/* Fix up path for di_init() */
} else if (*root_path != '/') {
*l_errnop = 0;
return (SCFGA_ERR);
}
/* Remove dynamic component if any */
*cp = '\0';
}
/* Remove minor name if any */
*cp = '\0';
}
/*
* Cached snapshots are always rooted at "/"
*/
/* Get a snapshot */
return (SCFGA_ERR);
}
/*
* Lookup the subtree of interest
*/
if (walk_root == DI_NODE_NIL) {
return (SCFGA_LIB_ERR);
}
DI_PATH_NIL) {
/* the path apid not found */
return (SCFGA_APID_NOEXIST);
}
do {
/* check the length first. */
continue;
}
/* compare bus addr. */
found = 1;
break;
}
} while (pi_node != DI_PATH_NIL);
if (!found) {
return (SCFGA_APID_NOEXIST);
}
/* Get client node path. */
if (client_node == DI_NODE_NIL) {
return (SCFGA_ERR);
} else {
if (client_path == NULL) {
return (SCFGA_ERR);
}
if (cmd == SCFGA_DEV_UNCONFIGURE) {
pi_node) != 0) {
/*
* last path. check if unconfiguring
* is okay.
*/
flags |= FLAG_CLIENT_DEV;
return (ret);
}
}
}
}
}
/*
* If an unconfigure fails, cancel the RCM offline.
* Discard any RCM failures so that the devctl
* failure will still be reported.
*/
if (cmd == SCFGA_DEV_UNCONFIGURE)
(void) scsi_rcm_online(dev_list,
}
}
return (ret);
}
const char *physpath,
int *l_errnop)
{
int (*func)(const devctl_hdl_t);
*l_errnop = 0;
state_func = NULL;
type = 0;
for (i = 0; i < N_GET_STATE_CMDS; i++) {
break;
}
}
if (state_func == NULL) {
for (i = 0; i < N_SET_STATE_CMDS; i++) {
break;
}
}
}
return (SCFGA_ERR);
}
/*
* Fix up path for calling devctl.
*/
return (SCFGA_LIB_ERR);
}
/* Remove dynamic component if any */
*cp = '\0';
}
/* Remove minor name */
*cp = '\0';
}
errno = 0;
} else {
}
return (SCFGA_ERR);
}
errno = 0;
/* Only getstate functions require a second argument */
} else {
rv = -1;
*l_errnop = 0;
}
}
/*
* Is device in a known state ? (One of BUSY, ONLINE, OFFLINE)
* BUSY --> One or more device special files are open. Implies online
* ONLINE --> driver attached
* OFFLINE --> CF1 with offline flag set.
* UNKNOWN --> None of the above
*/
int
{
/*
* CF1 without offline flag set is considered unknown state.
* We are in a known state if either CF2 (driver attached) or
* offline.
*/
return (1);
}
return (0);
}
void
{
}
}
/*
* Obtain the devlink from a /devices path
*/
typedef struct walk_link {
char *path;
char len;
char **linkpp;
} walk_link_t;
static int
{
/*
* When path is specified, it's the node path without minor
* name. Therefore, the ../.. prefixes needs to be stripped.
*/
/* line content must have minor node */
return (DI_WALK_CONTINUE);
}
return (DI_WALK_TERMINATE);
}
char *node_path,
char **logpp,
int *l_errnop,
int match_minor)
{
char *minor_path;
return (SCFGA_LIB_ERR);
}
if (match_minor) {
} else {
minor_path = NULL;
}
(void) di_devlink_fini(&hdl);
return (SCFGA_LIB_ERR);
return (SCFGA_OK);
}
int
{
int rv;
/* Remove dynamic component if any */
*cp = '\0';
}
*cp = '\0';
}
/* Remove minor names */
*cp = '\0';
}
*cp = '\0';
}
/* Check if HBA path is component of device path */
return (rv);
}
/* devpath must have '/' and 1 char in addition to hba path */
return (0);
} else {
return (-1);
}
}
int
{
int rv;
*mn1 = '\0';
}
*mn2 = '\0';
}
/* Separate out the minor names */
*mn1++ = '\0';
}
*mn2++ = '\0';
}
return (rv);
}
/*
* Compare minor names
*/
return (0);
return (-1);
return (1);
} else {
}
}