root/usr/src/cmd/srptadm/srptadm.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 (c) 2010, Oracle and/or its affiliates. All rights reserved.
 */
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <getopt.h>
#include <strings.h>
#include <ctype.h>
#include <libnvpair.h>
#include <libintl.h>
#include <libgen.h>
#include <pwd.h>
#include <auth_attr.h>
#include <secdb.h>
#include <libscf.h>
#include <limits.h>
#include <locale.h>
#include <dirent.h>

#include <libstmf.h>
#include <libsrpt.h>

/* SMF service info */
#define STMF_SVC        "svc:/system/stmf:default"

#define STMF_STALE(ret) {\
        if (ret == STMF_ERROR_PROV_DATA_STALE) {\
                (void) fprintf(stderr, "%s\n",\
                    gettext("Configuration changed during processing.  "\
                    "Check the configuration, then retry this command "\
                    "if appropriate."));\
        }\
}

#define SRPTADM_CHKAUTH(sec) {\
        if (!chkauthattr(sec, srptadm_uname)) {\
                (void) fprintf(stderr,\
                    gettext("Error, operation requires authorization %s"),\
                    sec);\
                (void) fprintf(stderr, "\n");\
                return (1);\
        }\
}

#define PROPS_FORMAT    "    %-20s: "

static struct option srptadm_long[] = {
        {"enable",              no_argument,            NULL, 'e'},
        {"disable",             no_argument,            NULL, 'd'},
        {"reset",               no_argument,            NULL, 'r'},
        {"help",                no_argument,            NULL, '?'},
        {"help",                no_argument,            NULL, 'h'},
        {NULL, 0, NULL, 0}
};

static char m_def[] = "srptadm modify-defaults [-e] [-d]";
static char l_def[] = "srptadm list-defaults";
static char s_tgt[] = "srptadm modify-target [-e] [-d] [-r] <hca>";
static char l_tgt[] = "srptadm list-target [<hca>]";

/* keep the order of this enum in the same order as the 'subcmds' struct */
typedef enum {
        MODIFY_DEFAULT,
        LIST_DEFAULT,
        MODIFY_TARGET,
        LIST_TARGET,
        NULL_SUBCMD     /* must always be last! */
} srptadm_sub_t;

typedef struct {
        char            *name;
        char            *shortopts;
        char            *usemsg;
} srptadm_subcmds_t;

static srptadm_subcmds_t        subcmds[] = {
        {"modify-defaults", "edh?", m_def},
        {"list-defaults", "h?", l_def},
        {"modify-target", "edrh?", s_tgt},
        {"list-target", "h?", l_tgt},
        {NULL, ":h?", NULL},
};

/* used for checking if user is authorized */
static char *srptadm_uname = NULL;

/* prototypes */
static int get_local_hcas(char **hcaArray, int count);
static int print_target_props(char *hca);
static int list_target(char *hca);
static int disable_target(char *hca);
static int reset_target(char *hca);
static int list_defaults(void);
static int enable_target(char *hca);
static int set_default_state(boolean_t enabled);

int
main(int argc, char *argv[])
{
        int             ret = 0;
        int             idx = NULL_SUBCMD;
        int             c;
        int             newargc = argc;
        char            **newargv = NULL;
        char            *objp;
        int             srptind = 0;
        struct passwd   *pwd = NULL;
        char            *smfstate = NULL;
        boolean_t       reset = B_FALSE;
        int             dflag = 0;
        int             eflag = 0;

        (void) setlocale(LC_ALL, "");
        (void) textdomain(TEXT_DOMAIN);

        if (argc < 2) {
                ret = 1;
                goto usage_error;
        }

        for (idx = 0; subcmds[idx].name != NULL; idx++) {
                if (strcmp(argv[1], subcmds[idx].name) == 0) {
                        break;
                }
        }

        /* get the caller's user name for subsequent chkauthattr() calls */
        pwd = getpwuid(getuid());
        if (pwd == NULL) {
                (void) fprintf(stderr, "%s\n",
                    gettext("Could not determine callers user name."));
                return (1);
        }

        srptadm_uname = strdup(pwd->pw_name);

        /* increment past command & subcommand */
        newargc--;
        newargv = &(argv[1]);

        while ((ret == 0) && (newargv)) {
                c = getopt_long(newargc, newargv, subcmds[idx].shortopts,
                    srptadm_long, &srptind);
                if (c == -1) {
                        break;
                }

                switch (c) {
                        case 0:
                                /* flag set by getopt */
                                break;
                        case 'd':
                                dflag++;
                                break;
                        case 'e':
                                eflag++;
                                break;
                        case 'r':
                                reset = B_TRUE;
                                break;
                        case '?':
                                /*
                                 * '?' is returned for both unrecognized
                                 * options and if explicitly provided on
                                 * the command line.  The latter should
                                 * be handled the same as -h.
                                 */
                                if (strcmp(newargv[optind-1], "-?") != 0) {
                                        (void) fprintf(stderr,
                                            gettext("Unrecognized option %s"),
                                            newargv[optind-1]);
                                        (void) fprintf(stderr, "\n");
                                        ret = 1;
                                }
                                goto usage_error;
                        case 'h':
                                goto usage_error;
                        case ':':
                                (void) fprintf(stderr,
                                    gettext("Option %s requires an operand."),
                                    newargv[optind-1]);
                                (void) fprintf(stderr, "\n");

                                /* FALLTHROUGH */
                        default:
                                ret = 1;
                                break;
                }
        }

        if (ret != 0) {
                goto usage_error;
        }

        /* after getopt() to allow handling of -h option */
        if ((srptadm_sub_t)idx == NULL_SUBCMD) {
                (void) fprintf(stderr, "%s\n",
                    gettext("Error, no subcommand specified"));
                ret = 1;
                goto usage_error;
        }

        newargc -= optind;
        if (newargc == 0) {
                newargv = NULL;
                objp = NULL;
        } else {
                newargv = &(newargv[optind]);
                objp = newargv[0];
        }

        if (objp == NULL) {
                switch ((srptadm_sub_t)idx) {
                case MODIFY_TARGET:
                        /* These subcommands need operands */
                        ret = 1;
                        goto usage_error;
                default:
                        break;
                }
        }

        if (newargc > 1) {
                switch ((srptadm_sub_t)idx) {
                case MODIFY_TARGET:
                case LIST_TARGET:
                        /* These subcommands should have at most one operand */
                        ret = 1;
                        goto usage_error;

                default:
                        break;
                }
        }


        /*
         * Make sure STMF service is enabled before proceeding.
         */
        smfstate = smf_get_state(STMF_SVC);
        if (!smfstate ||
            (strcmp(smfstate, SCF_STATE_STRING_ONLINE) != 0)) {
                (void) fprintf(stderr, "%s\n",
                    gettext("The STMF service must be online "
                    "before running this command."));
                (void) fprintf(stderr,
                    gettext("Use 'svcadm enable -r %s'"), STMF_SVC);
                (void) fprintf(stderr, "\n");
                (void) fprintf(stderr, "%s\n",
                    gettext("to enable the service and its prerequisite "
                    "services and/or"));
                (void) fprintf(stderr,
                    gettext("'svcs -x %s' to determine why it is not online."),
                    STMF_SVC);
                (void) fprintf(stderr, "\n");

                return (1);
        }

        switch ((srptadm_sub_t)idx) {
                case MODIFY_DEFAULT:
                        if (eflag) {
                                ret = set_default_state(B_TRUE);
                        } else if (dflag) {
                                ret = set_default_state(B_FALSE);
                        } else {
                                ret = 1;
                                goto usage_error;
                        }
                        break;
                case LIST_DEFAULT:
                        ret = list_defaults();
                        break;
                case MODIFY_TARGET:
                        if (reset) {
                                ret = reset_target(objp);
                        } else if (eflag) {
                                ret = enable_target(objp);
                        } else if (dflag) {
                                ret = disable_target(objp);
                        } else {
                                ret = 1;
                                goto usage_error;
                        }
                        break;
                case LIST_TARGET:
                        ret = list_target(objp);
                        break;
                default:
                        ret = 1;
                        goto usage_error;
        }

        if (ret != 0) {
                (void) fprintf(stderr,
                    gettext("srptadm %s failed with error %d"),
                    subcmds[idx].name, ret);
                (void) fprintf(stderr, "\n");
        }
        return (ret);

usage_error:
        if (subcmds[idx].name) {
                (void) printf("%s\n", gettext(subcmds[idx].usemsg));
        } else {
                /* overall usage */
                (void) printf("%s\n\n", gettext("srptadm usage:"));
                for (idx = 0; subcmds[idx].name != NULL; idx++) {
                        if (!subcmds[idx].usemsg) {
                                continue;
                        }
                        (void) printf("\t%s\n", gettext(subcmds[idx].usemsg));
                }
        }

        return (ret);
}

static int
set_default_state(boolean_t enabled)
{
        int             ret;
        char            *sec = "solaris.smf.modify.stmf";

        SRPTADM_CHKAUTH(sec);

        ret = srpt_SetDefaultState(enabled);

        return (ret);
}

static int
enable_target(char *hca)
{
        int             ret;
        char            *sec = "solaris.smf.modify.stmf";

        SRPTADM_CHKAUTH(sec);

        ret = srpt_SetTargetState(hca, B_TRUE);

        return (ret);
}

static int
disable_target(char *hca)
{
        int             ret;
        char            *sec = "solaris.smf.modify.stmf";

        SRPTADM_CHKAUTH(sec);

        ret = srpt_SetTargetState(hca, B_FALSE);

        return (ret);
}

static int
reset_target(char *hca)
{
        int             ret;
        char            *sec = "solaris.smf.modify.stmf";

        SRPTADM_CHKAUTH(sec);

        ret = srpt_ResetTarget(hca);

        return (ret);
}

static int
list_defaults(void)
{
        int             ret;
        char            *sec = "solaris.smf.read.stmf";
        boolean_t       enabled;

        SRPTADM_CHKAUTH(sec);

        /* only state set as default for now */
        ret = srpt_GetDefaultState(&enabled);

        if (ret == 0) {
                (void) printf("%s:\n\n",
                    gettext("SRP Target Service Default Properties"));

                (void) printf("    %s:\t",
                    gettext("Target creation enabled by default"));

                if (enabled) {
                        (void) printf("%s\n", gettext("true"));
                } else {
                        (void) printf("%s\n", gettext("false"));
                }
        }

        return (ret);
}

static int
list_target(char *hca)
{
        int             ret;
        char            *sec = "solaris.smf.read.stmf";
        char            *hcaArr[1024];  /* way bigger than we'll ever see */
        int             i;

        SRPTADM_CHKAUTH(sec);

        if (hca != NULL) {
                ret = print_target_props(hca);
                return (ret);
        }

        /* get list of HCAs configured on this system, from /dev/cfg */
        (void) memset(&hcaArr, 0, 1024 * sizeof (char *));

        ret = get_local_hcas(hcaArr, sizeof (hcaArr));
        if (ret == ETOOMANYREFS) {
                (void) fprintf(stderr, "Internal error:  too many HCAs\n");
                goto done;
        } else if (ret != 0) {
                (void) fprintf(stderr, "Error getting list of HCAs: %d\n", ret);
                goto done;
        }

        for (i = 0; i < 1024; i++) {
                if (hcaArr[i] == NULL) {
                        break;
                }
                ret = print_target_props(hcaArr[i]);
        }

done:
        for (i = 0; i < 1024; i++) {
                if (hcaArr[i] == NULL) {
                        break;
                }
                free(hcaArr[i]);
        }

        return (ret);
}

static int
print_target_props(char *hca)
{
        int             ret;
        boolean_t       enabled;
        char            buf[32];
        char            euibuf[64];
        uint64_t        hcaguid;
        stmfDevid       devid;
        stmfTargetProperties    props;
        char            *state;

        ret = srpt_NormalizeGuid(hca, buf, sizeof (buf), &hcaguid);
        if (ret != 0) {
                (void) fprintf(stderr, "Invalid target HCA: %s\n",
                    hca);
                return (ret);
        }

        /* only property set is enabled */
        ret = srpt_GetTargetState(buf, &enabled);
        if (ret != 0) {
                (void) fprintf(stderr,
                    "Could not get enabled state for %s: %d\n",
                    buf, ret);
                return (ret);
        }

        (void) printf("Target HCA %s:\n", buf);

        (void) printf(PROPS_FORMAT, gettext("Enabled"));

        if (enabled) {
                (void) printf("%s\n", gettext("true"));
        } else {
                (void) printf("%s\n", gettext("false"));
        }

        state = "-";

        (void) snprintf(euibuf, sizeof (euibuf), "eui.%016llX", hcaguid);

        ret = stmfDevidFromIscsiName(euibuf, &devid);
        if (ret == STMF_STATUS_SUCCESS) {
                ret = stmfGetTargetProperties(&devid, &props);
                if (ret == STMF_STATUS_SUCCESS) {
                        if (props.status == STMF_TARGET_PORT_ONLINE) {
                                state = "online";
                        } else {
                                state = "offline";
                        }
                }
        }

        (void) printf(PROPS_FORMAT, gettext("SRP Target Name"));
        (void) printf("%s\n", euibuf);
        (void) printf(PROPS_FORMAT, gettext("Operational Status"));
        (void) printf("%s\n", state);

        (void) printf("\n");

        return (0);
}


static int
get_local_hcas(char **hcaArray, int count)
{
        int             ret = 0;
        char            *cfgdir = "/dev/cfg";
        DIR             *dirp = NULL;
        struct dirent   *entry;
        int             idx = 0;
        char            *bufp;

        if ((hcaArray == NULL) || (count == 0)) {
                return (EINVAL);
        }

        dirp = opendir(cfgdir);

        if (dirp == NULL) {
                ret = errno;
                (void) fprintf(stderr, "Could not open %s: errno %d\n",
                    cfgdir, ret);
                return (ret);
        }

        while ((entry = readdir(dirp)) != NULL) {
                bufp = &entry->d_name[0];

                if (strncmp(bufp, "hca:", 4) != 0) {
                        continue;
                }

                bufp += 4;

                hcaArray[idx] = strdup(bufp);
                if (hcaArray[idx] == NULL) {
                        ret = ENOMEM;
                        break;
                }
                idx++;

                if (idx >= count) {
                        ret = ETOOMANYREFS;
                        break;
                }
        }

        return (ret);
}