/*
* 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 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 <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <nl_types.h>
#include <dirent.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. */
{ "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 int Options;
extern const int OPTION_CAPF;
/* Internal functions */
/* SENA and Individual FC device Hotplug */
timestruc_t *);
static int h_pre_remove_dev(Hotplug_Devlist *,
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
{
int err;
&rmt_time)) != 0) {
return (err);
}
"Please hit <RETURN> when you have finished"
" adding Fibre Channel Enclosure(s)/Device(s): "));
(void) getchar();
return (err);
}
/*
* no ses devices inserted.
* No need to call h_post_insert_encl().
*/
" No new enclosure(s) were added!!\n\n"));
return (0);
} else {
return (L_LSTAT_ES_DIR_ERROR);
}
}
/*
* the original mod time no need to call
* h_post_insert_encl().
*/
" No new enclosure(s) were added!!\n\n"));
return (0);
}
return (err);
}
return (0);
}
/*
* gets the devices state - check for disk's reservations.
*
* RETURNS:
* 0 if OK
* non-zero otherwise
*/
static int
{
int err;
MSGSTR(5501,
"Inquiry failed for %s\n"),
return (err);
}
FC_PORT_B, verbose_flag)) != 0) {
return (err);
}
} else {
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) {
L_RESERVED)) ||
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
{
MSGSTR(133,
"Error: Failed to fork a process.\n"));
return (-1);
} else if (pid == 0) {
/* child process */
MSGSTR(5502,
" Error: execvp() failed to run "
"the command:"));
}
/* let parent know about the error. */
}
}
/* parent executes the following. */
MSGSTR(5503,
"Error: waitpid() failed.\n"));
return (-1);
}
/* child failed to run the command string. */
return (-1);
}
return (0);
}
/*
* frees the hotplug disk list structure.
*
* RETURNS:
* N/A
*/
void
{
while (*hotplug_dlist != NULL) {
list = *hotplug_dlist;
}
}
/*
* 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
{
int err;
force_flag, verbose_flag)) != 0) {
if (err == L_DEV_BUSY) {
*busy_flag = 1;
} else {
return (err);
}
}
}
} else {
force_flag)) != 0) {
if (err == L_DEV_BUSY) {
*busy_flag = 1;
} else {
return (err);
}
}
}
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
{
int i = 1;
" (either busy or reserved) by the host:\n"));
if (enc_type == DAK_ENC_TYPE) {
" %d: Box Name: \"%s\" slot %d\n"),
} else {
" %d: Box Name: \"%s\" front slot %d\n"),
}
} else {
if (enc_type == DAK_ENC_TYPE) {
" %d: Box Name: \"%s\" slot %d\n"),
} else {
" %d: Box Name: \"%s\" rear slot %d\n"),
}
}
" %d: Device %s\n"),
" %d: Box: %s\n"),
}
}
/* Get the user input and continue accordingly. */
MSGSTR(5507,
" device(s) or\n'q' to Quit and run the"
" subcommand with\n-F (force) option. [Default: s]: "));
for (;;) {
choice[0] == '\0') {
break;
}
" Enter an appropriate option [s,<CR>,q]: "));
}
} else {
}
return (0);
}
/*
* prints the warning message.
*
* RETURNS:
* None.
*/
static void
{
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
{
int enc_type;
char *physpath;
/* Initialize structures and pointers here */
#ifdef DEBUG
"DEBUG: luxadm: hotplug() entering for \"%s\" ...\n",
#endif
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.
*/
(void) l_free_box_list(&box_list);
return (err);
}
&path_struct, verbose_flag)) != 0) {
/* Make sure we have a device path. */
todo == REMOVE_DEVICE) {
if (err != -1) {
(void) print_errString(err,
argv[path_index]);
err = 0;
continue;
}
&path_struct, verbose_flag)) != 0) {
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;
}
err = 0;
continue;
}
}
if (path_struct->ib_path_flag) {
} else {
if (err != -1) {
(void) print_errString(err,
argv[path_index]);
} else {
MSGSTR(33,
" Error: converting"
" %s to physical path.\n"
" Invalid pathname.\n"),
argv[path_index]);
}
err = 0;
continue;
}
}
if (path_struct->slot_valid ||
} else {
}
/* obtain phci */
exit(-1);
}
for (i = 0; i < pathcnt; i++) {
MAXPATHSTATE) {
p_pw = i;
break;
}
p_on = i;
}
p_st = i;
}
}
}
/* matching input pwwn */
/* on_line path */
} else {
/* standby or path0 */
}
} else {
}
!= 0) {
return (err);
}
/* public or fabric loop device */
"This operation is not "
"supported in this topology.\n"));
exit(-1);
}
if (todo == REPLACE_DEVICE) {
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)) {
found_nullwwn = 1;
/*
* set dev_path to NULL,
* if disk has null wwn.
*/
dev_location = SENA;
goto getinfo;
}
}
verbose_flag) != 0) {
/* Could be a non-photon disk device */
if ((todo == REMOVE_DEVICE) &&
(dtype == DTYPE_DIRECT ||
dtype == DTYPE_SEQUENTIAL)) {
verbose_flag)) != 0) {
goto done;
}
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",
goto loop;
}
continue;
}
dev_location = SENA;
&p_pathstruct, 0)) != 0) {
goto done;
}
goto done;
}
}
&l_state, verbose_flag)) != 0) {
goto done;
}
/* could be removing a photon */
if (todo == REMOVE_DEVICE) {
/*
* Need the select ID (tid) for the IB.
*/
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",
*dev_path = '\0';
/*
* Check if any disk in this photon
* is reserved by another host
*/
if (!force_flag) {
for (
i = 0;
i++) {
L_RESERVED) ||
L_RESERVED) ||
L_RESERVED) ||
L_RESERVED)) {
reserve_flag = 1;
}
}
}
goto loop;
}
MSGSTR(5512,
"Error: %s already exists!!\n"),
argv[path_index]);
goto done;
}
if (!path_struct->slot_valid) {
/* We are passing the disks path */
verbose_flag)) != 0) {
goto done;
}
}
if (path_struct->f_flag) {
(void) strcpy(node_wwn_s,
} else {
(void) strcpy(node_wwn_s,
}
if (found_nullwwn) {
goto loop;
}
if ((todo == INSERT_DEVICE) &&
(code != S_NOT_INSTALLED))) {
"already be present.\n"),
argv[path_index]);
if (path_struct->f_flag) {
!= L_NO_PATH_FOUND) &&
continue;
} else {
!= L_NO_PATH_FOUND) &&
continue;
}
}
/* Check if disk is reserved */
if (path_struct->f_flag) {
L_RESERVED) ||
L_RESERVED)) {
reserve_flag = 1;
}
} else {
L_RESERVED) ||
L_RESERVED)) {
reserve_flag = 1;
}
}
}
loop:
if ((disk_list = (Hotplug_Devlist *)
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
*/
wwn_list, verbose_flag)) != 0) {
(void) g_free_multipath(
}
goto done;
}
}
argv[path_index]);
if (dev_location == SENA) {
}
goto done;
}
}
goto done;
}
}
if (reserve_flag || busy_flag) {
if (reserve_flag)
if (busy_flag)
if (bsyRsrv_dskLst_head == NULL) {
} else {
}
reserve_flag = 0;
busy_flag = 0;
} else if (disk_list_head == NULL) {
} else {
}
}
if (bsyRsrv_dskLst_head != NULL) {
goto done;
}
(void) h_free_hotplug_dlist(&bsyRsrv_dskLst_head);
goto done;
}
}
if (disk_list_head != NULL) {
goto done;
}
force_flag)) != 0) {
goto done;
}
if (disk_list_head != NULL) {
if (todo == REMOVE_DEVICE) {
"\nHit <Return> after "
"removing the device(s)."));
} else {
"\nHit <Return> after "
"inserting the device(s)."));
}
(void) getchar();
enc_type)) != 0) {
goto done;
}
}
}
done:
(void) l_free_box_list(&box_list);
(void) g_free_wwn_list(&wwn_list);
return (err);
}
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
{
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.
*
* 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 *
{
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
*
* S10: Do NOT ignore dangling links, pointing to devfs nodes.
*/
return (g_alloc_string(source));
}
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
*
* 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.
*
* 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 *
{
char s[MAXPATHLEN];
int status = 0;
/* return invalid path if path NULL */
return (NULL);
}
/*
* S10: If string is devfs node we allow failed lstat.
*/
/* 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);
}
/*
* handle expert-mode hotplug commands
*
* return 0 iff all is okay
*/
int
{
char *ptr;
int exit_code;
switch (todo) {
case DEV_ONLINE:
case DEV_OFFLINE:
case DEV_GETSTATE:
case DEV_RESET:
/* get physical name */
argv[0]);
return (1);
}
if (verbose_flag) {
MSGSTR(5516,
"phys path = \"%s\"\n"),
}
/* acquire rights to hack on device */
"Error: can't acquire \"%s\": %s\n"),
return (1);
}
switch (todo) {
case DEV_ONLINE:
break;
case DEV_OFFLINE:
break;
case DEV_GETSTATE:
&devstate)) == 0) {
}
break;
case DEV_RESET:
break;
}
if (exit_code != 0) {
}
/* all done now -- release device */
break;
/* for hotplugging bus operations */
case BUS_QUIESCE:
case BUS_UNQUIESCE:
case BUS_GETSTATE:
case BUS_RESET:
case BUS_RESETALL:
/* get physical name */
NULL) {
argv[0]);
return (1);
}
if (verbose_flag) {
}
/* acquire rights to hack on device */
/* delete leaf part from path_phys. */
/* obtain phci */
return (1);
}
for (i = 0; i < pathcnt; i++) {
MAXPATHSTATE) {
p_pw = i;
break;
}
p_on = i;
}
p_st = i;
}
}
}
/* matching input pwwn */
/* on_line path */
} else {
/* standby or path0 */
}
} else {
if (ptr) {
*ptr = '\0';
} else {
return (1);
}
}
MSGSTR(5521,
" Error: can't acquire bus node from"
" the path \"%s\": %s\n"),
return (1);
}
switch (todo) {
case BUS_QUIESCE:
break;
case BUS_UNQUIESCE:
break;
case BUS_GETSTATE:
&devstate)) == 0) {
}
break;
case BUS_RESET:
break;
case BUS_RESETALL:
break;
}
if (exit_code != 0) {
}
/* all done now -- release device */
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
int verbose_flag, int force_flag)
{
int err;
/* Initialize pointers */
}
MSGSTR(157,
"stopping: %s...."), device_name);
return (err);
}
force_flag)) != 0) {
MSGSTR(160,
"\nonlining: %s\n"), device_name);
return (err);
}
return (err);
}
return (0);
}
/*
* Prepares a SENA enclosure or SENA FC_AL device
*
* RETURNS:
* 0 if OK
* non-zero otherwise.
*/
static int
int verbose_flag, int force_flag)
{
/* entire photon is being removed */
force_flag, verbose_flag)) != 0) {
return (err);
}
return (0);
}
/* if device is an individual sena disk */
while (dl) {
verbose_flag)) == 0)
break;
}
return (L_GET_STATUS_FAILED);
}
/* check if disk has null wwn */
if (f_r) {
(void) strncpy(node_wwn_s,
} else {
(void) strncpy(node_wwn_s,
}
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 =
} else {
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,
verbose_flag)) != 0) {
(void) print_errString(err,
MSGSTR(5530,
" %s: could not turn "
"on LED\n"),
}
} else {
/*
* Drive is there so start it.
*/
if ((err = l_encl_status_page_funcs
(SET_DRV_ON, 0, todo,
verbose_flag)) != 0) {
(void) print_errString(err,
MSGSTR(5531,
" could not enable"
" %s\n"),
}
}
break;
case REMOVE_DEVICE:
/*
* if disk has null wwn, then
* there is no need to check the
*/
if (found_null_wwn == 1) {
"Device %s has "
"null WWN.\n",
}
goto rmv;
}
if (hotplug_dev->f_flag) {
if (
== S_NOT_INSTALLED) {
MSGSTR(86,
" Notice: %s may already"
" be removed.\n"),
return (0);
}
} else if (
== S_NOT_INSTALLED) {
MSGSTR(86,
" Notice: %s may already"
" be removed.\n"),
return (0);
}
rmv:
} else {
}
MSGSTR(157,
"stopping: %s...."), device_name);
return (err);
}
force_flag)) != 0) {
MSGSTR(160,
"\nonlining: %s\n"), device_name);
(void) g_dev_start(dev_path, 0);
return (err);
}
/*
* Take the drive off the loop
* and blink the LED.
*/
slot, verbose_flag)) != 0) {
MSGSTR(5539,
" %s: could not blink"
" the yellow LED\n"),
}
}
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
{
char code;
/*
* 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",
while (dl) {
FC_TOP_FABRIC)) {
/* public or fabric loop device */
"This operation is not "
"supported in this topology.\n"));
return (0);
}
break;
}
}
}
}
if (dl) {
"Photon \"%s\" not removed."
" Onlining Drives in enclosure.\n"),
}
return (0);
}
/*
* Remove logical nodes for this
* photon, this includes ses and
* In Solaris7, disks with -C option
* 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;
!= 0) {
return (err);
}
}
} else {
MSGSTR(5541,
" Logical Nodes being removed"
}
}
}
return (err);
}
return (0);
}
/* post hotplug operations for a SENA disk. */
if (enc_type == DAK_ENC_TYPE) {
" Drive in Box Name \"%s\" slot %d"),
} else {
if (tid & 0x10) {
" Drive in Box Name \"%s\" rear slot %d"),
} else {
" Drive in Box Name \"%s\" front slot %d"),
}
}
while (dl) {
verbose_flag)) == 0)
break;
}
return (L_GET_STATUS_FAILED);
}
code = 0;
verbose_flag)) != 0) || (code != 0)) {
if (err) {
} else if (todo == REMOVE_DEVICE) {
MSGSTR(5544,
"\n Warning: Device has not been"
" removed from the enclosure\n"
" and is still on the loop."));
return (0);
} else {
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 >
err)) {
MSGSTR(5546,
"\n Warning: Disk status is"
" Not OK!\n\n"));
return (0);
}
(void) sleep(PHOTON_SPINUP_DELAY);
if (wait_spinup_flag++ == 0) {
" Waiting for the disk to spin up:"));
} else {
}
timeout++;
}
if (wait_spinup_flag) {
}
switch (todo) {
case INSERT_DEVICE:
/* check loop map that drive is present */
for (;;) {
while (dl) {
&map, verbose_flag)) != 0) {
MSGSTR(5548,
" Error: Could not get"
" map for %s.\n"),
ses_path);
return (err);
}
goto loop3;
}
}
if (timeout > PHOTON_SPINUP_TIMEOUT) {
MSGSTR(5549,
" Warning: Device not in"
" loop map.\n"));
return (0);
}
if (wait_map_flag++ == 0) {
MSGSTR(5550,
" Waiting for the device "
"to appear in the loop map:"));
} else {
}
timeout++;
(void) sleep(PHOTON_SPINUP_DELAY);
}
if (wait_map_flag) {
}
/*
* Run drvconfig and disks to create
* logical nodes
*/
for (;;) {
/* pass "disks" as cmdStrg */
nArg = 3;
MSGSTR(5551,
" Could not "
"run drvconfig.\n"));
return (L_DRVCONFIG_ERROR);
}
break;
if (timeout > PHOTON_SPINUP_TIMEOUT) {
MSGSTR(5552,
" Warning: Could not find "
"any node for inserted "
"device\n"));
return (0);
}
if (wait_node_flag++ == 0) {
MSGSTR(5553,
" Waiting for the logical "
"node to be created:"));
} else {
}
timeout++;
(void) sleep(PHOTON_SPINUP_DELAY);
}
if (wait_node_flag) {
}
/*
* 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;
return (L_DISKS_ERROR);
}
/*
* Get a new wwn list here in order to
* get the multiple paths to a newly added
* device.
*/
verbose_flag)) != 0) {
return (err);
}
newWwn_list, 0)) != 0) {
return (err);
}
return (err);
}
break;
case REMOVE_DEVICE:
/*
* TBD
* Need to check all loops.
*/
/* check whether device is still in loop map */
verbose_flag)) != 0) {
return (err);
}
FC_TOP_FABRIC)) {
/* public or fabric loop device */
"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);
}
" Warning: Device still in the loop map.\n"));
return (0);
}
/*
* 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;
return (L_DISKS_ERROR);
}
MSGSTR(5555,
" Logical Nodes being removed"
(void) h_print_logical_nodes(
break;
}
return (0);
}
/*
* directory for the newly added
* enclosures.
*
* RETURNS:
* 0 if OK
* non-zero otherwise
*/
static int
{
return (L_OPEN_ES_DIR_FAILED);
}
return (err);
}
/*
* insert so dir entry is checked at this time.
*/
continue;
(void) print_errString(L_LSTAT_ES_DIR_ERROR,
lname);
continue;
}
break;
}
continue;
/* New enclosure was detected. */
if (found_newlink == 1) {
}
}
}
if (!found_newlink) {
" No new enclosure(s) were added!!\n\n"));
}
(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
int todo, int verbose_flag)
{
int nArg;
(void) sprintf(device_name,
MSGSTR(5557,
"\n Device: %s"),
/*
* 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 == L_NO_DEVICES_FOUND) &&
!= 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) {
/* public or fabric loop device */
"This operation is not "
"supported in this topology.\n"));
return (0);
}
MSGSTR(5558,
" Warning: Device has"
" not been removed from\n"
" the slot and is still"
" in the loop map.\n\n"));
return (0);
}
}
/*
* 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;
return (L_DISKS_ERROR);
}
/* pass "tapes -C as cmdStrg. */
return (L_TAPES_ERROR);
}
return (0);
}
/*
* Gets the last modification time for
* and passes these values to the caller.
*
* RETURNS:
* 0 if OK
* non-zero in case of error
*/
static int
{
/*
* The host doesn't have to have any enclosure device
* configured.
*/
} else {
return (L_LSTAT_ES_DIR_ERROR);
}
} else {
}
return (L_STAT_DEV_DIR_ERROR);
} else {
}
/*
* The host doesn't have to have any tape device
* configured.
*/
} else {
return (L_STAT_RMT_DIR_ERROR);
}
} else {
}
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
*
* RETURNS:
* 0 if OK
* non-zero in case of error
*/
static int
{
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;
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;
return (L_DISKS_ERROR);
}
/* pass "tapes -C as cmdStrg */
return (L_TAPES_ERROR);
}
/* pass "devlinks" as cmdStrg */
nArg = 1;
return (L_DEVLINKS_ERROR);
}
if (!found_newlink) {
" 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
int verbose_flag, int force_flag)
{
int err = 0;
(void) print_errString(err,
goto delete;
}
verbose_flag, force_flag)) != 0) {
(void) print_errString(err,
goto delete;
}
}
continue;
}
if (list == *disk_list_head_ptr)
}
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
{
int err;
/* Do a lip on every loop so that we get the latest loop maps */
if (todo != INSERT_DEVICE) {
return (err);
}
}
while (hotplug_dlist != NULL) {
todo, verbose_flag)) != 0)
(void) print_errString(err,
}
}
return (0);
}
/*
* removes the device's logical paths.
*
* RETURNS:
* 0 if OK
* non-zero otherwise
*/
static int
{
return (L_READ_DEV_DIR_ERROR);
}
/* pass "disks" as cmdStrg */
return (L_DISKS_ERROR);
}
}
MSGSTR(5563,
" Removing Logical Nodes: \n"));
continue;
}
(size_t)MAXPATHLEN) <= 0) {
MSGSTR(5564,
" Error: Could not read %s\n"),
lname);
continue;
}
if (ptr)
*ptr = '\0';
MSGSTR(5565,
"\tRemoving %s\n"),
}
}
}
return (0);
}
/*
* removes the SENA's ses paths.
*
* RETURNS:
* 0 if OK
* non-zero otherwise
*/
static int
{
return (L_READ_DEV_DIR_ERROR);
}
/*
* Remove the ses entries
* of the form ses<#>
*/
continue;
(size_t)MAXPATHLEN) <= 0) {
MSGSTR(5564,
" Error: Could not read %s\n"),
lname);
continue;
}
MSGSTR(5568,
"\tRemoving %s\n"),
lname);
}
}
}
(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
{
int i, found_dev = 0;
"h", "hb", "hbn", "hn", "l", "lb",
"lbn", "ln", "m", "mb", "mbn", "mn",
continue;
continue;
found_dev++;
if (found_dev == 1)
for (i = 0; i <= 7; i++) {
}
}
found_dev = 0;
continue;
found_dev++;
if (found_dev == 1)
ptr++;
ptr++;
for (i = 0, ptr = tape_entries[0];
i++, ptr = tape_entries[i]) {
}
}
}
}
/*
* displays logical paths to a
* device to stdout.
*
* RETURNS:
* 0 if OK
* non-zero otherwise
*/
static int
{
return (L_READ_DEV_DIR_ERROR);
}
MSGSTR(5569,
continue;
}
(size_t)MAXPATHLEN) <= 0) {
continue;
}
if (d1)
*d1 = '\0';
"\t%s\n",
}
}
}
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
{
int i;
switch (todo) {
case INSERT_DEVICE:
break;
case REMOVE_DEVICE:
break;
}
if (enc_type == DAK_ENC_TYPE) {
" %d: Box Name: \"%s\" slot %d\n"),
} else {
" %d: Box Name: \"%s\" front slot %d\n"),
}
} else {
if (enc_type == DAK_ENC_TYPE) {
" %d: Box Name: \"%s\" slot %d\n"),
} else {
" %d: Box Name: \"%s\" rear slot %d\n"),
}
}
" %d: Device name: %s\n"),
" %d: Box name: %s\n"),
}
" Select ID:\t0x%x\n",
if (enc_type == DAK_ENC_TYPE) {
" Location: \tSlot %d \n",
+MAX_DRIVES_DAK/2);
} else {
" Location: \tSlot %d %s \n",
? "front" : "rear");
}
}
}
}
if (todo == REMOVE_DEVICE) {
" SENA (%s)\n"),
} else {
}
" SES Paths:\n"));
while (dl_ses) {
}
} else {
" Device Paths:\n"));
while (dl_multi) {
" %s\n"),
}
}
}
}
"\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 (;;) {
choice[0] == '\0') {
break;
}
" Enter an appropriate option [c,<CR>,q]: "));
}
return (-1);
}
return (0);
}
static int
{
char *link_ptr;
int found_newlink = 0;
return (0);
} else {
return (L_READ_DEV_DIR_ERROR);
}
}
continue;
}
(void) print_errString(L_LSTAT_ES_DIR_ERROR,
lname);
continue;
}
(size_t)MAXPATHLEN) <= 0) {
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.
*/
continue;
if (!g_get_path_type(link_ptr)) {
continue;
}
if (found_newlink == 1) {
" New Logical Nodes under "
}
}
}
}
return (found_newlink);
}
/*
* prints the device state.
*
* RETURNS:
* None.
*/
void
{
if (state & DEVICE_ONLINE) {
if (state & DEVICE_BUSY) {
(void) printf(" ");
}
if (state & DEVICE_DOWN) {
(void) printf(" ");
}
} else {
if (state & DEVICE_OFFLINE) {
if (state & DEVICE_DOWN) {
(void) printf(" ");
}
}
}
(void) printf("\n");
}
/*
* prints the bus state.
*
* RETURNS:
* None.
*/
void
{
if (state == BUS_QUIESCED) {
} else if (state == BUS_ACTIVE) {
} else if (state == BUS_SHUTDOWN) {
}
(void) printf("\n");
}