hotplug.c revision fcf3ce441efd61da9bb2884968af01cb7c1452cc
/*
* 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");
}