diff options
Diffstat (limited to 'usr/src/cmd/luxadm/hotplug.c')
-rw-r--r-- | usr/src/cmd/luxadm/hotplug.c | 3037 |
1 files changed, 3037 insertions, 0 deletions
diff --git a/usr/src/cmd/luxadm/hotplug.c b/usr/src/cmd/luxadm/hotplug.c new file mode 100644 index 0000000000..2efec8ba3c --- /dev/null +++ b/usr/src/cmd/luxadm/hotplug.c @@ -0,0 +1,3037 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + + +/*LINTLIBRARY*/ + + +/* + * Hotplug program for SENA, RSM and SSA + * subsystems and individual FC_AL devices. + */ + +/* #define _POSIX_SOURCE 1 */ + +/* + * I18N message number ranges + * This file: 5500 - 5999 + * Shared common messages: 1 - 1999 + */ + + +/* Includes */ +#include <stdlib.h> +#include <stdio.h> +#include <sys/file.h> +#include <sys/errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/utsname.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <sys/sunddi.h> +#include <sys/ddi.h> /* for min */ +#include <sys/scsi/scsi.h> +#include <nl_types.h> +#include <dirent.h> +#include <sys/wait.h> +#include <l_common.h> +#include <l_error.h> +#include <stgcom.h> +#include <a_state.h> +#include <a5k.h> +#include <rom.h> +#include "hot.h" +#include "common.h" +#include "luxadm.h" + + +/* Internal variables. */ +static char *cmdStrg[][4] = { + { "disks", "-C", 0, 0 }, + { "disks", 0, 0, 0 }, + { "drvconfig", "-i", "ssd", 0 }, + { "drvconfig", 0, 0, 0 }, + { "devlinks", 0, 0, 0 }, + { "tapes", "-C", 0, 0 } +}; + +/* External variables */ +extern char *dtype[]; /* From adm.c */ +extern int Options; +extern const int OPTION_CAPF; + +/* Internal functions */ +/* SENA and Individual FC device Hotplug */ +static int h_pre_insert_encl_dev(timestruc_t *, timestruc_t *, + timestruc_t *); +static int h_post_insert_dev(timestruc_t, timestruc_t); +static int h_pre_remove_dev(Hotplug_Devlist *, + WWN_list *wwn_list, int, int); +static int h_post_remove_dev(Hotplug_Devlist *, int, int); +static int h_pre_hotplug(Hotplug_Devlist **, + WWN_list *, int, int, int); +static int h_post_hotplug(Hotplug_Devlist *, + WWN_list *, int, int, int, int); +static int h_post_insert_encl(timestruc_t); +static int h_pre_hotplug_sena(Hotplug_Devlist *, + WWN_list *, int, int, int); +static int h_post_hotplug_sena(Hotplug_Devlist *, + WWN_list *, int, int, int, int); +static int h_remove_ses_nodes(struct dlist *); +static int h_print_list_warn(Hotplug_Devlist *, int, int); +static int h_display_logical_nodes(struct dlist *); +static void h_print_logical_nodes(struct dlist *); +static int h_remove_nodes(struct dlist *); +static int h_print_list(Hotplug_Devlist *, int *, int); +static int h_get_fcdev_state(char *, char *, int, int *, int *, int); +static int h_chk_dev_busy(Hotplug_Devlist *, + WWN_list *, int *, int, int); +static int h_execCmnd(char **, int); +int hotplug(int, char **, int, int); +int h_insertSena_fcdev(); +static int h_find_new_device_link(char *, timestruc_t); + + + +/* + * Assists the user in hot inserting FC_AL + * individual device(s) and SENA enclosure(s). + * + * RETURNS: + * 0 if OK + * non-zero otherwise + */ +int +h_insertSena_fcdev() +{ +timestruc_t ses_time, dsk_time, rmt_time; +int err; +struct stat ses_stat; + + if ((err = h_pre_insert_encl_dev(&ses_time, &dsk_time, + &rmt_time)) != 0) { + return (err); + } + (void) fprintf(stdout, MSGSTR(5500, + "Please hit <RETURN> when you have finished" + " adding Fibre Channel Enclosure(s)/Device(s): ")); + (void) getchar(); + + if ((err = h_post_insert_dev(dsk_time, rmt_time)) != 0) { + return (err); + } + + if (stat(SES_DIR, &ses_stat) < 0) { + /* + * Non existence of /dev/es dir indicates + * no ses devices inserted. + * No need to call h_post_insert_encl(). + */ + if (errno == ENOENT) { + (void) fprintf(stdout, MSGSTR(5662, + " No new enclosure(s) were added!!\n\n")); + return (0); + } else { + return (L_LSTAT_ES_DIR_ERROR); + } + } + + /* + * if the latest mod time of /dev/es is not newer than + * the original mod time no need to call + * h_post_insert_encl(). + */ + if ((&ses_time != (timestruc_t *)NULL) && + !(NEWER(ses_stat.st_ctim, ses_time))) { + (void) fprintf(stdout, MSGSTR(5662, + " No new enclosure(s) were added!!\n\n")); + return (0); + } + if ((err = h_post_insert_encl(ses_time)) != 0) { + return (err); + } + return (0); +} + + + +/* + * gets the devices state - check for disk's reservations. + * + * RETURNS: + * 0 if OK + * non-zero otherwise + */ +static int +h_get_fcdev_state(char *fc_dev, char *path_phys, int force_flag, + int *busy_flag, int *reserve_flag, int verbose_flag) +{ +int err; +L_inquiry inq; +L_disk_state l_disk_state; + + + if ((err = g_get_inquiry(path_phys, &inq)) != 0) { + (void) fprintf(stderr, + MSGSTR(5501, + "Inquiry failed for %s\n"), + path_phys); + return (err); + } + if (inq.inq_port) { + if ((err = l_get_disk_port_status(path_phys, &l_disk_state, + FC_PORT_B, verbose_flag)) != 0) { + return (err); + } + } else { + if ((err = l_get_disk_port_status(path_phys, &l_disk_state, + FC_PORT_A, verbose_flag)) != 0) { + return (err); + } + } + + /* + * Don't print error msg. if disk is reserved + * and tried to be removed from the same port. + * If force flag is set, remove the disk without + * checking the disk reservations. + */ + if (!force_flag) { + if (((inq.inq_port) && + (l_disk_state.g_disk_state.d_state_flags[FC_PORT_B] & + L_RESERVED)) || + ((!inq.inq_port) && + (l_disk_state.g_disk_state.d_state_flags[FC_PORT_A] & + L_RESERVED))) { + *reserve_flag = 1; + } + } + return (0); +} + + +/* + * Forks a child process and let the child to + * execute a given command string by calling the + * the execvp() function. Then, the parent process + * waits for the child to exit. Once the parent process + * is notified by the kernel with the termination of + * the child, then the parent checks for the exit + * status of the child and return to the caller with -1 in case + * of error and zero otherwise. + * + * RETURNS: + * 0 if OK + * non-zero otherwise + */ +int +h_execCmnd(char *argStr[], int nArg) +{ +pid_t pid; +int ix, status; + + if ((pid = fork()) < 0) { + (void) fprintf(stderr, + MSGSTR(133, + "Error: Failed to fork a process.\n")); + return (-1); + } else if (pid == 0) { + /* child process */ + if (execvp(argStr[0], argStr) < 0) { + (void) fprintf(stderr, + MSGSTR(5502, + " Error: execvp() failed to run " + "the command:")); + for (ix = 0; ix < nArg; ix++) { + (void) fprintf(stderr, + " %s", argStr[ix]); + } + (void) fprintf(stderr, "\n"); + /* let parent know about the error. */ + exit(ENOEXEC); + } + } + + /* parent executes the following. */ + if (waitpid(pid, &status, 0) != pid) { + (void) fprintf(stderr, + MSGSTR(5503, + "Error: waitpid() failed.\n")); + return (-1); + } + if (WIFEXITED(status) && + WEXITSTATUS(status) == ENOEXEC) { + /* child failed to run the command string. */ + return (-1); + } + return (0); + +} + + + + +/* + * frees the hotplug disk list structure. + * + * RETURNS: + * N/A + */ +void +h_free_hotplug_dlist(Hotplug_Devlist **hotplug_dlist) +{ +Hotplug_Devlist *list = NULL; + + while (*hotplug_dlist != NULL) { + list = *hotplug_dlist; + *hotplug_dlist = (*hotplug_dlist)->next; + (void) g_free_multipath(list->seslist); + (void) g_free_multipath(list->dlhead); + (void) free((void *)list); + } +} + + +/* + * finds whether device (SENA or an FCAL device) is busy or not. + * + * OUTPUT: + * busy_flag = 1 (if device busy) + * + * RETURNS: + * 0 if O.K. + * non-zero otherwise + */ +static int +h_chk_dev_busy(Hotplug_Devlist *hotplug_dev, WWN_list *wwn_list, + int *busy_flag, int force_flag, int verbose_flag) +{ +int err; +struct dlist *dlist; + + if (hotplug_dev->dev_type == DTYPE_ESI) { + if ((err = l_offline_photon(hotplug_dev, wwn_list, + force_flag, verbose_flag)) != 0) { + if (err == L_DEV_BUSY) { + *busy_flag = 1; + } else { + return (err); + } + } + for (dlist = hotplug_dev->dlhead; + dlist != NULL; dlist = dlist->next) { + (void) g_online_drive(dlist->multipath, + force_flag); + } + } else { + if ((err = g_offline_drive(hotplug_dev->dlhead, + force_flag)) != 0) { + if (err == L_DEV_BUSY) { + *busy_flag = 1; + } else { + return (err); + } + } + (void) g_online_drive(hotplug_dev->dlhead, force_flag); + } + return (0); +} + + + +/* + * prints the given list to stdout, + * gets the input from user whether + * to skip the busy devices or quit + * and passes that input to the calling + * function. + * + * OUTPUT: + * int *action + * s = Skip + * q = Quit + * + * RETURNS: + * 0 if OK + * non-zero otherwise + */ +static int +h_print_list(Hotplug_Devlist *bsyRsrv_disk_list, int *action, int enc_type) +{ +Hotplug_Devlist *list; +int i = 1; +char choice[2]; + + (void) fprintf(stdout, + MSGSTR(5504, "The list of devices being used" + " (either busy or reserved) by the host:\n")); + for (list = bsyRsrv_disk_list; list != NULL; list = list->next, i++) { + if ((list->dev_type == DTYPE_DIRECT) && + (list->dev_location == SENA)) { + if (list->f_flag != NULL) { + if (enc_type == DAK_ENC_TYPE) { + (void) fprintf(stdout, MSGSTR(5663, + " %d: Box Name: \"%s\" slot %d\n"), + i, list->box_name, list->slot); + } else { + (void) fprintf(stdout, MSGSTR(137, + " %d: Box Name: \"%s\" front slot %d\n"), + i, list->box_name, list->slot); + } + } else { + if (enc_type == DAK_ENC_TYPE) { + (void) fprintf(stdout, MSGSTR(5663, + " %d: Box Name: \"%s\" slot %d\n"), + i, list->box_name, list->slot + (MAX_DRIVES_DAK/2)); + } else { + (void) fprintf(stdout, MSGSTR(136, + " %d: Box Name: \"%s\" rear slot %d\n"), + i, list->box_name, list->slot); + } + } + } else if (((list->dev_type == DTYPE_DIRECT) || + (list->dev_type == DTYPE_SEQUENTIAL)) && + (list->dev_location == NON_SENA)) { + (void) fprintf(stdout, MSGSTR(5505, + " %d: Device %s\n"), + i, list->dev_name); + } else if (list->dev_type == DTYPE_ESI) { + (void) fprintf(stdout, MSGSTR(5506, + " %d: Box: %s\n"), + i, list->box_name); + } + } + + /* Get the user input and continue accordingly. */ + (void) fprintf(stdout, + MSGSTR(5507, + "\n\nPlease enter 's' or <CR> to Skip the \"busy/reserved\"" + " device(s) or\n'q' to Quit and run the" + " subcommand with\n-F (force) option. [Default: s]: ")); + for (;;) { + (void) gets(choice); + if (choice[0] == 'q' || choice[0] == 'Q' || + choice[0] == 's' || choice[0] == 'S' || + choice[0] == '\0') { + break; + } + (void) fprintf(stdout, MSGSTR(5508, + " Enter an appropriate option [s,<CR>,q]: ")); + } + if (choice[0] == 'q' || choice[0] == 'Q') { + *action = QUIT; + } else { + *action = SKIP; + } + (void) fprintf(stdout, "\n\n"); + return (0); +} + + + +/* + * prints the warning message. + * + * RETURNS: + * None. + */ +static void +h_prt_warning() +{ + (void) fprintf(stderr, + MSGSTR(5509, + "\n WARNING!!! Please ensure that no" + " filesystems are mounted on these device(s).\n" + " All data on these devices should have been" + " backed up.\n\n\n")); +} + + + +/* + * handle helper-mode hotplug commands + * + * RETURNS: + * 0 if OK + * non-zero otherwise + */ +int +hotplug(int todo, char **argv, int verbose_flag, int force_flag) +{ +char ses_path[MAXPATHLEN], dev_path[MAXPATHLEN]; +char *path_phys = NULL, code, node_wwn_s[WWN_S_LEN]; +char inq_path[MAXNAMELEN], *ptr = NULL; +uchar_t node_wwn[WWN_SIZE], port_wwn[WWN_SIZE]; +int tid, slot, path_index, dtype, f_r, err = 0; +int al_pa, i, dev_location, found_nullwwn = 0; +int busy_flag = 0, reserve_flag = 0, action = 0; +int pathcnt = 1; +L_state l_state; +gfc_map_t map; +Path_struct *path_struct; +WWN_list *wwn_list = NULL; +Box_list *box_list; +Hotplug_Devlist *disk_list, *disk_list_head, *disk_list_tail; +Hotplug_Devlist *bsyRsrv_dskLst_head, *bsyRsrv_dskLst_tail; +int enc_type; +L_inquiry inq; +char *physpath; +Path_struct *p_pathstruct; +char temp2path[MAXPATHLEN]; +mp_pathlist_t pathlist; +int p_pw = 0, p_on = 0, p_st = 0; + + /* Initialize structures and pointers here */ + disk_list_head = disk_list_tail = (Hotplug_Devlist *)NULL; + bsyRsrv_dskLst_head = (Hotplug_Devlist *)NULL; + bsyRsrv_dskLst_tail = (Hotplug_Devlist *)NULL; + map.dev_addr = NULL; + +#ifdef DEBUG + (void) fprintf(stderr, + "DEBUG: luxadm: hotplug() entering for \"%s\" ...\n", + argv[0] ? argv[0] : "<null ptr>"); +#endif + if ((err = l_get_box_list(&box_list, 0)) != 0) { + return (err); + } + + if (todo == REMOVE_DEVICE) { + (void) h_prt_warning(); + } + + /* + * At this point user want to insert or remove + * one or more pathnames they've specified. + */ + if ((err = g_get_wwn_list(&wwn_list, verbose_flag)) != 0) { + (void) l_free_box_list(&box_list); + return (err); + } + for (path_index = 0; argv[path_index] != NULL; path_index++) { + if ((err = l_convert_name(argv[path_index], &path_phys, + &path_struct, verbose_flag)) != 0) { + /* Make sure we have a device path. */ + (void) strcpy(inq_path, argv[path_index]); + if (((ptr = strstr(inq_path, ",")) != NULL) && + ((*(ptr + 1) == 'f') || (*(ptr + 1) == 'r') || + (*(ptr +1) == 's')) && + todo == REMOVE_DEVICE) { + if (err != -1) { + (void) print_errString(err, + argv[path_index]); + err = 0; + continue; + } + *ptr = NULL; + slot = path_struct->slot; + f_r = path_struct->f_flag; + if ((err = l_convert_name(inq_path, &path_phys, + &path_struct, verbose_flag)) != 0) { + (void) fprintf(stderr, "\n"); + (void) fprintf(stderr, + MSGSTR(33, + " Error: converting" + " %s to physical path.\n" + " Invalid pathname.\n"), + argv[path_index]); + if (err != -1) { + (void) print_errString(err, + argv[path_index]); + } + err = 0; + continue; + } + if ((err = print_devState(argv[path_index], + path_struct->p_physical_path, + f_r, slot, verbose_flag)) != 0) { + err = 0; + continue; + } + } + if (path_struct->ib_path_flag) { + path_phys = path_struct->p_physical_path; + } else { + if (err != -1) { + (void) print_errString(err, + argv[path_index]); + } else { + (void) fprintf(stderr, "\n"); + (void) fprintf(stderr, + MSGSTR(33, + " Error: converting" + " %s to physical path.\n" + " Invalid pathname.\n"), + argv[path_index]); + } + err = 0; + continue; + } + } + if (path_struct->slot_valid || + strstr(path_phys, DRV_NAME_SSD)) { + dtype = DTYPE_DIRECT; + } else if (strstr(path_phys, SLSH_DRV_NAME_ST)) { + dtype = DTYPE_SEQUENTIAL; + } else { + dtype = DTYPE_ESI; + } + + if (strstr(path_phys, SCSI_VHCI) != NULL) { + /* obtain phci */ + (void) strcpy(temp2path, path_phys); + if (err = g_get_pathlist(temp2path, &pathlist)) { + (void) print_errString(err, NULL); + exit(-1); + } + pathcnt = pathlist.path_count; + p_pw = p_on = p_st = 0; + for (i = 0; i < pathcnt; i++) { + if (pathlist.path_info[i].path_state < + MAXPATHSTATE) { + if (strstr(pathlist.path_info[i]. + path_addr, + path_struct->argv) != NULL) { + p_pw = i; + break; + } + if (pathlist.path_info[i].path_state == + MDI_PATHINFO_STATE_ONLINE) { + p_on = i; + } + if (pathlist.path_info[i].path_state == + MDI_PATHINFO_STATE_STANDBY) { + p_st = i; + } + } + } + if (strstr(pathlist.path_info[p_pw].path_addr, + path_struct->argv) != NULL) { + /* matching input pwwn */ + (void) strcpy(temp2path, + pathlist.path_info[p_pw].path_hba); + } else if (pathlist.path_info[p_on].path_state == + MDI_PATHINFO_STATE_ONLINE) { + /* on_line path */ + (void) strcpy(temp2path, + pathlist.path_info[p_on].path_hba); + } else { + /* standby or path0 */ + (void) strcpy(temp2path, + pathlist.path_info[p_st].path_hba); + } + free(pathlist.path_info); + (void) strcat(temp2path, FC_CTLR); + } else { + (void) strcpy(temp2path, path_phys); + } + + if ((err = g_get_dev_map(temp2path, &map, verbose_flag)) + != 0) { + return (err); + } + + if ((map.hba_addr.port_topology == FC_TOP_PUBLIC_LOOP) || + (map.hba_addr.port_topology == FC_TOP_FABRIC)) { + /* public or fabric loop device */ + free((void *)map.dev_addr); + (void) fprintf(stderr, MSGSTR(5540, + "This operation is not " + "supported in this topology.\n")); + exit(-1); + } + + if (todo == REPLACE_DEVICE) { + (void) fprintf(stderr, + MSGSTR(5511, + "Error:" + " replace_device is not supported" + " on this subsystem.\n")); + exit(-1); + } + + if ((todo == REMOVE_DEVICE) && + (dtype == DTYPE_DIRECT || + dtype == DTYPE_SEQUENTIAL || + dtype == DTYPE_UNKNOWN)) { + if (l_chk_null_wwn(path_struct, ses_path, + &l_state, verbose_flag) == 1) { + found_nullwwn = 1; + /* + * set dev_path to NULL, + * if disk has null wwn. + */ + *dev_path = NULL; + dev_location = SENA; + goto getinfo; + } + } + + (void) strcpy(ses_path, path_phys); + + if (strstr(ses_path, "ses") == NULL && + l_get_ses_path(path_phys, ses_path, &map, + verbose_flag) != 0) { + + /* Could be a non-photon disk device */ + if ((todo == REMOVE_DEVICE) && + (dtype == DTYPE_DIRECT || + dtype == DTYPE_SEQUENTIAL)) { + dev_location = NON_SENA; + + if ((err = h_get_fcdev_state(argv[path_index], + path_phys, force_flag, + &busy_flag, &reserve_flag, + verbose_flag)) != 0) { + goto done; + } + (void) strcpy(dev_path, path_phys); + if ((err = g_get_wwn(dev_path, port_wwn, + node_wwn, &al_pa, + verbose_flag)) != 0) { + goto done; + } + (void) sprintf(node_wwn_s, + "%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x", + node_wwn[0], node_wwn[1], node_wwn[2], + node_wwn[3], node_wwn[4], node_wwn[5], + node_wwn[6], node_wwn[7]); + tid = g_sf_alpa_to_switch[al_pa]; + goto loop; + } + continue; + } + + if (strstr(ses_path, "ses") != NULL) { + dev_location = SENA; + if ((err = l_convert_name(ses_path, &physpath, + &p_pathstruct, 0)) != 0) { + free(physpath); + free(p_pathstruct); + goto done; + + } + if ((err = g_get_inquiry(physpath, &inq)) != 0) { + free(physpath); + free(p_pathstruct); + goto done; + } + enc_type = l_get_enc_type(inq); + + } + if ((err = l_get_status(ses_path, + &l_state, verbose_flag)) != 0) { + goto done; + } + if (dtype == DTYPE_ESI) { + /* could be removing a photon */ + if (todo == REMOVE_DEVICE) { + /* + * Need the select ID (tid) for the IB. + */ + if ((err = g_get_wwn(ses_path, port_wwn, + node_wwn, &al_pa, + verbose_flag)) != 0) { + goto done; + } + (void) sprintf(node_wwn_s, + "%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x", + node_wwn[0], node_wwn[1], node_wwn[2], + node_wwn[3], node_wwn[4], node_wwn[5], + node_wwn[6], node_wwn[7]); + tid = g_sf_alpa_to_switch[al_pa]; + *dev_path = '\0'; + /* + * Check if any disk in this photon + * is reserved by another host + */ + if (!force_flag) { + for ( + i = 0; + i < l_state.total_num_drv/2; + i++) { + if ((l_state.drv_front[i].g_disk_state.d_state_flags[PORT_A] & + L_RESERVED) || + (l_state.drv_front[i].g_disk_state.d_state_flags[PORT_B] & + L_RESERVED) || + (l_state.drv_rear[i].g_disk_state.d_state_flags[PORT_A] & + L_RESERVED) || + (l_state.drv_rear[i].g_disk_state.d_state_flags[PORT_B] & + L_RESERVED)) { + reserve_flag = 1; + } + } + } + goto loop; + } + (void) fprintf(stderr, + MSGSTR(5512, + "Error: %s already exists!!\n"), + argv[path_index]); + goto done; + } +getinfo: + if (!path_struct->slot_valid) { + /* We are passing the disks path */ + if ((err = l_get_slot(path_struct, &l_state, + verbose_flag)) != 0) { + goto done; + } + } + + slot = path_struct->slot; + if (path_struct->f_flag) { + tid = l_state.drv_front[slot].ib_status.sel_id; + code = l_state.drv_front[slot].ib_status.code; + (void) strcpy(node_wwn_s, + l_state.drv_front[slot].g_disk_state.node_wwn_s); + } else { + tid = l_state.drv_rear[slot].ib_status.sel_id; + code = l_state.drv_rear[slot].ib_status.code; + (void) strcpy(node_wwn_s, + l_state.drv_rear[slot].g_disk_state.node_wwn_s); + } + + if (found_nullwwn) { + goto loop; + } + + l_make_node(ses_path, tid, dev_path, &map, 0); + + if ((todo == INSERT_DEVICE) && + (g_device_in_map(&map, tid) || + (code != S_NOT_INSTALLED))) { + (void) fprintf(stderr, + MSGSTR(5513, "\nNotice: %s may " + "already be present.\n"), + argv[path_index]); + if (path_struct->f_flag) { + if ((l_state.drv_front[slot].l_state_flag + != L_NO_PATH_FOUND) && + (!l_state.drv_front[slot].ib_status.dev_off)) + continue; + } else { + if ((l_state.drv_rear[slot].l_state_flag + != L_NO_PATH_FOUND) && + (!l_state.drv_rear[slot].ib_status.dev_off)) + continue; + } + } + + /* Check if disk is reserved */ + if ((todo == REMOVE_DEVICE) && (!force_flag)) { + if (path_struct->f_flag) { + if ((l_state.drv_front[slot].g_disk_state.d_state_flags[PORT_A] & + L_RESERVED) || + (l_state.drv_front[slot].g_disk_state.d_state_flags[PORT_B] & + L_RESERVED)) { + reserve_flag = 1; + } + } else { + if ((l_state.drv_rear[slot].g_disk_state.d_state_flags[PORT_A] & + L_RESERVED) || + (l_state.drv_rear[slot].g_disk_state.d_state_flags[PORT_B] & + L_RESERVED)) { + reserve_flag = 1; + } + } + } + +loop: + if ((disk_list = (Hotplug_Devlist *) + calloc(1, sizeof (Hotplug_Devlist))) == NULL) { + (void) print_errString(L_MALLOC_FAILED, NULL); + goto done; + } + + /* + * dev_path is NULL when removing a whole encloser. We + * don't want to call g_get_multipath while removing whole + * enclosure. Its being taken care later in the code path + */ + + if ((todo != INSERT_DEVICE) && (dtype != DTYPE_ESI)) { + if ((err = g_get_multipath(dev_path, + &(disk_list->dlhead), + wwn_list, verbose_flag)) != 0) { + if (disk_list->dlhead != NULL) { + (void) g_free_multipath( + disk_list->dlhead); + } + goto done; + } + } + disk_list->dev_type = dtype; + disk_list->dev_location = dev_location; + (void) strcpy(disk_list->dev_name, + argv[path_index]); + disk_list->tid = tid; + (void) strcpy(disk_list->node_wwn_s, node_wwn_s); + if (dev_location == SENA) { + if ((err = l_get_allses(ses_path, box_list, + &(disk_list->seslist), 0)) != 0) { + if (disk_list->seslist != NULL) { + (void) g_free_multipath(disk_list->seslist); + } + goto done; + } + (void) strcpy(disk_list->box_name, + (char *)l_state.ib_tbl.enclosure_name); + disk_list->slot = slot; + disk_list->f_flag = path_struct->f_flag; + } + if (todo == REMOVE_DEVICE && !force_flag && !reserve_flag) { + if ((err = h_chk_dev_busy(disk_list, wwn_list, + &busy_flag, force_flag, verbose_flag)) != 0) { + goto done; + } + } + + if (reserve_flag || busy_flag) { + if (reserve_flag) + disk_list->reserve_flag = 1; + if (busy_flag) + disk_list->busy_flag = 1; + + if (bsyRsrv_dskLst_head == NULL) { + bsyRsrv_dskLst_head = + bsyRsrv_dskLst_tail = disk_list; + } else { + disk_list->prev = bsyRsrv_dskLst_tail; + bsyRsrv_dskLst_tail->next = disk_list; + bsyRsrv_dskLst_tail = disk_list; + } + reserve_flag = 0; + busy_flag = 0; + + } else if (disk_list_head == NULL) { + disk_list_head = disk_list_tail = disk_list; + } else { + disk_list->prev = disk_list_tail; + disk_list_tail->next = disk_list; + disk_list_tail = disk_list; + } + } + + if (bsyRsrv_dskLst_head != NULL) { + if ((err = h_print_list(bsyRsrv_dskLst_head, + &action, enc_type)) != 0) { + goto done; + } + if (action == SKIP) { + (void) h_free_hotplug_dlist(&bsyRsrv_dskLst_head); + } else if (action == QUIT) { + goto done; + } + } + if (disk_list_head != NULL) { + if ((h_print_list_warn(disk_list_head, todo, enc_type)) != 0) { + goto done; + } + if ((err = h_pre_hotplug(&disk_list_head, wwn_list, todo, verbose_flag, + force_flag)) != 0) { + goto done; + } + if (disk_list_head != NULL) { + if (todo == REMOVE_DEVICE) { + (void) fprintf(stdout, MSGSTR(5514, + "\nHit <Return> after " + "removing the device(s).")); + } else { + (void) fprintf(stdout, MSGSTR(5515, + "\nHit <Return> after " + "inserting the device(s).")); + } + (void) getchar(); + (void) fprintf(stdout, "\n"); + if ((err = h_post_hotplug(disk_list_head, wwn_list, + todo, verbose_flag, force_flag, + enc_type)) != 0) { + goto done; + } + } + } +done: + (void) l_free_box_list(&box_list); + (void) g_free_wwn_list(&wwn_list); + if (err && err != -1) { + return (err); + } + free((void *)map.dev_addr); + return (0); +} + + + + +/* + * Internal routine to clean up ../'s in paths. + * returns 0 if no "../" are left. + * + * Wouldn't it be nice if there was a standard system library + * routine to do this...? + */ +static int +cleanup_dotdot_path(char *path) +{ + char holder[MAXPATHLEN]; + char *dotdot; + char *previous_slash; + + /* Find the first "/../" in the string */ + dotdot = strstr(path, "/../"); + if (dotdot == NULL) { + return (0); + } + + + /* + * If the [0] character is '/' and "../" immediatly + * follows it, then we can strip the ../ + * + * /../../foo/bar == /foo/bar + * + */ + if (dotdot == path) { + strcpy(holder, &path[3]); /* strip "/.." */ + strcpy(path, holder); + return (1); + } + + /* + * Now look for the LAST "/" before the "/../" + * as this is the parent dir we can get rid of. + * We do this by temporarily truncating the string + * at the '/' just before "../" using the dotdot pointer. + */ + *dotdot = '\0'; + previous_slash = strrchr(path, '/'); + if (previous_slash == NULL) { + /* + * hmm, somethings wrong. path looks something + * like "foo/../bar/" so we can't really deal with it. + */ + return (0); + } + /* + * Now truncate the path just after the previous '/' + * and slam everything after the "../" back on + */ + *(previous_slash+1) = '\0'; + (void) strcat(path, dotdot+4); + return (1); /* We may have more "../"s */ +} + + +/* + * Follow symbolic links from the logical device name to + * the /devfs physical device name. To be complete, we + * handle the case of multiple links. This function + * either returns NULL (no links, or some other error), + * or the physical device name, alloc'ed on the heap. + * + * For S10 the physical path may be non-existent. + * + * NOTE: If the path is relative, it will be forced into + * an absolute path by pre-pending the pwd to it. + */ +char * +h_get_physical_name_from_link(char *path) +{ + struct stat stbuf; + char source[MAXPATHLEN]; + char scratch[MAXPATHLEN]; + char pwd[MAXPATHLEN]; + char *tmp; + int cnt; + + /* return NULL if path is NULL */ + if (path == NULL) { + return (NULL); + } + + strcpy(source, path); + for (;;) { + + /* + * First make sure the path is absolute. If not, make it. + * If it's already an absolute path, we have no need + * to determine the cwd, so the program should still + * function within security-by-obscurity directories. + */ + if (source[0] != '/') { + tmp = getcwd(pwd, MAXPATHLEN); + if (tmp == NULL) { + O_DPRINTF("getcwd() failed - %s\n", + strerror(errno)); + return (NULL); + } + /* + * Handle special case of "./foo/bar" + */ + if (source[0] == '.' && source[1] == '/') { + strcpy(scratch, source+2); + } else { /* no "./" so just take everything */ + strcpy(scratch, source); + } + strcpy(source, pwd); + (void) strcat(source, "/"); + (void) strcat(source, scratch); + } + + /* + * Clean up any "../"s that are in the path + */ + while (cleanup_dotdot_path(source)); + + /* + * source is now an absolute path to the link we're + * concerned with + * + * S10: Do NOT ignore dangling links, pointing to devfs nodes. + */ + if (strstr(source, "/devices")) { + return (g_alloc_string(source)); + } + + if (lstat(source, &stbuf) == -1) { + O_DPRINTF("lstat() failed for - %s\n", + source, strerror(errno)); + return (NULL); + } + /* + * If the file is not a link, we're done one + * way or the other. If there were links, + * return the full pathname of the resulting + * file. + * + * Note: All of our temp's are on the stack, + * so we have to copy the final result to the heap. + */ + if (!S_ISLNK(stbuf.st_mode)) { + return (g_alloc_string(source)); + } + cnt = readlink(source, scratch, sizeof (scratch)); + if (cnt < 0) { + O_DPRINTF("readlink() failed - %s\n", + strerror(errno)); + return (NULL); + } + /* + * scratch is on the heap, and for some reason readlink + * doesn't always terminate things properly so we have + * to make certain we're properly terminated + */ + scratch[cnt] = '\0'; + + /* + * Now check to see if the link is relative. If so, + * then we have to append it to the directory + * which the source was in. (This is non trivial) + */ + if (scratch[0] != '/') { + tmp = strrchr(source, '/'); + if (tmp == NULL) { /* Whoa! Something's hosed! */ + O_DPRINTF("Internal error... corrupt path.\n"); + return (NULL); + } + /* Now strip off just the directory path */ + *(tmp+1) = '\0'; /* Keeping the last '/' */ + /* and append the new link */ + (void) strcat(source, scratch); + /* + * Note: At this point, source should have "../"s + * but we'll clean it up in the next pass through + * the loop. + */ + } else { + /* It's an absolute link so no worries */ + strcpy(source, scratch); + } + } + /* Never reach here */ +} + +/* + * Function for getting physical pathnames + * + * For S10 the physical path may not exist at the time devctl calls + * are made. So we should not return error if stat fails on /devices path. + * + * This function can handle 2 different inputs. + * + * 1) Inputs of the form /dev/rdsk/cNtNdNsN + * These are identified by being a link + * The physical path they are linked to is returned. + * + * 2) Inputs of the form /devices/... + * These are actual physical names. + * They are not converted. + */ +char * +h_get_physical_name(char *path) +{ + struct stat stbuf; + char s[MAXPATHLEN]; + char savedir[MAXPATHLEN]; + char *result = NULL; + int status = 0; + + /* return invalid path if path NULL */ + if (path == NULL) { + return (NULL); + } + + (void) strcpy(s, path); + + status = lstat(s, &stbuf); + + /* + * S10: If string is devfs node we allow failed lstat. + */ + if ((status == -1) || !S_ISLNK(stbuf.st_mode)) { + /* Make sure a full path as that is required. */ + if (strstr(s, "/devices")) { + result = g_alloc_string(s); + } else { + if (getcwd(savedir, + sizeof (savedir)) == NULL) { + return (NULL); + } + /* + * Check for this format: + * ./ssd@0,1:g,raw + */ + if (s[0] == '.') { + (void) strcat(savedir, &s[1]); + } else { + (void) strcat(savedir, "/"); + (void) strcat(savedir, s); + } + if ((status != -1) || strstr(s, "/devices")) { + result = g_alloc_string(savedir); + } + } + } else { + /* + * Entry is linked file + * so follow link to physical name + */ + result = h_get_physical_name_from_link(path); + } + +exit: + return (result); +} + + +/* + * handle expert-mode hotplug commands + * + * return 0 iff all is okay + */ +int +hotplug_e(int todo, char **argv, int verbose_flag, int force_flag) +{ +char *path_phys = NULL; +char bus_path[MAXPATHLEN]; +char *ptr; +int exit_code; +devctl_hdl_t dcp; +uint_t devstate; +int i = 0, pathcnt = 1; +mp_pathlist_t pathlist; +int p_pw = 0, p_on = 0, p_st = 0; + + + switch (todo) { + case DEV_ONLINE: + case DEV_OFFLINE: + case DEV_GETSTATE: + case DEV_RESET: + /* get physical name */ + if ((path_phys = h_get_physical_name(argv[0])) == NULL) { + + (void) fprintf(stderr, + MSGSTR(112, "Error: Invalid pathname (%s)"), + argv[0]); + (void) fprintf(stderr, "\n"); + return (1); + } + + if (verbose_flag) { + (void) fprintf(stdout, + MSGSTR(5516, + "phys path = \"%s\"\n"), + path_phys); + } + + /* acquire rights to hack on device */ + if ((dcp = devctl_device_acquire(path_phys, + force_flag ? 0 : DC_EXCL)) == NULL) { + + (void) fprintf(stderr, MSGSTR(5517, + "Error: can't acquire \"%s\": %s\n"), + path_phys, strerror(errno)); + return (1); + } + + switch (todo) { + case DEV_ONLINE: + exit_code = devctl_device_online(dcp); + break; + case DEV_OFFLINE: + exit_code = devctl_device_offline(dcp); + break; + case DEV_GETSTATE: + if ((exit_code = devctl_device_getstate(dcp, + &devstate)) == 0) { + print_dev_state(argv[0], devstate); + } + break; + case DEV_RESET: + exit_code = devctl_device_reset(dcp); + break; + } + + if (exit_code != 0) { + perror(MSGSTR(5518, "devctl")); + } + + /* all done now -- release device */ + devctl_release(dcp); + break; + + /* for hotplugging bus operations */ + case BUS_QUIESCE: + case BUS_UNQUIESCE: + case BUS_GETSTATE: + case BUS_RESET: + case BUS_RESETALL: + /* get physical name */ + if ((path_phys = h_get_physical_name(argv[0])) == + NULL) { + (void) fprintf(stderr, + MSGSTR(112, "Error: Invalid pathname (%s)"), + argv[0]); + (void) fprintf(stderr, "\n"); + return (1); + } + if (verbose_flag) { + printf(MSGSTR(5519, "phys path = \"%s\"\n"), path_phys); + } + + /* acquire rights to hack on device */ + /* delete leaf part from path_phys. */ + if (strstr(path_phys, SCSI_VHCI) != NULL) { + /* obtain phci */ + (void) strcpy(bus_path, path_phys); + if (g_get_pathlist(bus_path, &pathlist)) { + (void) fprintf(stderr, + MSGSTR(112, "Error: Invalid pathname (%s)"), + path_phys); + (void) fprintf(stderr, "\n"); + return (1); + } + pathcnt = pathlist.path_count; + p_pw = p_on = p_st = 0; + for (i = 0; i < pathcnt; i++) { + if (pathlist.path_info[i].path_state < + MAXPATHSTATE) { + if (strstr(pathlist.path_info[i]. + path_addr, + argv[0]) != NULL) { + p_pw = i; + break; + } + if (pathlist.path_info[i].path_state == + MDI_PATHINFO_STATE_ONLINE) { + p_on = i; + } + if (pathlist.path_info[i].path_state == + MDI_PATHINFO_STATE_STANDBY) { + p_st = i; + } + } + } + if (strstr(pathlist.path_info[p_pw].path_addr, + argv[0]) != NULL) { + /* matching input pwwn */ + (void) strcpy(bus_path, + pathlist.path_info[p_pw].path_hba); + } else if (pathlist.path_info[p_on].path_state == + MDI_PATHINFO_STATE_ONLINE) { + /* on_line path */ + (void) strcpy(bus_path, + pathlist.path_info[p_on].path_hba); + } else { + /* standby or path0 */ + (void) strcpy(bus_path, + pathlist.path_info[p_st].path_hba); + } + free(pathlist.path_info); + } else { + + (void) strcpy(bus_path, path_phys); + ptr = strrchr(bus_path, '/'); + if (ptr) { + *ptr = '\0'; + } else { + (void) fprintf(stderr, + MSGSTR(112, "Error: Invalid pathname (%s)"), + path_phys); + (void) fprintf(stderr, "\n"); + return (1); + } + } + + if ((dcp = devctl_bus_acquire(bus_path, + force_flag ? 0 : DC_EXCL)) == NULL) { + (void) fprintf(stderr, + MSGSTR(5521, + " Error: can't acquire bus node from" + " the path \"%s\": %s\n"), + bus_path, strerror(errno)); + return (1); + } + + switch (todo) { + case BUS_QUIESCE: + exit_code = devctl_bus_quiesce(dcp); + break; + case BUS_UNQUIESCE: + exit_code = devctl_bus_unquiesce(dcp); + break; + case BUS_GETSTATE: + if ((exit_code = devctl_bus_getstate(dcp, + &devstate)) == 0) { + print_bus_state(argv[0], devstate); + } + break; + case BUS_RESET: + exit_code = devctl_bus_reset(dcp); + break; + case BUS_RESETALL: + exit_code = devctl_bus_resetall(dcp); + break; + } + + if (exit_code != 0) { + perror(MSGSTR(5522, "devctl")); + } + + /* all done now -- release device */ + devctl_release(dcp); + break; + } + + return (exit_code); +} + + + +/* + * Prepares an individual FC_AL device + * to be removed from the specified + * slot. + * + * RETURNS: + * 0 if OK + * non-zero otherwise. + */ +static int +h_pre_remove_dev(Hotplug_Devlist *hotplug_disk, WWN_list *wwn_list, + int verbose_flag, int force_flag) +{ +char *dev_path, device_name[MAXNAMELEN]; +int err; + + /* Initialize pointers */ + dev_path = NULL; + + if (hotplug_disk->dlhead != NULL) { + dev_path = hotplug_disk->dlhead->dev_path; + (void) strcpy(device_name, (hotplug_disk->dlhead)->logical_path); + } + (void) fprintf(stdout, + MSGSTR(157, + "stopping: %s...."), device_name); + if (!(strstr(dev_path, SLSH_DRV_NAME_ST))) { + if ((err = g_dev_stop(dev_path, wwn_list, verbose_flag)) != 0) + return (err); + } + (void) fprintf(stdout, MSGSTR(156, "Done\n")); + + (void) fprintf(stdout, + MSGSTR(158, "offlining: %s...."), device_name); + if ((err = g_offline_drive(hotplug_disk->dlhead, + force_flag)) != 0) { + (void) fprintf(stdout, + MSGSTR(160, + "\nonlining: %s\n"), device_name); + + (void) g_online_drive(hotplug_disk->dlhead, force_flag); + (void) fprintf(stdout, + MSGSTR(159, "starting: %s...."), + device_name); + if ((err = g_dev_start(dev_path, 0)) != 0) { + return (err); + } + (void) fprintf(stdout, MSGSTR(156, "Done\n")); + return (err); + } + (void) fprintf(stdout, MSGSTR(156, "Done\n")); + return (0); +} + + + +/* + * Prepares a SENA enclosure or SENA FC_AL device + * to be inserted/removed from a specified slot. + * + * RETURNS: + * 0 if OK + * non-zero otherwise. + */ +static int +h_pre_hotplug_sena(Hotplug_Devlist *hotplug_dev, + WWN_list *wwn_list, int todo, + int verbose_flag, int force_flag) +{ +int slot, f_r, i, found_null_wwn = 0, err; +char *ses_path, *dev_path, code; +char node_wwn_s[WWN_SIZE], device_name[MAXNAMELEN]; +struct l_state_struct l_state; +struct dlist *dl; + + + if (hotplug_dev->dev_type == DTYPE_ESI) { + /* entire photon is being removed */ + if ((err = l_offline_photon(hotplug_dev, wwn_list, + force_flag, verbose_flag)) != 0) { + return (err); + } + return (0); + } + + /* if device is an individual sena disk */ + dl = hotplug_dev->seslist; + while (dl) { + ses_path = dl->dev_path; + if ((err = l_get_status(ses_path, &l_state, + verbose_flag)) == 0) + break; + dl = dl->next; + } + if (dl == NULL) { + return (L_GET_STATUS_FAILED); + } + + f_r = hotplug_dev->f_flag; + slot = hotplug_dev->slot; + (void) l_get_drive_name(device_name, slot, f_r, hotplug_dev->box_name); + + /* check if disk has null wwn */ + if (f_r) { + (void) strncpy(node_wwn_s, + l_state.drv_front[slot].g_disk_state.node_wwn_s, WWN_SIZE); + } else { + (void) strncpy(node_wwn_s, + l_state.drv_rear[slot].g_disk_state.node_wwn_s, WWN_SIZE); + } + for (i = 0; i < WWN_SIZE; i++) { + if (node_wwn_s[i] != '0') + break; + found_null_wwn = 1; + } + + switch (todo) { + case INSERT_DEVICE: + if (hotplug_dev->f_flag) { + code = + l_state.drv_front[slot].ib_status.code; + } else { + code = + l_state.drv_rear[slot].ib_status.code; + } + if (code & S_NOT_INSTALLED) { + /* + * At this point we know that the drive is not + * there. Turn on the RQST INSERT bit to make + * the LED blink + */ + if ((err = l_encl_status_page_funcs + (SET_RQST_INSRT, 0, todo, + ses_path, &l_state, f_r, slot, + verbose_flag)) != 0) { + (void) print_errString(err, + device_name); + (void) fprintf(stderr, + MSGSTR(5530, + " %s: could not turn " + "on LED\n"), + device_name); + } + } else { + /* + * Drive is there so start it. + */ + if ((err = l_encl_status_page_funcs + (SET_DRV_ON, 0, todo, + ses_path, &l_state, f_r, slot, + verbose_flag)) != 0) { + (void) print_errString(err, + device_name); + (void) fprintf(stderr, + MSGSTR(5531, + " could not enable" + " %s\n"), + device_name); + } + } + break; + + case REMOVE_DEVICE: + /* + * if disk has null wwn, then + * there is no need to check the + * disk/loop status. + */ + if (found_null_wwn == 1) { + if (getenv("_LUX_W_DEBUG") != NULL) { + (void) fprintf(stdout, + "Device %s has " + "null WWN.\n", + device_name); + } + goto rmv; + } + if (hotplug_dev->f_flag) { + if ( + l_state.drv_front[slot].ib_status.code + == S_NOT_INSTALLED) { + (void) fprintf(stderr, + MSGSTR(86, + " Notice: %s may already" + " be removed.\n"), + device_name); + return (0); + } + } else if ( + l_state.drv_rear[slot].ib_status.code + == S_NOT_INSTALLED) { + (void) fprintf(stderr, + MSGSTR(86, + " Notice: %s may already" + " be removed.\n"), + device_name); + return (0); + } + +rmv: + if (hotplug_dev->dlhead == NULL) { + dev_path = NULL; + } else { + dev_path = hotplug_dev->dlhead->dev_path; + } + + (void) fprintf(stdout, + MSGSTR(157, + "stopping: %s...."), device_name); + if ((err = g_dev_stop(dev_path, wwn_list, 0)) != 0) { + return (err); + } + (void) fprintf(stdout, MSGSTR(156, "Done\n")); + + (void) fprintf(stdout, + MSGSTR(158, "offlining: %s...."), + device_name); + if ((err = g_offline_drive(hotplug_dev->dlhead, + force_flag)) != 0) { + (void) fprintf(stdout, + MSGSTR(160, + "\nonlining: %s\n"), device_name); + (void) g_online_drive(hotplug_dev->dlhead, force_flag); + + (void) fprintf(stdout, + MSGSTR(159, "starting: %s...."), + device_name); + (void) g_dev_start(dev_path, 0); + (void) fprintf(stdout, MSGSTR(156, "Done\n")); + return (err); + } + (void) fprintf(stdout, MSGSTR(156, "Done\n")); + + /* + * Take the drive off the loop + * and blink the LED. + */ + if (hotplug_dev->dev_location == SENA) { + if ((err = l_encl_status_page_funcs(SET_RQST_RMV, 0, + todo, ses_path, &l_state, f_r, + slot, verbose_flag)) != 0) { + (void) print_errString(err, device_name); + (void) fprintf(stderr, + MSGSTR(5539, + " %s: could not blink" + " the yellow LED\n"), + device_name); + } + } + break; + } + return (0); +} + + + +/* + * Performs the post removal operations for + * a SENA enclosure or a SENA FC_AL disk. + * + * RETURNS: + * 0 if OK + * non-zero otherwise + */ +static int +h_post_hotplug_sena(Hotplug_Devlist *hotplug_dev, + WWN_list *wwn_list, int todo, + int verbose_flag, int force_flag, int enc_type) +{ +char *ses_path, *dev_path = NULL, device_name[MAXNAMELEN]; +int tid, slot, f_r, al_pa, timeout = 0; +uchar_t port_wwn[WWN_SIZE], node_wwn[WWN_SIZE]; +char code; +int wait_spinup_flag = 0, wait_map_flag = 0; +int wait_node_flag = 0, err = 0, nArg; +gfc_map_t map; +WWN_list *newWwn_list = NULL; +struct dlist *dl, *dl1; +struct l_state_struct l_state; + + + dl = hotplug_dev->seslist; + slot = hotplug_dev->slot; + f_r = hotplug_dev->f_flag; + tid = hotplug_dev->tid; + + if (hotplug_dev->dev_type == DTYPE_ESI) { + /* + * See if photon has really been removed. If not, + * try onlining the devices if applicable + */ + H_DPRINTF(" post_hotplug_sena: Seeing if enclosure " + "has really been removed:\n" + " tid=0x%x, ses_path %s\n", + tid, dl->dev_path); + + while (dl) { + ses_path = dl->dev_path; + if ((err = g_get_dev_map(ses_path, &map, 0)) == 0) { + if ((map.hba_addr.port_topology == + FC_TOP_PUBLIC_LOOP) || + (map.hba_addr.port_topology == + FC_TOP_FABRIC)) { + /* public or fabric loop device */ + free((void *)map.dev_addr); + (void) fprintf(stdout, MSGSTR(5540, + "This operation is not " + "supported in this topology.\n")); + return (0); + } + if ((err = g_get_wwn(ses_path, port_wwn, + node_wwn, &al_pa, verbose_flag)) == 0) { + tid = g_sf_alpa_to_switch[al_pa]; + if (g_device_in_map(&map, tid)) { + free((void *)map.dev_addr); + break; + } + } + FREE_DEV_ADDR(map.dev_addr); + } + + dl = dl->next; + } + FREE_DEV_ADDR(map.dev_addr); + if (dl) { + (void) fprintf(stdout, MSGSTR(5640, + "Photon \"%s\" not removed." + " Onlining Drives in enclosure.\n"), + hotplug_dev->box_name); + for (dl = hotplug_dev->dlhead; dl; ) { + (void) g_online_drive(dl->multipath, + force_flag); + (void) g_free_multipath(dl->multipath); + dl1 = dl; + dl = dl->next; + (void) free(dl1); + } + hotplug_dev->dlhead = NULL; + return (0); + } + /* + * Remove logical nodes for this + * photon, this includes ses and + * /dev/dsk entries. + * In Solaris7, disks with -C option + * removes the /dev/dsk entries. + * The -C option is available + * only for Solaris7. From Solaris8 + * or higher releases, the "disks" + * program will be replaced by the + * devfsadm program. + */ + /* pass "disks -C" as cmdStrg. */ + nArg = 2; + if (h_execCmnd(cmdStrg[0], nArg) != 0) { + for (dl = hotplug_dev->dlhead; + dl != NULL; dl = dl->next) { + if ((err = h_remove_nodes(dl->multipath)) + != 0) { + return (err); + } + } + } else { + (void) fprintf(stdout, + MSGSTR(5541, + " Logical Nodes being removed" + " under /dev/dsk/ and /dev/rdsk:\n")); + for (dl = hotplug_dev->dlhead; + dl != NULL; dl = dl->next) { + (void) h_print_logical_nodes(dl->multipath); + } + } + + for (dl = hotplug_dev->dlhead; dl != NULL; ) { + (void) g_free_multipath(dl->multipath); + dl1 = dl; + dl = dl->next; + (void) free(dl1); + } + hotplug_dev->dlhead = NULL; + if ((err = h_remove_ses_nodes(hotplug_dev->seslist)) != 0) { + return (err); + } + return (0); + } + + /* post hotplug operations for a SENA disk. */ + if (enc_type == DAK_ENC_TYPE) { + (void) sprintf(device_name, MSGSTR(5664, + " Drive in Box Name \"%s\" slot %d"), + hotplug_dev->box_name, + f_r ? slot : slot + (MAX_DRIVES_DAK/2)); + } else { + if (tid & 0x10) { + (void) sprintf(device_name, MSGSTR(5542, + " Drive in Box Name \"%s\" rear slot %d"), + hotplug_dev->box_name, slot); + } else { + (void) sprintf(device_name, MSGSTR(5543, + " Drive in Box Name \"%s\" front slot %d"), + hotplug_dev->box_name, slot); + } + } + (void) fprintf(stdout, "%s\n", device_name); + + dl = hotplug_dev->seslist; + while (dl) { + ses_path = dl->dev_path; + if ((err = l_get_status(ses_path, &l_state, + verbose_flag)) == 0) + break; + dl = dl->next; + } + if (dl == NULL) { + print_errString(err, ses_path); + return (L_GET_STATUS_FAILED); + } + + code = 0; + while (((err = l_encl_status_page_funcs(OVERALL_STATUS, + &code, todo, ses_path, &l_state, f_r, slot, + verbose_flag)) != 0) || (code != 0)) { + if (err) { + (void) print_errString(err, ses_path); + } else if (todo == REMOVE_DEVICE) { + if (code == S_OK) { + (void) fprintf(stderr, + MSGSTR(5544, + "\n Warning: Device has not been" + " removed from the enclosure\n" + " and is still on the loop.")); + return (0); + } else { + (void) fprintf(stderr, + MSGSTR(5545, + " Notice: Device has not been" + " removed from the enclosure.\n" + " It has been removed from the" + " loop and is ready to be\n" + " removed" + " from the enclosure, and" + " the LED is blinking.\n\n")); + } + goto loop2; + } else if ((todo == INSERT_DEVICE) && + ((code != S_NOT_AVAILABLE) || + (timeout > + PHOTON_SPINUP_TIMEOUT) || + err)) { + (void) fprintf(stderr, + MSGSTR(5546, + "\n Warning: Disk status is" + " Not OK!\n\n")); + return (0); + } + (void) sleep(PHOTON_SPINUP_DELAY); + if (wait_spinup_flag++ == 0) { + (void) fprintf(stdout, MSGSTR(5547, + " Waiting for the disk to spin up:")); + } else { + (void) fprintf(stdout, "."); + } + timeout++; + } + if (wait_spinup_flag) { + (void) fprintf(stdout, "\n"); + } +loop2: + switch (todo) { + case INSERT_DEVICE: + /* check loop map that drive is present */ + for (;;) { + dl = hotplug_dev->seslist; + map.dev_addr = (gfc_port_dev_info_t *)NULL; + while (dl) { + ses_path = dl->dev_path; + if ((err = g_get_dev_map(ses_path, + &map, verbose_flag)) != 0) { + (void) fprintf(stderr, + MSGSTR(5548, + " Error: Could not get" + " map for %s.\n"), + ses_path); + return (err); + } + if (g_device_in_map(&map, tid)) { + goto loop3; + } + FREE_DEV_ADDR(map.dev_addr); + dl = dl->next; + } + if (timeout > PHOTON_SPINUP_TIMEOUT) { + (void) fprintf(stderr, + MSGSTR(5549, + " Warning: Device not in" + " loop map.\n")); + FREE_DEV_ADDR(map.dev_addr); + return (0); + } + if (wait_map_flag++ == 0) { + (void) fprintf(stdout, + MSGSTR(5550, + " Waiting for the device " + "to appear in the loop map:")); + } else { + (void) fprintf(stdout, "."); + } + timeout++; + (void) sleep(PHOTON_SPINUP_DELAY); + } +loop3: + if (wait_map_flag) { + (void) fprintf(stdout, "\n"); + } + + /* + * Run drvconfig and disks to create + * logical nodes + */ + for (;;) { + /* pass "disks" as cmdStrg */ + nArg = 3; + if (h_execCmnd(cmdStrg[2], nArg) != 0) { + (void) fprintf(stderr, + MSGSTR(5551, + " Could not " + "run drvconfig.\n")); + FREE_DEV_ADDR(map.dev_addr); + return (L_DRVCONFIG_ERROR); + } + + if (l_device_present(ses_path, tid, &map, + verbose_flag, &dev_path) == 1) + break; + if (timeout > PHOTON_SPINUP_TIMEOUT) { + (void) fprintf(stderr, + MSGSTR(5552, + " Warning: Could not find " + "any node for inserted " + "device\n")); + FREE_DEV_ADDR(map.dev_addr); + return (0); + } + if (wait_node_flag++ == 0) { + (void) fprintf(stdout, + MSGSTR(5553, + " Waiting for the logical " + "node to be created:")); + } else { + (void) fprintf(stdout, "."); + } + timeout++; + (void) sleep(PHOTON_SPINUP_DELAY); + } + FREE_DEV_ADDR(map.dev_addr); + if (wait_node_flag) { + (void) fprintf(stdout, "\n"); + } + /* + * In Solaris7, disks with -C + * option creates the new links + * and removes any stale links. + * In pre-Solaris7 releases, just + * disks should do it all. + */ + /* pass "disks -C" as cmdStrg */ + nArg = 2; + if (h_execCmnd(cmdStrg[0], nArg) != 0) { + return (L_DISKS_ERROR); + } + /* + * Get a new wwn list here in order to + * get the multiple paths to a newly added + * device. + */ + if ((err = g_get_wwn_list(&newWwn_list, + verbose_flag)) != 0) { + return (err); + } + if ((err = g_get_multipath(dev_path, &dl, + newWwn_list, 0)) != 0) { + return (err); + } + if ((err = h_display_logical_nodes(dl)) != 0) { + return (err); + } + break; + + case REMOVE_DEVICE: +/* + * TBD + * Need to check all loops. + */ + /* check whether device is still in loop map */ + if ((err = g_get_dev_map(ses_path, &map, + verbose_flag)) != 0) { + return (err); + } + + if ((map.hba_addr.port_topology == + FC_TOP_PUBLIC_LOOP) || + (map.hba_addr.port_topology == + FC_TOP_FABRIC)) { + /* public or fabric loop device */ + free((void *)map.dev_addr); + (void) fprintf(stderr, MSGSTR(5540, + "This operation is not " + "supported in this topology.\n")); + /* + * calling routine expects a 0 return code + * or a pre-defined luxadm error code. + * Here we do not have a pre-defined error + * code, a 0 is returned. + */ + return (0); + } + + if (g_device_in_map(&map, tid)) { + (void) fprintf(stderr, MSGSTR(5554, + " Warning: Device still in the loop map.\n")); + FREE_DEV_ADDR(map.dev_addr); + return (0); + } + FREE_DEV_ADDR(map.dev_addr); + /* + * In Solaris7, "disks -C" program + * removes the /dev/{r}dsk entries. + * The -C option is available only + * for Solaris7. From Solaris8 or + * higher releases, the "disks" program + * will be replaced by devfsadm. + */ + /* pass "disks -C" as cmdStrg */ + nArg = 2; + if (h_execCmnd(cmdStrg[0], nArg) != 0) { + return (L_DISKS_ERROR); + } + (void) fprintf(stdout, + MSGSTR(5555, + " Logical Nodes being removed" + " under /dev/dsk/ and /dev/rdsk:\n")); + (void) h_print_logical_nodes( + hotplug_dev->dlhead); + break; + } + return (0); +} + + + + +/* + * Creates new ses entries under /dev/es + * directory for the newly added + * enclosures. + * + * RETURNS: + * 0 if OK + * non-zero otherwise + */ +static int +h_post_insert_encl(timestruc_t ses_lastmtim) +{ +struct stat ses_stat; +char lname[MAXPATHLEN]; +int err, found_newlink = 0; +DIR *dir; +struct dirent *dirent; +Box_list *bl1, *box_list = NULL; + + + if ((dir = opendir(SES_DIR)) == NULL) { + return (L_OPEN_ES_DIR_FAILED); + } + if ((err = l_get_box_list(&box_list, 0)) != 0) { + closedir(dir); + return (err); + } + + /* + * The mod time of /dev/es was newer than the mod time prior to + * insert so dir entry is checked at this time. + */ + while ((dirent = readdir(dir)) != (struct dirent *)NULL) { + if (strcmp(dirent->d_name, ".") == 0 || + strcmp(dirent->d_name, "..") == 0) + continue; + + (void) sprintf(lname, SES_DIR"/%s", dirent->d_name); + if (lstat(lname, &ses_stat) < 0) { + (void) print_errString(L_LSTAT_ES_DIR_ERROR, + lname); + continue; + } + + for (bl1 = box_list; bl1; bl1 = bl1->box_next) { + if (strstr(lname, bl1->b_physical_path)) + break; + } + + if (box_list && bl1) + continue; + + if (NEWER(ses_stat.st_ctim, ses_lastmtim)) { + /* New enclosure was detected. */ + found_newlink++; + if (found_newlink == 1) { + (void) fprintf(stdout, MSGSTR(5556, + " New Logical Nodes under /dev/es:\n")); + } + (void) fprintf(stdout, "\t%s\n", + dirent->d_name); + } + } + if (!found_newlink) { + (void) fprintf(stdout, MSGSTR(5662, + " No new enclosure(s) were added!!\n\n")); + } + + closedir(dir); + + (void) l_free_box_list(&box_list); + return (0); +} + + + +/* + * performs the post removal of individual + * FC_AL disks. + * + * RETURNS: + * 0 if OK + * non-zero otherwise + */ +static int +h_post_remove_dev(Hotplug_Devlist *hotplug_disk, + int todo, int verbose_flag) +{ +char device_name[MAXNAMELEN], *dev_path = NULL; +int tid, err; +gfc_map_t map; +int nArg; + + + tid = hotplug_disk->tid; + (void) sprintf(device_name, + MSGSTR(5557, + "\n Device: %s"), + (hotplug_disk->dlhead)->logical_path); + + (void) fprintf(stdout, "%s\n", device_name); + + dev_path = (hotplug_disk->dlhead)->dev_path; + + /* + * On qlc, after a forcelip on a FC combo box, it sometimes takes 17 + * seconds for the loop to come back online. During this 17 seconds, + * g_get_dev_map * will return L_NO_DEVICES_FOUND. This delay + * has been added to assure that the L_NO_DEVICES_FOUND returned from + * g_get_dev_map is not the result of the 17 second delay on FC combo. + * This only affects qlc. + */ + if ((err = g_get_dev_map(dev_path, &map, verbose_flag)) != 0) { + if ((err == L_NO_DEVICES_FOUND) && + (strstr(dev_path, "SUNW,qlc@") != NULL)) { + sleep(QLC_LIP_DELAY); + if ((err = g_get_dev_map(dev_path, &map, verbose_flag)) + != 0) { + if (err != L_NO_DEVICES_FOUND) + return (err); + } + } else if (err != L_NO_DEVICES_FOUND) + return (err); + } + + /* + * if g_get_dev_map returns L_NO_DEVICES_FOUND, then there are not + * devices attached to the HBA and there is no sense in calling + * g_device_in_map(). + */ + if (err != L_NO_DEVICES_FOUND) { + if ((map.hba_addr.port_topology == FC_TOP_PUBLIC_LOOP) || + (map.hba_addr.port_topology == FC_TOP_FABRIC)) { + /* public or fabric loop device */ + free((void *)map.dev_addr); + (void) fprintf(stderr, MSGSTR(5540, + "This operation is not " + "supported in this topology.\n")); + return (0); + } + + if (g_device_in_map(&map, tid) != 0) { + (void) fprintf(stderr, + MSGSTR(5558, + " Warning: Device has" + " not been removed from\n" + " the slot and is still" + " in the loop map.\n\n")); + free((void *)map.dev_addr); + return (0); + } + free((void *)map.dev_addr); + } + /* + * In Solaris7, "disks -C" program + * removes the /dev/{r}dsk entries. + * The -C option is available only + * for Solaris7. From Solaris8 or + * higher releases, the "disks" program + * will be replaced by devfsadm. + */ + /* pass "disks -C" as cmdStrg. */ + nArg = 2; + if (h_execCmnd(cmdStrg[0], nArg) != 0) { + return (L_DISKS_ERROR); + } + /* pass "tapes -C as cmdStrg. */ + if (h_execCmnd(cmdStrg[5], nArg) != 0) { + return (L_TAPES_ERROR); + } + (void) h_print_logical_nodes(hotplug_disk->dlhead); + + return (0); +} + + + +/* + * Gets the last modification time for + * /dev/es/ and /dev/rdsk directories + * and passes these values to the caller. + * + * RETURNS: + * 0 if OK + * non-zero in case of error + */ +static int +h_pre_insert_encl_dev(timestruc_t *ses_time, timestruc_t *dsk_time, + timestruc_t *rmt_time) +{ +struct stat ses_stat, dsk_stat, rmt_stat; + + if (stat(SES_DIR, &ses_stat) < 0) { + /* + * Even if there exists no /dev/es don't fail it. + * The host doesn't have to have any enclosure device + * configured. + */ + if (errno == ENOENT) { + ses_time = (timestruc_t *)NULL; + } else { + return (L_LSTAT_ES_DIR_ERROR); + } + } else { + *ses_time = ses_stat.st_mtim; + } + + if (stat(DEV_DSK_DIR, &dsk_stat) < 0) { + return (L_STAT_DEV_DIR_ERROR); + } else { + *dsk_time = dsk_stat.st_mtim; + } + if (stat(DEV_TAPE_DIR, &rmt_stat) < 0) { + /* + * Even if there exists no /dev/rmt don't fail it. + * The host doesn't have to have any tape device + * configured. + */ + if (errno == ENOENT) { + rmt_time = (timestruc_t *)NULL; + } else { + return (L_STAT_RMT_DIR_ERROR); + } + } else { + *rmt_time = rmt_stat.st_mtim; + } + + return (0); +} + + + +/* + * Waits for loop intialization to complete + * and runs drvconfig, disks and devlinks to create device nodes + * for devices that are being added and prints the newly created + * /dev/rdsk entries. + * + * RETURNS: + * 0 if OK + * non-zero in case of error + */ + +static int +h_post_insert_dev(timestruc_t dsk_lastmtim, timestruc_t rmt_lastmtim) +{ +int found_newlink = 0, nArg; + + (void) fprintf(stdout, + MSGSTR(5560, + "\nWaiting for Loop Initialization to complete...\n")); + + /* + * We sleep here to let the system create nodes. Not sleeping + * could cause the drvconfig below to run too soon. + */ + + (void) sleep(NODE_CREATION_TIME); + + /* + * Run drvconfig and disks to create + * logical nodes + */ + /* pass "drvconfig" as cmdStrg */ + nArg = 1; + if (h_execCmnd(cmdStrg[3], nArg) != 0) { + return (L_DRVCONFIG_ERROR); + } + + /* + * In 2.7, disks with the -C + * option should be used to + * create new links and remove + * any stale links. + * In pre-2.7 releases, just + * disks should do it all. + */ + + /* pass "disks -C" as cmdStrg */ + nArg = 2; + if (h_execCmnd(cmdStrg[0], nArg) != 0) { + return (L_DISKS_ERROR); + } + /* pass "tapes -C as cmdStrg */ + if (h_execCmnd(cmdStrg[5], nArg) != 0) { + return (L_TAPES_ERROR); + } + + /* pass "devlinks" as cmdStrg */ + nArg = 1; + if (h_execCmnd(cmdStrg[4], nArg) != 0) { + return (L_DEVLINKS_ERROR); + } + + /* check /dev/dsk and /dev/rmt for new links */ + found_newlink = h_find_new_device_link(DEV_DSK_DIR, dsk_lastmtim) + + h_find_new_device_link(DEV_TAPE_DIR, rmt_lastmtim); + + if (!found_newlink) { + (void) fprintf(stdout, MSGSTR(5562, + " No new device(s) were added!!\n\n")); + } + + return (0); +} + + + +/* + * Performs the pre hotplug operations on SENA enclosure(s), + * SENA disk(s) and individual fcal disk(s). + * If the device is failed to remove, then it removes the device from the + * hotplug list and continues with the next device in the list. + * + * RETURNS: + * 0 if OK + * prints an error message to stderr and returns 0 + */ +static int +h_pre_hotplug(Hotplug_Devlist **disk_list_head_ptr, + WWN_list *wwn_list, int todo, + int verbose_flag, int force_flag) +{ +Hotplug_Devlist *list, *disk_list; +int err = 0; + + disk_list = *disk_list_head_ptr; + while (disk_list != NULL) { + if ((disk_list->dev_type == DTYPE_ESI) || + (disk_list->dev_location == SENA)) { + if ((err = h_pre_hotplug_sena(disk_list, wwn_list, + todo, verbose_flag, force_flag)) != 0) { + (void) print_errString(err, + disk_list->dev_name); + goto delete; + } + } else if (disk_list->dev_location == NON_SENA) { + if ((err = h_pre_remove_dev(disk_list, wwn_list, + verbose_flag, force_flag)) != 0) { + (void) print_errString(err, + disk_list->dev_name); + goto delete; + } + } + disk_list = disk_list->next; + continue; +delete: + list = disk_list->prev; + if (list != NULL) { + list->next = disk_list->next; + if (list->next != NULL) + list->next->prev = list; + } + list = disk_list; + disk_list = disk_list->next; + if (list == *disk_list_head_ptr) + *disk_list_head_ptr = disk_list; + (void) g_free_multipath(list->seslist); + (void) g_free_multipath(list->dlhead); + (void) free(list); + } + return (0); +} + + + +/* + * Performs the post removal of a list of SENA enclosure(s), + * SENA disk(s) and individual fcal disk(s). + * + * RETURNS: + * 0 O.K. + * non-zero otherwise + */ +static int +h_post_hotplug(Hotplug_Devlist *hotplug_dlist, + WWN_list *wwn_list, int todo, + int verbose_flag, int force_flag, int enc_type) +{ +Hotplug_Devlist *list; +int err; + + /* Do a lip on every loop so that we get the latest loop maps */ + if (todo != INSERT_DEVICE) { + if ((err = g_forcelip_all(hotplug_dlist)) != 0) { + return (err); + } + } + + while (hotplug_dlist != NULL) { + if ((hotplug_dlist->dev_location == SENA) || + (hotplug_dlist->dev_type == DTYPE_ESI)) { + if ((err = h_post_hotplug_sena(hotplug_dlist, wwn_list, todo, + verbose_flag, force_flag, enc_type)) != 0) + (void) print_errString(err, hotplug_dlist->dev_name); + } else if (hotplug_dlist->dev_location == NON_SENA) { + if ((err = h_post_remove_dev(hotplug_dlist, + todo, verbose_flag)) != 0) + (void) print_errString(err, + hotplug_dlist->dev_name); + } + list = hotplug_dlist; + hotplug_dlist = hotplug_dlist->next; + (void) g_free_multipath(list->seslist); + (void) g_free_multipath(list->dlhead); + (void) free(list); + } + return (0); +} + + +/* + * removes the device's logical paths. + * + * RETURNS: + * 0 if OK + * non-zero otherwise + */ +static int +h_remove_nodes(struct dlist *dl) +{ +char link[MAXPATHLEN], path[MAXPATHLEN]; +char lname[MAXPATHLEN], *ptr; +DIR *dir; +struct dirent *dirent; +struct dlist *dlist; + + if ((dir = opendir(DEV_DSK_DIR)) == NULL) { + return (L_READ_DEV_DIR_ERROR); + } + if (dl == NULL) { + /* pass "disks" as cmdStrg */ + if (h_execCmnd(cmdStrg[1], 1) != 0) { + return (L_DISKS_ERROR); + } + } + + (void) fprintf(stdout, + MSGSTR(5563, + " Removing Logical Nodes: \n")); + + while ((dirent = readdir(dir)) != (struct dirent *)NULL) { + if (strcmp(dirent->d_name, ".") == 0 || + strcmp(dirent->d_name, "..") == 0) { + continue; + } + (void) sprintf(lname, DEV_DSK_DIR"/%s", dirent->d_name); + if (readlink((const char *)lname, (char *)link, + (size_t)MAXPATHLEN) <= 0) { + (void) fprintf(stderr, + MSGSTR(5564, + " Error: Could not read %s\n"), + lname); + continue; + } + for (dlist = dl; dlist != NULL; dlist = dlist->next) { + (void) strcpy(path, dlist->dev_path); + ptr = strrchr(path, ':'); + if (ptr) + *ptr = '\0'; + if (strstr(link, path)) { + (void) unlink(lname); + (void) sprintf(lname, "/dev/rdsk/%s", + dirent->d_name); + (void) fprintf(stdout, + MSGSTR(5565, + "\tRemoving %s\n"), + dirent->d_name); + (void) unlink(lname); + } + } + } + closedir(dir); + return (0); +} + + + +/* + * removes the SENA's ses paths. + * + * RETURNS: + * 0 if OK + * non-zero otherwise + */ +static int +h_remove_ses_nodes(struct dlist *dlist) +{ +char link[MAXPATHLEN], lname[MAXPATHLEN]; +DIR *dir; +struct dirent *dirent; +struct dlist *dl; + + + if ((dir = opendir(SES_DIR)) == NULL) { + return (L_READ_DEV_DIR_ERROR); + } + + (void) fprintf(stdout, MSGSTR(5566, " Removing Ses Nodes:\n")); + + /* + * Remove the ses entries + * of the form ses<#> + * from the /dev/es directory. + */ + + while ((dirent = readdir(dir)) != (struct dirent *)NULL) { + if (strcmp(dirent->d_name, ".") == 0 || + strcmp(dirent->d_name, "..") == 0) + continue; + + (void) sprintf(lname, SES_DIR"/%s", dirent->d_name); + if (readlink((const char *)lname, (char *)link, + (size_t)MAXPATHLEN) <= 0) { + (void) fprintf(stderr, + MSGSTR(5564, + " Error: Could not read %s\n"), + lname); + continue; + } + for (dl = dlist; dl != NULL; dl = dl->next) { + if (strstr(link, dl->dev_path)) { + (void) fprintf(stdout, + MSGSTR(5568, + "\tRemoving %s\n"), + lname); + (void) unlink(lname); + } + } + } + closedir(dir); + (void) g_free_multipath(dlist); + return (0); +} + + +/* + * prints the device's logical + * paths for disks to stdout. + * + * RETURNS: + * 0 if OK + * non-zero otherwise + */ +static void +h_print_logical_nodes(struct dlist *disk_list) +{ +char *lpath, *ptr, *buf_ptr, buf[MAXNAMELEN], dev[MAXNAMELEN]; +struct dlist *dlist; +int i, found_dev = 0; +char *tape_entries[] = { "", "b", "bn", "c", "cb", "cbn", "cn", + "h", "hb", "hbn", "hn", "l", "lb", + "lbn", "ln", "m", "mb", "mbn", "mn", + "n", "u", "ub", "ubn", "un", NULL}; + + for (dlist = disk_list; dlist != NULL; dlist = dlist->next) { + lpath = dlist->logical_path; + if ((ptr = strrchr(lpath, 'c')) == NULL) + continue; + (void) strcpy(buf, ptr); + if ((ptr = strrchr(buf, 's')) == NULL) + continue; + *(++ptr) = NULL; + found_dev++; + if (found_dev == 1) + (void) fprintf(stdout, + MSGSTR(5559, " Logical Nodes being " + "removed under /dev/dsk/ and " + "/dev/rdsk:\n")); + for (i = 0; i <= 7; i++) { + (void) sprintf(dev, "%s%d", buf, i); + (void) fprintf(stdout, "\t%s\n", dev); + } + } + found_dev = 0; + for (dlist = disk_list; dlist != NULL; dlist = dlist->next) { + lpath = dlist->logical_path; + if (strstr(lpath, DEV_TAPE_DIR)) { + if ((ptr = strrchr(lpath, '/')) == NULL) + continue; + found_dev++; + if (found_dev == 1) + (void) fprintf(stdout, "Logical Nodes being " + "removed under /dev/rmt:\n"); + ptr++; + buf_ptr = ptr; + while (*ptr >= '0' && *ptr <= '9') + ptr++; + *ptr = NULL; + for (i = 0, ptr = tape_entries[0]; + ptr != NULL; + i++, ptr = tape_entries[i]) { + (void) sprintf(dev, "%s%s", buf_ptr, ptr); + (void) fprintf(stdout, "\t%s\n", dev); + } + } + } +} + +/* + * displays logical paths to a + * device to stdout. + * + * RETURNS: + * 0 if OK + * non-zero otherwise + */ +static int +h_display_logical_nodes(struct dlist *dlist) +{ +char link[MAXPATHLEN], path[MAXPATHLEN]; +char lname[MAXPATHLEN], *d1; +DIR *dir; +struct dirent *dirent; +struct dlist *dl; + + + if ((dir = opendir(DEV_DSK_DIR)) == NULL) { + return (L_READ_DEV_DIR_ERROR); + } + (void) fprintf(stdout, + MSGSTR(5569, + " Logical Nodes under /dev/dsk and /dev/rdsk :\n")); + + while ((dirent = readdir(dir)) != (struct dirent *)NULL) { + if (strcmp(dirent->d_name, ".") == 0 || + strcmp(dirent->d_name, "..") == 0) { + continue; + } + (void) sprintf(lname, DEV_DSK_DIR"/%s", dirent->d_name); + if (readlink((const char *)lname, (char *)link, + (size_t)MAXPATHLEN) <= 0) { + (void) print_errString(L_SYMLINK_ERROR, lname); + continue; + } + for (dl = dlist; dl; dl = dl->next) { + (void) strcpy(path, dl->dev_path); + d1 = strrchr(path, ':'); + if (d1) + *d1 = '\0'; + if (strstr(link, path)) { + (void) fprintf(stdout, + "\t%s\n", + dirent->d_name); + } + } + } + + closedir(dir); + return (0); +} + + + +/* + * prints a list of devices which + * will be inserted or removed + * to the stdout and asks for + * the user's confirmation. + * + * RETURNS: + * 0 if OK + * non-zero otherwise + */ +static int +h_print_list_warn(Hotplug_Devlist *disk_list_head, int todo, int enc_type) +{ +int i; +char choice[2]; +struct dlist *dl_ses, *dl_multi; +Hotplug_Devlist *disk_list = disk_list_head; + + (void) fprintf(stdout, + MSGSTR(5570, "The list of devices which will be ")); + switch (todo) { + case INSERT_DEVICE: + (void) fprintf(stdout, + MSGSTR(5571, "inserted is:\n")); + break; + case REMOVE_DEVICE: + (void) fprintf(stdout, + MSGSTR(5572, "removed is:\n")); + break; + } + + for (i = 1; disk_list; i++, disk_list = disk_list->next) { + if ((disk_list->dev_type == DTYPE_DIRECT) && + (disk_list->dev_location == SENA)) { + if (disk_list->f_flag != NULL) { + if (enc_type == DAK_ENC_TYPE) { + (void) fprintf(stdout, MSGSTR(5665, + " %d: Box Name: \"%s\" slot %d\n"), + i, disk_list->box_name, disk_list->slot); + } else { + (void) fprintf(stdout, MSGSTR(137, + " %d: Box Name: \"%s\" front slot %d\n"), + i, disk_list->box_name, disk_list->slot); + } + } else { + if (enc_type == DAK_ENC_TYPE) { + (void) fprintf(stdout, MSGSTR(5665, + " %d: Box Name: \"%s\" slot %d\n"), + i, disk_list->box_name, + disk_list->slot + (MAX_DRIVES_DAK/2)); + } else { + (void) fprintf(stdout, MSGSTR(136, + " %d: Box Name: \"%s\" rear slot %d\n"), + i, disk_list->box_name, disk_list->slot); + } + } + } else if (((disk_list->dev_type == DTYPE_DIRECT) || + (disk_list->dev_type == DTYPE_SEQUENTIAL)) && + (disk_list->dev_location == NON_SENA)) { + (void) fprintf(stdout, MSGSTR(5573, + " %d: Device name: %s\n"), + i, disk_list->dev_name); + } else if (disk_list->dev_type == DTYPE_ESI) { + (void) fprintf(stdout, MSGSTR(5574, + " %d: Box name: %s\n"), + i, disk_list->box_name); + } + if (getenv("_LUX_H_DEBUG") != NULL) { + if (disk_list->dev_location == SENA) { + (void) fprintf(stdout, + " Select ID:\t0x%x\n", + disk_list->tid); + if (disk_list->dev_type != DTYPE_ESI) { + if (enc_type == DAK_ENC_TYPE) { + (void) fprintf(stdout, + " Location: \tSlot %d \n", + disk_list->f_flag + ? disk_list->slot + : disk_list->slot + +MAX_DRIVES_DAK/2); + } else { + (void) fprintf(stdout, + " Location: \tSlot %d %s \n", + disk_list->slot, disk_list->f_flag + ? "front" : "rear"); + } + } + } + } + if (todo == REMOVE_DEVICE) { + (void) fprintf(stdout, " "); + (void) fprintf(stdout, MSGSTR(90, "Node WWN:")); + (void) fprintf(stdout, " %s\n", + disk_list->node_wwn_s); + + (void) fprintf(stdout, " "); + (void) fprintf(stdout, MSGSTR(35, "Device Type:")); + if (disk_list->dev_type == DTYPE_ESI) { + (void) fprintf(stdout, MSGSTR(5581, + " SENA (%s)\n"), + dtype[disk_list->dev_type]); + } else { + (void) fprintf(stdout, "%s\n", + dtype[disk_list->dev_type]); + } + + if (disk_list->dev_type == DTYPE_ESI) { + dl_ses = disk_list->seslist; + (void) fprintf(stdout, MSGSTR(5575, + " SES Paths:\n")); + while (dl_ses) { + (void) fprintf(stdout, MSGSTR(5576, + " %s\n"), dl_ses->dev_path); + dl_ses = dl_ses->next; + } + } else { + dl_multi = disk_list->dlhead; + (void) fprintf(stdout, MSGSTR(5577, + " Device Paths:\n")); + while (dl_multi) { + (void) fprintf(stdout, MSGSTR(5578, + " %s\n"), + dl_multi->logical_path); + dl_multi = dl_multi->next; + } + } + } + (void) fprintf(stdout, "\n"); + } + (void) fprintf(stdout, MSGSTR(5579, + "\nPlease verify the above list of devices" + " and\nthen enter 'c' or <CR> to Continue" + " or 'q' to Quit. [Default: c]: ")); + + /* Get the user input and continue accordingly. */ + for (;;) { + (void) gets(choice); + if (choice[0] == 'c' || choice[0] == 'C' || + choice[0] == 'q' || choice[0] == 'Q' || + choice[0] == '\0') { + break; + } + (void) fprintf(stdout, MSGSTR(5580, + " Enter an appropriate option [c,<CR>,q]: ")); + } + + if (choice[0] == 'q' || choice[0] == 'Q') { + return (-1); + } + return (0); +} + + +static int +h_find_new_device_link(char *device_dir, timestruc_t lastmtim) +{ +struct stat dsk_stat; +char lname[MAXPATHLEN], link[MAXPATHLEN]; +char *link_ptr; +DIR *dir; +struct dirent *dirent; +int found_newlink = 0; + + + if ((dir = opendir(device_dir)) == NULL) { + if (errno == ENOENT) { + return (0); + } else { + return (L_READ_DEV_DIR_ERROR); + } + } + + while ((dirent = readdir(dir)) != (struct dirent *)NULL) { + if (strcmp(dirent->d_name, ".") == 0 || + strcmp(dirent->d_name, "..") == 0) { + continue; + } + (void) sprintf(lname, "%s/%s", device_dir, dirent->d_name); + if (lstat(lname, &dsk_stat) < 0) { + (void) print_errString(L_LSTAT_ES_DIR_ERROR, + lname); + continue; + } + if (readlink((const char *)lname, (char *)link, + (size_t)MAXPATHLEN) <= 0) { + (void) print_errString(L_SYMLINK_ERROR, lname); + continue; + } + + /* + * "link" can be a relative pathname. But, since + * g_get_path_type() only accepts absolute paths, we + * will skip to the part where "/devices/" begins and pass a + * pointer from there. Since "link" is got from readlink(), + * it is unlikely that it will not have /devices string, but + * we will check for it anyways. + */ + if (!(link_ptr = strstr(link, "/devices/"))) + continue; + if (!g_get_path_type(link_ptr)) { + continue; + } + if (NEWER(dsk_stat.st_ctim, lastmtim)) { + found_newlink++; + if (found_newlink == 1) { + if (! (strcmp(device_dir, DEV_DSK_DIR))) { + (void) fprintf(stdout, MSGSTR(5561, + " New Logical Nodes under " + "/dev/dsk and /dev/rdsk :\n")); + } else { /* device_dir is /dev/rmt */ + (void) fprintf(stdout, "New Logical " + "Node under /dev/rmt:\n"); + } + } + (void) fprintf(stdout, "\t%s\n", dirent->d_name); + } + } + closedir(dir); + return (found_newlink); +} + + +/* + * prints the device state. + * + * RETURNS: + * None. + */ +void +print_dev_state(char *devname, int state) +{ + (void) printf("\t%s: ", devname); + if (state & DEVICE_ONLINE) { + (void) printf(MSGSTR(3000, "Online")); + if (state & DEVICE_BUSY) { + (void) printf(" "); + (void) printf(MSGSTR(37, "Busy")); + } + if (state & DEVICE_DOWN) { + (void) printf(" "); + (void) printf(MSGSTR(118, "Down")); + } + } else { + if (state & DEVICE_OFFLINE) { + (void) printf(MSGSTR(3001, "Offline")); + if (state & DEVICE_DOWN) { + (void) printf(" "); + (void) printf(MSGSTR(118, "Down")); + } + } + } + (void) printf("\n"); +} + + +/* + * prints the bus state. + * + * RETURNS: + * None. + */ +void +print_bus_state(char *devname, int state) +{ + (void) printf("\t%s: ", devname); + if (state == BUS_QUIESCED) { + (void) printf(MSGSTR(3002, "Quiesced")); + } else if (state == BUS_ACTIVE) { + (void) printf(MSGSTR(39, "Active")); + } else if (state == BUS_SHUTDOWN) { + (void) printf(MSGSTR(3003, "Shutdown")); + } + (void) printf("\n"); +} |