diff options
Diffstat (limited to 'usr/src/cmd/itadm/itadm.c')
-rw-r--r-- | usr/src/cmd/itadm/itadm.c | 2215 |
1 files changed, 2215 insertions, 0 deletions
diff --git a/usr/src/cmd/itadm/itadm.c b/usr/src/cmd/itadm/itadm.c new file mode 100644 index 0000000..87a6971 --- /dev/null +++ b/usr/src/cmd/itadm/itadm.c @@ -0,0 +1,2215 @@ +/* + * 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) 2008, 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 <libstmf.h> +#include <libiscsit.h> + +/* what's this used for?? */ +#define ITADM_VERSION "1.0" + +/* SMF service info */ +#define ISCSIT_SVC "svc:/network/iscsi/target:default" + +#define STMF_STALE(ret) {\ + if (ret == STMF_ERROR_PROV_DATA_STALE) {\ + output_config_error(ret, NULL);\ + } else if (ret != 0) {\ + output_config_error(ret,\ + gettext("Configuration change failed"));\ + }\ +} + +#define ITADM_CHKAUTH(sec) {\ + if (!chkauthattr(sec, itadm_uname)) {\ + (void) fprintf(stderr,\ + gettext("Error, operation requires authorization %s"),\ + sec);\ + (void) fprintf(stderr, "\n");\ + return (1);\ + }\ +} + + +static struct option itadm_long[] = { + {"alias", required_argument, NULL, 'l'}, + {"auth-method", required_argument, NULL, 'a'}, + {"chap-secret", no_argument, NULL, 's'}, + {"chap-secret-file", required_argument, NULL, 'S'}, + {"chap-user", required_argument, NULL, 'u'}, + {"force", no_argument, NULL, 'f'}, + {"help", no_argument, NULL, 'h'}, + {"help", no_argument, NULL, '?'}, + {"isns", required_argument, NULL, 'i'}, + {"isns-server", required_argument, NULL, 'I'}, + {"node-name", required_argument, NULL, 'n'}, + {"radius-secret", no_argument, NULL, 'd'}, + {"radius-secret-file", required_argument, NULL, 'D'}, + {"radius-server", required_argument, NULL, 'r'}, + {"tpg-tag", required_argument, NULL, 't'}, + {"verbose", no_argument, NULL, 'v'}, + {"version", no_argument, NULL, 'V'}, + {NULL, 0, NULL, 0} +}; + +char c_tgt[] = "itadm create-target [-a radius|chap|none|default] [-s] \ +[-S chap-secret-path] [-u chap-username] [-n target-node-name] \ +[-l alias] [-t tpg-name[,tpg-name,...]]"; + +static char m_tgt[] = "itadm modify-target [-a radius|chap|none|default] [-s] \ +[-S chap-secret-path] [-u chap-username] [-n new-target-node-name] \ +[-l alias] [-t tpg-name[,tpg-name,...]] target-node-name"; + +static char d_tgt[] = "itadm delete-target [-f] target-node-name"; + +static char l_tgt[] = "itadm list-target [-v] [target-node-name]"; + +static char c_tpg[] = "itadm create-tpg tpg-name IP-address[:port] \ +[IP-address[:port]] [...]"; + +static char l_tpg[] = "itadm list-tpg [-v] [tpg-name]"; + +static char d_tpg[] = "itadm delete-tpg [-f] tpg-name"; + +static char c_ini[] = "itadm create-initiator [-s] [-S chap-secret-path] \ +[-u chap-username] initiator-node-name"; + +static char m_ini[] = "itadm modify-initiator [-s] [-S chap-secret-path] \ +[-u chap-username] initiator-node-name"; + +static char l_ini[] = "itadm list-initiator [-v] initiator-node-name"; + +static char d_ini[] = "itadm delete-inititator initiator-node-name"; + +static char m_def[] = "itadm modify-defaults [-a radius|chap|none] \ +[-r IP-address[:port]] [-d] [-D radius-secret-path] [-i enable|disable] \ +[-I IP-address[:port][,IP-adddress[:port]]]"; + +static char l_def[] = "itadm list-defaults"; + + +/* keep the order of this enum in the same order as the 'subcmds' struct */ +typedef enum { + CREATE_TGT, + MODIFY_TGT, + DELETE_TGT, + LIST_TGT, + CREATE_TPG, + DELETE_TPG, + LIST_TPG, + CREATE_INI, + MODIFY_INI, + LIST_INI, + DELETE_INI, + MODIFY_DEF, + LIST_DEF, + NULL_SUBCMD /* must always be last! */ +} itadm_sub_t; + +typedef struct { + char *name; + char *shortopts; + char *usemsg; +} itadm_subcmds_t; + +static itadm_subcmds_t subcmds[] = { + {"create-target", ":a:sS:u:n:l:t:h?", c_tgt}, + {"modify-target", ":a:sS:u:n:l:t:h?", m_tgt}, + {"delete-target", ":fh?", d_tgt}, + {"list-target", ":vh?", l_tgt}, + {"create-tpg", ":h?", c_tpg}, + {"delete-tpg", ":fh?", d_tpg}, + {"list-tpg", ":vh?", l_tpg}, + {"create-initiator", ":sS:u:h?", c_ini}, + {"modify-initiator", ":sS:u:h?", m_ini}, + {"list-initiator", ":vh?", l_ini}, + {"delete-initiator", ":h?", d_ini}, + {"modify-defaults", ":a:r:dD:i:I:h?", m_def}, + {"list-defaults", ":h?", l_def}, + {NULL, ":h?", NULL}, +}; + +/* used for checking if user is authorized */ +static char *itadm_uname = NULL; + +/* prototypes */ +static int +itadm_get_password(nvlist_t *nvl, char *key, char *passfile, + char *phrase); + +static int +itadm_opt_to_arr(nvlist_t *nvl, char *key, char *opt, uint32_t *num); + +static int +create_target(char *tgt, nvlist_t *proplist); + +static int +modify_target(char *tgt, char *new, nvlist_t *proplist); + +static int +delete_target(char *tgt, boolean_t force); + +static int +list_target(char *tgt, boolean_t verbose, boolean_t script); + +static int +create_tpg(char *tpg, int addrc, char **addrs); + +static int +list_tpg(char *tpg, boolean_t verbose, boolean_t script); + +static int +delete_tpg(char *tpg, boolean_t force); + +static int +modify_initiator(char *ini, nvlist_t *proplist, boolean_t create); + +static int +list_initiator(char *ini, boolean_t verbose, boolean_t script); + +static int +delete_initiator(char *ini); + +static int +modify_defaults(nvlist_t *proplist); + +static int +list_defaults(boolean_t script); + +static void +tag_name_to_num(char *tagname, uint16_t *tagnum); + +/* prototype from iscsit_common.h */ +extern int +sockaddr_to_str(struct sockaddr_storage *sa, char **addr); + +static void output_config_error(int error_code, char *msg); + +int +main(int argc, char *argv[]) +{ + int ret = 0; + int idx = NULL_SUBCMD; + char c; + int newargc = argc; + char **newargv = NULL; + char *objp; + int itind = 0; + nvlist_t *proplist = NULL; + boolean_t verbose = B_FALSE; + boolean_t scripting = B_FALSE; + boolean_t tbool; + char *targetname = NULL; + char *propname; + boolean_t force = B_FALSE; + struct passwd *pwd = NULL; + uint32_t count = 0; + char *smfstate = NULL; + + (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); + } + + itadm_uname = strdup(pwd->pw_name); + + /* increment past command & subcommand */ + newargc--; + newargv = &(argv[1]); + + ret = nvlist_alloc(&proplist, NV_UNIQUE_NAME, 0); + if (ret != 0) { + ret = errno; + output_config_error(ret, gettext("Could not allocate nvlist")); + ret = 1; + goto usage_error; + } + + while ((ret == 0) && (newargv)) { + c = getopt_long(newargc, newargv, subcmds[idx].shortopts, + itadm_long, &itind); + if (c == -1) { + break; + } + + switch (c) { + case 0: + /* flag set by getopt */ + break; + case 'a': + ret = nvlist_add_string(proplist, + "auth", optarg); + break; + case 'd': + ret = itadm_get_password(proplist, + "radiussecret", NULL, + gettext("Enter RADIUS secret: ")); + break; + case 'D': + ret = itadm_get_password(proplist, + "radiussecret", optarg, NULL); + break; + case 'f': + force = 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 'i': + if (strncmp(optarg, "enable", strlen(optarg)) + == 0) { + tbool = B_TRUE; + } else if (strncmp(optarg, "disable", + strlen(optarg)) == 0) { + tbool = B_FALSE; + } else { + (void) fprintf(stderr, "%s\n", + gettext("invalid value for -i")); + ret = 1; + break; + } + ret = nvlist_add_boolean_value(proplist, + "isns", tbool); + break; + case 'I': + /* possibly multi-valued */ + ret = itadm_opt_to_arr(proplist, + "isnsserver", optarg, &count); + if ((ret == 0) && (count > 8)) { + (void) fprintf(stderr, "%s\n", + gettext( + "Too many iSNS servers specified, " + "maximum of 8 allowed")); + ret = 1; + } + break; + case 'l': + ret = nvlist_add_string(proplist, + "alias", optarg); + break; + case 'n': + targetname = strdup(optarg); + if (targetname == NULL) { + ret = ENOMEM; + } + break; + case 'r': + ret = nvlist_add_string(proplist, + "radiusserver", optarg); + break; + case 's': + if ((idx == CREATE_TGT) || + (idx == MODIFY_TGT)) { + propname = "targetchapsecret"; + } else { + propname = "chapsecret"; + } + ret = itadm_get_password(proplist, + propname, NULL, + gettext("Enter CHAP secret: ")); + break; + case 'S': + if ((idx == CREATE_TGT) || + (idx == MODIFY_TGT)) { + propname = "targetchapsecret"; + } else { + propname = "chapsecret"; + } + ret = itadm_get_password(proplist, + propname, optarg, NULL); + break; + case 't': + /* possibly multi-valued */ + ret = itadm_opt_to_arr(proplist, + "tpg-tag", optarg, NULL); + break; + case 'u': + if ((idx == CREATE_TGT) || + (idx == MODIFY_TGT)) { + propname = "targetchapuser"; + } else { + propname = "chapuser"; + } + ret = nvlist_add_string(proplist, + propname, optarg); + break; + case 'v': + verbose = B_TRUE; + break; + case ':': + (void) fprintf(stderr, + gettext("Option %s requires an operand"), + newargv[optind-1]); + (void) fprintf(stderr, "\n"); + + /* fall through to default */ + default: + ret = 1; + break; + } + } + + if (ret != 0) { + goto usage_error; + } + + /* after getopt() to allow handling of -h option */ + if ((itadm_sub_t)idx == NULL_SUBCMD) { + (void) fprintf(stderr, "%s\n", + gettext("Error, no subcommand specified")); + ret = 1; + goto usage_error; + } + + /* + * some subcommands take multiple operands, so adjust now that + * getopt is complete + */ + newargc -= optind; + if (newargc == 0) { + newargv = NULL; + objp = NULL; + } else { + newargv = &(newargv[optind]); + objp = newargv[0]; + } + + if (objp == NULL) { + switch ((itadm_sub_t)idx) { + case MODIFY_TGT: + case DELETE_TGT: + case CREATE_TPG: + case DELETE_TPG: + case CREATE_INI: + case MODIFY_INI: + case DELETE_INI: + /* These subcommands need at least one operand */ + (void) fprintf(stderr, + gettext("Error, %s requires an operand"), + subcmds[idx].name); + (void) fprintf(stderr, "\n"); + + ret = 1; + goto usage_error; + default: + break; + } + } + + if (newargc > 1) { + switch ((itadm_sub_t)idx) { + case MODIFY_TGT: + case DELETE_TGT: + case LIST_TGT: + case DELETE_TPG: + case LIST_TPG: + case CREATE_INI: + case MODIFY_INI: + case LIST_INI: + case DELETE_INI: + /* These subcommands should have at most one operand */ + (void) fprintf(stderr, + gettext("Error, %s accepts only a single operand"), + subcmds[idx].name); + (void) fprintf(stderr, "\n"); + + ret = 1; + goto usage_error; + + default: + break; + } + } + + if (newargc > 0) { + switch ((itadm_sub_t)idx) { + case CREATE_TGT: + case MODIFY_DEF: + case LIST_DEF: + /* These subcommands do not support an operand */ + (void) fprintf(stderr, + gettext("Error, %s does not support any operands"), + subcmds[idx].name); + (void) fprintf(stderr, "\n"); + + ret = 1; + goto usage_error; + + default: + break; + } + } + + /* + * XXX - this should probably get pushed down to the library + * depending on the decision to allow/disallow configuratoin + * without the service running. + */ + /* + * Make sure iSCSI target service is enabled before + * proceeding. + */ + smfstate = smf_get_state(ISCSIT_SVC); + if (!smfstate || + (strcmp(smfstate, SCF_STATE_STRING_ONLINE) != 0)) { + (void) fprintf(stderr, "%s\n", + gettext("The iSCSI target service must be online " + "before running this command.")); + (void) fprintf(stderr, + gettext("Use 'svcadm enable -r %s'"), ISCSIT_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."), + ISCSIT_SVC); + (void) fprintf(stderr, "\n"); + + return (1); + } + + switch ((itadm_sub_t)idx) { + case CREATE_TGT: + /* + * OK for targetname to be NULL here. If the + * user did not specify a target name, + * one will be generated. + */ + ret = create_target(targetname, proplist); + break; + case MODIFY_TGT: + ret = modify_target(objp, targetname, proplist); + break; + case DELETE_TGT: + ret = delete_target(objp, force); + break; + case LIST_TGT: + ret = list_target(objp, verbose, scripting); + break; + case CREATE_TPG: + ret = create_tpg(objp, newargc - 1, &(newargv[1])); + break; + case DELETE_TPG: + ret = delete_tpg(objp, force); + break; + case LIST_TPG: + ret = list_tpg(objp, verbose, scripting); + break; + case CREATE_INI: + ret = modify_initiator(objp, proplist, B_TRUE); + break; + case MODIFY_INI: + ret = modify_initiator(objp, proplist, B_FALSE); + break; + case LIST_INI: + ret = list_initiator(objp, verbose, scripting); + break; + case DELETE_INI: + ret = delete_initiator(objp); + break; + case MODIFY_DEF: + ret = modify_defaults(proplist); + break; + case LIST_DEF: + ret = list_defaults(scripting); + break; + default: + ret = 1; + goto usage_error; + } + + if (ret != 0) { + (void) fprintf(stderr, + gettext("itadm %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("itadm 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 +create_target(char *tgt, nvlist_t *proplist) +{ + int ret; + it_config_t *cfg = NULL; + it_tgt_t *tgtp; + char **tags = NULL; + uint32_t count = 0; + nvlist_t *errlist = NULL; + int i; + it_tpg_t *tpg = NULL; + uint16_t tagid = 0; + it_tpgt_t *tpgt; + char *sec = "solaris.smf.modify.stmf"; + boolean_t did_it_config_load = B_FALSE; + + ITADM_CHKAUTH(sec); + + if (tgt) { + /* + * Validate target name. + */ + if (!IS_IQN_NAME(tgt) && !IS_EUI_NAME(tgt)) { + (void) fprintf(stderr, gettext("Invalid name %s"), + tgt); + (void) fprintf(stderr, "\n"); + return (EINVAL); + } + } + + ret = it_config_load(&cfg); + if (ret != 0) { + output_config_error(ret, + gettext("Error retrieving iSCSI target configuration")); + goto done; + } + + did_it_config_load = B_TRUE; + + ret = it_tgt_create(cfg, &tgtp, tgt); + if (ret != 0) { + if (ret == EFAULT) { + (void) fprintf(stderr, + gettext("Invalid iSCSI name %s"), tgt); + (void) fprintf(stderr, "\n"); + } else if (ret == EEXIST) { + (void) fprintf(stderr, + gettext("iSCSI target %s already configured"), + tgt); + (void) fprintf(stderr, "\n"); + } else if (ret == E2BIG) { + (void) fprintf(stderr, + gettext("Maximum of %d iSCSI targets"), + MAX_TARGETS); + (void) fprintf(stderr, "\n"); + } else { + output_config_error(ret, + gettext("Error creating target")); + } + + goto done; + } + + /* set the target portal group tags */ + ret = nvlist_lookup_string_array(proplist, "tpg-tag", &tags, + &count); + + if (ret == ENOENT) { + /* none specified. is this ok? */ + ret = 0; + } else if (ret != 0) { + output_config_error(ret, gettext("Internal error")); + goto done; + } + + /* special case, don't set any TPGs */ + if (tags && (count == 1) && (strcmp("default", tags[0]) == 0)) { + count = 0; + } + + for (i = 0; i < count; i++) { + if (!tags[i]) { + continue; + } + + /* see that all referenced groups are already defined */ + tpg = cfg->config_tpg_list; + while (tpg != NULL) { + if (strcmp(tags[i], tpg->tpg_name) == 0) { + break; + } + + tpg = tpg->tpg_next; + } + if (tpg == NULL) { + (void) fprintf(stderr, + gettext("Invalid tpg-tag %s, tag not defined"), + tags[i]); + (void) fprintf(stderr, "\n"); + ret = 1; + goto done; + } + + /* generate the tag number to use */ + tag_name_to_num(tags[i], &tagid); + + ret = it_tpgt_create(cfg, tgtp, &tpgt, tags[i], tagid); + if (ret != 0) { + (void) fprintf(stderr, gettext( + "Could not add target portal group tag %s: "), + tags[i]); + output_config_error(ret, NULL); + goto done; + } + tagid++; + } + + /* remove the tags from the proplist before continuing */ + if (tags) { + (void) nvlist_remove_all(proplist, "tpg-tag"); + } + + ret = it_tgt_setprop(cfg, tgtp, proplist, &errlist); + if (ret != 0) { + (void) fprintf(stderr, + gettext("Error setting target properties: %d"), ret); + (void) fprintf(stderr, "\n"); + if (errlist) { + nvpair_t *nvp = NULL; + char *nn; + char *nv; + + while ((nvp = nvlist_next_nvpair(errlist, nvp)) + != NULL) { + nv = NULL; + + nn = nvpair_name(nvp); + (void) nvpair_value_string(nvp, &nv); + + if (nv != NULL) { + (void) fprintf(stderr, "\t%s: %s\n", + nn, nv); + } + } + + nvlist_free(errlist); + } + goto done; + } + + if (ret == 0) { + ret = it_config_commit(cfg); + STMF_STALE(ret); + } + +done: + if (ret == 0) { + (void) printf(gettext("Target %s successfully created"), + tgtp->tgt_name); + (void) printf("\n"); + } + + if (did_it_config_load) + it_config_free(cfg); + + return (ret); +} + +int +list_target(char *tgt, boolean_t verbose, boolean_t script) +{ + int ret; + it_config_t *cfg; + it_tgt_t *ptr; + boolean_t found = B_FALSE; + boolean_t first = B_TRUE; + boolean_t first_tag = B_TRUE; + char *gauth = "none"; + char *galias = "-"; + char *auth; + char *alias; + char *chapu; + char *chaps; + it_tpgt_t *tagp; + char *sec = "solaris.smf.read.stmf"; + stmfDevid devid; + stmfSessionList *sess = NULL; + stmfTargetProperties props; + char *state; + int num_sessions; + + ITADM_CHKAUTH(sec); + + ret = it_config_load(&cfg); + if (ret != 0) { + output_config_error(ret, + gettext("Error retrieving iSCSI target configuration")); + return (ret); + } + + ptr = cfg->config_tgt_list; + + /* grab global defaults for auth, alias */ + if (cfg->config_global_properties) { + (void) nvlist_lookup_string(cfg->config_global_properties, + "alias", &galias); + (void) nvlist_lookup_string(cfg->config_global_properties, + "auth", &gauth); + } + + for (; ptr != NULL; ptr = ptr->tgt_next) { + if (found) { + break; + } + + if (tgt) { + /* + * We do a case-insensitive match in case + * a non-lower case value got stored. + */ + if (strcasecmp(tgt, ptr->tgt_name) != 0) { + continue; + } else { + found = B_TRUE; + } + } + + state = "-"; + num_sessions = 0; + sess = NULL; + + /* + * make a best effort to retrieve target status and + * number of active sessions from STMF. + */ + ret = stmfDevidFromIscsiName(ptr->tgt_name, &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"; + } + } + } + if (ret == STMF_STATUS_SUCCESS) { + ret = stmfGetSessionList(&devid, &sess); + if (ret == STMF_STATUS_SUCCESS) { + num_sessions = sess->cnt; + free(sess); + } + } + + /* reset ret so we don't return an error */ + ret = 0; + + if (!script && first) { + (void) printf("%-61s%-9s%-9s\n", "TARGET NAME", + "STATE", "SESSIONS"); + first = B_FALSE; + } + + if (!script) { + /* + * try not to let columns run into each other. + * Stick a tab after too-long fields. + * Lengths chosen are for the 'common' cases. + */ + (void) printf("%-61s", ptr->tgt_name); + if (strlen(ptr->tgt_name) > 60) { + (void) printf("\t"); + } + (void) printf("%-9s%-9d", state, num_sessions); + } else { + (void) printf("%s\t%s\t%d", ptr->tgt_name, + state, num_sessions); + } + + if (!verbose) { + (void) printf("\n"); + continue; + } + + auth = gauth; + alias = galias; + chapu = "-"; + chaps = "unset"; + + if (ptr->tgt_properties) { + (void) nvlist_lookup_string(ptr->tgt_properties, + "auth", &auth); + (void) nvlist_lookup_string(ptr->tgt_properties, + "alias", &alias); + if (nvlist_exists(ptr->tgt_properties, + "targetchapsecret")) { + chaps = "set"; + } + (void) nvlist_lookup_string(ptr->tgt_properties, + "targetchapuser", &chapu); + } + + if (!script) { + (void) printf("\n\t%-20s\t%s\n\t%-20s\t%s %s\n" + "\t%-20s\t%s\n\t%-20s\t%s\n\t%-20s\t", + "alias:", alias, "auth:", auth, + ((auth == gauth) ? "(defaults)" : ""), + "targetchapuser:", + chapu, "targetchapsecret:", chaps, "tpg-tags:"); + } else { + (void) printf("\t%s\t%s %s\t%s\t%s\t", + alias, auth, + ((auth == gauth) ? "(defaults)" : ""), + chapu, chaps); + } + + first_tag = B_TRUE; + tagp = ptr->tgt_tpgt_list; + for (; tagp != NULL; tagp = tagp->tpgt_next) { + if (!first_tag) { + (void) printf(","); + } else { + first_tag = B_FALSE; + } + (void) printf("%s = %d", + tagp->tpgt_tpg_name, tagp->tpgt_tag); + } + + if (first_tag) { + /* didn't find any */ + (void) printf("default"); + } + + (void) printf("\n"); + } + + if (tgt && (!found)) { + (void) fprintf(stderr, + gettext("Target %s not found!"), tgt); + (void) fprintf(stderr, "\n"); + ret = 1; + } + + it_config_free(cfg); + + return (ret); +} + +int +delete_target(char *tgt, boolean_t force) +{ + int ret; + it_config_t *cfg; + it_tgt_t *ptr; + char *sec = "solaris.smf.modify.stmf"; + + ITADM_CHKAUTH(sec); + + if (!tgt) { + (void) fprintf(stderr, "%s\n", + gettext("Error, no target specified")); + return (EINVAL); + } + + ret = it_config_load(&cfg); + if (ret != 0) { + output_config_error(ret, + gettext("Error retrieving iSCSI target configuration")); + return (ret); + } + + ptr = cfg->config_tgt_list; + while (ptr) { + /* + * We do a case-insensitive match in case + * a non-lower case value got stored. + */ + if (strcasecmp(ptr->tgt_name, tgt) == 0) { + break; + } + + ptr = ptr->tgt_next; + } + + if (ptr) { + ret = it_tgt_delete(cfg, ptr, force); + + if (ret != 0) { + if (ret == EBUSY) { + (void) fprintf(stderr, + gettext("The target is online or busy. " + "Use the -f (force) option, or " + "'stmfadm offline-target %s'"), tgt); + (void) fprintf(stderr, "\n"); + } else { + output_config_error(ret, gettext( + "Error deleting target")); + } + } + + if (ret == 0) { + ret = it_config_commit(cfg); + STMF_STALE(ret); + } + } else { + (void) fprintf(stderr, + gettext("Target %s not found"), tgt); + (void) fprintf(stderr, "\n"); + ret = 1; + } + + it_config_free(cfg); + + return (ret); +} + +static int +modify_target(char *tgt, char *newname, nvlist_t *proplist) +{ + int ret; + it_config_t *cfg = NULL; + it_tgt_t *ptr = NULL; + it_tgt_t *tgtp = NULL; + char **tags = NULL; + uint32_t count = 0; + nvlist_t *errlist = NULL; + int i; + it_tpg_t *tpg = NULL; + uint16_t tagid; + it_tpgt_t *tpgt = NULL; + char *sec = "solaris.smf.modify.stmf"; + boolean_t did_it_config_load = B_FALSE; + + ITADM_CHKAUTH(sec); + + /* XXX: Do we need to offline anything here too? */ + + if (!tgt) { + (void) fprintf(stderr, "%s\n", + gettext("Error, no target specified")); + ret = EINVAL; + goto done; + } + + ret = it_config_load(&cfg); + if (ret != 0) { + output_config_error(ret, + gettext("Error retrieving iSCSI target configuration")); + goto done; + } + + did_it_config_load = B_TRUE; + + /* + * If newname is specified, ensure it is a valid name. + */ + if (newname) { + if (!validate_iscsi_name(newname)) { + (void) fprintf(stderr, + gettext("Invalid iSCSI name %s"), newname); + (void) fprintf(stderr, "\n"); + ret = 1; + goto done; + } + } + + /* + * Loop through to verify that the target to be modified truly + * exists. If this target is to be renamed, ensure the new + * name is not already in use. + */ + ptr = cfg->config_tgt_list; + while (ptr) { + /* + * Does a target with the new name already exist? + */ + if (newname && + (strcasecmp(newname, ptr->tgt_name) == 0)) { + (void) fprintf(stderr, + gettext("A target with name %s already exists"), + newname); + (void) fprintf(stderr, "\n"); + ret = 1; + goto done; + } + + if (strcasecmp(ptr->tgt_name, tgt) == 0) { + tgtp = ptr; + } + + ptr = ptr ->tgt_next; + } + + if (!tgtp) { + (void) fprintf(stderr, + gettext("Target %s not found"), tgt); + (void) fprintf(stderr, "\n"); + ret = EINVAL; + goto done; + } + + /* set the target portal group tags */ + ret = nvlist_lookup_string_array(proplist, "tpg-tag", &tags, + &count); + + if (ret == ENOENT) { + /* none specified. is this ok? */ + ret = 0; + } else if (ret != 0) { + output_config_error(ret, gettext("Internal error")); + goto done; + } + + /* special case, remove all explicit TPGs, and don't add any */ + if (tags && (count == 1) && (strcmp("default", tags[0]) == 0)) { + count = 0; + } + + for (i = 0; i < count; i++) { + if (!tags || !tags[i]) { + continue; + } + + /* see that all referenced groups are already defined */ + tpg = cfg->config_tpg_list; + while (tpg != NULL) { + if (strcmp(tags[i], tpg->tpg_name) == 0) { + break; + } + tpg = tpg->tpg_next; + } + if (tpg == NULL) { + (void) fprintf(stderr, + gettext("Invalid tpg-name %s: not defined"), + tags[i]); + (void) fprintf(stderr, "\n"); + ret = 1; + goto done; + } + } + + /* + * don't recreate tags that are already associated, + * remove tags not requested. + */ + if (tags) { + tpgt = tgtp->tgt_tpgt_list; + while (tpgt) { + for (i = 0; i < count; i++) { + if (!tags[i]) { + continue; + } + + if (strcmp(tpgt->tpgt_tpg_name, tags[i]) + == 0) { + /* non-null tags will be created */ + tags[i] = NULL; + break; + } + } + if (i == count) { + /* one to remove */ + it_tpgt_t *ptr = tpgt; + + tpgt = ptr->tpgt_next; + it_tpgt_delete(cfg, tgtp, ptr); + } else { + tpgt = tpgt->tpgt_next; + } + } + } + + /* see if there are any left to add */ + for (i = 0; i < count; i++) { + if (!tags || !tags[i]) { + continue; + } + + /* generate the tag number to use */ + tag_name_to_num(tags[i], &tagid); + + ret = it_tpgt_create(cfg, tgtp, &tpgt, tags[i], tagid); + if (ret != 0) { + if (ret == E2BIG) { + (void) fprintf(stderr, "%s\n", + gettext("Error, no portal tag available")); + } else { + (void) fprintf(stderr, gettext( + "Could not add target portal group" + " tag %s: "), tags[i]); + output_config_error(ret, NULL); + } + goto done; + } + } + + /* remove the tags from the proplist before continuing */ + (void) nvlist_remove_all(proplist, "tpg-tag"); + + /* + * Rename this target, if requested. Save the old name in + * the property list, so the kernel knows this is a renamed + * target, and not a new one. + */ + if (newname && (strlen(newname) > 0)) { + ret = nvlist_add_string(proplist, "oldtargetname", + tgtp->tgt_name); + if (ret != 0) { + output_config_error(ret, + gettext("Error renaming target")); + goto done; + } + (void) strlcpy(tgtp->tgt_name, newname, + sizeof (tgtp->tgt_name)); + } + + ret = it_tgt_setprop(cfg, tgtp, proplist, &errlist); + if (ret != 0) { + (void) fprintf(stderr, + gettext("Error setting target properties: %d"), ret); + (void) fprintf(stderr, "\n"); + if (errlist) { + nvpair_t *nvp = NULL; + char *nn; + char *nv; + + while ((nvp = nvlist_next_nvpair(errlist, nvp)) + != NULL) { + nv = NULL; + + nn = nvpair_name(nvp); + (void) nvpair_value_string(nvp, &nv); + + if (nv != NULL) { + (void) fprintf(stderr, "\t%s: %s\n", + nn, nv); + } + } + + nvlist_free(errlist); + } + goto done; + } + + if (ret == 0) { + ret = it_config_commit(cfg); + STMF_STALE(ret); + } + +done: + if (ret == 0) { + (void) printf(gettext("Target %s successfully modified"), + tgtp->tgt_name); + (void) printf("\n"); + } + + if (did_it_config_load) + it_config_free(cfg); + + return (ret); +} + +int +create_tpg(char *tpg, int addrc, char **addrs) +{ + int ret; + it_config_t *cfg; + it_tpg_t *tpgp; + int count = 0; + it_portal_t *ptl; + char *sec = "solaris.smf.modify.stmf"; + int i = 0; + + ITADM_CHKAUTH(sec); + + if (!tpg) { + (void) fprintf(stderr, "%s\n", + gettext("Error, no target portal group specified")); + return (EINVAL); + } + + if (strlen(tpg) > (MAX_TPG_NAMELEN - 1)) { + (void) fprintf(stderr, + gettext("Target Portal Group name must be no longer " + "than %d characters"), (MAX_TPG_NAMELEN - 1)); + (void) fprintf(stderr, "\n"); + return (EINVAL); + } + + if (!addrs || (addrc <= 0)) { + (void) fprintf(stderr, "%s\n", + gettext("Error, no portal addresses specified")); + return (EINVAL); + } + + ret = it_config_load(&cfg); + if (ret != 0) { + output_config_error(ret, + gettext("Error retrieving iSCSI target configuration")); + return (ret); + } + + tpgp = cfg->config_tpg_list; + while (tpgp != NULL) { + if (strcmp(tpgp->tpg_name, tpg) == 0) { + (void) fprintf(stderr, + gettext("Target Portal Group %s already exists"), + tpg); + (void) fprintf(stderr, "\n"); + it_config_free(cfg); + return (1); + } + tpgp = tpgp->tpg_next; + } + + /* + * Ensure that the addrs don't contain commas. + */ + for (i = 0; i < addrc; i++) { + if (strchr(addrs[i], ',')) { + (void) fprintf(stderr, + gettext("Bad portal name %s"), + addrs[i]); + (void) fprintf(stderr, "\n"); + + it_config_free(cfg); + return (EINVAL); + } + } + + /* + * Create the portal group and first portal + */ + ret = it_tpg_create(cfg, &tpgp, tpg, addrs[count]); + if (ret != 0) { + if (ret == EEXIST) { + (void) fprintf(stderr, + gettext("Portal %s already in use"), + addrs[count]); + (void) fprintf(stderr, "\n"); + } else { + output_config_error(ret, gettext("Could not create the " + "target portal group")); + } + it_config_free(cfg); + return (ret); + } + + /* + * Add the remaining portals + */ + for (count = 1; count < addrc; count++) { + if (!addrs[count]) { + continue; + } + + ret = it_portal_create(cfg, tpgp, &ptl, addrs[count]); + if (ret != 0) { + if (ret == EEXIST) { + (void) fprintf(stderr, + gettext("Portal %s already in use"), + addrs[count]); + (void) fprintf(stderr, "\n"); + } else { + (void) fprintf(stderr, + gettext("Error adding portal %s: "), + addrs[count]); + output_config_error(ret, NULL); + break; + } + } + } + + if (ret == 0) { + ret = it_config_commit(cfg); + STMF_STALE(ret); + } + + it_config_free(cfg); + + return (ret); +} + +static int +list_tpg(char *tpg, boolean_t verbose, boolean_t script) +{ + int ret; + it_config_t *cfg; + it_tpg_t *ptr; + boolean_t found = B_FALSE; + it_portal_t *portal; + boolean_t first = B_TRUE; + boolean_t first_portal; + char *pstr; + char *sec = "solaris.smf.read.stmf"; + + ITADM_CHKAUTH(sec); + + ret = it_config_load(&cfg); + if (ret != 0) { + output_config_error(ret, + gettext("Error retrieving iSCSI target configuration")); + return (ret); + } + + ptr = cfg->config_tpg_list; + + for (; ptr != NULL; ptr = ptr->tpg_next) { + if (found) { + break; + } + + if (tpg) { + if (strcmp(tpg, ptr->tpg_name) != 0) { + continue; + } else { + found = B_TRUE; + } + } + + if (!script && first) { + (void) printf("%-30s%-9s\n", "TARGET PORTAL GROUP", + "PORTAL COUNT"); + first = B_FALSE; + } + + if (!script) { + (void) printf("%-30s", ptr->tpg_name); + if (strlen(ptr->tpg_name) > 30) { + (void) printf("\t"); + } + (void) printf("%-9d", ptr->tpg_portal_count); + } else { + (void) printf("%s\t%d", ptr->tpg_name, + ptr->tpg_portal_count); + } + + if (!verbose) { + (void) printf("\n"); + continue; + } + + if (!script) { + (void) printf("\n portals:"); + } + + first_portal = B_TRUE; + + portal = ptr->tpg_portal_list; + for (; portal != NULL; portal = portal->portal_next) { + ret = sockaddr_to_str(&(portal->portal_addr), &pstr); + if (ret != 0) { + /* invalid addr? */ + continue; + } + if (!first_portal) { + (void) printf(","); + } else { + (void) printf("\t"); + first_portal = B_FALSE; + } + + (void) printf("%s", pstr); + free(pstr); + } + + if (first_portal) { + /* none found */ + (void) printf("\t<none>"); + } + + (void) printf("\n"); + } + + if (tpg && (!found)) { + (void) fprintf(stderr, + gettext("Target Portal Group %s not found!\n"), tpg); + (void) fprintf(stderr, "\n"); + ret = 1; + } + + it_config_free(cfg); + + return (ret); +} + +static int +delete_tpg(char *tpg, boolean_t force) +{ + int ret; + it_config_t *cfg; + it_tpg_t *ptpg = NULL; + char *sec = "solaris.smf.modify.stmf"; + + ITADM_CHKAUTH(sec); + + if (!tpg) { + (void) fprintf(stderr, "%s\n", + gettext("Error, no target portal group specified")); + return (EINVAL); + } + + ret = it_config_load(&cfg); + if (ret != 0) { + output_config_error(ret, + gettext("Error retrieving iSCSI target configuration")); + return (ret); + } + + ptpg = cfg->config_tpg_list; + for (; ptpg != NULL; ptpg = ptpg->tpg_next) { + if (strcmp(tpg, ptpg->tpg_name) == 0) { + break; + } + } + + if (!ptpg) { + (void) fprintf(stderr, + gettext("Target portal group %s does not exist"), + tpg); + (void) fprintf(stderr, "\n"); + ret = 1; + } else { + ret = it_tpg_delete(cfg, ptpg, force); + if (ret == EBUSY) { + (void) fprintf(stderr, "%s\n", + gettext( + "Target portal group associated with one or more " + "targets. Cannot delete.")); + } else if (ret != 0) { + output_config_error(ret, gettext("Could not delete " + "target portal group")); + } + + if (ret == 0) { + ret = it_config_commit(cfg); + STMF_STALE(ret); + } + } + + it_config_free(cfg); + + return (ret); +} + +static int +modify_initiator(char *ini, nvlist_t *proplist, boolean_t create) +{ + int ret; + it_config_t *cfg; + it_ini_t *inip; + nvlist_t *errlist = NULL; + nvpair_t *nvp = NULL; + char *sec = "solaris.smf.modify.stmf"; + boolean_t changed = B_TRUE; + + ITADM_CHKAUTH(sec); + + if (!ini) { + (void) fprintf(stderr, "%s\n", + gettext("Error, no initiator specified")); + return (EINVAL); + } else if (create) { + /* + * validate input name - what are the rules for EUI + * and IQN values? + */ + if (!IS_IQN_NAME(ini) && !IS_EUI_NAME(ini)) { + (void) fprintf(stderr, gettext("Invalid name %s"), + ini); + (void) fprintf(stderr, "\n"); + return (EINVAL); + } + } + + /* + * See if any properties were actually specified. + */ + if (proplist) { + nvp = nvlist_next_nvpair(proplist, nvp); + } + + if ((nvp == NULL) && !create) { + changed = B_FALSE; + } + + /* + * If no properties, and this is really a modify op, verify + * that the requested initiator exists, but then don't do anything. + * Modifying non-existent is an error; doing nothing to a defined + * initiator is not. + */ + + ret = it_config_load(&cfg); + if (ret != 0) { + output_config_error(ret, + gettext("Error retrieving iSCSI target configuration")); + return (ret); + } + + inip = cfg->config_ini_list; + while (inip) { + if (strcasecmp(inip->ini_name, ini) == 0) { + break; + } + + inip = inip->ini_next; + } + + if (create) { + if (inip) { + (void) fprintf(stderr, + gettext("Initiator %s already exists"), + inip->ini_name); + (void) fprintf(stderr, "\n"); + ret = EINVAL; + } else { + ret = it_ini_create(cfg, &inip, ini); + if (ret != 0) { + if (ret == EFAULT) { + (void) fprintf(stderr, + gettext("Invalid iSCSI name %s"), + ini); + (void) fprintf(stderr, "\n"); + } else { + output_config_error(ret, gettext( + "Error creating initiator")); + } + } + } + } else if (!inip) { + ret = ENOENT; + (void) fprintf(stderr, + gettext("Error, initiator %s not found"), + ini); + (void) fprintf(stderr, "\n"); + } + + if ((ret == 0) && nvp) { + ret = it_ini_setprop(inip, proplist, &errlist); + + if (ret != 0) { + (void) fprintf(stderr, + gettext("Error setting initiator properties: %d"), + ret); + (void) fprintf(stderr, "\n"); + if (errlist) { + nvpair_t *nvp = NULL; + char *nn; + char *nv; + + while ((nvp = nvlist_next_nvpair(errlist, nvp)) + != NULL) { + nv = NULL; + + nn = nvpair_name(nvp); + (void) nvpair_value_string(nvp, &nv); + + if (nv != NULL) { + (void) fprintf(stderr, + "\t%s: %s\n", nn, nv); + } + } + + nvlist_free(errlist); + } + } + } + + if ((ret == 0) && changed) { + ret = it_config_commit(cfg); + STMF_STALE(ret); + } + + it_config_free(cfg); + + return (ret); +} + +static int +list_initiator(char *ini, boolean_t verbose, boolean_t script) /* ARGSUSED */ +{ + int ret; + it_config_t *cfg; + it_ini_t *ptr; + boolean_t found = B_FALSE; + boolean_t first = B_TRUE; + char *isecret; + char *iuser; + char *sec = "solaris.smf.read.stmf"; + + ITADM_CHKAUTH(sec); + + ret = it_config_load(&cfg); + if (ret != 0) { + output_config_error(ret, + gettext("Error retrieving iSCSI target configuration")); + return (ret); + } + + ptr = cfg->config_ini_list; + + for (; ptr != NULL; ptr = ptr->ini_next) { + isecret = "unset"; + iuser = "<none>"; + + if (found) { + break; + } + + if (ini) { + if (strcasecmp(ini, ptr->ini_name) != 0) { + continue; + } else { + found = B_TRUE; + } + } + + if (ptr->ini_properties) { + if (nvlist_exists(ptr->ini_properties, "chapsecret")) { + isecret = "set"; + } + (void) nvlist_lookup_string(ptr->ini_properties, + "chapuser", &iuser); + + } + + /* there's nothing to print for verbose yet */ + if (!script && first) { + (void) printf("%-61s%-10s%-7s\n", "INITIATOR NAME", + "CHAPUSER", "SECRET"); + first = B_FALSE; + } + + if (!script) { + /* + * try not to let columns run into each other. + * Stick a tab after too-long fields. + * Lengths chosen are for the 'common' cases. + */ + (void) printf("%-61s", ptr->ini_name); + + if (strlen(ptr->ini_name) > 60) { + (void) printf("\t"); + } + + (void) printf("%-15s", iuser); + if (strlen(iuser) >= 15) { + (void) printf("\t"); + } + + (void) printf("%-4s", isecret); + } else { + (void) printf("%s\t%s\t%s", ptr->ini_name, + iuser, isecret); + } + + (void) printf("\n"); + } + + if (ini && (!found)) { + (void) fprintf(stderr, + gettext("Initiator %s not found!"), ini); + (void) fprintf(stderr, "\n"); + ret = 1; + } + + it_config_free(cfg); + + return (ret); +} + +int +delete_initiator(char *ini) +{ + int ret; + it_config_t *cfg; + it_ini_t *ptr; + char *sec = "solaris.smf.modify.stmf"; + + ITADM_CHKAUTH(sec); + + if (!ini) { + (void) fprintf(stderr, "%s\n", + gettext("Error, no initiator specified")); + return (EINVAL); + } + + ret = it_config_load(&cfg); + if (ret != 0) { + output_config_error(ret, + gettext("Error retrieving iSCSI target configuration")); + return (ret); + } + + ptr = cfg->config_ini_list; + while (ptr) { + if (strcasecmp(ptr->ini_name, ini) == 0) { + break; + } + + ptr = ptr->ini_next; + } + + if (ptr) { + it_ini_delete(cfg, ptr); + + ret = it_config_commit(cfg); + STMF_STALE(ret); + } else { + (void) fprintf(stderr, + gettext("Initiator %s not found"), ini); + (void) fprintf(stderr, "\n"); + ret = 1; + } + + return (ret); +} + +static int +modify_defaults(nvlist_t *proplist) +{ + int ret; + it_config_t *cfg; + nvlist_t *errlist = NULL; + nvpair_t *nvp = NULL; + char *sec = "solaris.smf.modify.stmf"; + + ITADM_CHKAUTH(sec); + + if (proplist) { + /* make sure at least one property is specified */ + nvp = nvlist_next_nvpair(proplist, nvp); + } + + if (nvp == NULL) { + /* empty list */ + (void) fprintf(stderr, "%s\n", + gettext("Error, no properties specified")); + return (EINVAL); + } + + ret = it_config_load(&cfg); + if (ret != 0) { + output_config_error(ret, + gettext("Error retrieving iSCSI target configuration")); + return (ret); + } + + ret = it_config_setprop(cfg, proplist, &errlist); + if (ret != 0) { + (void) fprintf(stderr, + gettext("Error setting global properties: %d"), + ret); + (void) fprintf(stderr, "\n"); + if (errlist) { + nvpair_t *nvp = NULL; + char *nn; + char *nv; + + while ((nvp = nvlist_next_nvpair(errlist, nvp)) + != NULL) { + nv = NULL; + + nn = nvpair_name(nvp); + (void) nvpair_value_string(nvp, &nv); + + if (nv != NULL) { + (void) fprintf(stderr, "\t%s: %s\n", + nn, nv); + } + } + + nvlist_free(errlist); + } + } + + if (ret == 0) { + ret = it_config_commit(cfg); + STMF_STALE(ret); + } + + it_config_free(cfg); + + return (ret); +} + +static int +list_defaults(boolean_t script) +{ + int ret; + it_config_t *cfg; + nvlist_t *nvl; + char *alias = "<none>"; + char *auth = "<none>"; + char *isns = "disabled"; + char **isvrs = NULL; + uint32_t scount = 0; + char *rsvr = "<none>"; + char *rsecret = "unset"; + boolean_t val = B_FALSE; + int i; + char *sec = "solaris.smf.read.stmf"; + + ITADM_CHKAUTH(sec); + + ret = it_config_load(&cfg); + if (ret != 0) { + output_config_error(ret, + gettext("Error retrieving iSCSI target configuration")); + return (ret); + } + + nvl = cfg->config_global_properties; + + /* look up all possible options */ + (void) nvlist_lookup_string(nvl, "alias", &alias); + (void) nvlist_lookup_string(nvl, "auth", &auth); + (void) nvlist_lookup_boolean_value(nvl, "isns", &val); + if (val == B_TRUE) { + isns = "enabled"; + } + (void) nvlist_lookup_string_array(nvl, "isnsserver", &isvrs, + &scount); + (void) nvlist_lookup_string(nvl, "radiusserver", &rsvr); + if (nvlist_exists(nvl, "radiussecret")) { + rsecret = "set"; + } + + if (!script) { + (void) printf("%s:\n\n", + gettext("iSCSI Target Default Properties")); + } + + if (script) { + (void) printf("%s\t%s\t%s\t%s\t%s\t", + alias, auth, rsvr, rsecret, isns); + } else { + (void) printf("%-15s\t%s\n%-15s\t%s\n%-15s\t%s\n%-15s\t%s\n" + "%-15s\t%s\n%-15s\t", + "alias:", alias, "auth:", auth, "radiusserver:", + rsvr, "radiussecret:", rsecret, "isns:", isns, + "isnsserver:"); + } + + for (i = 0; i < scount; i++) { + if (!isvrs || !isvrs[i]) { + break; + } + if (i > 0) { + (void) printf(","); + } + (void) printf("%s", isvrs[i]); + } + + if (i == 0) { + (void) printf("%s", "<none>"); + } + + (void) printf("\n"); + + it_config_free(cfg); + + return (0); +} + +static int +itadm_get_password(nvlist_t *nvl, char *key, char *passfile, + char *phrase) +{ + int ret = 0; + char *pass; + char buf[1024]; + int fd; + struct stat64 sbuf; + size_t rd; + + if (!nvl || !key) { + return (EINVAL); + } + + if (passfile) { + ret = stat64(passfile, &sbuf); + if ((ret != 0) || (!S_ISREG(sbuf.st_mode))) { + (void) fprintf(stderr, + gettext("Invalid secret file %s"), + passfile); + (void) fprintf(stderr, "\n"); + return (EBADF); + } + + fd = open64(passfile, O_RDONLY); + if (fd == -1) { + ret = errno; + (void) fprintf(stderr, + gettext("Could not open secret file %s: "), + passfile); + output_config_error(ret, NULL); + return (ret); + } + + rd = read(fd, buf, sbuf.st_size); + (void) close(fd); + + if (rd != sbuf.st_size) { + ret = EIO; + (void) fprintf(stderr, + gettext("Could not read secret file %s: "), + passfile); + output_config_error(ret, NULL); + return (ret); + } + + /* ensure buf is properly terminated */ + buf[rd] = '\0'; + + /* if last char is a newline, strip it off */ + if (buf[rd - 1] == '\n') { + buf[rd - 1] = '\0'; + } + + /* validate length */ + if ((strlen(buf) > 255) || (strlen(buf) < 12)) { + (void) fprintf(stderr, "%s\n", + gettext( + "Secret must be between 12 and 255 characters")); + return (EINVAL); + } + } else { + /* prompt for secret */ + if (!phrase) { + return (EINVAL); + } + + pass = getpassphrase(phrase); + if (!pass) { + ret = errno; + output_config_error(ret, + gettext("Could not read secret")); + return (ret); + } + + /* validate length */ + if ((strlen(pass) > 255) || (strlen(pass) < 12)) { + (void) fprintf(stderr, "%s\n", + gettext( + "Secret must be between 12 and 255 characters")); + return (EINVAL); + } + + (void) strlcpy(buf, pass, sizeof (buf)); + + /* confirm entered secret */ + pass = getpassphrase(gettext("Re-enter secret: ")); + if (!pass) { + ret = errno; + output_config_error(ret, + gettext("Could not read secret")); + return (ret); + } + + if (strcmp(buf, pass) != 0) { + ret = EINVAL; + (void) fprintf(stderr, "%s\n", + gettext("Secret validation failed")); + return (ret); + } + + } + + ret = nvlist_add_string(nvl, key, buf); + + return (ret); +} + +static int +itadm_opt_to_arr(nvlist_t *nvl, char *key, char *opt, uint32_t *num) +{ + int count; + char *bufp; + char **arr; + + if (!opt || !key || !nvl) { + return (EINVAL); + } + + bufp = opt; + count = 1; + + for (;;) { + bufp = strchr(bufp, ','); + if (!bufp) { + break; + } + bufp++; + count++; + } + + arr = calloc(count, sizeof (char *)); + if (!arr) { + return (ENOMEM); + } + + bufp = opt; + /* set delimiter to comma */ + (void) bufsplit(",", 0, NULL); + + /* split up that buf! */ + (void) bufsplit(bufp, count, arr); + + /* if requested, return the number of array members found */ + if (num) { + *num = count; + } + + return (nvlist_add_string_array(nvl, key, arr, count)); +} + +static void +tag_name_to_num(char *tagname, uint16_t *tagnum) +{ + ulong_t id; + char *ptr = NULL; + + if (!tagname || !tagnum) { + return; + } + + *tagnum = 0; + + id = strtoul(tagname, &ptr, 10); + + /* Must be entirely numeric and in-range */ + if (ptr && (*ptr != '\0')) { + return; + } + + if ((id <= UINT16_MAX) && (id > 1)) { + *tagnum = (uint16_t)id; + } +} + +/* + * Print error messages to stderr for errnos and expected stmf errors. + * This function should generally not be used for cases where the + * calling code can generate a more detailed error message based on + * the contextual knowledge of the meaning of specific errors. + */ +static void +output_config_error(int error, char *msg) +{ + + if (msg) { + (void) fprintf(stderr, "%s: ", msg); + } + + if (error & STMF_STATUS_ERROR) { + switch (error) { + case STMF_ERROR_PERM: + (void) fprintf(stderr, "%s", + gettext("permission denied")); + break; + case STMF_ERROR_BUSY: + (void) fprintf(stderr, "%s", + gettext("resource busy")); + break; + case STMF_ERROR_NOMEM: + (void) fprintf(stderr, "%s", + gettext("out of memory")); + break; + case STMF_ERROR_SERVICE_NOT_FOUND: + (void) fprintf(stderr, "%s", + gettext("STMF service not found")); + break; + case STMF_ERROR_SERVICE_DATA_VERSION: + (void) fprintf(stderr, "%s", + gettext("STMF service version incorrect")); + break; + case STMF_ERROR_PROV_DATA_STALE: + (void) fprintf(stderr, "%s", + gettext("Configuration changed during processing. " + "Check the configuration, then retry this " + "command if appropriate.")); + break; + default: + (void) fprintf(stderr, "%s", gettext("unknown error")); + break; + } + } else { + char buf[80] = ""; + + (void) strerror_r(error, buf, sizeof (buf)); + (void) fprintf(stderr, "%s", buf); + } + + (void) fprintf(stderr, "\n"); +} |