itadm.c revision 42bf653b44c42fc6b637ae50e590489d69399241
/*
* 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
* 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
*/
/*
*/
#include <stdlib.h>
#include <stdio.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) {\
} else if (ret != 0) {\
gettext("Configuration change failed"));\
}\
}
#define ITADM_CHKAUTH(sec) {\
gettext("Error, operation requires authorization %s"),\
sec);\
return (1);\
}\
}
static struct option itadm_long[] = {
};
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 {
NULL_SUBCMD /* must always be last! */
} itadm_sub_t;
typedef struct {
char *name;
char *shortopts;
char *usemsg;
static itadm_subcmds_t subcmds[] = {
};
/* used for checking if user is authorized */
static char *itadm_uname = NULL;
/* prototypes */
static int
char *phrase);
static int
static int
static int
static int
static int
static int
static int
static int
static int
static int
static int
delete_initiator(char *ini);
static int
static int
static void
/* prototype from iscsit_common.h */
extern int
int
{
int ret = 0;
int idx = NULL_SUBCMD;
char c;
char *objp;
int itind = 0;
char *targetname = NULL;
char *propname;
(void) textdomain(TEXT_DOMAIN);
if (argc < 2) {
ret = 1;
goto usage_error;
}
break;
}
}
/* get the caller's user name for subsequent chkauthattr() calls */
gettext("Could not determine callers user name"));
return (1);
}
/* increment past command & subcommand */
newargc--;
if (ret != 0) {
ret = 1;
goto usage_error;
}
itadm_long, &itind);
if (c == -1) {
break;
}
switch (c) {
case 0:
/* flag set by getopt */
break;
case 'a':
"auth", optarg);
break;
case 'd':
"radiussecret", NULL,
gettext("Enter RADIUS secret: "));
break;
case 'D':
break;
case 'f':
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.
*/
gettext("Unrecognized option %s"),
ret = 1;
}
goto usage_error;
case 'h':
goto usage_error;
case 'i':
== 0) {
} else {
gettext("invalid value for -i"));
ret = 1;
break;
}
"isns", tbool);
break;
case 'I':
/* possibly multi-valued */
"Too many iSNS servers specified, "
"maximum of 8 allowed"));
ret = 1;
}
break;
case 'l':
"alias", optarg);
break;
case 'n':
if (targetname == NULL) {
}
break;
case 'r':
"radiusserver", optarg);
break;
case 's':
if ((idx == CREATE_TGT) ||
(idx == MODIFY_TGT)) {
propname = "targetchapsecret";
} else {
propname = "chapsecret";
}
gettext("Enter CHAP secret: "));
break;
case 'S':
if ((idx == CREATE_TGT) ||
(idx == MODIFY_TGT)) {
propname = "targetchapsecret";
} else {
propname = "chapsecret";
}
break;
case 't':
/* possibly multi-valued */
break;
case 'u':
if ((idx == CREATE_TGT) ||
(idx == MODIFY_TGT)) {
propname = "targetchapuser";
} else {
propname = "chapuser";
}
break;
case 'v':
break;
case ':':
gettext("Option %s requires an operand"),
/* fall through to default */
default:
ret = 1;
break;
}
}
if (ret != 0) {
goto usage_error;
}
/* after getopt() to allow handling of -h option */
gettext("Error, no subcommand specified"));
ret = 1;
goto usage_error;
}
/*
* some subcommands take multiple operands, so adjust now that
* getopt is complete
*/
if (newargc == 0) {
} else {
}
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 */
gettext("Error, %s requires an operand"),
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 */
gettext("Error, %s accepts only a single operand"),
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 */
gettext("Error, %s does not support any operands"),
ret = 1;
goto usage_error;
default:
break;
}
}
/*
* XXX - this should probably get pushed down to the library
* without the service running.
*/
/*
* Make sure iSCSI target service is enabled before
* proceeding.
*/
if (!smfstate ||
gettext("The iSCSI target service must be online "
"before running this command."));
gettext("to enable the service and its prerequisite "
gettext("'svcs -x %s' to determine why it is not online."),
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.
*/
break;
case MODIFY_TGT:
break;
case DELETE_TGT:
break;
case LIST_TGT:
break;
case CREATE_TPG:
break;
case DELETE_TPG:
break;
case LIST_TPG:
break;
case CREATE_INI:
break;
case MODIFY_INI:
break;
case LIST_INI:
break;
case DELETE_INI:
break;
case MODIFY_DEF:
break;
case LIST_DEF:
break;
default:
ret = 1;
goto usage_error;
}
if (ret != 0) {
gettext("itadm %s failed with error %d"),
}
return (ret);
} else {
/* overall usage */
continue;
}
}
}
return (ret);
}
static int
{
int ret;
int i;
char *sec = "solaris.smf.modify.stmf";
if (tgt) {
/*
* Validate target name.
*/
tgt);
return (EINVAL);
}
}
if (ret != 0) {
gettext("Error retrieving iSCSI target configuration"));
goto done;
}
if (ret != 0) {
gettext("iSCSI target %s already configured"),
tgt);
gettext("Maximum of %d iSCSI targets"),
} else {
gettext("Error creating target"));
}
goto done;
}
/* set the target portal group tags */
&count);
/* none specified. is this ok? */
ret = 0;
} else if (ret != 0) {
goto done;
}
/* special case, don't set any TPGs */
count = 0;
}
for (i = 0; i < count; i++) {
if (!tags[i]) {
continue;
}
/* see that all referenced groups are already defined */
break;
}
}
gettext("Invalid tpg-tag %s, tag not defined"),
tags[i]);
ret = 1;
goto done;
}
/* generate the tag number to use */
if (ret != 0) {
"Could not add target portal group tag %s: "),
tags[i]);
goto done;
}
tagid++;
}
/* remove the tags from the proplist before continuing */
if (tags) {
}
if (ret != 0) {
if (errlist) {
char *nn;
char *nv;
!= NULL) {
}
}
}
goto done;
}
if (ret == 0) {
}
done:
if (ret == 0) {
(void) printf("\n");
}
if (did_it_config_load)
return (ret);
}
int
{
int ret;
char *gauth = "none";
char *galias = "-";
char *auth;
char *alias;
char *chapu;
char *chaps;
char *sec = "solaris.smf.read.stmf";
char *state;
int num_sessions;
if (ret != 0) {
gettext("Error retrieving iSCSI target configuration"));
return (ret);
}
/* grab global defaults for auth, alias */
if (cfg->config_global_properties) {
"alias", &galias);
"auth", &gauth);
}
if (found) {
break;
}
if (tgt) {
/*
* We do a case-insensitive match in case
* a non-lower case value got stored.
*/
continue;
} else {
}
}
state = "-";
num_sessions = 0;
/*
* make a best effort to retrieve target status and
* number of active sessions from STMF.
*/
if (ret == STMF_STATUS_SUCCESS) {
if (ret == STMF_STATUS_SUCCESS) {
state = "online";
} else {
state = "offline";
}
}
}
if (ret == STMF_STATUS_SUCCESS) {
if (ret == STMF_STATUS_SUCCESS) {
}
}
/* reset ret so we don't return an error */
ret = 0;
"STATE", "SESSIONS");
}
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("\t");
}
} else {
}
if (!verbose) {
(void) printf("\n");
continue;
}
chapu = "-";
chaps = "unset";
if (ptr->tgt_properties) {
"auth", &auth);
"alias", &alias);
"targetchapsecret")) {
chaps = "set";
}
"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",
"targetchapuser:",
} else {
(void) printf("\t%s\t%s %s\t%s\t%s\t",
}
if (!first_tag) {
(void) printf(",");
} else {
}
(void) printf("%s = %d",
}
if (first_tag) {
/* didn't find any */
(void) printf("default");
}
(void) printf("\n");
}
ret = 1;
}
return (ret);
}
int
{
int ret;
char *sec = "solaris.smf.modify.stmf";
if (!tgt) {
gettext("Error, no target specified"));
return (EINVAL);
}
if (ret != 0) {
gettext("Error retrieving iSCSI target configuration"));
return (ret);
}
while (ptr) {
/*
* We do a case-insensitive match in case
* a non-lower case value got stored.
*/
break;
}
}
if (ptr) {
if (ret != 0) {
gettext("The target is online or busy. "
"Use the -f (force) option, or "
"'stmfadm offline-target %s'"), tgt);
} else {
"Error deleting target"));
}
}
if (ret == 0) {
}
} else {
ret = 1;
}
return (ret);
}
static int
{
int ret;
int i;
char *sec = "solaris.smf.modify.stmf";
/* XXX: Do we need to offline anything here too? */
if (!tgt) {
gettext("Error, no target specified"));
goto done;
}
if (ret != 0) {
gettext("Error retrieving iSCSI target configuration"));
goto done;
}
/*
* If newname is specified, ensure it is a valid name.
*/
if (newname) {
if (!validate_iscsi_name(newname)) {
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.
*/
while (ptr) {
/*
* Does a target with the new name already exist?
*/
if (newname &&
gettext("A target with name %s already exists"),
newname);
ret = 1;
goto done;
}
}
}
if (!tgtp) {
goto done;
}
/* set the target portal group tags */
&count);
/* none specified. is this ok? */
ret = 0;
} else if (ret != 0) {
goto done;
}
/* special case, remove all explicit TPGs, and don't add any */
count = 0;
}
for (i = 0; i < count; i++) {
continue;
}
/* see that all referenced groups are already defined */
break;
}
}
gettext("Invalid tpg-name %s: not defined"),
tags[i]);
ret = 1;
goto done;
}
}
/*
* don't recreate tags that are already associated,
* remove tags not requested.
*/
if (tags) {
while (tpgt) {
for (i = 0; i < count; i++) {
if (!tags[i]) {
continue;
}
== 0) {
/* non-null tags will be created */
break;
}
}
if (i == count) {
/* one to remove */
} else {
}
}
}
/* see if there are any left to add */
for (i = 0; i < count; i++) {
continue;
}
/* generate the tag number to use */
if (ret != 0) {
gettext("Error, no portal tag available"));
} else {
"Could not add target portal group"
" tag %s: "), tags[i]);
}
goto done;
}
}
/* remove the tags from the proplist before continuing */
/*
* 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 (ret != 0) {
gettext("Error renaming target"));
goto done;
}
}
if (ret != 0) {
if (errlist) {
char *nn;
char *nv;
!= NULL) {
}
}
}
goto done;
}
if (ret == 0) {
}
done:
if (ret == 0) {
(void) printf("\n");
}
if (did_it_config_load)
return (ret);
}
int
{
int ret;
int count = 0;
char *sec = "solaris.smf.modify.stmf";
int i = 0;
if (!tpg) {
gettext("Error, no target portal group specified"));
return (EINVAL);
}
gettext("Target Portal Group name must be no longer "
return (EINVAL);
}
gettext("Error, no portal addresses specified"));
return (EINVAL);
}
if (ret != 0) {
gettext("Error retrieving iSCSI target configuration"));
return (ret);
}
gettext("Target Portal Group %s already exists"),
tpg);
return (1);
}
}
/*
* Ensure that the addrs don't contain commas.
*/
for (i = 0; i < addrc; i++) {
gettext("Bad portal name %s"),
addrs[i]);
return (EINVAL);
}
}
/*
* Create the portal group and first portal
*/
if (ret != 0) {
gettext("Portal %s already in use"),
} else {
"target portal group"));
}
return (ret);
}
/*
* Add the remaining portals
*/
continue;
}
if (ret != 0) {
gettext("Portal %s already in use"),
} else {
gettext("Error adding portal %s: "),
break;
}
}
}
if (ret == 0) {
}
return (ret);
}
static int
{
int ret;
char *pstr;
char *sec = "solaris.smf.read.stmf";
if (ret != 0) {
gettext("Error retrieving iSCSI target configuration"));
return (ret);
}
if (found) {
break;
}
if (tpg) {
continue;
} else {
}
}
"PORTAL COUNT");
}
if (!script) {
(void) printf("\t");
}
} else {
}
if (!verbose) {
(void) printf("\n");
continue;
}
if (!script) {
(void) printf("\n portals:");
}
if (ret != 0) {
/* invalid addr? */
continue;
}
if (!first_portal) {
(void) printf(",");
} else {
(void) printf("\t");
}
}
if (first_portal) {
/* none found */
(void) printf("\t<none>");
}
(void) printf("\n");
}
ret = 1;
}
return (ret);
}
static int
{
int ret;
char *sec = "solaris.smf.modify.stmf";
if (!tpg) {
gettext("Error, no target portal group specified"));
return (EINVAL);
}
if (ret != 0) {
gettext("Error retrieving iSCSI target configuration"));
return (ret);
}
break;
}
}
if (!ptpg) {
gettext("Target portal group %s does not exist"),
tpg);
ret = 1;
} else {
"Target portal group associated with one or more "
"targets. Cannot delete."));
} else if (ret != 0) {
"target portal group"));
}
if (ret == 0) {
}
}
return (ret);
}
static int
{
int ret;
char *sec = "solaris.smf.modify.stmf";
if (!ini) {
gettext("Error, no initiator specified"));
return (EINVAL);
} else if (create) {
/*
* validate input name - what are the rules for EUI
* and IQN values?
*/
ini);
return (EINVAL);
}
}
/*
* See if any properties were actually specified.
*/
if (proplist) {
}
}
/*
* 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.
*/
if (ret != 0) {
gettext("Error retrieving iSCSI target configuration"));
return (ret);
}
while (inip) {
break;
}
}
if (create) {
if (inip) {
gettext("Initiator %s already exists"),
} else {
if (ret != 0) {
gettext("Invalid iSCSI name %s"),
ini);
} else {
"Error creating initiator"));
}
}
}
} else if (!inip) {
gettext("Error, initiator %s not found"),
ini);
}
if (ret != 0) {
gettext("Error setting initiator properties: %d"),
ret);
if (errlist) {
char *nn;
char *nv;
!= NULL) {
}
}
}
}
}
}
return (ret);
}
static int
{
int ret;
char *isecret;
char *iuser;
char *sec = "solaris.smf.read.stmf";
if (ret != 0) {
gettext("Error retrieving iSCSI target configuration"));
return (ret);
}
isecret = "unset";
iuser = "<none>";
if (found) {
break;
}
if (ini) {
continue;
} else {
}
}
if (ptr->ini_properties) {
isecret = "set";
}
"chapuser", &iuser);
}
/* there's nothing to print for verbose yet */
"CHAPUSER", "SECRET");
}
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("\t");
}
(void) printf("\t");
}
} else {
}
(void) printf("\n");
}
ret = 1;
}
return (ret);
}
int
delete_initiator(char *ini)
{
int ret;
char *sec = "solaris.smf.modify.stmf";
if (!ini) {
gettext("Error, no initiator specified"));
return (EINVAL);
}
if (ret != 0) {
gettext("Error retrieving iSCSI target configuration"));
return (ret);
}
while (ptr) {
break;
}
}
if (ptr) {
} else {
ret = 1;
}
return (ret);
}
static int
{
int ret;
char *sec = "solaris.smf.modify.stmf";
if (proplist) {
/* make sure at least one property is specified */
}
/* empty list */
gettext("Error, no properties specified"));
return (EINVAL);
}
if (ret != 0) {
gettext("Error retrieving iSCSI target configuration"));
return (ret);
}
if (ret != 0) {
gettext("Error setting global properties: %d"),
ret);
if (errlist) {
char *nn;
char *nv;
!= NULL) {
}
}
}
}
if (ret == 0) {
}
return (ret);
}
static int
{
int ret;
char *alias = "<none>";
char *auth = "<none>";
char *isns = "disabled";
char *rsvr = "<none>";
char *rsecret = "unset";
int i;
char *sec = "solaris.smf.read.stmf";
if (ret != 0) {
gettext("Error retrieving iSCSI target configuration"));
return (ret);
}
/* look up all possible options */
isns = "enabled";
}
&scount);
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",
} 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",
"isnsserver:");
}
for (i = 0; i < scount; i++) {
break;
}
if (i > 0) {
(void) printf(",");
}
}
if (i == 0) {
}
(void) printf("\n");
return (0);
}
static int
char *phrase)
{
int ret = 0;
char *pass;
char buf[1024];
int fd;
return (EINVAL);
}
if (passfile) {
gettext("Invalid secret file %s"),
passfile);
return (EBADF);
}
if (fd == -1) {
gettext("Could not open secret file %s: "),
passfile);
return (ret);
}
gettext("Could not read secret file %s: "),
passfile);
return (ret);
}
/* ensure buf is properly terminated */
/* if last char is a newline, strip it off */
}
/* validate length */
"Secret must be between 12 and 255 characters"));
return (EINVAL);
}
} else {
/* prompt for secret */
if (!phrase) {
return (EINVAL);
}
if (!pass) {
gettext("Could not read secret"));
return (ret);
}
/* validate length */
"Secret must be between 12 and 255 characters"));
return (EINVAL);
}
/* confirm entered secret */
if (!pass) {
gettext("Could not read secret"));
return (ret);
}
gettext("Secret validation failed"));
return (ret);
}
}
return (ret);
}
static int
{
int count;
char *bufp;
char **arr;
return (EINVAL);
}
count = 1;
for (;;) {
if (!bufp) {
break;
}
bufp++;
count++;
}
if (!arr) {
return (ENOMEM);
}
/* set delimiter to comma */
/* split up that buf! */
/* if requested, return the number of array members found */
if (num) {
}
}
static void
{
return;
}
*tagnum = 0;
/* Must be entirely numeric and in-range */
return;
}
}
}
/*
* 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
{
if (msg) {
}
if (error & STMF_STATUS_ERROR) {
switch (error) {
case STMF_ERROR_PERM:
gettext("permission denied"));
break;
case STMF_ERROR_BUSY:
gettext("resource busy"));
break;
case STMF_ERROR_NOMEM:
gettext("out of memory"));
break;
gettext("STMF service not found"));
break;
gettext("STMF service version incorrect"));
break;
gettext("Configuration changed during processing. "
"Check the configuration, then retry this "
"command if appropriate."));
break;
default:
break;
}
} else {
}
}