root/usr/src/cmd/luxadm/hotplug.c
/*
 * 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 != 0) {
                    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 = 0, path_index, dtype, f_r, err = 0;
int             al_pa, i, dev_location = 0, 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 = 0;
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 = '\0';
                                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 = '\0';
                                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 = 1;
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 = NULL, *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) = '\0';
                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 = '\0';
                        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 != 0) {
                        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");
}