root/usr/src/cmd/luxadm/x86_adm.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.
 */


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <hbaapi.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/fibre-channel/fcio.h>
#include <sys/fibre-channel/impl/fc_error.h>
#include <sys/scsi/adapters/scsi_vhci.h>
#include "common.h"
#include "errorcodes.h"
#include <locale.h>

/* The i18n catalog */
nl_catd l_catd;

void
i18n_catopen() {
        static int fileopen = 0;

        if (setlocale(LC_ALL, "") == NULL) {
                (void) fprintf(stderr,
                "Cannot operate in the locale requested. "
                "Continuing in the default C locale\n");
        }
        if (!fileopen) {
                l_catd = catopen("a5k_g_fc_i18n_cat", NL_CAT_LOCALE);
                if (l_catd == (nl_catd)-1) {
                        return;
                }
                fileopen = 1;
        }
        return;

}

/*
 * Given an error number, this functions
 * calls the get_errString() to print a
 * corresponding error message to the stderr.
 * get_errString() always returns an error
 * message, even in case of undefined error number.
 * So, there is no need to check for a NULL pointer
 * while printing the error message to the stdout.
 *
 * RETURNS: N/A
 *
 */
void
print_errString(int errnum, char *devpath)
{

char    *errStr;

        errStr = get_errString(errnum);

        if (devpath == NULL) {
                (void) fprintf(stderr,
                                "%s \n\n", errStr);
        } else {
                (void) fprintf(stderr,
                                "%s - %s.\n\n", errStr, devpath);
        }

        /* free the allocated memory for error string */
        if (errStr != NULL)
                (void) free(errStr);
}

static void terminate() {
        fprintf(stdout, MSGSTR(2506, "Unsupported"));
        fprintf(stdout, "\n");
        exit(1);
}

/*ARGSUSED*/
int adm_display_config(char **a) {
        terminate();
        return (1);
}

/*ARGSUSED*/
void adm_download(char **a, char *b) {
        terminate();
}

/*ARGSUSED*/
void up_encl_name(char **a, int b) {
        terminate();
}

void adm_failover(char **argv) {
        int             path_index = 0, err = 0, fd;
        char            path_class[MAXNAMELEN];
        char            client_path[MAXPATHLEN];
        char            *path_phys = NULL, *trailingMinor;
        sv_switch_to_cntlr_iocdata_t    iocsc;

        (void) memset(path_class, 0, sizeof (path_class));
        (void) strcpy(path_class, argv[path_index++]);
        if ((strcmp(path_class, "primary") != 0) &&
                (strcmp(path_class, "secondary") != 0)) {
                        (void) fprintf(stderr,
                        MSGSTR(2300, "Incorrect pathclass\n"));
                        exit(-1);
        }

        if ((fd = open("/devices/scsi_vhci:devctl", O_RDWR)) < 0) {
            print_errString(L_OPEN_PATH_FAIL, "/devices/scsi_vhci:devctl");
            exit(-1);
        }

        iocsc.client = client_path;
        iocsc.class = path_class;

        while (argv[path_index] != NULL) {
                path_phys =
                    get_slash_devices_from_osDevName(argv[path_index++],
                        STANDARD_DEVNAME_HANDLING);
                if ((path_phys == NULL) ||
                        (strstr(path_phys, "/devices/scsi_vhci") == NULL)) {
                                (void) fprintf(stderr,
                                MSGSTR(2301, "Incorrect pathname\n"));
                                close(fd);
                                exit(-1);
                }

                strcpy(iocsc.client, path_phys + strlen("/devices"));

                /* Now chop off the trailing ":xxx" portion if present */
                if ((trailingMinor = strrchr(iocsc.client, ':')) != NULL) {
                        trailingMinor[0] = '\0';
                }

                if (ioctl(fd, SCSI_VHCI_SWITCH_TO_CNTLR, &iocsc) != 0) {
                    switch (errno) {
                        case EALREADY:
                                err = L_SCSI_VHCI_ALREADY_ACTIVE;
                                break;
                        case ENXIO:
                                err = L_INVALID_PATH;
                                break;
                        case EIO:
                                err = L_SCSI_VHCI_NO_STANDBY;
                                break;
                        case ENOTSUP:
                                err = L_SCSI_VHCI_FAILOVER_NOTSUP;
                                break;
                        case EBUSY:
                                err = L_SCSI_VHCI_FAILOVER_BUSY;
                                break;
                        case EFAULT:
                        default:
                                err = L_SCSI_VHCI_ERROR;
                    }
                }

                if (err != 0) {
                    close(fd);
                    print_errString(err, path_phys);
                    exit(-1);
                }
        }

        close(fd);
}

/*ARGSUSED*/
int adm_inquiry(char **a) {
        terminate();
        return (1);
}

/*ARGSUSED*/
void pho_probe() {
        terminate();
}

/*ARGSUSED*/
void non_encl_probe() {
        terminate();
}

/*ARGSUSED*/
void adm_led(char **a, int b) {
        terminate();
}

/*ARGSUSED*/
void up_password(char **a) {
        terminate();
}

/*ARGSUSED*/
int adm_reserve(char *path) {
        terminate();
        return (1);
}

/*ARGSUSED*/
int adm_release(char *path) {
        terminate();
        return (1);
}

/*ARGSUSED*/
int adm_start(char **a) {
        terminate();
        return (1);
}

/*ARGSUSED*/
int adm_stop(char **a) {
        terminate();
        return (1);
}

/*ARGSUSED*/
int adm_power_off(char **a, int b) {
        terminate();
        return (1);
}

int
adm_forcelip(char **argv)
{
        int             path_index = 0, fd;
        uint64_t        wwn;
        fcio_t          fcio;
        HBA_HANDLE handle;
        HBA_ADAPTERATTRIBUTES hbaAttrs;
        HBA_PORTATTRIBUTES portAttrs;
        HBA_FCPTARGETMAPPINGV2    *map;
        HBA_STATUS status;
        int count, adapterIndex, portIndex, mapIndex;
        char name[256];
        int             matched, ret = 0, wwnCompare = 0, ntries;
        char        *physical = NULL, *slash_OSDeviceName = NULL;

        if ((status = loadLibrary())) {
            /* loadLibrary print out error msg */
            return (ret++);
        }
        for (path_index = 0; argv[path_index] != NULL; path_index++) {

            if (is_wwn(argv[path_index])) {
                (void) sscanf(argv[path_index], "%016llx", &wwn);
                wwnCompare = 1;
            } else if (!is_path(argv[path_index])) {
                print_errString(L_INVALID_PATH, argv[path_index]);
                ret++;
                continue;
            }
            if (!wwnCompare) {
                /* Convert the paths to phsyical paths */
                physical = get_slash_devices_from_osDevName(argv[path_index],
                        STANDARD_DEVNAME_HANDLING);
                if (!physical) {
                    print_errString(L_INVALID_PATH, argv[path_index]);
                    ret++;
                    continue;
                }
            }

            count = getNumberOfAdapters();

            matched = 0;
            for (adapterIndex = 0; adapterIndex < count; adapterIndex ++) {
                status = HBA_GetAdapterName(adapterIndex, (char *)&name);
                if (status != HBA_STATUS_OK) {
                    /* May have been DR'd */
                    continue;
                }
                handle = HBA_OpenAdapter(name);
                if (handle == 0) {
                    /* May have been DR'd */
                    continue;
                }

                if (getAdapterAttrs(handle, name, &hbaAttrs)) {
                    /* Should never happen */
                    HBA_CloseAdapter(handle);
                    continue;
                }

                /* Loop over all HBA Ports */
                for (portIndex = 0; portIndex < hbaAttrs.NumberOfPorts;
                        portIndex++) {
                    if (getAdapterPortAttrs(handle, name, portIndex,
                            &portAttrs)) {
                        continue;
                    }

                    matched = 0;
                    if (is_wwn(argv[path_index])) {
                        if (wwn == wwnConversion(
                                portAttrs.NodeWWN.wwn) ||
                                wwn == wwnConversion(
                                portAttrs.PortWWN.wwn)) {
                            matched = 1;
                        }
                    } else {
                        slash_OSDeviceName = get_slash_devices_from_osDevName(
                            portAttrs.OSDeviceName, STANDARD_DEVNAME_HANDLING);
                        if (!slash_OSDeviceName) {
                            continue;
                        } else {
                            if (strncmp(physical, slash_OSDeviceName,
                                    strlen(slash_OSDeviceName) -
                                    strlen(strrchr(slash_OSDeviceName, ':')))
                                == 0) {
                                matched = 1;
                            }
                            free(slash_OSDeviceName);
                        }
                    }

                    if (!matched) {
                        if (!fetch_mappings(handle, portAttrs.PortWWN, &map)) {
                                /*
                                 * matchr_mapping checks the arg
                                 * so we pass argv here.
                                 */
                            mapIndex = match_mappings(argv[path_index], map);
                            if (mapIndex >= 0) {
                                matched = 1;
                            }
                        } else {
                            continue;
                        }
                    }

                    if (matched) {
                        if ((fd = open(portAttrs.OSDeviceName,
                                O_RDONLY | O_EXCL)) == -1) {
                            print_errString(L_OPEN_PATH_FAIL,
                                    portAttrs.OSDeviceName);
                            return (ret++);
                        }

                        fcio.fcio_cmd = FCIO_RESET_LINK;
                        fcio.fcio_xfer = FCIO_XFER_WRITE;
                        /*
                         * Reset the local loop here (fcio_ibuf = 0).
                         * Reset a remote loop on the Fabric by
                         * passing its node wwn (fcio_len = sizeof(nwwn)
                         * and fcio_ibuf = (caddr_t)&nwwn) to the port driver.
                         */
                        (void) memset(&wwn, 0, sizeof (wwn));
                        fcio.fcio_ilen = sizeof (wwn);
                        fcio.fcio_ibuf = (caddr_t)&wwn;

                        for (ntries = 0; ntries < RETRY_FCIO_IOCTL; ntries++) {
                            errno = 0;
                            if (ioctl(fd, FCIO_CMD, &fcio) != 0) {
                                /*
                                 * When port is offlined, qlc
                                 * returns the FC_OFFLINE error and errno
                                 * is set to EIO.
                                 * We do want to ignore this error,
                                 * especially when an enclosure is
                                 * removed from the loop.
                                 */
                                if (fcio.fcio_errno == FC_OFFLINE)
                                    break;
                                if ((errno == EAGAIN) &&
                                    (ntries+1 < RETRY_FCIO_IOCTL)) {
                                    /* wait WAIT_FCIO_IOCTL */
                                    (void) usleep(WAIT_FCIO_IOCTL);
                                    continue;
                                }
                                I_DPRINTF("FCIO ioctl failed.\n"
                                    "Error: %s. fc_error = %d (0x%x)\n",
                                strerror(errno), fcio.fcio_errno,
                                    fcio.fcio_errno);
                                close(fd);
                                print_errString(L_FCIO_FORCE_LIP_FAIL,
                                    portAttrs.OSDeviceName);
                                return (ret++);
                            } else {
                                break; /* ioctl succeeds. */
                            }
                        }
                        close(fd);
                        if (ntries == RETRY_FCIO_IOCTL) {
                            print_errString(L_FCIO_FORCE_LIP_FAIL,
                            portAttrs.OSDeviceName);
                            return (ret++);
                        }
                    }
                    if (matched)
                        break; /* for HBA port for loop */
                }
                if (matched) /* HBA adapter for loop */
                    break;
            }

            if (!matched) {
                print_errString(L_INVALID_PATH, argv[path_index]);
                ret++;
            }
        }
        HBA_FreeLibrary();
        return (ret);
}

/*ARGSUSED*/
void adm_bypass_enable(char **argv, int bypass_flag) {
        terminate();
}

/*ARGSUSED*/
int adm_port_offline_online(char **a, int b) {
        terminate();
        return (1);
}

/*ARGSUSED*/
void display_link_status(char **a) {
        terminate();
}

/*ARGSUSED*/
void dump_map(char **argv) {
        terminate();
}

/*ARGSUSED*/
int adm_display_port(int a) {
        terminate();
        return (1);
}

/*ARGSUSED*/
int adm_port_loopback(char *a, int b) {
        terminate();
        return (1);
}

/*ARGSUSED*/
int hotplug_e(int todo, char **argv, int verbose_flag, int force_flag) {
        terminate();
        return (1);
}

/*ARGSUSED*/
int
setboot(unsigned int yes, unsigned int verbose, char *fname)
{
        terminate();
        return (1);
}

/*ARGSUSED*/
int hotplug(int todo, char **argv, int verbose_flag, int force_flag) {
        terminate();
        return (1);
}

/*ARGSUSED*/
int adm_check_file(char **argv, int flag) {
        terminate();
        return (1);
}

/*ARGSUSED*/
int sysdump(int verbose) {
        terminate();
        return (1);
}

/*ARGSUSED*/
int fcal_update(unsigned int verbose, char *file) {
        terminate();
        return (1);
}

/*ARGSUSED*/
int q_qlgc_update(unsigned int verbose, char *file) {
        terminate();
        return (1);
}

/*ARGSUSED*/
int emulex_update(char *file) {
        terminate();
        return (1);
}

/*ARGSUSED*/
int emulex_fcode_reader(int fcode_fd, char *pattern, char *pattern_value,
    uint32_t pattern_value_size) {
        terminate();
        return (1);
}

/*ARGSUSED*/
void dump(char **argv) {
        terminate();
}

/*ARGSUSED*/
int h_insertSena_fcdev() {
        terminate();
        return (1);
}