root/usr/src/lib/cfgadm_plugins/scsi/common/cfga_scsi.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 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include "cfga_scsi.h"

/*
 * This file contains the entry points to the plug-in as defined in the
 * config_admin(3CFGADM) man page.
 */

/*
 * Set the version number
 */
int cfga_version = CFGA_HSL_V2;

/*
 * For debugging - higher values increase verbosity
 */
int _scfga_debug = 0;

#pragma init(_cfgadm_scsi_init)

static void
_cfgadm_scsi_init()
{
        char *tstr;

        if (tstr = getenv("SCFGA_DEBUG")) {
                _scfga_debug = atoi(tstr);
        }
}

/*ARGSUSED*/
cfga_err_t
cfga_change_state(
        cfga_cmd_t state_change_cmd,
        const char *ap_id,
        const char *options,
        struct cfga_confirm *confp,
        struct cfga_msg *msgp,
        char **errstring,
        cfga_flags_t flags)
{
        apid_t apidt = {NULL};
        scfga_ret_t ret;

        if (errstring != NULL) {
                *errstring = NULL;
        }

        /*
         * All sub-commands which can change state of device require
         * root privileges.
         */
        if (geteuid() != 0) {
                return (CFGA_PRIV);
        }

        if (options != NULL && strcmp(options, OPT_DISABLE_RCM) != 0) {
                cfga_err(errstring, 0, ERRARG_OPT_INVAL, options, 0);
                return (CFGA_ERROR);
        }

        if ((ret = apidt_create(ap_id, &apidt, errstring)) != SCFGA_OK) {
                return (err_cvt(ret));
        }

        if (options != NULL)
                apidt.flags |= FLAG_DISABLE_RCM;

        /* A dynamic component indicates a device, else it is the bus */
        if (apidt.dyncomp != NULL) {
                ret = dev_change_state(state_change_cmd, &apidt, flags,
                    errstring);
        } else {
                ret = bus_change_state(state_change_cmd, &apidt, confp, flags,
                    errstring);
        }

        apidt_free(&apidt);
        return (err_cvt(ret));
}

/*ARGSUSED*/
cfga_err_t
cfga_private_func(
        const char *func,
        const char *ap_id,
        const char *options,
        struct cfga_confirm *confp,
        struct cfga_msg *msgp,
        char **errstring,
        cfga_flags_t flags)
{
        apid_t apidt = {NULL};
        prompt_t args = {NULL};
        scfga_ret_t ret;

        if (errstring != NULL)
                *errstring = NULL;

        if (geteuid() != 0) {
                return (CFGA_PRIV);
        }

        if (func == NULL) {
                return (CFGA_ERROR);
        }

        if (options != NULL && strcmp(options, OPT_DISABLE_RCM) != 0) {
                cfga_err(errstring, 0, ERRARG_OPT_INVAL, options, 0);
                return (CFGA_ERROR);
        }

        if ((ret = apidt_create(ap_id, &apidt, errstring)) != SCFGA_OK) {
                return (err_cvt(ret));
        }

        if (apidt.dyntype == PATH_APID) {
                return (CFGA_OPNOTSUPP);
        }

        if (options != NULL)
                apidt.flags |= FLAG_DISABLE_RCM;

        args.confp = confp;
        args.msgp = msgp;

        /*
         * Process command
         */
        ret = invoke_cmd(func, &apidt, &args, flags, errstring);

        apidt_free(&apidt);
        return (err_cvt(ret));
}

/*ARGSUSED*/
cfga_err_t
cfga_test(
        const char *ap_id,
        const char *options,
        struct cfga_msg *msgp,
        char **errstring,
        cfga_flags_t flags)
{
        if (errstring != NULL) {
                *errstring = NULL;
        }

        if (geteuid() != 0) {
                return (CFGA_PRIV);
        }

        return (CFGA_OPNOTSUPP);
}

/*ARGSUSED*/
cfga_err_t
cfga_list_ext(
        const char *ap_id,
        cfga_list_data_t **ap_id_list,
        int *nlistp,
        const char *options,
        const char *listopts,
        char **errstring,
        cfga_flags_t flags)
{
        int hba, expand, nelem;
        ldata_list_t *llp = NULL;
        apid_t apidt = {NULL};
        scfga_cmd_t cmd;
        scfga_ret_t ret;

        if (errstring != NULL) {
                *errstring = NULL;
        }

        if (ap_id_list == NULL || nlistp == NULL) {
                return (CFGA_ERROR);
        }

        *ap_id_list = NULL;
        *nlistp = 0;

        /*
         * There is no RCM involvement in "list" operations.
         * The only supported option is OPT_USE_DIFORCE.
         */
        if (options != NULL && strcmp(options, OPT_USE_DIFORCE) != 0) {
                cfga_err(errstring, 0, ERRARG_OPT_INVAL, options, 0);
                return (CFGA_ERROR);
        }

        hba = 0;
        if (GET_DYN(ap_id) == NULL) {
                hba = 1;
        }

        expand = 0;
        if ((flags & CFGA_FLAG_LIST_ALL) == CFGA_FLAG_LIST_ALL) {
                expand = 1;
        }

        /*
         * We expand published attachment points but not
         * dynamic attachment points
         */

        if (!hba) { /* Stat a single device - no expansion for devices */
                cmd = SCFGA_STAT_DEV;
        } else if (!expand) { /* Stat only the HBA */
                cmd = SCFGA_STAT_BUS;
        } else { /* Expand HBA attachment point */
                cmd = SCFGA_STAT_ALL;
        }

        if ((ret = apidt_create(ap_id, &apidt, errstring)) != SCFGA_OK) {
                return (err_cvt(ret));
        }

        /*
         * Currently only 1 option supported
         */
        if (options)
                apidt.flags |= FLAG_USE_DIFORCE;

        llp = NULL;
        nelem = 0;

        ret = do_list(&apidt, cmd, &llp, &nelem, errstring);
        if (ret != SCFGA_OK) {
                goto out;
        }

        assert(llp != NULL);

        if (list_ext_postprocess(&llp, nelem, ap_id_list, nlistp,
            errstring) != SCFGA_OK) {
                assert(*ap_id_list == NULL && *nlistp == 0);
                ret = SCFGA_LIB_ERR;
        } else {
                assert(*ap_id_list != NULL && *nlistp == nelem);
                ret = SCFGA_OK;
        }

        /* FALLTHROUGH */
out:
        list_free(&llp);
        apidt_free(&apidt);
        return (err_cvt(ret));
}


/*ARGSUSED*/
cfga_err_t
cfga_help(struct cfga_msg *msgp, const char *options, cfga_flags_t flags)
{
        cfga_msg(msgp, MSG_HELP_HDR, MSG_HELP_USAGE, 0);

        return (CFGA_OK);
}

/*
 * cfga_ap_id_cmp -- use default_ap_id_cmp() in libcfgadm
 */